Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authentication in WebApi with AllowAnonymous attribute

I have implemented a JWT-based authentication by inheriting DelegatingHandler and adding the class as configuration.MessageHandlers.Add(new MyDelegatingHandler()).

When implementing DelegatingHandler, I override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken). The logic is simple there - I retrieve a token from Authorization header, check it's validity. If it is valid - I set Thread.CurrentPrincipal and HttpContext.Current.User, otherwise I return new HttpResponseMessage(HttpStatusCode.Unauthorized)

Basically it looks like this (very simplified):

public class TokenValidationHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = GetTokenFromAuthorizeHeader(request);
        if (TokenIsValid(token)) {
           var principal = CreatePrincipal(token);
           Thread.CurrentPrincipal = principal;
           HttpContext.Current.User = principal;
           return base.SendAsync(request, cancellationToken);
        } else {
           // TODO: fix
           return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(HttpStatusCode.Unauthorized));
        }
    }
}

Now this method is called even on WebApi method that has [AllowAnonymous] attribute with it. This is good because I want to set principal even if the method allows anonymous. But this logic fails if the supplied token in Authorization header is invalid.

User sends a request to resource with [AllowAnonymous] and with an invalid token in Authorization header, which should come through because the resourse allows anonymous, but my code checks authorization, find out that the token is invalid and sends HttpStatusCode.Unauthorized.

The fix would be to check where // TODO: fix is whether the resource the user is accessing allows anonymous or not, and only send HttpStatusCode.Unauthorized if it is not, but I can't figure out how to properly do this.

How can I do this?

like image 618
Archeg Avatar asked May 24 '18 08:05

Archeg


People also ask

What is AllowAnonymous attribute in Web API?

One of the new features in ASP.NET MVC 4 is the AllowAnonymous Attribute that helps you secure an entire ASP.NET MVC 4 Website or Controller while providing a convenient means of allowing anonymous users access to certain controller actions, like the login and register Actions.

What is AllowAnonymous attribute?

[AllowAnonymous] bypasses all authorization statements. If you combine [AllowAnonymous] and any [Authorize] attribute, the [Authorize] attributes are ignored. For example if you apply [AllowAnonymous] at the controller level, any [Authorize] attributes on the same controller (or on any action within it) are ignored.

Which type of authentication is used in Web API?

There are four ways to authenticate when calling a web API: API key authentication. Basic authentication. OAuth 2.0 Client Credentials Grant.


1 Answers

1. Authentication Filter

Use Authentication Filters, which Web API 2 introduced. They perform just authentication, but say nothing about if user has access to a resource or not. Exactly what you need.

public class JwtAuthenticationFilter : IAuthenticationFilter
{
    public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
    {
        var token = GetTokenFromAuthorizeHeader(context.Request);
        if (TokenIsValid(token)) {
            var principal = CreatePrincipal(token);
            // Use context.Principal instead of Thread.CurrentPrincipal
            // and HttpContext.Current.User whenever.
            context.Principal = principal;
        }
        return Task.CompletedTask;
    }

    // TODO: Implement remaining IAuthencitaionFilter members.
}

Apply this JwtAuthenticationFilter globally to perform authentication for all requests:

// httpConfig is an instance of HttpConfiguration
httpConfig.Filters.Add(new JwtAuthenticationFilter());

In this way user is authenticated if token is OK. But still all users have access to your API - even the ones with invalid token. Let's move further and protect it.

2. AuthorizeAttribute

AuthorizeAttribute is what you need to restrict access to Web API from unauthenticated users. You can apply it globally by using the same approach as above:

httpConfig.Filters.Add(new AuthorizeAttribute());

The one who doesn't have valid token won't pass. Good. The last step is to allow access to some particular resources for users with invalid tokens.

3. AllowAnonymousAttribute

From now on AllowAnonymousAttribute should work. Basically AuthorizeAttribute just checks if resource is marked by [AllowAnonymous] and skips authorization in this case.


Conclusion

Generally Web API is protected from unauthenticated users, but some resources can disable authorization by applying [AllowAnonymous]. JwtAuthenticationFilter we implemented above works in any case, so users with valid token will be always authenticated - even if a resource allows anonymous access.

like image 94
Yuriy Oborozhnyi Avatar answered Oct 16 '22 07:10

Yuriy Oborozhnyi