Secure Server‑Side Blazor Authentication: Minimal Circuit‑Level Design
Learn how to build a lean, secure Blazor Server authentication flow using ASP.NET Core Identity, SignalR, and CircuitOptions. The guide covers requirements, a minimal architecture, trust boundaries, operational checks, failure modes, and when to rethink the design.
17 Aug 2025, 09:12 UTC

Requirements
When you need a secure, server‑side Blazor app that authenticates users with ASP.NET Core Identity, the following prerequisites are mandatory:
- ASP.NET Core 8.0 or later, with the
Microsoft.AspNetCore.Components.Serverpackage. - Identity configured to use cookie authentication (the default for Blazor Server).
- SignalR enabled over HTTPS; the hub must require authenticated connections.
- CircuitOptions configured to control the number of retained circuits and idle timeout.
- Client browsers that support WebSockets or Server‑Sent Events for SignalR.
Smallest Viable Architecture
At its core, the design consists of four moving parts:
- ASP.NET Core Middleware – Handles authentication, cookie issuance, and HTTPS enforcement.
- Identity Store – Persists user credentials and claims.
- SignalR Hub – Maintains a dedicated circuit per authenticated user.
- AuthenticationStateProvider – Exposes the current
ClaimsPrincipalto Razor components.
Below is a minimal Program.cs that wires everything together. Replace placeholders with your own values.
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options => {
options.Cookie.Name = "_Auth";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax;
options.ExpireTimeSpan = TimeSpan.FromDays(14);
});
builder.Services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<YourDbContext>()
.AddDefaultTokenProviders();
builder.Services.AddServerSideBlazor();
builder.Services.Configure<CircuitOptions>(options => {
options.MaxRetainedCircuits = 1000; // adjust based on load
options.CircuitIdleTimeout = TimeSpan.FromMinutes(30);
});
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapBlazorHub();
app.MapFallbackToPage("_Host");
app.Run();
Key points:
- The cookie is marked
HttpOnlyandSecureto mitigate XSS and man‑in‑the‑middle attacks. - SignalR is protected by the same authentication middleware; unauthenticated connections are rejected.
- Circuits are scoped per user and automatically disposed after
CircuitIdleTimeoutof inactivity.
Trust and Data Boundaries
Blazor Server keeps the UI logic on the server, exposing only the ClaimsPrincipal to the client through AuthenticationStateProvider. The boundaries are:
- Client UI – Renders markup and sends user input via SignalR. It never receives raw data from the database.
- Server Logic – Executes Razor components, accesses Identity, and performs business logic.
- Identity Store – Holds encrypted passwords and claims; never sent directly to the client.
Only claims that are explicitly added to the authentication cookie are visible to the client. Sensitive data such as password hashes or personal identifiers must be accessed only on the server side.
Operational Checks
To keep the system healthy, validate the following at runtime or during CI:
- Cookie attributes
- Open the browser dev tools, navigate to
Application > Cookies, and verify that the authentication cookie hasHttpOnly,Secure, andSameSite=LaxorStrictset.
- Open the browser dev tools, navigate to
- Circuit cleanup
- In
appsettings.json, setCircuitIdleTimeoutand monitor theMicrosoft.AspNetCore.Components.Server.Circuitslogs. Verify that circuits are disposed after the timeout.
- In
- SignalR authentication
- Attempt to connect to
/blazorhubwithout a valid cookie; the connection should be rejected with a 401 status.
- Attempt to connect to
- Load test
- Use a tool like k6 or JMeter to simulate 500 concurrent users. Observe the circuit count in the server logs and ensure it does not exceed
MaxRetainedCircuits.
- Use a tool like k6 or JMeter to simulate 500 concurrent users. Observe the circuit count in the server logs and ensure it does not exceed
Failure Modes and Mitigations
| Failure Mode | Impact | Mitigation |
|---|---|---|
| Network drop during a circuit | UI freezes; user may lose state | SignalR automatically reconnects; after CircuitIdleTimeout the user is redirected to login
|
| Cookie compromise (XSS or CSRF) | Session hijacking | Use HttpOnly, Secure, SameSite attributes; rotate tokens on password change
|
| Server restart | All circuits lost; users must re‑authenticate | Acceptable for server‑side Blazor; consider sticky sessions if using load balancers |
| High latency or packet loss | Frequent circuit churn; degraded UX | Configure CircuitIdleTimeout to a longer value; use WebSocket fallback to SSE
|
Large user base exceeding MaxRetainedCircuits
| New users denied access | Increase MaxRetainedCircuits or implement circuit pooling; consider moving to Blazor WebAssembly for heavy scale
|
When to Re‑Design
While the minimal architecture works well for moderate traffic, consider a redesign under these conditions:
- Scaling to thousands of concurrent users – Server‑side Blazor consumes CPU for each circuit. Switch to
Blazor WebAssemblywith API‑only authentication or useSignalRhubs with a dedicated circuit pool. - Strict latency requirements – If users experience frequent reconnections, evaluate
CircuitIdleTimeoutor move to a client‑side rendering model. - Compliance mandates – Some regulations require data to stay on the server and avoid client exposure. Verify that only claims are sent; sensitive data should be fetched via secure APIs.
- Multi‑tenant architecture – When each tenant needs isolated authentication stores, consider a custom
AuthenticationStateProviderthat pulls tenant claims from a shared service. - Hybrid offline scenarios – If the app must work offline, server‑side Blazor is unsuitable; move to WebAssembly with local storage and sync logic.
In each case, the core principle remains: keep authentication state scoped to the server, expose only what the UI needs, and enforce strict cookie and SignalR security.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.