Skip to content

Latest commit

 

History

History
816 lines (555 loc) · 34 KB

File metadata and controls

816 lines (555 loc) · 34 KB
title author description monikerRange ms.author ms.custom ms.date uid
Secure ASP.NET Core server-side Blazor apps
guardrex
Learn how to secure server-side Blazor apps as ASP.NET Core applications.
>= aspnetcore-3.1
riande
mvc
11/14/2023
blazor/security/server/index

Secure ASP.NET Core server-side Blazor apps

[!INCLUDE]

This article explains how to secure server-side Blazor apps as ASP.NET Core applications.

[!INCLUDE]

Server-side Blazor apps are configured for security in the same manner as ASP.NET Core apps. For more information, see the articles under xref:security/index.

The authentication context is only established when the app starts, which is when the app first connects to the WebSocket. The authentication context is maintained for the lifetime of the circuit. Apps periodically revalidate the user's authentication state, currently every 30 minutes by default.

If the app must capture users for custom services or react to updates to the user, see xref:blazor/security/server/additional-scenarios#circuit-handler-to-capture-users-for-custom-services.

Blazor differs from a traditional server-rendered web apps that make new HTTP requests with cookies on every page navigation. Authentication is checked during navigation events. However, cookies aren't involved. Cookies are only sent when making an HTTP request to a server, which isn't what happens when the user navigates in a Blazor app. During navigation, the user's authentication state is checked within the Blazor circuit, which you can update at any time on the server using the RevalidatingAuthenticationStateProvider abstraction.

Important

Implementing a custom NavigationManager to achieve authentication validation during navigation isn't recommended. If the app must execute custom authentication state logic during navigation, use a custom AuthenticationStateProvider.

Note

The code examples in this article adopt nullable reference types (NRTs) and .NET compiler null-state static analysis, which are supported in ASP.NET Core 6.0 or later. When targeting ASP.NET Core 5.0 or earlier, remove the null type designation (?) from the examples in this article.

Project template

Create a new server-side Blazor app by following the guidance in xref:blazor/tooling.

After choosing the server-side app template and configuring the project, select the app's authentication under Authentication type:

:::moniker range=">= aspnetcore-8.0"

  • None (default): No authentication.
  • Individual Accounts: User accounts are stored within the app using ASP.NET Core Identity.

:::moniker-end

:::moniker range="< aspnetcore-8.0"

  • None (default): No authentication.
  • Individual Accounts: User accounts are stored within the app using ASP.NET Core Identity.
  • Microsoft identity platform: For more information, see xref:blazor/security/index#additional-resources.
  • Windows: Use Windows Authentication.

:::moniker-end

When issuing the .NET CLI command to create and configure the server-side Blazor app, indicate the authentication mechanism with the -au|--auth option:

-au {AUTHENTICATION}

Note

For the full command, see xref:blazor/tooling.

Permissible authentication values for the {AUTHENTICATION} placeholder are shown in the following table.

:::moniker range=">= aspnetcore-8.0"

Authentication mechanism Description
None (default) No authentication
Individual Users stored in the app with ASP.NET Core Identity

:::moniker-end

:::moniker range="< aspnetcore-8.0"

Authentication mechanism Description
None (default) No authentication
Individual Users stored in the app with ASP.NET Core Identity
IndividualB2C Users stored in Azure AD B2C
SingleOrg Organizational authentication for a single tenant
MultiOrg Organizational authentication for multiple tenants
Windows Windows Authentication

:::moniker-end

For more information, see the dotnet new command in the .NET Core Guide.

When issuing the .NET CLI command to create and configure the server-side Blazor app, indicate the authentication mechanism with the -au|--auth option:

-au {AUTHENTICATION}

Note

For the full command, see xref:blazor/tooling.

Permissible authentication values for the {AUTHENTICATION} placeholder are shown in the following table.

:::moniker range=">= aspnetcore-8.0"

Authentication mechanism Description
None (default) No authentication
Individual Users stored in the app with ASP.NET Core Identity

:::moniker-end

:::moniker range="< aspnetcore-8.0"

Authentication mechanism Description
None (default) No authentication
Individual Users stored in the app with ASP.NET Core Identity
IndividualB2C Users stored in Azure AD B2C
SingleOrg Organizational authentication for a single tenant
MultiOrg Organizational authentication for multiple tenants
Windows Windows Authentication

