Open Closed

Blazor Hybrid on Azure Container Apps (ACA) returns HTTP 400 “Antiforgery token validation failed / Unprotect ticket failed” after login #9660


User avatar
0
Sturla created

I have been battling with this issue for two days now and I need assitance.

Observed Problem

  • Browser to my site in ingognito
  • First page load → OK
  • Click “Sign in” (public login page)
  • Submitting credentials triggers HTTP 400 and ~/Views/Error/404 or 400.cshtml.
  • Console log excerpt: Antiforgery token validation failed. Validation of the provided antiforgery token failed. The cookie token and the request token were swapped. Identity.Application was not authenticated. Failure message: Unprotect ticket failed

Environment

| Item | Details | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | ABP version | 9.2.2 (Commercial) | | Template | Blazor Hybrid (Account Public + Admin Pro + LeptonX) | | Elsa Studio | v3 – integrated as standalone WASM under /elsa/… | | Hosting | Azure Container Apps (ACA) – single container, Linux | | Ingress | external=true, targetPort=8080, transport=auto | | Probes | /health (liveness) & /health/ready (readiness/startup) | | Data Protection | Keys persisted to Azure Blob Storage via Azure.Extensions.AspNetCore.DataProtection.Blobs, user‑assigned managed identity | | Forwarded Headers | Middleware added first: app.UseForwardedHeaders(); options include XForwardedProto, XForwardedFor, XForwardedHost | | Antiforgery | HeaderName = "X-XSRF-TOKEN", Cookie.Name = "XSRF-TOKEN", SecurePolicy = Always, SameSite = None | | DataProtection code | .SetApplicationName("YourSite") + .PersistKeysToAzureBlobStorage(blobClient) | | Cookies | Application cookie unchanged; antiforgery cookie now present |

HttpOverrides trace shows Scheme=https and correct host!

Relevant Code Snippets

This is the code I have in my BlazorModule.cs file

ConfigureServices()

// 1️⃣ Forwarded‑Header options (ACA IPs change frequently)
services.Configure<ForwardedHeadersOptions>(o =>
{
    o.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
                         ForwardedHeaders.XForwardedProto |
                         ForwardedHeaders.XForwardedHost;
    o.KnownNetworks.Clear();  // allow any ingress IP
    o.KnownProxies.Clear();
    o.ForwardLimit = null;    // unlimited hops
});

// 2️⃣ Data‑Protection keys → Azure Blob
var blobClient = new BlobClient(new Uri(cfg["DataProtection:BlobUri"]),
                                new DefaultAzureCredential(new DefaultAzureCredentialOptions {
                                    ManagedIdentityClientId = cfg["DataProtection:ManagedIdentityClientId"]
                                }));
services.AddDataProtection()
        .SetApplicationName("YourSite")
        .PersistKeysToAzureBlobStorage(blobClient);

// 3️⃣ Antiforgery settings
services.AddAntiforgery(o =>
{
    o.HeaderName = "X-XSRF-TOKEN";
    o.Cookie.Name = "XSRF-TOKEN";
    o.Cookie.SameSite = SameSiteMode.None;
    o.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});

// 4️⃣ Pin discriminator after all modules
services.PostConfigure<DataProtectionOptions>(o =>
{
    o.ApplicationDiscriminator = "YourSite";
});

OnApplicationInitialization (first lines)

var app = context.GetApplicationBuilder();

// 1️⃣ MUST be first – handle X‑Forwarded‑* from ACA
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor |
                       ForwardedHeaders.XForwardedProto |
                       ForwardedHeaders.XForwardedHost,
    KnownNetworks = { }, KnownProxies = { }, ForwardLimit = null
});

// 2️⃣ Ensure HTTPS inside cluster
app.Use(async (ctx, next) =>
{
    if (!ctx.Request.IsHttps)
    {
        ctx.Response.Redirect($"https://{ctx.Request.Host}{ctx.Request.Path}{ctx.Request.QueryString}", false);
        return;
    }
    await next();
});

app.UseHttpsRedirection();
// … ABP default pipeline continues …

What we tried & outcome

| Attempt | Outcome | | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | | Persist keys + SetApplicationName | ✅ Key‑ring loads without “key not found”. | | PostConfigure<DataProtectionOptions> last | ✅ Logs show application isolator: YourSite. | | UseForwardedHeaders first | ✅ Trace says Request IsHttps = True. | | Antiforgery header & secure cookie config | ❌ Still get 400 & “cookie token and request token were swapped”. | | Temporarily set CookieSecurePolicy = None | Browser blocks cookie (SameSite=None requires Secure) – still 400. | | Confirmed via DevTools: request sends no XSRF‑TOKEN cookie; only the form header. | |

