Description
Authentication state is lost when Blazor Web App transitions from Server-side rendering to WebAssembly (Interactive Auto render mode) in production environment with Docker + nginx reverse proxy. The issue does NOT occur in development environment.
Environment
- ABP Framework Version: 9.x (Commercial)
- .NET Version: 9.0
- UI Framework: Blazor Web App
- Render Mode: Interactive Auto (@rendermode="InteractiveAuto")
- Authentication: OpenIdConnect + Cookie Authentication
- Deployment: Docker containers behind nginx reverse proxy
- Data Protection: Redis (shared across containers)
- Database: PostgreSQL
Project Structure
- Auth Server: Separate container running OpenIddict
- Blazor App: Main application with Interactive Auto render mode
- API: Separate API container
- All containers use Redis for Data Protection keys
- nginx reverse proxy handles SSL termination and routing
Steps to Reproduce
- Deploy Blazor Web App with Interactive Auto render mode to Docker with nginx reverse proxy
- Configure ForwardedHeaders, cookie settings as per ABP documentation
- Login successfully (first visit - Server-side rendering works)
- Navigate between pages (subsequent visits switch to WebAssembly)
- Result: User appears authenticated on first page load, but loses authentication after WebAssembly hydration
Expected Behavior
- User remains authenticated when transitioning from Server to WebAssembly render mode
- Authentication state should persist across render mode switches
- Works correctly in development (IIS Express) Actual Behavior
- Authentication works on first visit (Server-side)
- After transition to WebAssembly (on subsequent navigations), authentication is lost
- User is redirected to login page
- Cookies appear to be present in browser but not recognized by WebAssembly side
Configuration Details
App.razor
<Routes @rendermode="InteractiveAuto" />
<HeadOutlet @rendermode="InteractiveAuto" />
XxxBlazorModule.cs (Server)
// ConfigureServices
context.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto |
ForwardedHeaders.XForwardedHost;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
context.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
// Cookie configuration
.AddCookie("Cookies", options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(365);
options.IntrospectAccessToken();
if (!hostingEnvironment.IsDevelopment())
{
options.Cookie.SameSite = SameSiteMode.Lax;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.HttpOnly = true;
}
})
// OnApplicationInitialization
if (!env.IsDevelopment())
{
app.UseForwardedHeaders();
}
XxxBlazorClientModule.cs (WebAssembly)
private static void ConfigureAuthentication(WebAssemblyHostBuilder builder)
{
builder.Services.AddBlazorWebAppServices();
}
nginx Configuration
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Host $host;
What We've Tried
- ✅ Added AddAuthenticationStateSerialization() on server (didn't help)
- ✅ Configured ForwardedHeaders properly
- ✅ Set cookie SameSite=Lax and SecurePolicy=Always
- ✅ Verified Redis Data Protection is working (keys are shared)
- ✅ Configured nginx to forward all required headers
- ✅ Set OIDC correlation and nonce cookies to Lax/Always
- ✅ Verified cookies are present in browser DevTools
- ✅ Cleared browser cache/cookies multiple times
Questions
- Does ABP's Interactive Auto mode require additional configuration for production beyond what's documented?
- Is there a special authentication state provider needed for Docker + nginx scenarios?
- Should AddAuthenticationStateSerialization() be used for ABP's OpenIdConnect setup?
- Are there any known issues with cookie-based authentication in Interactive Auto mode behind reverse proxy?
Additional Context
- The exact same code works perfectly in development (localhost with IIS Express)
- Issue only occurs in production with Docker + nginx
- ABP Framework's template uses AddBlazorWebAppServices() on client side
- Server uses standard ABP OpenIdConnect configuration with cookie authentication Related Documentation
- ABP Blazor Web App Documentation
- Microsoft: Blazor Web App Authentication
Request
Please provide guidance on:
- Proper authentication state management for Interactive Auto mode in production
- Any ABP-specific configuration required for Docker + nginx deployments
- Whether authentication state serialization is needed/recommended for ABP's OpenIdConnect setup
- Any known workarounds or best practices for this scenario
5 Answer(s)
-
0
- Solution
Root cause
- In Interactive Auto, the initial request is SSR (server-side) and then the WebAssembly sub-app takes over. When running behind a reverse proxy, the WebAssembly sub-app must also be able to authenticate requests. If cookie, token, or origin settings aren’t aligned for the WebAssembly sub-app, the hydration step loses the authenticated state and you appear logged out on client-side navigation.
Mandatory checks and fixes
Forwarded headers and cookie security behind nginx
- Keep UseForwardedHeaders early in the pipeline in production (before auth and endpoints).
- Ensure nginx forwards X-Forwarded-Proto and you use CookieSecurePolicy.Always and SameSite=Lax (or Strict only if you do not do cross-site redirects). Interactive flows usually need Lax.
- Your configuration snippet for forwarded headers is correct; just ensure:
- app.UseForwardedHeaders() is before app.UseAuthentication(), app.UseAuthorization(), app.UseAntiforgery(), endpoints.
- nginx’s proxy_set_header Host $host; and X-Forwarded-* are set (as you showed).
Data Protection key ring must be shared across every server that issues or reads cookies
- You’ve already configured Redis for data protection. Verify all app instances (Blazor host, API, auth server if it emits cookies) read/write the same key ring and use the same application name and purpose strings for data protection. If any instance uses a different key ring or app name, the cookie can’t be decrypted by the WebAssembly sub-app’s endpoints it calls during hydration.
SameSite for all OIDC correlation/nonce and auth cookies
- Ensure OpenIdConnect correlation/nonce cookies and your primary auth cookie align with your global cookie policy:
- SameSite=Lax
- Secure=Always
- HttpOnly=true
- Mismatched SameSite here causes the client-side OIDC callbacks or cookie roundtrips to fail specifically in production behind a proxy.
- Ensure OpenIdConnect correlation/nonce cookies and your primary auth cookie align with your global cookie policy:
WebAssembly sub-app authentication for Blazor Web App
- The WebAssembly side must be configured to authenticate against the same authority/base URLs as the server-side, and it must be able to send cookies to the server endpoints it calls after hydration.
- In ABP Blazor Web App, do:
- In the WebAssembly client module ConfigureAuthentication, ensure the standard ABP client setup is added (AddBlazorWebAppServices is fine) and that the appsettings for the client include:
- AuthServer.Authority and RemoteServices.Default.BaseUrl pointing to the externally reachable HTTPS origin that the browser sees (the nginx public host), not the internal container hostname.
- Important: If origin changes between SSR and WASM (e.g., SSR sees internal http://backend:8080 but client sees https://example.com), cookies may not roundtrip as expected. All URLs used by the client must be the public origin.
- In the WebAssembly client module ConfigureAuthentication, ensure the standard ABP client setup is added (AddBlazorWebAppServices is fine) and that the appsettings for the client include:
CORS and RedirectAllowedUrls
- In your Auth Server/OpenIddict and API configuration, include RedirectAllowedUrls/CorsOrigins that match the exact public origin of the Blazor app through nginx (scheme + host + port). For InteractiveAuto with WebAssembly sub-app, the same public origin must be whitelisted for redirects and CORS on any endpoints the WASM side calls.
- If the WebAssembly sub-app serves under the same host, use same-site cookies and do not enable cross-site scenarios unless needed.
Cookie path and domain
- Ensure the auth cookie Domain and Path are compatible with the URLs used after hydration. Typically:
- Do not set a custom Domain unless you need a parent domain. Let it default to the request host so it’s valid for both SSR and WASM navigations.
- Path should be “/”.
- Ensure the auth cookie Domain and Path are compatible with the URLs used after hydration. Typically:
WebSocket support (for Server interop and signalr features used during SSR/interactive server)
- Behind some hosting/proxies, missing WebSocket upgrade can cause odd interaction failures. Ensure nginx allows WebSocket upgrades if you rely on Interactive Server during the transition.
Place app.UseBlazorFrameworkFiles (if hosting WASM assets) before app.UseStaticFiles when applicable
- If you host the WASM assets in the same server app, ensure app.UseBlazorFrameworkFiles(); then app.UseStaticFiles(); and correct public base path so the browser fetches the correct boot resources under the public origin.
Do not rely on AddAuthenticationStateSerialization to fix this
- That extension is not a requirement for ABP’s OpenIdConnect + cookie setup in Interactive Auto and will not solve lost auth across SSR-to-WASM. The issue is typically misaligned origins/cookies/forwarded headers.
Concrete checklist you can apply now
- Move app.UseForwardedHeaders(); immediately after app.UseCorrelationId() and before authentication:
- app.UseForwardedHeaders();
- app.UseAbpRequestLocalization();
- app.UseStaticFiles();
- app.UseRouting();
- app.UseAuthentication();
- app.UseAuthorization();
- endpoints...
- Verify nginx:
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Forwarded-Host $host;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host $host;
- For WebSockets, include upgrade headers if you need interactive server:
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection $connection_upgrade;
- Ensure AuthServer.Authority, RemoteServices.Default.BaseUrl, and SelfUrl in Blazor client point to the public https origin (the same value in DbMigrator/seed RedirectAllowedUrls and CorsOrigins).
- Confirm cookie settings in production:
- options.Cookie.SameSite = SameSiteMode.Lax;
- options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
- options.Cookie.HttpOnly = true;
- Do not set Cookie.Domain manually unless you truly need it across subdomains.
- Ensure Redis Data Protection configuration is identical for all apps that read/write the auth cookie (same key ring and application name).
- If you offload TLS at nginx, do NOT terminate to http internally without forwarded headers; ASP.NET Core must see the original scheme via X-Forwarded-Proto so it marks cookies as secure and builds correct redirect URIs.
- Move app.UseForwardedHeaders(); immediately after app.UseCorrelationId() and before authentication:
Answers to your specific questions
- Additional configuration for ABP Interactive Auto in production?
- No ABP-specific extra switch. What’s critical is correct public origin alignment, forwarded headers, shared data protection, and cookie settings as above. This is the same baseline required by ASP.NET Core + Blazor Web App behind a reverse proxy.
- Special authentication state provider for Docker + nginx?
- No special provider is required. Use the default ABP setup. The problem is typically cookies/forwarded headers/origin, not the provider.
- Should AddAuthenticationStateSerialization() be used?
- Not required for ABP’s OpenIdConnect + cookie. It doesn’t address this scenario.
- Known issues with cookie auth in Interactive Auto behind reverse proxies?
- The common pitfalls are:
- X-Forwarded-Proto/Host not processed early, causing wrong cookie scheme or redirect URIs.
- Public origin mismatch between SSR host and WASM sub-app settings.
- SameSite/Secure mismatch for OIDC correlation/nonce and primary cookies.
- Non-shared data protection keys across scaling units.
- The common pitfalls are:
- Additional configuration for ABP Interactive Auto in production?
Minimal code/config examples
Server (Program/Module order hints):
public override void OnApplicationInitialization(ApplicationInitializationContext context) { var app = context.GetApplicationBuilder(); var env = context.GetEnvironment(); if (!env.IsDevelopment()) { app.UseForwardedHeaders(); // before auth } app.UseCorrelationId(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseConfiguredEndpoints(); }Cookie settings:
services.AddAuthentication() .AddCookie("Cookies", options => { options.ExpireTimeSpan = TimeSpan.FromDays(365); if (!env.IsDevelopment()) { options.Cookie.SameSite = SameSiteMode.Lax; options.Cookie.SecurePolicy = CookieSecurePolicy.Always; options.Cookie.HttpOnly = true; } });nginx (add upgrade headers if needed):
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header Host $host; # For WebSockets (interactive server features) proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade;WebAssembly client appsettings (points to public HTTPS origin):
{ "App": { "SelfUrl": "https://your-public-host" }, "AuthServer": { "Authority": "https://your-public-host", "ClientId": "Your_Blazor_Client", "ResponseType": "code" }, "RemoteServices": { "Default": { "BaseUrl": "https://your-public-host" } } }- Sources
- https://abp.io/docs/latest/framework/ui/blazor/authentication
- https://abp.io/docs/latest/framework/ui/blazor/authorization
- https://learn.microsoft.com/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-9.0
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
-
0
I have checked all of this, and everything seems to be configured correctly, but still not working. You can give url of the application to the support team if needed.
-
0
hi
Can you share a test URL and test user?
liming.ma@volosoft.com Thanks
-
0
Hi @maliming! sent you an email with url and credentials.Thanks
-
0
