Secure Auth Flow for Blazor Server with ASP.NET Core Identity and SignalR
Build a minimal yet secure authentication architecture for a Blazor Server app, tying ASP.NET Core Identity, cookie auth, and SignalR together. The guide covers trust boundaries, operational checks, and failure handling.
16 Jun 2026, 04:10 UTC

Problem Statement
When building a Blazor Server application, the developer must ensure that the user’s identity is consistently available to every component while the SignalR circuit remains secure. A broken or mis‑configured authentication flow can expose sensitive data or allow unauthenticated users to access protected UI. This article lays out the minimal architecture that satisfies these constraints, defines trust boundaries, and provides operational checks and graceful failure handling.
Requirements
- ASP.NET Core 7.0 or later.
- Blazor Server project with
Microsoft.AspNetCore.Components.Server. - ASP.NET Core Identity for user management.
- HTTPS enforced site (HSTS).
- SameSite cookie policy set to Lax or Strict.
- SignalR automatically used by Blazor for component rendering.
Minimal Architecture
The core of the design revolves around a single cookie that carries the authentication ticket. This cookie is issued by the AuthenticationMiddleware, validated on every request, and automatically shared with the SignalR connection that Blazor Server uses. The diagram below shows the flow from login to component rendering.
Login Page → Authentication Middleware → Identity Cookie ↔ SignalR Circuit → Protected Components
Identity Cookie as the Single Source of Truth
ASP.NET Core Identity issues an authentication cookie containing a ClaimsPrincipal. Because the same cookie is sent with every HTTP request, including the WebSocket upgrade that establishes the SignalR circuit, the server can verify the user’s identity without additional round‑trips.
AuthenticationStateProvider
Register ServerAuthenticationStateProvider in Program.cs to subscribe to authentication state changes. This provider automatically updates cascading parameters so that components can react to sign‑in or sign‑out events.
builder.Services.AddScoped<AuthenticationStateProvider, ServerAuthenticationStateProvider>();
Trust & Data Boundaries
- Client‑to‑Server: All requests must use HTTPS, ensuring that the authentication cookie is encrypted in transit.
- Server‑to‑SignalR: The SignalR connection inherits the same cookie, so the server trusts the cookie value as the sole identity source.
- Client‑side State: Components should never store sensitive data locally; they must always request fresh claims from the
AuthenticationStateProvider.
Operational Checks
- Post‑Login Verification
After a successful login, verify that:- The cookie appears in the browser’s storage with
HttpOnlyandSecureflags. - Calling
AuthenticationStateProvider.GetAuthenticationStateAsync()returns aClaimsPrincipalwith the expected claims. - SignalR connection is established without redirection.
- The cookie appears in the browser’s storage with
- Protected Component Access
Attempt to navigate to a protected component without a valid cookie. The middleware should redirect to the login page, and the SignalR circuit should not be created. - Cookie Expiration Simulation
Force the cookie to expire or revoke it server‑side. The application should detect the state change, drop the SignalR circuit, and prompt the user to re‑authenticate.
Failure Modes & Graceful Handling
- SignalR Disconnection: If the connection drops (network issue, server restart), the client automatically attempts to reconnect. During reconnection, the authentication cookie is re‑validated. If validation fails, the app redirects to the login page.
- Stale Authentication State: A mis‑configured
AuthenticationStateProvidercan cause components to render with old claims. To mitigate, always use the provider’sAuthenticationStateChangedevent to refresh UI. - Cookie Theft: Without
HttpOnlyandSecureflags, attackers can steal the cookie. Ensureoptions.Cookie.HttpOnly = true; options.Cookie.SecurePolicy = CookieSecurePolicy.Always;inAddIdentityconfiguration. - Cross‑Site Request Forgery (CSRF): Set
SameSite=LaxorStricton the cookie to prevent CSRF. Blazor Server’s SignalR uses WebSocket, so SameSite is respected.
Conditions Requiring Redesign
- High‑throughput, low‑latency scenarios where the SignalR circuit becomes a bottleneck. Consider moving to a WebAssembly (client‑side) model or implementing a hybrid approach.
- Multiple domains or sub‑domains that need to share authentication state. The cookie must be scoped to
Domain=.example.comand the authentication scheme must support cross‑domain cookies. - Granular authorization beyond simple role checks. Introduce policy‑based authorization and ensure the
ClaimsPrincipalcontains the necessary policy claims. - Server‑side rendering of highly dynamic content that requires frequent authentication checks. In this case, evaluate using
AuthorizeViewwithOnAuthorizeAsynccallbacks to reduce unnecessary SignalR traffic.
Concrete Configuration Example
Below is a typical Program.cs snippet that ties everything together.
var builder = WebApplication.CreateBuilder(args);
// Add services
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddDbContext(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddDefaultIdentity(options =>
options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores();
builder.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax;
options.LoginPath = "/Identity/Account/Login";
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
});
builder.Services.AddScoped();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseHsts();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
Practical Verification Steps
- Run the app and navigate to a protected page. Verify the browser redirects to the login page.
- Log in. Inspect the cookie in the browser dev tools: it should be
HttpOnly,Secure, and haveSameSite=Lax. - Open a protected component and check that the
AuthenticationStateProviderreports the correct user. - Simulate cookie expiration by clearing the cookie or setting a past expiration. The app should detect the unauthenticated state, drop the SignalR circuit, and redirect to login.
By following this minimal yet robust architecture, you can ensure that your Blazor Server application remains secure, consistent, and user‑friendly across all operational scenarios.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.