Guides correct AuthenticationStateProvider registration when migrating ASP.NET Web Forms Identity/OWIN authentication to Blazor Server using scoped providers and cookie-based auth. Covers DI lifetime pitfalls (singleton vs scoped vs transient), minimal API auth endpoints, and integration with BWFC LoginView. Use when migrating authentication, fixing session bleed across browser tabs, debugging shared auth state in Playwright tests, or setting up cookie-based login and registration endpoints in a Blazor Server migration.
88
86%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Discovered in WingtipToys Run 7, Iteration 2. When migrating ASP.NET Web Forms applications with Identity/OWIN auth to Blazor Server, the AuthenticationStateProvider registration pattern is critical. A singleton provider shares auth state across all browser sessions — causing test interference and real-world session bleed.
Confirmed by: Run 7 acceptance tests (14/14 pass after fix). The RegisterAndLogin_EndToEnd test failed reliably with singleton registration and passed reliably with scoped + cookie auth.
NEVER register AuthenticationStateProvider as a singleton in Blazor Server. Blazor Server maintains per-circuit state; singletons break this model by sharing state across all connections.
// Program.cs — correct auth registration for Blazor Server migration
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options => options.LoginPath = "/Account/Login");
builder.Services.AddScoped<MockAuthenticationStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(sp =>
sp.GetRequiredService<MockAuthenticationStateProvider>());Why this works:
AddAuthentication + AddCookie gives ASP.NET Core's cookie middleware — session isolation is built-in.AddScoped<MockAuthenticationStateProvider> creates one provider per Blazor circuit.AddScoped<AuthenticationStateProvider>(sp => ...)) lets DI resolve the concrete type through the abstract base.MockAuthenticationStateProvider with real Identity later.// WRONG — auth state shared across ALL browser sessions
builder.Services.AddSingleton<AuthenticationStateProvider, MockAuthenticationStateProvider>();Symptoms:
// WRONG — creates a new instance per resolution, loses state within a circuit
builder.Services.AddTransient<AuthenticationStateProvider, MockAuthenticationStateProvider>();Symptoms:
When using cookie auth, register and login forms POST to minimal API endpoints:
// Program.cs — minimal API auth endpoints
app.MapPost("/Account/DoRegister", async (HttpContext ctx,
MockAuthenticationStateProvider auth,
[FromForm] string email, [FromForm] string password) =>
{
// Register user logic here
await auth.LoginAsync(email);
await ctx.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(new ClaimsIdentity(
new[] { new Claim(ClaimTypes.Name, email) },
CookieAuthenticationDefaults.AuthenticationScheme)));
return Results.Redirect("/");
});The BWFC LoginView component wraps Blazor's <AuthorizeView> internally. It works correctly with scoped auth providers — no special configuration needed beyond the registration pattern above.
1bd9b17
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.