Questions for ABP Support

  1. Is there ABP middleware in Blazor‑Hybrid that rewrites or clears the antiforgery cookie after UseForwardedHeaders, causing the browser to miss it?
  2. Does the Account Public login page in 9.2.2 expect a different antiforgery header configuration than the one we applied?
  3. Is any additional ABP setting required when running behind Envoy (ACA) to keep the XSRF‑TOKEN cookie?
  4. How can we enable even more detailed tracing for ABP’s antiforgery pipeline to see why the cookie is not returned?
  5. Is ABP explicitly expecting a different antiforgery token pattern or explicitly using an internal header?
  6. Could the separate Elsa Studio WASM app (added _Host.cshtml) under Elsa interfere with antiforgery for Account routes?

4 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • In ABP, the antiforgery system is enabled by default and uses a cookie (default: XSRF-TOKEN) and a request header (default: RequestVerificationToken). Your custom header X-XSRF-TOKEN and cookie name are valid, but you must ensure the client-side code (JS or Blazor) reads the cookie and sends it in the header for every POST/PUT request. If the header is sent but the cookie is missing, validation fails.
    • The error “cookie token and request token were swapped” typically means the antiforgery system received the tokens in the wrong order, or the cookie was not present at all. This can happen if the browser blocks the cookie (e.g., due to SameSite=None without Secure, or proxy misconfiguration).
    • Your configuration for Cookie.SameSite = None and Cookie.SecurePolicy = Always is correct for cross-site scenarios, but only if HTTPS is enforced end-to-end. If any hop is HTTP, the browser will not send the cookie.
    • When running behind Azure Container Apps (Envoy), ensure that:
      • UseForwardedHeaders is the very first middleware.
      • The X-Forwarded-Proto header is set to https by the ingress.
      • The application always sees requests as HTTPS (Request.IsHttps == true).
    • If the antiforgery cookie is not being set or sent, check:
      • The initial GET to the login page must return a Set-Cookie: XSRF-TOKEN=...; SameSite=None; Secure header.
      • The browser must accept and store this cookie. Use DevTools to confirm.
      • The POST request must include both the cookie and the header.
    • There is no ABP middleware that rewrites or clears the antiforgery cookie after UseForwardedHeaders. However, if the pipeline is misordered or the request is not seen as HTTPS, the cookie may not be set.
    • The Account Public login page expects the antiforgery header to match the configured name. If you changed it to X-XSRF-TOKEN, ensure all client-side code (including Blazor and any JS) uses this header.
    • No additional ABP setting is required for Envoy/ACA, but you must ensure forwarded headers and HTTPS are handled correctly.
    • For more detailed tracing, enable logging for Microsoft.AspNetCore.Antiforgery and Volo.Abp.AspNetCore.Mvc.AntiForgery at the Debug or Trace level in your logging configuration.
    • ABP does not use a different antiforgery token pattern; it uses the standard ASP.NET Core antiforgery system.
    • If Elsa Studio WASM is served from the same domain and shares cookies, it could interfere if it changes or clears cookies, but this is unlikely unless it explicitly manipulates the antiforgery cookie.

    Troubleshooting Steps:

    1. Confirm the initial GET to the login page sets the XSRF-TOKEN cookie with SameSite=None; Secure.
    2. Confirm the POST request includes both the XSRF-TOKEN cookie and the X-XSRF-TOKEN header.
    3. Ensure all requests are seen as HTTPS by the app (check Request.IsHttps).
    4. If using a custom header, ensure all ABP and client code use the same header name.
    5. If the problem persists only in incognito or after a cold start, it may be a timing or caching issue—ensure the cookie is set before the POST.
    6. If using multiple containers or instances, ensure all share the same data protection keys and application name/discriminator.

    Example: Logging Configuration

    // appsettings.json
    "Logging": {
      "LogLevel": {
        "Default": "Information",
        "Microsoft.AspNetCore.Antiforgery": "Debug",
        "Volo.Abp.AspNetCore.Mvc.AntiForgery": "Debug"
      }
    }
    

    References:


    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.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    The cookie token and the request token were swapped

    This error indicates that the Antiforgerytoken in the header/form is using the Antiforgerytoken from cookies.

    Can you share an online website? I will test it online.

    liming.ma@volosoft.com

    Thanks

  • User Avatar
    0
    Sturla created

    Thank you liming but I figured this out finally. It was _Host.cshtml from Elsa that was causing my issue

    I hade builder.MapFallbackToPage("/_Host");

    and then had to do things arround that.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Great 👍

Learn More, Pay Less
33% OFF
All Trainings!
Get Your Deal
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.0.0-preview. Updated on September 18, 2025, 07:10