I'm using ASP.NET Core Identity (cookie authentication) with a YARP reverse proxy to protect another ASP.NET Core web API which also hosts an Angular SPA. All requests are sent to the reverse proxy which checks if the request is authenticated. If it is, the request is forwarded to the web API/SPA. If not, the page is redirected to a login page.
When the user is logged in and the SPA is loaded, requests to edit or create data (PUT/POST) are sent from the SPA to the web API and also pass through the reverse proxy. This works quite well until the end of the cookie lifetime is reached and a user wants to edit something. The reverseproxy wants to redirect to the login page, but then returns a 400 error and logs the following message:
Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.AutoValidateAntiforgeryTokenAuthorizationFilter[1]
Antiforgery token validation failed. Validation of the provided antiforgery token failed. The cookie token and the request token were swapped.
Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: Validation of the provided antiforgery token failed. The cookie token and the request token were swapped.
at Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet)
at Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateRequestAsync(HttpContext httpContext)
at Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.ValidateAntiforgeryTokenAuthorizationFilter.OnAuthorizationAsync(AuthorizationFilterContext context)
I modified the AntiforgeryOptions
as below:
builder.Services.Configure<AntiforgeryOptions>(options =>
{
options.Cookie.Name = "XSRF-TOKEN";
options.HeaderName = "X-XSRF-TOKEN";
options.Cookie.HttpOnly = false;
});
The Angular SPA has an HttpInterceptor which extracts the xsrf-token from the cookie and appends it to the headers of all outgoing requests. Verifying the tokens with browser dev tools, the token from the cookie is exactly the same as the one being added to the request headers. Therefore I don't understand why the validation of the token fails.
What am I overlooking here?