Basic Authentication with ASP.NET Web API Using OWIN Middleware

One of the decisions to be made while implementing authentication for ASP.NET Web API is where to implement the authentication logic – message handler, authorization filter or HTTP module. Authorization filter is a bad choice for the obvious reason that it is for authorization and not authentication. For message handler versus HTTP module, a good read is the ASP.NET site itself. A rule of thumb is to use an HTTP module if Web API is going to be exclusively web-hosted and to use a message handler otherwise. One of the greatest advantages of a message handler is that it is host-agnostic but the downside is that the principal set in the message handler reverts back to the previous principal when the response leaves the web API pipeline. If you use IIS logs, for example, it will know nothing about the principal you set in the message handler. HTTP module locks you into IIS but it has it’s own advantage. Notable one being the fact that IIS/ASP.NET does recognize the principal set from the HTTP module. For host-agnostic reasons, in the book that I have written Pro ASP.NET Web API Security, I have extensively used message handlers.

In ASP.NET Web API 2, there is supposed to be an authentication filter but I do not have much of information on that [UPDATE 2/13/2014 – Here is a blog post on authentication filter]. However, with OWIN coming into the picture, there is one more choice for implementing authentication – an OWIN middleware. In fact, if you look at the source of Katana, Microsoft.Owin.Security classes use OWIN middleware for authentication. Advantage of using an OWIN middleware is that the identity established in the middleware can be shared by all the frameworks used in your application. For example, if you are using two frameworks, say ASP.NET Web API and SignalR, the identity established in an OWIN middleware will apply to both the frameworks. If you use message handler, the identity will be applicable only to ASP.NET Web API.

In this post, I show you how to create an OWIN middleware to implement HTTP basic authentication. Microsoft.Owin.Security has classes to do this for you but the point of this post is to only show you how to write an OWIN middleware. Perhaps the right way of doing this will be to derive from AuthenticationMiddleware in the namespace Microsoft.Owin.Security.Infrastructure, but just for the heck of it, I derive from OwinMiddleware, the same class that AuthenticationMiddleware derives from. Deriving from AuthenticationMiddleware will be the subject of a future post.

Create a console application and add the following NuGet packages.

<packages>
  <package id="Microsoft.AspNet.WebApi.Client" version="5.0.0-beta2"
                                              targetFramework="net45" />
  <package id="Microsoft.AspNet.WebApi.Core" version="5.0.0-beta2"
                                              targetFramework="net45" />
  <package id="Microsoft.AspNet.WebApi.Owin" version="5.0.0-beta2"
                                              targetFramework="net45" />
  <package id="Microsoft.Owin" version="1.1.0-beta2"
                                              targetFramework="net45" />
  <package id="Microsoft.Owin.Host.HttpListener" version="1.1.0-beta2"
                                              targetFramework="net45" />
  <package id="Microsoft.Owin.Hosting" version="1.1.0-beta2"
                                              targetFramework="net45" />
  <package id="Newtonsoft.Json" version="4.5.11"
                                              targetFramework="net45" />
  <package id="Owin" version="1.0" targetFramework="net45" />
</packages>

Create the middleware class deriving from OwinMiddleware. Unlike Web API message handlers, you will not be able to add any response header after calling Next.Invoke(). Well, you can but the response will not have that header. You will need to do that in the OnSendingHeaders callback. Thanks to Youssef Moussaoui for answering my question on this topic, in Stack Overflow.

public class AuthenticationMiddleware : OwinMiddleware
{
    public AuthenticationMiddleware(OwinMiddleware next) :
                                                   base(next) { }

