Bewerken

Delen via


ASP.NET Core Blazor authentication state

Note

This isn't the latest version of this article. For the current release, see the .NET 8 version of this article.

Warning

This version of ASP.NET Core is no longer supported. For more information, see .NET and .NET Core Support Policy. For the current release, see the .NET 8 version of this article.

Important

This information relates to a pre-release product that may be substantially modified before it's commercially released. Microsoft makes no warranties, express or implied, with respect to the information provided here.

For the current release, see the .NET 8 version of this article.

This article explains how to create a custom authentication state provider and receive user authentication state change notifications in code.

The general approaches taken for server-side and client-side Blazor apps are similar but differ in their exact implementations, so this article pivots between server-side Blazor apps and client-side Blazor apps. Use the pivot selector at the top of the article to change the article's pivot to match the type of Blazor project that you're working with:

  • Server-side Blazor apps (Server pivot): Blazor Server for .NET 7 or earlier and the server project of a Blazor Web App for .NET 8 or later.
  • Client-side Blazor apps (Blazor WebAssembly pivot): Blazor WebAssembly for all versions of .NET or the .Client project of a Blazor Web App for .NET 8 or later.

Abstract AuthenticationStateProvider class

The Blazor framework includes an abstract AuthenticationStateProvider class to provide information about the authentication state of the current user with the following members:

Implement a custom AuthenticationStateProvider

The app must reference the Microsoft.AspNetCore.Components.Authorization NuGet package, which provides authentication and authorization support for Blazor apps.

Note

For guidance on adding packages to .NET apps, see the articles under Install and manage packages at Package consumption workflow (NuGet documentation). Confirm correct package versions at NuGet.org.

Configure the following authentication, authorization, and cascading authentication state services in the Program file.

When you create a Blazor app from one of the Blazor project templates with authentication enabled, the app is preconfigured with the following service registrations, which includes exposing the authentication state as a cascading parameter. For more information, see ASP.NET Core Blazor authentication and authorization with additional information presented in the article's Customize unauthorized content with the Router component section.

using Microsoft.AspNetCore.Components.Authorization;

...

builder.Services.AddAuthorization();
builder.Services.AddCascadingAuthenticationState();

Configure authentication and authorization services in the Program file.

When you create a Blazor app from one of the Blazor project templates with authentication enabled, the app includes the following service registration.

using Microsoft.AspNetCore.Components.Authorization;

...

builder.Services.AddAuthorization();

Configure authentication and authorization services in Startup.ConfigureServices of Startup.cs.

When you create a Blazor app from one of the Blazor project templates with authentication enabled, the app includes the following service registration.

using Microsoft.AspNetCore.Components.Authorization;

...

services.AddAuthorization();

In Blazor WebAssembly apps (all .NET versions) or the .Client project of a Blazor Web App (.NET 8 or later), configure authentication, authorization, and cascading authentication state services in the Program file.

When you create a Blazor app from one of the Blazor project templates with authentication enabled, the app is preconfigured with the following service registrations, which includes exposing the authentication state as a cascading parameter. For more information, see ASP.NET Core Blazor authentication and authorization with additional information presented in the article's Customize unauthorized content with the Router component section.

using Microsoft.AspNetCore.Components.Authorization;

...

builder.Services.AddAuthorizationCore();
builder.Services.AddCascadingAuthenticationState();

Configure authentication and authorization services in the Program file.

When you create a Blazor app from one of the Blazor project templates with authentication enabled, the app includes the following service registration.

using Microsoft.AspNetCore.Components.Authorization;

...

builder.Services.AddAuthorizationCore();

Subclass AuthenticationStateProvider and override GetAuthenticationStateAsync to create the user's authentication state. In the following example, all users are authenticated with the username mrfibuli.

CustomAuthStateProvider.cs:

using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;

public class CustomAuthStateProvider : AuthenticationStateProvider
{
    public override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var identity = new ClaimsIdentity(
        [
            new Claim(ClaimTypes.Name, "mrfibuli"),
        ], "Custom Authentication");

