Hi ABP Team, This is a continuation of the following support thread: https://abp.io/support/questions/10739/Multi-tenant-authentication-provider-selection-based-on-route-parameter# As suggested, I implemented tenant resolution from the route based on the following ABP Community article in both the Blazor project and the AuthServer: https://abp.io/community/articles/resolving-tenant-from-route-in-abp-framework-ah7oru97#gsc.tab=0 The tenant route resolution has been implemented in both the Blazor application and the AuthServer using the approach described in the blog above.
Issue
After implementing this, authentication starts correctly, but when the authentication flow returns to the Blazor application, I receive the following exception: InvalidOperationException: This form is being accessed with an invalid anti-forgery token. Validate the IAntiforgeryValidationFeature on the request before reading from the form. The exception occurs during the OIDC callback to: /signin-oidc
Authentication Flow
- https://localhost:44314/Tenant1: Here tenant gets resolved and when I click login, It navigates to
- https://localhost:44314/Account/Login and then
- https://localhost:44322/Account/Login?ReturnUrl..................
- After Signing in it navigates to https://localhost:44314/signin-oidc, at this point the exception occurs as mentioned above.
Questions
- Is there any special consideration when using the route-based tenant resolution approach together with the OpenID Connect callback (/signin-oidc)?
- Is there a recommended way to prevent the route-based tenant middleware from interfering with the OIDC authentication flow?
- Is there any known issue or additional configuration required to avoid the anti-forgery validation exception in this scenario?
Any guidance or recommended approach would be greatly appreciated. Thank you!
2 Answer(s)
-
0
POST /signin-oidcis an OpenID Connect callback endpoint and must not be affected by your “tenant-from-route” Path/PathBase rewriting middleware/contributor logic.- With the route-based approach from the community article, the middleware often rewrites:
Request.Path(e.g., from/Tenant1/signin-oidcto/signin-oidc) and/orRequest.PathBase(e.g., to/Tenant1) which is fine for normal pages, but it can break OIDC’s correlation/nonce/state validation flow. That frequently manifests as antiforgery/form validation exceptions when the callback is processed.
1) Exclude OIDC callback paths from the route-tenant middleware
In both Blazor Server UI and (if you added similar logic there) AuthServer, modify the middleware shown in the article so it skips authentication callback endpoints.
Example (based on the article’s
app.Use(async (httpContext, next) => { ... })middleware):public override void OnApplicationInitialization(ApplicationInitializationContext context) { var app = context.GetApplicationBuilder(); app.Use(async (httpContext, next) => { // Exclude OIDC endpoints from tenant route/pathbase rewriting: var path = httpContext.Request.Path; if (path.StartsWithSegments("/signin-oidc") || path.StartsWithSegments("/signout-callback-oidc") || path.StartsWithSegments("/Account") || path.StartsWithSegments("/connect")) // if needed { await next(); return; } // Your existing tenant-from-route PathBase logic (from the community article) here... await next(); }); app.UseRouting(); // ... }This prevents the route-based tenant middleware from interfering with
/signin-oidc(and related endpoints), which is the safest approach.2) Keep the OIDC callback at the application root (
/signin-oidc)Do not try to make the callback tenant-prefixed (e.g.
/Tenant1/signin-oidc). Let it stay at/signin-oidc, and resolve tenant after sign-in using your normal mechanisms (cookie/current user, etc.). With the exclusion above, the callback will be processed consistently.3) Don’t “fix” this by disabling antiforgery globally
Disabling antiforgery is not recommended for this scenario; the correct fix is to ensure the OIDC callback request is not mutated by the tenant-from-route pipeline.
Relevant references (validated):
- https://abp.io/community/articles/ah7oru97
- https://abp.io/docs/latest/framework/infrastructure/csrf-anti-forgery
- https://abp.io/support/questions/10739/Multi-tenant-authentication-provider-selection-based-on-route-parameter
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.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Hi,
The exception comes from endpoint routing. A broad route in your Blazor app — a page routed on something like
/{__tenant}— also matches/signin-oidc, and Razor component endpoints accept POST and require antiforgery validation by default. The OIDCform_postcallback is not an application form, so it carries no antiforgery token, and in the current pipeline that validation runs before the OpenID Connect handler gets to consume the callback.Move
app.UseAntiforgery()after bothapp.UseAuthentication()andapp.UseAuthorization()inOnApplicationInitializationof your Blazor module:app.UseRouting(); app.MapAbpStaticAssets(); app.UseAbpSecurityHeaders(); app.UseAuthentication(); if (MultiTenancyConsts.IsEnabled) { app.UseMultiTenancy(); } app.UseDynamicClaims(); app.UseAuthorization(); app.UseAntiforgery();That is the placement ASP.NET Core requires, and it keeps antiforgery validation on your own Blazor forms — routing has already picked the endpoint, and the middleware still runs before the endpoint executes.
One more thing that comes with the route-based tenant approach, separate from the exception above: the antiforgery cookie takes its path from
Request.PathBase. Once the tenant middleware setsPathBase = /Tenant1, the cookie is scoped to/Tenant1, and a form rendered under the tenant path but posted to a root path fails validation with a 400. Pin the path on each host where that happens:using Microsoft.AspNetCore.Antiforgery; context.Services.Configure<AntiforgeryOptions>(options => { options.Cookie.Path = "/"; });Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)