:::moniker-end

For more information:

  • See the dotnet new command in the .NET Core Guide.

  • Execute the help command for the template in a command shell:

    dotnet new {PROJECT TEMPLATE} --help
    

    In the preceding command, the {PROJECT TEMPLATE} placeholder is the project template.


:::moniker range=">= aspnetcore-8.0"

Blazor Identity UI (Individual Accounts)

Blazor supports generating a full Blazor-based Identity UI when you choose the authentication option for Individual Accounts.

The Blazor Web App template scaffolds Identity code for a SQL Server database. The command line version uses SQLite by default and includes a SQLite database for Identity.

The template handles the following:

  • Adds Identity Razor components and related logic for routine authentication tasks, such as signing users in and out.
    • The Identity components also support advanced Identity features, such as account confirmation and password recovery and multifactor authentication using a third-party app.
    • Interactive server-side rendering (interactive SSR) and client-side rendering (CSR) scenarios are supported.
  • Adds the Identity-related packages and dependencies.
  • References the Identity packages in _Imports.razor.
  • Creates a custom user Identity class (ApplicationUser).
  • Creates and registers an EF Core database context (ApplicationDbContext).
  • Configures routing for the built-in Identity endpoints.
  • Includes Identity validation and business logic.

When you choose the Interactive WebAssembly or Interactive Auto render modes, the server handles all authentication and authorization requests, and the Identity components remain on the server in the Blazor Web App's main project. The project template includes a PersistentAuthenticationStateProvider class in the .Client project to synchronize the user's authentication state between the server and the browser. The class is a custom implementation of xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider. The provider uses the xref:Microsoft.AspNetCore.Components.PersistentComponentState class to prerender the authentication state and persist it to the page.

In the main project of a Blazor Web App, the authentication state provider is named either IdentityRevalidatingAuthenticationStateProvider (Server interactivity solutions only) or PersistingRevalidatingAuthenticationStateProvider (WebAssembly or Auto interactivity solutions).

For more information on persisting prerendered state, see xref:blazor/components/prerender#persist-prerendered-state.

For more information on the Blazor Identity UI and guidance on integrating external logins through social websites, see What's new with identity in .NET 8.

:::moniker-end

:::moniker range="< aspnetcore-8.0"

Scaffold Identity

:::moniker-end

:::moniker range=">= aspnetcore-6.0 < aspnetcore-8.0"

For more information on scaffolding Identity into a server-side Blazor app, see xref:security/authentication/scaffold-identity#scaffold-identity-into-a-server-side-blazor-app.

:::moniker-end

:::moniker range="< aspnetcore-6.0"

Scaffold Identity into a server-side Blazor app:

  • Without existing authorization.
  • With authorization.

:::moniker-end

Additional claims and tokens from external providers

To store additional claims from external providers, see xref:security/authentication/social/additional-claims.

Azure App Service on Linux with Identity Server

Specify the issuer explicitly when deploying to Azure App Service on Linux with Identity Server. For more information, see xref:security/authentication/identity/spa#azure-app-service-on-linux.

Implement a custom AuthenticationStateProvider

If the app requires a custom provider, implement xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider and override xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider.GetAuthenticationStateAsync%2A.

In the following example, all users are authenticated with the username mrfibuli.

CustomAuthenticationStateProvider.cs:

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

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

        var user = new ClaimsPrincipal(identity);

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

:::moniker range=">= aspnetcore-8.0"

The CustomAuthenticationStateProvider service is registered in the Program file:

using Microsoft.AspNetCore.Components.Authorization;

...

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

:::moniker-end

:::moniker range=">= aspnetcore-6.0 < aspnetcore-8.0"

The CustomAuthenticationStateProvider service is registered in the Program file after the call to xref:Microsoft.Extensions.DependencyInjection.ComponentServiceCollectionExtensions.AddServerSideBlazor%2A:

using Microsoft.AspNetCore.Components.Authorization;

...

builder.Services.AddServerSideBlazor();

...

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

:::moniker-end

:::moniker range="< aspnetcore-6.0"