        var user = new ClaimsPrincipal(identity);

        return Task.FromResult(new AuthenticationState(user));
    }
}

Note

The preceding code that creates a new ClaimsIdentity uses simplified collection initialization introduced with C# 12 (.NET 8). For more information, see Collection expressions - C# language reference.

The CustomAuthStateProvider service is registered in the Program file. Register the service scoped with AddScoped:

builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();

In a Blazor Server app, register the service scoped with AddScoped after the call to AddServerSideBlazor:

builder.Services.AddServerSideBlazor();

builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();

In a Blazor Server app, register the service scoped with AddScoped after the call to AddServerSideBlazor:

services.AddServerSideBlazor();

services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();

The CustomAuthStateProvider service is registered in the Program file. Register the service singleton with AddSingleton:

builder.Services.AddSingleton<AuthenticationStateProvider, CustomAuthStateProvider>();

If it isn't present, add an @using statement to the _Imports.razor file to make the Microsoft.AspNetCore.Components.Authorization namespace available across components:

@using Microsoft.AspNetCore.Components.Authorization;

Confirm or change the route view component to an AuthorizeRouteView in the Router component definition. The location of the Router component differs depending on the type of app. Use search to locate the component if you're unaware of its location in the project.

<Router ...>
    <Found ...>
        <AuthorizeRouteView RouteData="routeData" 
            DefaultLayout="typeof(Layout.MainLayout)" />
        ...
    </Found>
</Router>

Note

When you create a Blazor app from one of the Blazor project templates with authentication enabled, the app includes the AuthorizeRouteView component. For more information, see ASP.NET Core Blazor authentication and authorization with additional information presented in the article's Customize unauthorized content with the Router component section.

Where the Router component is located:

The location of the Router component differs depending on the type of app. Use search to locate the component if you're unaware of its location in the project.

<CascadingAuthenticationState>
    <Router ...>
        <Found ...>
            <AuthorizeRouteView RouteData="routeData" 
                DefaultLayout="typeof(MainLayout)" />
            ...
        </Found>
    </Router>
</CascadingAuthenticationState>

Note

When you create a Blazor app from one of the Blazor project templates with authentication enabled, the app includes the AuthorizeRouteView and CascadingAuthenticationState components. For more information, see ASP.NET Core Blazor authentication and authorization with additional information presented in the article's Customize unauthorized content with the Router component section.

The following example AuthorizeView component demonstrates the authenticated user's name:

<AuthorizeView>
    <Authorized>
        <p>Hello, @context.User.Identity?.Name!</p>
    </Authorized>
    <NotAuthorized>
        <p>You're not authorized.</p>
    </NotAuthorized>
</AuthorizeView>

For guidance on the use of AuthorizeView, see ASP.NET Core Blazor authentication and authorization.

Authentication state change notifications

A custom AuthenticationStateProvider can invoke NotifyAuthenticationStateChanged on the AuthenticationStateProvider base class to notify consumers of the authentication state change to rerender.

The following example is based on implementing a custom AuthenticationStateProvider by following the guidance in the Implement a custom AuthenticationStateProvider section earlier in this article. If you already followed the guidance in that section, the following CustomAuthStateProvider replaces the one shown in the section.

The following CustomAuthStateProvider implementation exposes a custom method, AuthenticateUser, to sign in a user and notify consumers of the authentication state change.

CustomAuthStateProvider.cs:

using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;

public class CustomAuthStateProvider : AuthenticationStateProvider
{
    public override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var identity = new ClaimsIdentity();
        var user = new ClaimsPrincipal(identity);

        return Task.FromResult(new AuthenticationState(user));
    }

    public void AuthenticateUser(string userIdentifier)
    {
        var identity = new ClaimsIdentity(
        [
            new Claim(ClaimTypes.Name, userIdentifier),
        ], "Custom Authentication");

        var user = new ClaimsPrincipal(identity);

        NotifyAuthenticationStateChanged(
            Task.FromResult(new AuthenticationState(user)));
    }
}

Note

