Using tt.idm Hawk Authentication OWIN Middleware with IIS-Hosted ASP.NET Web API

Hawk Authentication in Thinktecture.IdentityModel can be hooked into your ASP.NET Web API through the message handler (HawkAuthenticationHandler) or the OWIN middleware (HawkAuthenticationMiddleware). The sample here is based on a self-hosted web API (WCF channel stack) using the message handler and another self-hosted web API (OWIN host adapter) using the OWIN middleware. It is possible to use the OWIN middleware and enable Hawk authentication for your ASP.NET Web API which is hosted in IIS. Of course, you can use the message handler, which is the simplest option but we will see how message handler measures up to OWIN middleware in the case of hosting in IIS.

Using the Message Handler

Create an ASP.NET MVC project using Web API template. Go to Properties > Web and make sure it runs in local IIS server (not the express one). Modify ValuesController like so.

public class ValuesController : ApiController
{
    [Authorize(Users="Steve")]
    public string Get()
    {
        return User.Identity.Name;
    }
}

Add the Thinktecture.IdentityModel.Hawk and Microsoft.Owin.Security packages from the PM Console like so.

Install-Package Thinktecture.IdentityModel.Hawk -Pre
Install-Package Microsoft.Owin.Security

Now, hook up HawkAuthenticationHandler. Here is the WebApiConfig class.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // config.Routes.MapHttpRoute and the rest of the code

        var options = new Options()
        {
            ClockSkewSeconds = 60,
            LocalTimeOffsetMillis = 0,
            CredentialsCallback = (id) => new Credential()
            {
                Id = "dh37fgj492je",
                Algorithm = SupportedAlgorithms.SHA256,
                User = "Steve",
                Key = "werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn"
            }
        };

        config.MessageHandlers.Add(
                       new HawkAuthenticationHandler(options));
    }
}

Create a client app (Console App) and add the Thinktecture.IdentityModel.Hawk and Microsoft.AspNet.WebApi.Client packages.

Install-Package Thinktecture.IdentityModel.Hawk -Pre
Install-Package Microsoft.AspNet.WebApi.Client

Here is the Main() method of the client.

static void Main(string[] args)
{
    string uri = "http://localhost/myappiniis/api/values";

    var credential = new Credential()
    {
        Id = "dh37fgj492je",
        Algorithm = SupportedAlgorithms.SHA256,
        User = "Steve",
        Key = "werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn"
    };

    var options = new ClientOptions()
    {
        CredentialsCallback = () => credential
    };

    var handler = new HawkValidationHandler(options);
    HttpClient client = HttpClientFactory.Create(handler);

    var response = client.GetAsync(uri).Result;
    Console.WriteLine(
		response.Content.ReadAsStringAsync().Result);

    Console.Read();
}

With that, run the client. Authentication should go through successfully and you should see the name of the identity, “Steve”, in this case getting returned by Web API. However, if you now open the IIS log, cs-username field will not contain anything, since IIS knows nothing about the identity established in the message handler. In fact, other components running in the IIS pipeline, say an HTTP module will also know nothing about the identity, even though authorization enforced on the action method by the Authorize attribute works just fine. Let’s now add an HTTP module and see how it works. Here is the module.

public class MyModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.PostAuthenticateRequest += OnPostAuthenticateRequest;
    }

    void OnPostAuthenticateRequest(object sender, EventArgs e)
    {
        var userName = HttpContext.Current.User.Identity.Name;
    }

    public void Dispose() { }
}

Add this module to Web.config under <system.webServer> like so.

<modules>
      <add name="MyModule" type="yourns.MyModule" />
</modules>

If you now put a break point in the OnPostAuthenticateRequest method and see what the userName variable contains, it will be just an empty string, even though User.Identity.Name returned by web API is “Steve” and that authorization based on the user name of “Steve” still works. So, the ASP.NET Web API framework is happy with the identity established in the message handler but neither IIS nor any of the components running in ASP.NET pipeline are.

Using the OWIN Middleware

Now, let us see how the previous scenario works out, if we use the OWIN middleware with IIS, instead of the message handler. First, comment out the line config.MessageHandlers.Add(new HawkAuthenticationHandler(options)); in WebApiConfig. This will ensure the message handler no longer runs. Second, plug the middleware in by bringing the Microsoft.Owin.Host.SystemWeb package in by running the following command from the Package Manager Console.

Install-Package Microsoft.Owin.Host.SystemWeb

Create the Startup class like so.

using Microsoft.Owin;
using Owin;
using Thinktecture.IdentityModel.Hawk.Core;
using Thinktecture.IdentityModel.Hawk.Core.Helpers;
using Thinktecture.IdentityModel.Hawk.Owin;
using Thinktecture.IdentityModel.Hawk.Owin.Extensions;

[assembly: OwinStartup(typeof(YourNamespace.Startup))]
namespace YourNamespace
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var options = new Options()
            {
                ClockSkewSeconds = 60,
                LocalTimeOffsetMillis = 0,
                CredentialsCallback = (id) => new Credential()
                {
                    Id = "dh37fgj492je",
                    Algorithm = SupportedAlgorithms.SHA256,
                    User = "Steve",
                    Key = "werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn"
                }
            };

            app.UseHawkAuthentication(
                          new HawkAuthenticationOptions(options));
        }

    }
}

Now, rebuild the solution and run ASP.NET Web API and the client application. When the execution breaks inside the HTTP module, you will see that the userName variable is now set to “Steve”. Also, the IIS log will have Steve in the cs-username field.

The OWIN middleware is plugged into the pipeline using the extension method UseHawkAuthentication in the HawkAuthenticationExtension class. This method calls the UseStageMarker method to ensure the middleware runs at the authentication stage like so.

public static IAppBuilder UseHawkAuthentication(this IAppBuilder app,
                                       HawkAuthenticationOptions options)
{
    app.Use(typeof(HawkAuthenticationMiddleware), app, options);
    app.UseStageMarker(PipelineStage.Authenticate);
    return app;
}

Because of this, IIS and the other components running in ASP.NET pipeline suddenly becomes aware of the identity established by the middleware, which is a much better situation to be in! Here is a great article on the topic of running OWIN middleware in IIS and it provides more info on stage markers.

One thought on “Using tt.idm Hawk Authentication OWIN Middleware with IIS-Hosted ASP.NET Web API

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.