The CustomAuthenticationStateProvider service is registered in Startup.ConfigureServices of Startup.cs after the call to xref:Microsoft.Extensions.DependencyInjection.ComponentServiceCollectionExtensions.AddServerSideBlazor%2A:

using Microsoft.AspNetCore.Components.Authorization;

...

services.AddServerSideBlazor();

...

services.AddScoped<AuthenticationStateProvider, 
    CustomAuthenticationStateProvider>();

:::moniker-end

:::moniker range=">= aspnetcore-8.0"

Confirm or add an xref:Microsoft.AspNetCore.Components.Authorization.AuthorizeRouteView to the xref:Microsoft.AspNetCore.Components.Routing.Router component.

In the Routes component (Components/Routes.razor):

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

Add cascading authentication state services to the service collection in the Program file:

builder.Services.AddCascadingAuthenticationState();

Note

When you create a Blazor app from one of the Blazor project templates with authentication enabled, the app includes the xref:Microsoft.AspNetCore.Components.Authorization.AuthorizeRouteView and call to xref:Microsoft.Extensions.DependencyInjection.CascadingAuthenticationStateServiceCollectionExtensions.AddCascadingAuthenticationState%2A. For more information, see xref:blazor/security/index#expose-the-authentication-state-as-a-cascading-parameter with additional information presented in the article's Customize unauthorized content with the Router component section.

:::moniker-end

:::moniker range="< aspnetcore-8.0"

Confirm or add an xref:Microsoft.AspNetCore.Components.Authorization.AuthorizeRouteView and xref:Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState to the xref:Microsoft.AspNetCore.Components.Routing.Router component:

<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 xref:Microsoft.AspNetCore.Components.Authorization.AuthorizeRouteView and xref:Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState components shown in the preceding example. For more information, see xref:blazor/security/index#expose-the-authentication-state-as-a-cascading-parameter with additional information presented in the article's Customize unauthorized content with the Router component section.

:::moniker-end

An xref:Microsoft.AspNetCore.Components.Authorization.AuthorizeView demonstrates the authenticated user's name in any component:

<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 xref:Microsoft.AspNetCore.Components.Authorization.AuthorizeView, see xref:blazor/security/index#authorizeview-component.

Notification about authentication state changes

A custom AuthenticationStateProvider can invoke xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider.NotifyAuthenticationStateChanged%2A on the xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider base class to notify consumers of the authentication state change to rerender.

The following example is based on implementing a custom xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider by following the guidance in the Implement a custom AuthenticationStateProvider section.

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

CustomAuthenticationStateProvider.cs:

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

public class CustomAuthenticationStateProvider : 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[]
        {
            new Claim(ClaimTypes.Name, userIdentifier),
        }, "Custom Authentication");

        var user = new ClaimsPrincipal(identity);

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

In a component:

  • Inject xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider.
  • Add a field to hold the user's identifier.
  • Add a button and a method to cast the xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider to CustomAuthenticationStateProvider and call AuthenticateUser with the user's identifier.

:::moniker range=">= aspnetcore-8.0"

@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()
    {
        ((CustomAuthenticationStateProvider)AuthenticationStateProvider)
            .AuthenticateUser(userIdentifier);
    }
}

:::moniker-end

:::moniker range="< aspnetcore-8.0"

@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()
    {
        ((CustomAuthenticationStateProvider)AuthenticationStateProvider)
            .AuthenticateUser(userIdentifier);
    }
}

:::moniker-end

The preceding approach can be enhanced to trigger notifications of authentication state changes via a custom service. The following AuthenticationService maintains the current user's claims principal in a backing field (currentUser) with an event (UserChanged) that the xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider can subscribe to, where the event invokes xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider.NotifyAuthenticationStateChanged%2A. With the additional configuration later in this section, the AuthenticationService can be injected into a component with logic that sets the CurrentUser to trigger the UserChanged event.

using System.Security.Claims;

public class AuthenticationService
{
    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);
            }
        }
    }
}

:::moniker range=">= aspnetcore-6.0"

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

builder.Services.AddScoped<AuthenticationService>();

:::moniker-end

:::moniker range="< aspnetcore-6.0"

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

services.AddScoped<AuthenticationService>();

:::moniker-end