    public override async Task Invoke(OwinRequest request,
                                            OwinResponse response)
    {

        request.OnSendingHeaders(state =>
        {
            var resp = (OwinResponse)state;

            if (resp.StatusCode == 401)
                resp.SetHeader("WWW-Authenticate", "Basic");
        }, response);

        var header = request.GetHeader("Authorization");

        if (!String.IsNullOrWhiteSpace(header))
        {
            var authHeader = System.Net.Http.Headers
                               .AuthenticationHeaderValue.Parse(header);

            if ("Basic".Equals(authHeader.Scheme,
                                     StringComparison.OrdinalIgnoreCase))
            {
                string parameter = Encoding.UTF8.GetString(
                                      Convert.FromBase64String(
                                            authHeader.Parameter));
                var parts = parameter.Split(':');

                string userName = parts[0];
                string password = parts[1];

                if (userName == password) // Just a dumb check
                {
                    var claims = new[]
                    {
                        new Claim(ClaimTypes.Name, "Badri")
                    };
                    var identity = new ClaimsIdentity(claims, "Basic");

                    request.User = new ClaimsPrincipal(identity);
                }
            }
        }

        await Next.Invoke(request, response);
    }
}

Create the Startup class. We register the middleware here.

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Use(typeof(AuthenticationMiddleware));

        HttpConfiguration config = new HttpConfiguration();

        config.Routes.MapHttpRoute(
              "DefaultWebApi",
                  "{controller}/{id}",
                        new { id = RouteParameter.Optional });

        app.UseWebApi(config);
    }
}

Main method in the Program class: just calls WebApp.Start and waits.

class Program
{
    static void Main(string[] args)
    {
        const string baseUrl = "http://localhost:12345/";

        using (WebApp.Start<Startup>(new StartOptions(baseUrl)))
        {
            Console.WriteLine("Press Enter to terminate.");
            Console.Read();
        }
    }
}

Finally, the controller class. [Authorize] ensures the identity is authenticated for the action method to execute.

[Authorize]
public class ValuesController : ApiController
{
    public HttpResponseMessage Get()
    {
        return Request.CreateResponse<string>(
                    "Hello, " + User.Identity.Name);
    }
}

Run the console app and go to http://localhost:12345/values in Internet Explorer. Authorize attribute sends back a 401, since the identity is not an authenticated identity at this point. We catch it in the middleware (OnSendingHeaders callback) and send back a WWW-Authenticate: Basic header. On receiving 401 with WWW-Authenticate: Basic, IE pops up the dialog and asks for the user name and password. Enter some user name and repeat the same in the password text box. This time IE sends Authorize header and our middleware creates the principal and sets it in request.User. This time around, [Authorize] is happy because principal has been set and we just return a friendly message using User.Identity.Name from the action method.

Advertisements

4 thoughts on “Basic Authentication with ASP.NET Web API Using OWIN Middleware

  1. Wonderful advice on this website, thanks much for presenting. I don’t know how I passed on in this web site really but so far I am happy with what I’ve learned.

  2. Thank you for this. I had to make a few changes (couple minor errors, and changes to released version of Microsoft.Owin):

    public override async Task Invoke(IOwinContext context)
    {
    var response = context.Response;
    var request = context.Request;

    response.OnSendingHeaders(state =>
    {
    var resp = (OwinResponse)state;

    if (resp.StatusCode == 401)
    {
    resp.Headers.Add(“WWW-Authenticate”, new [] {“Basic”});
    resp.StatusCode = 403;
    resp.ReasonPhrase = “Forbidden”;
    }
    }, response);

    var header = request.Headers[“Authorization”];

    if (!String.IsNullOrWhiteSpace(header))
    {
    var authHeader = System.Net.Http.Headers.AuthenticationHeaderValue.Parse(header);

    if (“Basic”.Equals(authHeader.Scheme, StringComparison.OrdinalIgnoreCase))
    {
    var parameter = Encoding.UTF8.GetString(Convert.FromBase64String(authHeader.Parameter));

    var parts = parameter.Split(‘:’);

    var username = parts[0];
    var password = parts[1];

    if (ValidateUser(username, password))
    {
    var claims = new[]
    {
    new Claim(ClaimTypes.Name, username)
    };
    var identity = new ClaimsIdentity(claims, “Basic”);
    request.User = new ClaimsPrincipal(identity);
    }
    }
    }

    await Next.Invoke(context);
    }

    private bool ValidateUser(string username, string password)
    {
    // do actual password validation here
    return username != password;
    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s