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-src to 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-modal CSS 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.