2
votes

I have an intranet site, hosted locally within my organisation. The same site also exposes some data through various web services. It's written using ASP.NET MVC 5 and WebAPI 2, and it's .NET 4.5, not Core.

At the moment users can login to the website using Windows Authentication, and once authenticated they can access the APIs. However, I need to also allow access to the APIs using tokens so that they can be interrogated by automated processes, so I've created a page where authenticated users can go and request a token.

It's my intention that this token can be used as a Bearer token, included in the header of HTTP requests to the Web API, to allow access to the APIs. As I understand it, a Bearer token intrinsically represents the User's right to access the data and doesn't require any other information (even a username).

However, I've struggled to find a complete, end-to-end tutorial for authenticating and authorizing the requests. There are questions on this site and Microsoft aritcles which give some great pointers but I feel that they're perhaps hinting at something much too complicated for my requirements. I don't need to return any kind of Identity with Claims or anything like that, and I'm not concerned with OAuth at all.

I'm using Microsoft's Web API framework so it seems reasonable to assume that it should be fairly straightforward to do something as basic as extract and check a token from the request header!

Would somebody be able to outline the components and the process I need to put in place within my application to allow it to extract the Bearer token from the HTTP request, use my own code to check its validity and then support the Authorize attribute on Web API Methods if the token is valid?

2
You don't need to do anything manually, most is covered already. Take a look at the docs: docs.microsoft.com/en-us/aspnet/web-api/overview/security/… - Camilo Terevinto
I've read those docs several times, but I'm still not clear on what is the bare minimum required to simply check a Bearer token - I don't want tons of references and classes in my project that I don't really need. And I definitely do need to do something manually to verify the token - I don't know what the default code in that documentation considers valid for a token, but I need to do certain checks against my database for my application consider it valid. - Philip Stratford
are you running the web api on owin? Do you have a Startup.cs or Global.asax? - Marcus Höglund
I have a Global.asax but not a Startup.cs. I did add OWIN to my project at one point whilst trying to follow some of the online tutorials, along with a bunch of other libraries, but stripped them all out before writing this question because, again, I'm trying to achieve this whilst only adding the references I actually need. - Philip Stratford
The HTTP request has an Authorization header with the Bearer token. If you want, you can manually parse this. Pseudocode - request.Headers["Authorization"].Split(" ")[1] should give you the token. - Mina

2 Answers

4
votes

Looks like we have the same need, I also just needed a quick bearer token verification to not leave the API completely wide open.

I copied most parts from here and tweaked it so it just checks the Bearer token https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/authentication-filters

Add filter in WebApiConfig.cs

public class WebApiConfig    
{    
    public static void Register(HttpConfiguration config)    
    {    
        // Add authentication    
        config.Filters.Add(new SimpleAuthenticationFilter()):  
        foo
    }  
}

SimpleAuthenticationFilter.cs

public class SimpleAuthenticationFilter : IAuthenticationFilter
{
    private readonly string _bearerToken = ConfigurationManager.AppSettings["simpleToken"];
    public bool AllowMultiple { get; }

    public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
    {
        // 1. Look for credentials in the request.
        var request = context.Request;
        var authorization = request.Headers.Authorization;

        // 2. If there are no credentials, do nothing.
        if (authorization == null)
        {
            context.ErrorResult = new AuthenticationFailureResult("Authorization header is 'null''", request);
            return;
        }

        // 3. If there are credentials but the filter does not recognize the 
        //    authentication scheme, do nothing.
        if (!authorization.Scheme.Equals("Bearer"))
        {
            context.ErrorResult = new AuthenticationFailureResult("Authentication type must be 'Bearer'", request);
            return;
        }

        // 4. If there are credentials that the filter understands, try to validate them.
        // 5. If the credentials are bad, set the error result.
        if (string.IsNullOrEmpty(authorization.Parameter))
        {
            context.ErrorResult = new AuthenticationFailureResult("Bearer token is null or empty", request);
            return;
        }

        if (!authorization.Parameter.Equals(_bearerToken))
        {
            context.ErrorResult = new AuthenticationFailureResult("Bearer token invalid", request);
        }
    }

    public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
    {
        return Task.FromResult(0);
    }
}

AuthenticationFailureResponse.cs

  public class AuthenticationFailureResult : IHttpActionResult
  {
    public AuthenticationFailureResult(string reasonPhrase, HttpRequestMessage request)
    {
        ReasonPhrase = reasonPhrase;
        Request = request;
    }

    private string ReasonPhrase { get; }

    private HttpRequestMessage Request { get; }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(Execute());
    }

    private HttpResponseMessage Execute()
    {
        var response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
        {
            RequestMessage = Request, ReasonPhrase = ReasonPhrase
        };
        return response;
    }
}
1
votes

Expanding on Min's answer above:

string token = Request.Headers.Authorization.ToString().Split(' ')[1];