The following CustomAuthenticationStateProvider subscribes to the AuthenticationService.UserChanged event. GetAuthenticationStateAsync returns the user's authentication state. Initially, the authentication state is based on the value of the AuthenticationService.CurrentUser. When there's a change in user, a new authentication state is created with the new user (new AuthenticationState(newUser)) for calls to GetAuthenticationStateAsync:

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

public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
    private AuthenticationState authenticationState;

    public CustomAuthenticationStateProvider(AuthenticationService service)
    {
        authenticationState = new AuthenticationState(service.CurrentUser);

        service.UserChanged += (newUser) =>
        {
            authenticationState = new AuthenticationState(newUser);

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

    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 AuthenticationService.CurrentUser:

:::moniker range=">= aspnetcore-8.0"

@inject AuthenticationService AuthenticationService

<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 = AuthenticationService.CurrentUser;

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

        var newUser = new ClaimsPrincipal(identity);

        AuthenticationService.CurrentUser = newUser;
    }
}

:::moniker-end

:::moniker range="< aspnetcore-8.0"

@inject AuthenticationService AuthenticationService

<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 = AuthenticationService.CurrentUser;

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

        var newUser = new ClaimsPrincipal(identity);

        AuthenticationService.CurrentUser = newUser;
    }
}

:::moniker-end

Inject AuthenticationStateProvider for services scoped to a component

Don't attempt to resolve xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider within a custom scope because it results in the creation of a new instance of the xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider that isn't correctly initialized.

To access the xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider within a service scoped to a component, inject the xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider with the @inject directive or the [Inject] attribute and pass it to the service as a parameter. This approach ensures that the correct, initialized instance of the xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider is used for each user app instance.

ExampleService.cs:

public class ExampleService
{
    public async Task<string> ExampleMethod(AuthenticationStateProvider authStateProvider)
    {
        var authState = await authStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;

        if (user.Identity is not null && user.Identity.IsAuthenticated)
        {
            return $"{user.Identity.Name} is authenticated.";
        }
        else
        {
            return "The user is NOT authenticated.";
        }
    }
}

Register the service as scoped. In a server-side Blazor app, scoped services have a lifetime equal to the duration of the client connection circuit.

:::moniker range=">= aspnetcore-6.0"

In the Program file:

builder.Services.AddScoped<ExampleService>();

:::moniker-end

:::moniker range="< aspnetcore-6.0"

In Startup.ConfigureServices of Startup.cs:

services.AddScoped<ExampleService>();

:::moniker-end

In the following InjectAuthStateProvider component:

  • The component inherits xref:Microsoft.AspNetCore.Components.OwningComponentBase.
  • The xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider is injected and passed to ExampleService.ExampleMethod.
  • ExampleService is resolved with xref:Microsoft.AspNetCore.Components.OwningComponentBase.ScopedServices?displayProperty=nameWithType and xref:Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService%2A, which returns the correct, initialized instance of ExampleService that exists for the lifetime of the user's circuit.

InjectAuthStateProvider.razor:

:::moniker range=">= aspnetcore-8.0"

@page "/inject-auth-state-provider"
@inherits OwningComponentBase
@inject AuthenticationStateProvider AuthenticationStateProvider

<h1>Inject <code>AuthenticationStateProvider</code> Example</h1>

<p>@message</p>

@code {
    private string? message;
    private ExampleService? ExampleService { get; set; }

    protected override async Task OnInitializedAsync()
    {
        ExampleService = ScopedServices.GetRequiredService<ExampleService>();

        message = await ExampleService.ExampleMethod(AuthenticationStateProvider);
    }
}

:::moniker-end

:::moniker range="< aspnetcore-8.0"

@page "/inject-auth-state-provider"
@inject AuthenticationStateProvider AuthenticationStateProvider
@inherits OwningComponentBase

<h1>Inject <code>AuthenticationStateProvider</code> Example</h1>

<p>@message</p>

@code {
    private string? message;
    private ExampleService? ExampleService { get; set; }

    protected override async Task OnInitializedAsync()
    {
        ExampleService = ScopedServices.GetRequiredService<ExampleService>();

        message = await ExampleService.ExampleMethod(AuthenticationStateProvider);
    }
}

