Blazor Security
|
This section documents ASP.NET Core on .NET 10 (LTS), the current release — the minimal hosting model, the middleware pipeline, dependency injection, Minimal APIs, MVC & Razor Pages, Blazor with the current render modes, SignalR and gRPC, EF Core, ASP.NET Core Identity and policy-based authorization, output caching, rate limiting, and Native-AOT-aware building — as described by the official documentation at Microsoft Learn, which is the reference these pages are written and verified against. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. .NET ships a major release every November and its APIs continue to evolve: the examples here target .NET 10 / C# 14. This section’s bibliography lists the reference material consulted while preparing these pages. |
Blazor authorization builds on the same authentication and authorization primitives used everywhere else in ASP.NET Core, with a component-model layer on top.
AuthenticationStateProvider and CascadingAuthenticationState
AuthenticationStateProvider is the abstraction components query for the current user; <CascadingAuthenticationState>
(wired up automatically by AddAuthorizationCore/AddCascadingAuthenticationState in a Blazor Web App
template) makes an AuthenticationState available as a cascading value to the whole component tree:
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddAuthorization();
@inject AuthenticationStateProvider AuthState
@code {
protected override async Task OnInitializedAsync()
{
var state = await AuthState.GetAuthenticationStateAsync();
var user = state.User; // ClaimsPrincipal
}
}
AuthorizeView, [Authorize], and AuthorizeRouteView
<AuthorizeView Policy="AdminOnly">
<Authorized><AdminPanel /></Authorized>
<NotAuthorized><p>Access denied.</p></NotAuthorized>
</AuthorizeView>
<AuthorizeView Roles="Manager,Admin" Context="authState">
<p>Welcome, @authState.User.Identity?.Name</p>
</AuthorizeView>
@page "/admin"
@attribute [Authorize(Policy = "AdminOnly")]
<AuthorizeView> shows/hides UI fragments; [Authorize] on a routed @page component gates the whole page.
The two combine through AuthorizeRouteView, which Routes.razor uses in place of a plain RouteView so a
denied [Authorize] page renders a NotAuthorized/NotAuthenticated fragment instead of the page itself:
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)">
<NotAuthorized><p>Please sign in.</p></NotAuthorized>
</AuthorizeRouteView>
</Found>
</Router>
Auth in Server vs. WebAssembly: why WASM auth is never a trust boundary
Under Interactive Server, [Authorize]/<AuthorizeView> checks run on the server, over the same
ClaimsPrincipal an MVC controller would see — they are a real trust boundary, and hiding a component is
equivalent to never sending its markup or invoking its code at all.
Under Interactive WebAssembly, all component code — including anything an [Authorize] attribute
"protects" — runs in the user’s own browser, downloaded and executable regardless of whether the check passes.
Hiding a button or a route with <AuthorizeView>/[Authorize] there is a UX convenience, not a security
control: it stops a legitimate user from seeing UI they shouldn’t act on, but a motivated attacker can still
call the underlying API directly. Every actual authorization decision must be re-enforced server-side, on
the API the WebAssembly client calls — exactly as if that client were untrusted, because it is. See
Secure ASP.NET Core Blazor
WebAssembly.
ASP.NET Core Identity in a Blazor Web App
The dotnet new blazor -au Individual template wires up ASP.NET Core Identity’s cookie-based authentication
for a Blazor Web App directly (registration, login, external logins, two-factor, and the .NET 10 passkey flow
below), reusing the Identity pieces covered in
Authentication and ASP.NET Core Identity. Identity’s own
account-management pages are still Razor Pages/MVC-rendered, since they run once, outside the interactive
circuit.
OIDC with an external identity provider
For sign-in against Entra ID, Auth0, Duende IdentityServer, or another OpenID Connect provider, add the OIDC handler at the server (Static SSR / Interactive Server sign-in still happens through a normal browser redirect and cookie, not inside the WebAssembly runtime):
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddCookie()
.AddOpenIdConnect(o =>
{
o.Authority = "https://login.microsoftonline.com/{tenant}/v2.0";
o.ClientId = builder.Configuration["Oidc:ClientId"];
o.ResponseType = "code";
});
See Authentication and ASP.NET Core Identity for OAuth 2.0/OIDC handler details that apply identically here.
Token handling and AuthorizationMessageHandler
A standalone or Interactive WebAssembly client calling a separate API attaches an access token via a
DelegatingHandler added to its HttpClient, rather than relying on the cookie used for the Blazor app itself:
builder.Services.AddHttpClient("OrdersApi", c => c.BaseAddress = new Uri("https://api.example.com"))
.AddHttpMessageHandler<AuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("OrdersApi"));
AuthorizationMessageHandler (from Microsoft.AspNetCore.Components.WebAssembly.Authentication) attaches the
token acquired via the WebAssembly OIDC/MSAL sign-in flow and refreshes it as needed. See
HTTP Client and Resilience for IHttpClientFactory
itself.
Passkeys / WebAuthn (.NET 10)
The .NET 10 Identity-backed Blazor Web App template supports passkey (WebAuthn) sign-in out of the box — users register a passkey (a platform authenticator or security key) as an alternative to a password, using the browser’s WebAuthn API under the hood with no extra client-side code required from the app.
Antiforgery, CSP, and reconnection UI
-
Antiforgery — SSR form posts (see Data Binding, Forms, and Validation) are protected the same way as any other ASP.NET Core form post; see Security Hardening.
-
Content Security Policy — Interactive WebAssembly needs
script-srcto allow the WASM runtime’s own scripts (and, without further hardening,'unsafe-eval'in some hosting configurations); test a CSP against the actual render modes in use rather than assuming an MVC-app policy carries over unchanged. -
Reconnection UI — an Interactive Server circuit that drops shows a built-in "Attempting to reconnect…" overlay (customizable via the
components-reconnect-modalCSS classes/JS) while it tries to resume; if it cannot, the user is prompted to reload. Combine with .NET 10 circuit state persistence (see Blazor State Management) so a successful reconnect does not also lose in-progress work.