Grzegorz Smulko
Grzegorz Smulko

Reputation: 2803

How to implement custom ValidateAntiforgeryTokenAuthorizationFilter in ASP.NET Core 3.1

I'd like to implement a filter that skips validation of an antiforgery token when an auth token authentication (Bearer) is used.

In the ASP.NET Core 2.2 the ValidateAntiforgeryTokenAuthorizationFilter and AutoValidateAntiforgeryTokenAuthorizationFilter were public (even though living in the Microsoft.AspNetCore.Mvc.ViewFeatures.Internal namespace), so I was able to just inherit from the latter and override the ShouldValidate method easily.

In the ASP.NET Core 3.0 they became internal, so it's not possible to just inherit from them. I can just copy-paste the code, but it's not the ideal solution obviously.

I was following the Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks in ASP.NET Core article from MSDN, but it doesn't really mention anything relevant to my scenario.

Upvotes: 10

Views: 7546

Answers (1)

itminus
itminus

Reputation: 25350

Normally you can use [IgnoreAntiforgeryToken] attribute if you can determine at compile-time that the csrf token should be ignored. If you want such an ability at run-time, you could create a custom FilterProvider that will provide an IAntiforgeryPolicy if there's a Authroization: Bearer json-web-token header.

For example, we can create a custom AutoSkipAntiforgeryFilterProvider as below:

public class AutoSkipAntiforgeryFilterProvider: IFilterProvider
{
    private const string BEARER_STRING = "Bearer";
    public int Order => 999;
    public void OnProvidersExecuted(FilterProviderContext context) { }
    public void OnProvidersExecuting(FilterProviderContext context)
    {
        if (context == null) { throw new ArgumentNullException(nameof(context)); }
        if (context.ActionContext.ActionDescriptor.FilterDescriptors != null)
        {
            var headers = context.ActionContext.HttpContext.Request.Headers;
            if (headers.ContainsKey("Authorization"))
            {
                var header = headers["Authorization"].FirstOrDefault();
                if(header.StartsWith(BEARER_STRING,StringComparison.OrdinalIgnoreCase))
                {
                    var FilterDescriptor = new FilterDescriptor(SkipAntiforgeryPolicy.Instance, FilterScope.Last);
                    var filterItem = new FilterItem( FilterDescriptor,SkipAntiforgeryPolicy.Instance);
                    context.Results.Add(filterItem);
                }
            }
        }
    }

    // a dummy IAntiforgeryPolicy
    class SkipAntiforgeryPolicy : IAntiforgeryPolicy, IAsyncAuthorizationFilter
    {
        // a singleton 
        public static SkipAntiforgeryPolicy Instance = new SkipAntiforgeryPolicy();
        public Task OnAuthorizationAsync(AuthorizationFilterContext context) => Task.CompletedTask;
    }
}

And register this filter provider in Startup :

services.TryAddEnumerable( ServiceDescriptor.Singleton<IFilterProvider, AutoSkipAntiforgeryFilterProvider>());

Now it will bypass the AntiForgery even there's a [ValidateAntiForgeryToken]attribute.


[Demo]

Assume we have an action method annotated with [ValidateAntiForgeryToken]:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name")] XModel xModel)
{
    ....
}

Normally, it will protect this method with CSRF token. But if you send a request like:

POST /XModels/Create HTTP/1.1
Authorization: Bearer Xyz 
Content-Type: application/x-www-form-urlencoded

...

it won't validate the csrf token.

Upvotes: 16

Related Questions