:::moniker-end

For more information, see the guidance on xref:Microsoft.AspNetCore.Components.OwningComponentBase in xref:blazor/fundamentals/dependency-injection#owningcomponentbase.

Unauthorized content display while prerendering with a custom AuthenticationStateProvider

To avoid showing unauthorized content while prerendering with a custom AuthenticationStateProvider, adopt one of the following approaches:

  • Implement xref:Microsoft.AspNetCore.Components.Authorization.IHostEnvironmentAuthenticationStateProvider for the custom xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider to support prerendering: For an example implementation of xref:Microsoft.AspNetCore.Components.Authorization.IHostEnvironmentAuthenticationStateProvider, see the Blazor framework's xref:Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider implementation in ServerAuthenticationStateProvider.cs (reference source).

    [!INCLUDE]

:::moniker range=">= aspnetcore-8.0"

  • Disable prerendering: Indicate the render mode with the prerender parameter set to false at the highest-level component in the app's component hierarchy that isn't a root component.

    [!NOTE] Making a root component interactive, such as the App component, isn't supported. Therefore, prerendering can't be disabled directly by the App component.

    For apps based on the Blazor Web App project template, prerendering is typically disabled where the Routes component is used in the App component (Components/App.razor) :

    <Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />

    Also, disable prerendering for the HeadOutlet component:

    <HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />

:::moniker-end

:::moniker range="< aspnetcore-8.0"

  • Disable prerendering: Open the _Host.cshtml file and change the render-mode attribute of the Component Tag Helper to xref:Microsoft.AspNetCore.Mvc.Rendering.RenderMode.Server:

    <component type="typeof(App)" render-mode="Server" />

:::moniker-end

  • Authenticate the user on the server before the app starts: To adopt this approach, the app must respond to a user's initial request with the Identity-based sign-in page or view and prevent any requests to Blazor endpoints until they're authenticated. For more information, see xref:security/authorization/secure-data#require-authenticated-users. After authentication, unauthorized content in prerendered Razor components is only shown when the user is truly unauthorized to view the content.

User state management

In spite of the word "state" in the name, xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider isn't for storing general user state. xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider only indicates the user's authentication state to the app, whether they are signed into the app and who they are signed in as.

Authentication uses the same ASP.NET Core Identity authentication as Razor Pages and MVC apps. The user state stored for ASP.NET Core Identity flows to Blazor without adding additional code to the app. Follow the guidance in the ASP.NET Core Identity articles and tutorials for the Identity features to take effect in the Blazor parts of the app.

For guidance on general state management outside of ASP.NET Core Identity, see xref:blazor/state-management?pivots=server.

Additional security abstractions

Two additional abstractions participate in managing authentication state:

  • xref:Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider (reference source): An xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider used by the Blazor framework to obtain authentication state from the server.

  • xref:Microsoft.AspNetCore.Components.Server.RevalidatingServerAuthenticationStateProvider (reference source): A base class for xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider services used by the Blazor framework to receive an authentication state from the host environment and revalidate it at regular intervals.

    The default 30 minute revalidation interval can be adjusted in RevalidatingIdentityAuthenticationStateProvider (Areas/Identity/RevalidatingIdentityAuthenticationStateProvider.cs). The following example shortens the interval to 20 minutes:

    protected override TimeSpan RevalidationInterval => TimeSpan.FromMinutes(20);

[!INCLUDE]

:::moniker range=">= aspnetcore-8.0"

Temporary redirection URL validity duration

This section applies to Blazor Web Apps.

Use the xref:Microsoft.AspNetCore.Components.Endpoints.RazorComponentsServiceOptions.TemporaryRedirectionUrlValidityDuration%2A?displayProperty=nameWithType option to get or set the lifetime of data protection validity for temporary redirection URLs emitted by Blazor server-side rendering. These are only used transiently, so the lifetime only needs to be long enough for a client to receive the URL and begin navigation to it. However, it should also be long enough to allow for clock skew across servers. The default value is five minutes.

In the following example the value is extended to seven minutes:

builder.Services.AddRazorComponents(options => 
    options.TemporaryRedirectionUrlValidityDuration = 
        TimeSpan.FromMinutes(7));

:::moniker-end

Additional resources