The preceding code that creates a new ClaimsIdentity uses simplified collection initialization introduced with C# 12 (.NET 8). For more information, see Collection expressions - C# language reference.

In a component:

@inject AuthenticationStateProvider AuthenticationStateProvider

<input @bind="userIdentifier" />
<button @onclick="SignIn">Sign in</button>

<AuthorizeView>
    <Authorized>
        <p>Hello, @context.User.Identity?.Name!</p>
    </Authorized>
    <NotAuthorized>
        <p>You're not authorized.</p>
    </NotAuthorized>
</AuthorizeView>

@code {
    public string userIdentifier = string.Empty;

    private void SignIn()
    {
        ((CustomAuthStateProvider)AuthenticationStateProvider)
            .AuthenticateUser(userIdentifier);
    }
}

The preceding approach can be enhanced to trigger notifications of authentication state changes via a custom service. The following CustomAuthenticationService class maintains the current user's claims principal in a backing field (currentUser) with an event (UserChanged) that the authentication state provider can subscribe to, where the event invokes NotifyAuthenticationStateChanged. With the additional configuration later in this section, the CustomAuthenticationService can be injected into a component with logic that sets the CurrentUser to trigger the UserChanged event.

CustomAuthenticationService.cs:

using System.Security.Claims;

public class CustomAuthenticationService
{
    public event Action<ClaimsPrincipal>? UserChanged;
    private ClaimsPrincipal? currentUser;

    public ClaimsPrincipal CurrentUser
    {
        get { return currentUser ?? new(); }
        set
        {
            currentUser = value;

            if (UserChanged is not null)
            {
                UserChanged(currentUser);
            }
        }
    }
}

In the Program file, register the CustomAuthenticationService in the dependency injection container:

builder.Services.AddScoped<CustomAuthenticationService>();

In Startup.ConfigureServices of Startup.cs, register the CustomAuthenticationService in the dependency injection container:

services.AddScoped<CustomAuthenticationService>();

In the Program file, register the CustomAuthenticationService in the dependency injection container:

builder.Services.AddSingleton<CustomAuthenticationService>();

The following CustomAuthStateProvider subscribes to the CustomAuthenticationService.UserChanged event. The GetAuthenticationStateAsync method returns the user's authentication state. Initially, the authentication state is based on the value of the CustomAuthenticationService.CurrentUser. When the user changes, a new authentication state is created for the new user (new AuthenticationState(newUser)) for calls to GetAuthenticationStateAsync:

using Microsoft.AspNetCore.Components.Authorization;

public class CustomAuthStateProvider : AuthenticationStateProvider
{
    private AuthenticationState authenticationState;

    public CustomAuthStateProvider(CustomAuthenticationService service)
    {
        authenticationState = new AuthenticationState(service.CurrentUser);

        service.UserChanged += (newUser) =>
        {
            authenticationState = new AuthenticationState(newUser);
            NotifyAuthenticationStateChanged(Task.FromResult(authenticationState));
        };
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(authenticationState);
}

The following component's SignIn method creates a claims principal for the user's identifier to set on CustomAuthenticationService.CurrentUser:

@using System.Security.Claims
@inject CustomAuthenticationService AuthService

<input @bind="userIdentifier" />
<button @onclick="SignIn">Sign in</button>

<AuthorizeView>
    <Authorized>
        <p>Hello, @context.User.Identity?.Name!</p>
    </Authorized>
    <NotAuthorized>
        <p>You're not authorized.</p>
    </NotAuthorized>
</AuthorizeView>

@code {
    public string userIdentifier = string.Empty;

    private void SignIn()
    {
        var currentUser = AuthService.CurrentUser;

        var identity = new ClaimsIdentity(
            [
                new Claim(ClaimTypes.Name, userIdentifier),
            ],
            "Custom Authentication");

        var newUser = new ClaimsPrincipal(identity);

        AuthService.CurrentUser = newUser;
    }
}

Note

The preceding code that creates a new ClaimsIdentity uses simplified collection initialization introduced with C# 12 (.NET 8). For more information, see Collection expressions - C# language reference.

Additional resources