Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET CORE API with Angular 4 - The cookie token and the request token were swapped

I keep getting this message when trying to implement XSRF with Angular and .NET CORE: "Validation of the provided antiforgery token failed. The cookie token and the request token were swapped." I have the cookie and header names configured the same in both the Angular and API. Anyone have any ideas?

Process

Angular makes an initial call to this API method to retrieve cookie

    [HttpGet("startSession")]
    public async Task<IActionResult> StartSession()
    {
        AntiforgeryTokenSet tokens = this.antiForgery.GetAndStoreTokens(this.HttpContext);

        this.HttpContext.Response.Cookies.Append(this.options.Value.Cookie.Name, tokens.RequestToken, new CookieOptions { HttpOnly = false });

        return this.Ok(
            new
            {
                Success = true
            });
    }

Angular then intercepts the next POST request and overrides default XSRF handling slightly since I need it to work for HTTPS URLs

    // Override default Angular XSRF handling since it won't work for         
    absolute URLs and we have to prefix with "https://"
    // Source:https://github.com/angular/angular/blob/master/packages/common/http/src/xsrf.ts
    @Injectable()
    export class HchbHttpXsrfInterceptor implements HttpInterceptor {
    constructor(
    private tokenService: HttpXsrfTokenExtractor) {}

    intercept(req: HttpRequest<any>, next: HttpHandler): 
    Observable<HttpEvent<any>> {
    const headerName = 'X-XSRF-TOKEN';
    const lcUrl = req.url.toLowerCase();
    // Skip both non-mutating requests.
    // Non-mutating requests don't require a token
    // anyway as the cookie set
    // on our origin is not the same as the token expected by another origin.
    if (req.method === 'GET' || req.method === 'HEAD' ) {
         return next.handle(req);
    }
    const token = this.tokenService.getToken();

    // Be careful not to overwrite an existing header of the same name.
    if (token !== null && !req.headers.has(headerName)) {
       req = req.clone({headers: req.headers.set(headerName, token)});
    }
    return next.handle(req);
    }
    }
like image 595
Bob B. Avatar asked Mar 07 '23 14:03

Bob B.


1 Answers

I hit the same problem and I think I found the issue. The options.Cookie.Name in the AddAntiforgery has to be different than the cookie you set manually using context.Response.Cookies.Append.

Try to change the name of one of them and it will work. Right now you override the generated cookie that is using the options.Cookie.Name name with the tokens.RequestToken value.

You can notice the difference in the Developer Tools.

  • The default token generated using options.Cookie.Name is marked as http only (HttpOnly = true)
  • The manually attached token using context.Response.Cookies.Append is marked as HttpOnly = false

The second one is read from JS/Angular (you can read it in JS because the HttpOnly=false and sent as a header in your ajax requests and validated against the default one that can't be read from JS)

like image 117
Corneliu Avatar answered Mar 19 '23 12:03

Corneliu