7

I am working on Angular 6 application with SSO login and .net core web API. The code hits the back end on /token url first time which is a post operation. How do I do the anti forgery in this scenario. Please explain the flow of token transfer

cdev
  • 5,043
  • 2
  • 33
  • 32
Abhinav Singh
  • 233
  • 1
  • 3
  • 11
  • refer this - https://learn.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-2.1 For Angular services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN"); – cdev Nov 01 '18 at 07:30
  • Looking into that article only. As per this artical as per this one- if ( string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) || string.Equals(path, "/index.html", StringComparison.OrdinalIgnoreCase)) ) But the first call is /token only which itself needs to be validated for anti forgery. – Abhinav Singh Nov 01 '18 at 08:41

2 Answers2

9

I'm not sure if that's what you're looking for, but I'll try to explain how I achieved it in a similar case.

First of all Angular has built in helpers for XSRF handling:

So the hardest part is to create custom XSRF middleware at api level.

I did it some time ago for one of my apps which was built with Angular 6 on the front and ASP.NET Core WebApi on the back-end.

Article which help me with it:

Your middleware could look like this:

public class AntiForgeryTokenMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IAntiforgery _antiforgery;

    public AntiForgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery)
    {
        _next = next;
        _antiforgery = antiforgery;
    }

    public Task Invoke(HttpContext context)
    {
        if (context.Request.Path.Value.IndexOf("/your api endpoint, e.g. /api", StringComparison.OrdinalIgnoreCase) != -1)
        {
            var tokens = _antiforgery.GetAndStoreTokens(context);
            context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions { HttpOnly = false, Secure = false });
        }
        return _next(context);
    }
}

Then as per mentioned article you have to add it to services in ConfigureServices method of Startup class:

services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

And use it in Configure method:

app.UseAntiforgeryToken();

And of course to make use of it you have to decorate your api methods with [ValidateAntiForgeryToken] attribute.

Then in your Angular app you could create HttpInterceptor to send token only when it's needed.

@Injectable()
export class XsrfInterceptor implements HttpInterceptor {

    constructor(private tokenExtractor: HttpXsrfTokenExtractor) {}

    private actions: string[] = ["POST", "PUT", "DELETE"];
    private forbiddenActions: string[] = ["HEAD", "OPTIONS"];

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        let token = this.tokenExtractor.getToken();
        let permitted =  this.findByActionName(request.method, this.actions);
        let forbidden =  this.findByActionName(request.method, this.forbiddenActions);;

        if (permitted !== undefined && forbidden === undefined && token !== null) {
            request = request.clone({ setHeaders: { "X-XSRF-TOKEN": token } });
        }
        
        return next.handle(request);
    }

    private findByActionName(name: string, actions: string[]): string {
        return actions.find(action => action.toLocaleLowerCase() === name.toLocaleLowerCase());
    }
}
Milo
  • 3,365
  • 9
  • 30
  • 44
Matt
  • 308
  • 4
  • 10
  • how do you intercept the call? I use: import { XsrfInterceptor } from 'src/app/interceptors/tok.interceptor'; but then "is declared but never read" – Leandro Bardelli Jul 17 '22 at 06:00
  • 1
    You do not intercept calls by yourself, you only have to import your interceptor in the module you need it, e.g. providers: [ { provide: HTTP_INTERCEPTORS, useClass: InterceptorService, multi: true } ] – Matt Jul 18 '22 at 07:32
1

This question is old however my solution could help somebody. What worked for us:

  • on Angular FE side HttpXsrfTokenInterceptor is used which is setting X-XSRF-TOKEN header. Of course cookie has to contain token under XSRF-TOKEN

  • on .net core side: Basically approach describe above, using domstamand's approach. However, and this is important, you have to add validation action into the middleware. Apparently using your custom middlaware turn's off validation done OOB by .net core antiforgery service. So after update your code for Invoke method should looks like this:

     public Task Invoke(HttpContext context)
     {
         if (context.Request.Headers.ContainsKey("X-XSRF-TOKEN"))
         {
             _antiForgery.ValidateRequestAsync(context);
         }
    
         var tokens = _antiForgery.GetAndStoreTokens(context);
         context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions { 
             HttpOnly = false,
             Secure = true
         });
    
         return _next(context);
     }
    

I added check for X-XSRF-TOKEN to avoid possible issues e.g. when GET or OPTIONS preflight check is called.

Update: Problem with my solution is that if you don't include X-XSRF-TOKEN into the HTTP request header validation doesn't execute at all. I trying to find fix for that.

Diomos
  • 420
  • 5
  • 15