Secure ASP.NET Core Blazor Server apps
This article explains how to secure Blazor Server apps as ASP.NET Core applications.
Blazor Server apps are configured for security in the same manner as ASP.NET Core apps. For more information, see the articles under ASP.NET Core security topics. Topics under this overview apply specifically to Blazor Server.
In Blazor Server apps, 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. Blazor Server 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 ASP.NET Core Blazor Server additional security scenarios.
Blazor Server 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 Server 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.
Note
Implementing a custom NavigationManager
to achieve authentication validation during navigation isn't recommended for Blazor Server apps. If the app must execute custom authentication state logic during navigation, use a custom AuthenticationStateProvider
.
Blazor Server project template
The Blazor Server project template can be configured for authentication when the project is created.
Follow the Visual Studio guidance in Tooling for ASP.NET Core Blazor to create a new Blazor Server project with an authentication mechanism.
After choosing the Blazor Server App template and configuring the project, select the app's authentication under Authentication type:
- Individual Accounts: User accounts are stored within the app using ASP.NET Core Identity.
- Microsoft identity platform: For more information, see ASP.NET Core Blazor authentication and authorization.
- Windows: Use Windows Authentication.
Scaffold Identity
For more information on scaffolding Identity into a Blazor Server project, see Scaffold Identity in ASP.NET Core projects.
Scaffold Identity into a Blazor Server project:
Additional claims and tokens from external providers
To store additional claims from external providers, see Persist additional claims and tokens from external providers in ASP.NET Core.
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 Introduction to authentication for Single Page Apps on ASP.NET Core.
Implement a custom AuthenticationStateProvider
If the app requires a custom provider, implement AuthenticationStateProvider and override GetAuthenticationStateAsync.
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));
}
}
The CustomAuthenticationStateProvider
service is registered in Program.cs
after the call to AddServerSideBlazor:
using Microsoft.AspNetCore.Components.Authorization;
...
builder.Services.AddServerSideBlazor();
...
builder.Services.AddScoped<AuthenticationStateProvider,
CustomAuthenticationStateProvider>();
The CustomAuthenticationStateProvider
service is registered in Startup.ConfigureServices
of Startup.cs
after the call to AddServerSideBlazor:
using Microsoft.AspNetCore.Components.Authorization;
...
services.AddServerSideBlazor();
...
services.AddScoped<AuthenticationStateProvider,
CustomAuthenticationStateProvider>();
Confirm or add an AuthorizeRouteView and CascadingAuthenticationState to the App
component.
In App.razor
:
<CascadingAuthenticationState>
<Router ...>
<Found ...>
<AuthorizeRouteView RouteData="@routeData"
DefaultLayout="@typeof(MainLayout)" />
...
</Found>
<NotFound>
...
</NotFound>
</Router>
</CascadingAuthenticationState>
Note
When you create a Blazor app from one of the Blazor project templates with authentication enabled, the App
component includes the AuthorizeRouteView and CascadingAuthenticationState components shown in the preceding example. 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.
An 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 AuthorizeView, see ASP.NET Core Blazor authentication and authorization.
Notification about authentication state changes
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.
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 emailAddress)
{
var identity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, emailAddress),
}, "Custom Authentication");
var user = new ClaimsPrincipal(identity);
NotifyAuthenticationStateChanged(
Task.FromResult(new AuthenticationState(user)));
}
}
In a component:
- Inject AuthenticationStateProvider.
- Add a field to hold the user's identifier.
- Add a button and a method to cast the AuthenticationStateProvider to
CustomAuthenticationStateProvider
and callAuthenticateUser
with the user's identifier.
@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);
}
}
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 AuthenticationStateProvider can subscribe to, where the event invokes NotifyAuthenticationStateChanged. 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);
}
}
}
}
In Program.cs
, register the AuthenticationService
in the dependency injection container:
builder.Services.AddSingleton<AuthenticationService>();
In Startup.ConfigureServices
of Startup.cs
, register the AuthenticationService
in the dependency injection container:
services.AddSingleton<AuthenticationService>();
The following CustomAuthenticationStateProvider
subscribes to the AuthenticationService.UserChanged
event. GetAuthenticationStateAsync
returns the authentication state of the service's current user (AuthenticationService.CurrentUser
).
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) =>
{
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
:
@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()
{
var currentUser = AuthenticationService.CurrentUser;
var identity = new ClaimsIdentity(
new[]
{
new Claim(ClaimTypes.Name, userIdentifier),
},
"Custom Authentication");
var newUser = new ClaimsPrincipal(identity);
AuthenticationService.CurrentUser = newUser;
}
}
Inject AuthenticationStateProvider
for services scoped to a component
Don't attempt to resolve AuthenticationStateProvider within a custom scope because it results in the creation of a new instance of the AuthenticationStateProvider that isn't correctly initialized.
To access the AuthenticationStateProvider within a service scoped to a component, inject the 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 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 Blazor Server app, scoped services have a lifetime equal to the duration of the client connection circuit.
In Program.cs
:
builder.Services.AddScoped<ExampleService>();
In Startup.ConfigureServices
of Startup.cs
:
services.AddScoped<ExampleService>();
In the following InjectAuthStateProvider
component:
- The component inherits OwningComponentBase.
- The AuthenticationStateProvider is injected and passed to
ExampleService.ExampleMethod
. ExampleService
is resolved with OwningComponentBase.ScopedServices and GetRequiredService, which returns the correct, initialized instance ofExampleService
that exists for the lifetime of the user's circuit.
Pages/InjectAuthStateProvider.razor
:
@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);
}
}
@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);
}
}
For more information, see the guidance on OwningComponentBase in ASP.NET Core Blazor dependency injection.
Unauthorized content display during prerendering
To avoid showing unauthorized content during prerendering, implement IHostEnvironmentAuthenticationStateProvider to support prerendering, disable prerendering, maintain the current behavior, or authenticate the user on the server before the app starts.
User state management
In spite of the word "state" in the name, AuthenticationStateProvider isn't for storing general user state. 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.
In Blazor Server apps, 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 ASP.NET Core Blazor state management.
Additional security abstractions
Two additional abstractions participate in managing authentication state:
ServerAuthenticationStateProvider (reference source): An AuthenticationStateProvider used by the Blazor framework to obtain authentication state from the server.
RevalidatingServerAuthenticationStateProvider (reference source): A base class for AuthenticationStateProvider services used by the Blazor framework to receive an authentication state from the host environment and revalidate it at regular intervals.
For a Blazor Server app created from the Blazor Server project template with authentication enabled, 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);
Note
Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next release of .NET. To select a tag for a specific release, use the Switch branches or tags dropdown list. For more information, see How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205).
Additional resources
- Quickstart: Add sign-in with Microsoft to an ASP.NET Core web app
- Quickstart: Protect an ASP.NET Core web API with Microsoft identity platform
- Configure ASP.NET Core to work with proxy servers and load balancers: Includes guidance on:
- Using Forwarded Headers Middleware to preserve HTTPS scheme information across proxy servers and internal networks.
- Additional scenarios and use cases, including manual scheme configuration, request path changes for correct request routing, and forwarding the request scheme for Linux and non-IIS reverse proxies.
Feedback
Submit and view feedback for