A WebAPI Basic Authentication MessageHandler
In my last post I showed a very simple Basic Authentication Filter implementation and several people commented that the 'right' way to do this is by using a MessageHandler instead. In the post I discussed why I opted for a filter rather than the MessageHandler: A filter is much simpler to implement and keeps all the relevant code pieces in one place instead of scattering them throughout the Web API pipeline. This might not be the right choice for all authentication, but if you're doing custom authentication/authorization in your app you're not going to mix and match and plug a multitude of auth mechanisms together. For simple auth scenarios a filter is just fine, especially since even when you implement a MessageHandler you need to implement an AuthorizationFilter anyway.
Just as an exercise, I spend a little time today to put together a message handler based Basic Authentication implementation to contrast the two. There are a few more moving pieces to this implementation:
- A MessageHandler to handle the Basic Auth processing
- A custom Identity to pass the username and password around
- An Authorization filter to validate the user
MessageHandler for Authentication
MessageHandlers in Web API are chainable components that hook into the request/response pipeline. You can plug many message handlers together to provide many module like features. MessageHandlers can handle processing on the inbound request cycle and the output response cycle, via a simple Task<T> abstraction that provides the asynchronous pipeline processing.
To implement the BasicAuthenticationHandler you can create a class derived from DelegatingHandler and override the SendAsync method:
public class BasicAuthenticationHandler : DelegatingHandler { private const string WWWAuthenticateHeader = "WWW-Authenticate"; protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var credentials = ParseAuthorizationHeader(request); if (credentials != null) { var identity = new BasicAuthenticationIdentity(credentials.Name, credentials.Password); var principal = new GenericPrincipal(identity, null); Thread.CurrentPrincipal = principal; //if (HttpContext.Current != null) // HttpContext.Current.User = principal; } return base.SendAsync(request, cancellationToken) .ContinueWith(task => { var response = task.Result; if (credentials == null && response.StatusCode == HttpStatusCode.Unauthorized) Challenge(request, response); return response; }); } /// <summary> /// Parses the Authorization header and creates user credentials /// </summary> /// <param name="actionContext"></param> protected virtual BasicAuthenticationIdentity ParseAuthorizationHeader(HttpRequestMessage request) { string authHeader = null; var auth = request.Headers.Authorization; if (auth != null && auth.Scheme == "Basic") authHeader = auth.Parameter; if (string.IsNullOrEmpty(authHeader)) return null; authHeader = Encoding.Default.GetString(Convert.FromBase64String(authHeader)); var tokens = authHeader.Split(':'); if (tokens.Length < 2) return null; return new BasicAuthenticationIdentity(tokens[0], tokens[1]); } /// <summary> /// Send the Authentication Challenge request /// </summary> /// <param name="message"></param> /// <param name="actionContext"></param> void Challenge(HttpRequestMessage request, HttpResponseMessage response) { var host = request.RequestUri.DnsSafeHost; response.Headers.Add(WWWAuthenticateHeader, string.Format("Basic realm=\"{0}\"", host)); } }If you looked at my last post this should look fairly familiar - the basic auth logic is very similar to the filter. I reused the Challenge and ParseAuthorizationHeader changing just the inputs to the request and response messages respectively.
The message handler works in two distinct steps - the initial code that fires on the inbound request, which tries to parse the authentication header into a BasicAuthenticationIdentity and assigning that identity to the thread principle.
The second step - the part in the ContinueWith() Task block - handles the processing on the outbound response. Things have to be broken up like this in a MessageHandler because the Response doesn't exist on the inbound request yet. The code here is responsible for issuing the challenge if the response status is unauthorized.
So the logic goes like this:
- Request is authenticated already - goes through
- Request is not authenticated and returns a 401 (from an AuthFilter or explicit 401 ResponseMessage from code)
- Request is not authenticated and returns something other 401 - request goes through
To make all this work there are a couple more things that need to be implemented.
BasicAuthenticationIdentity
Basic Authentication works via a username and password that is passed as a base64 encoded, clear text string. In order to authorize the user in a custom authorization scenario that username and password has to be passed up the pipeline into the AuthorizationFilter that actually handles the authorization of the user.
To do this I opted to create a BasicAuthenticationIdentity class. Using this identity the handler can set the username and password on the Identity and pass it to AuthorizeFilter. Here's the implementation:
/// <summary> /// Custom Identity that adds a password captured by basic authentication /// to allow for an auth filter to do custom authorization /// </summary> public class BasicAuthenticationIdentity : GenericIdentity { public BasicAuthenticationIdentity(string name, string password) : base(name,"Basic") { this.Password = password; } /// <summary> /// Basic Auth Password for custom authentication /// </summary> public string Password { get; set; } }
AuthorizeFilter
Next we need a filter to handle the authorization of the user. This logic most likely will be application specific. Because all we'll need to do here is validate the user's credentials and return yay or nay, an AuthorizeFilter is the easiest:
public class MyAuthorizationFilter : AuthorizeAttribute { protected override bool IsAuthorized(HttpActionContext actionContext) { var identity = Thread.CurrentPrincipal.Identity; if (identity == null && HttpContext.Current != null) identity = HttpContext.Current.User.Identity; if (identity != null && identity.IsAuthenticated) { var basicAuth = identity as BasicAuthenticationIdentity; // do your business validation as needed var user = new BusUser(); if (user.Authenticate(basicAuth.Name, basicAuth.Password)) return true; } return false; } }
In the filter you can simply override the IsAuthorized() method and return true or false. If you return false WebAPI automatically fires a 401 status code, which triggers the Challenge() in the BasicAuthenticationHandler that's monitoring for 401's.
The IsAuthorized method implementation typically has business specific code in it that handles the authorization of the user. Basically you can capture the Thread Principal and the BasicAuthenticationIdentity and retrieve the username and password. You can then go to town on the username and password. In my example here a business object is fired up to authenticate the user.
By the way, notice that in my last post I used an AuthorizationFilter - and here I'm using an AuthorizeFilter. AuthorizeFilter works great if all you need to do is validate a user and return true or false. If there's more logic involved that, like creating a new response then an AuthorizationFilter is the better choice.
Configuration
Once the handler and filter exist they have to be hooked up. MessageHandlers have to be added in the configuration:
GlobalConfiguration.Configuration.MessageHandlers.Add(new BasicAuthenticationHandler());
The AuthorizationFilter can either be applied via global configuration or on the controller:
GlobalConfiguration.Configuration.Filters.Add(new MyAuthorizationFilter());
or you can apply it on the controller:
[MyAuthorizationFilter] public class QueueController : ApiController
Filter or MessageHandler - you decide
Comparing the two modes of operation - Authentication MessageHandler or AuthorizationFilter - there's not a tremendous difference in implementation. To me the filter is more compact and easier to follow what's going on simply because everything is in one place. For most typical custom login scenarios that are tied to business logic, that'll be totally sufficient. The advantage of a message handler is that it's globally applied and is part of the WebAPI pipeline so if several components need to take advantage of BasicAuthentication with different Authorization that would work. But then again you can do that with a filter as well, especially since a MessageHandler still requires a filter for it's authorization. <shrug>
Either way you can take your pick from these two implementations.
Resources
The Voices of Reason
# re: A WebAPI Basic Authentication MessageHandler
If the code is strictly for .NET 4.5 then the await is the way to go, but if also support 4.0 then Task.ContinueWith() is it.
# re: A WebAPI Basic Authentication MessageHandler
# re: A WebAPI Basic Authentication MessageHandler
# re: A WebAPI Basic Authentication MessageHandler
if (identity != null && identity.IsAuthenticated)
{
var basicAuth = identity as BasicAuthenticationIdentity;
var User = DatabaseDb.Authenticate(basicAuth.Name, basicAuth.Password.CreateHash());
if (User != null)
return true;
}
return false;
This line of code throws error when the user fails authentication:
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
var response = task.Result; //AggregateException was unhandled by usercode
//Type 'BasicAuthenticationIdentity' in assembly is not marked as serializable.
if (credentials == null && response.StatusCode == HttpStatusCode.Unauthorized)
Challenge(request, response);
return response;
});
after debugging I notice task.Result shows as {not computed yet} ?
# re: A WebAPI Basic Authentication MessageHandler
# re: A WebAPI Basic Authentication MessageHandler
The biggest reason for preferring a MessageHandler over a filter is that the handler is invoked very early in the pipeline and can create the response directly, thus skipping the rest of the pipeline. It also prevents exploits targeting modules deeper in the pipeline -- code that isn't executed is very secure.
# re: A WebAPI Basic Authentication MessageHandler
# re: A WebAPI Basic Authentication MessageHandler
# re: A WebAPI Basic Authentication MessageHandler
Also took a look at the source and discovered your toolbox.
I grabbed a copy and will have a look at it later but the least I can do shout out loud:
THANKS! (caps intended)
Though through a lot of confusing a finally have made my way through ASP.NET Identity and I have my with it - but you can never get enough of good code samples to feed your codemonkey when he is grumbling over stubborn code.
Have a very good day.
By the way - forgive me - I recently found that http://jw.org can feed your minds hunger for answers to big questions of life.
# re: A WebAPI Basic Authentication MessageHandler
Is there a way to get this working again under the current version of WebAPI?
# re: A WebAPI Basic Authentication MessageHandler
var principal = new GenericPrincipal (identity, new string [] {role});
how can I authorize methods of the controller with the role?
Thank You
JK
# re: A WebAPI Basic Authentication MessageHandler
This was the one problem I had, as the request would come in but nothing told me about the IIS/basic auth issue. For those who don't know, if basic authentication is enabled in web.config, IIS will intercept the authentication routine and your authorization/authentication handlers and attributes will never be hit while debugging.
Thanks, though. The rest of the article was great!
# re: A WebAPI Basic Authentication MessageHandler
Great post(s), very helpful for me, especially as I'm a complete novice in this area.
I would like to persist the user information to my api controller...so that when I authenticate them I am able to tell which user exactly it is so that I can perform the correct tasks in my action methods. For instance if I call the API with my username and password encryption, and the method is to return my messages, I want to be able to know it is me in the API so I can query the db accordingly.
Have you any advice in this regard?
Also I'm having trouble integrating this because I have added Web Api into an existing MVC project, and when it fails this authorization it is strangely still returning a 200 response with a login page cshtml. Is there anything I need to change in my pipeline when integrating this with an existing MVC site?
Any help would be VERY appreciated, thanks a lot for the post.
# re: A WebAPI Basic Authentication MessageHandler
Have you tried something like:
var response = await base.SendAsync(request, cancellationToken); if (credentials == null && response.StatusCode == HttpStatusCode.Unauthorized) Challenge(request, response); return response;Instead of:
return base.SendAsync(request, cancellationToken) .ContinueWith(task => { var response = task.Result; if (credentials == null && response.StatusCode == HttpStatusCode.Unauthorized) Challenge(request, response); return response; });