Using Thinktecture Hawk Katana Authentication Middleware with ASP.NET 5.0 (ASP.NET MVC 6)

In this post, I have covered Katana middleware versus ASP.NET 5.0 middleware. Calling a normal Katana middleware that accepts AppFunc from ASP.NET 5.0 pipeline is not that difficult. You can just use the UseOwin extension method on IApplicationBuilder, like so.

app.UseOwin(addToPipeline =>
{
    addToPipeline(next =>
    {
        return new MyNormalKatanaMiddleware(next).Invoke;
    });
});


By normal Katana middleware, I mean the ones like this, with a constructor taking in AppFunc.

public class MyNormalKatanaMiddleware
{
    public MyNormalKatanaMiddleware(AppFunc next) { ... }

    public async Task Invoke(IDictionary<string, object> env) { ... }
}

However, using Katana authentication middleware is a bit tricky, since the base authentication middleware class in Katana derives from OwinMiddleware. We need to use AppBuilder to plug the Katana authentication middleware in.

To illustrate this point, I’ll now show how to use Thinktecture.IdentityModel.Hawk in an ASP.NET MVC 6 app. In Visual Studio 2015 preview, create an ASP.NET Web Application using the ASP.NET 5 Empty template. Modify project.json like so, so that the necessary dependencies are in place. BTW, we cannot use Thinktecture.IdentityModel.Hawk with core CLR (not yet). So, we have only “aspnet50” under “frameworks”.

{
    "webroot": "wwwroot",
    "dependencies": {
        "Microsoft.AspNet.Server.IIS": "1.0.0-beta1",
        "Microsoft.AspNet.Mvc": "6.0.0-beta1",
        "Microsoft.AspNet.Owin": "1.0.0-beta1",
        "Microsoft.Owin": "3.0.0",
        "Microsoft.Owin.Security": "3.0.0",
        "Thinktecture.IdentityModel.Hawk": "2.1.1.0"
    },
    "frameworks": {
        "aspnet50": { }
    }
}

Our controller looks like this. Nothing much to write home about.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Mvc;

namespace HawkAuthAspNet5.Controllers.Controllers
{
    [Authorize]
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        [HttpGet]
        public string Get()
        {
            return "Hello, " + User.Identity.Name;
        }

        [HttpPost]
        public string Post([FromBody]string name)
        {
            return String.Format("Hello, {0}. Thanks for flying Hawk", name);
        }
    }
}

And, finally the Startup class. Basically, we need to use AppBuilder and set “builder.DefaultApp” to the next pipeline component. Then, we just return the result of Build. More info here from Chris.

using Microsoft.AspNet.Builder;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Owin.Builder;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Thinktecture.IdentityModel.Hawk.Core;
using Thinktecture.IdentityModel.Hawk.Core.Helpers;
using Thinktecture.IdentityModel.Hawk.Owin;
using Thinktecture.IdentityModel.Hawk.Owin.Extensions;

namespace HawkAuthAspNet5
{
    using AppFunc = Func<IDictionary<string, object>, Task>;
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app)
        {
            var credentialStorage = new List<Credential>()
            {
                new Credential()
                {
                    Id = "dh37fgj492je",
                    Algorithm = SupportedAlgorithms.SHA256,
                    User = "Steve",
                    Key = Convert.FromBase64String(
                              "wBgvhp1lZTr4Tb6K6+5OQa1bL9fxK7j8wBsepjqVNiQ=")
                }
            };

            var options = new Options()
            {
                ClockSkewSeconds = 60,
                LocalTimeOffsetMillis = 0,
                CredentialsCallback = (id) => credentialStorage.FirstOrDefault(
                                                                 c => c.Id == id),
                ResponsePayloadHashabilityCallback = (r) => true,
                VerificationCallback = (request, ext) =>
                {
                    if (String.IsNullOrEmpty(ext))
                        return true;

                    string name = "X-Request-Header-To-Protect";
                    return ext.Equals(name + ":" + request.Headers[name].First());
                }
            };

            app.UseOwin(addToPipeline =>
            {
                addToPipeline(next =>
                {
                    var appBuilder = new AppBuilder();
                    appBuilder.Properties["builder.DefaultApp"] = next;

                    appBuilder.UseHawkAuthentication(
                                    new HawkAuthenticationOptions(options));

                    return appBuilder.Build<AppFunc>();
                });
            });


            app.UseMvc();
        }
    }
}

That’s pretty much the server-side code. To call the Web API, we can just use a Console app.

using System;
using System.Linq;
using System.Net.Http;
using Thinktecture.IdentityModel.Hawk.Client;
using Thinktecture.IdentityModel.Hawk.Core;
using Thinktecture.IdentityModel.Hawk.Core.Helpers;

namespace HawkClientApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string uri = "http://localhost:25532/api/values/";

            var credential = new Credential()
            {
                Id = "dh37fgj492je",
                Algorithm = SupportedAlgorithms.SHA256,
                User = "Steve",
                Key = Convert.FromBase64String("wBgvhp1lZTr4Tb6K6+5OQa1bL9fxK7j8wBsepjqVNiQ=")
            };

            // GET and POST using the Authorization header
            var options = new ClientOptions()
            {
                CredentialsCallback = () => credential,
                RequestPayloadHashabilityCallback = (r) => true,
                NormalizationCallback = (req) =>
                {
                    string name = "X-Request-Header-To-Protect";
                    return req.Headers.ContainsKey(name) ?
                                name + ":" + req.Headers[name].First() : null;
                }
            };

            var handler = new HawkValidationHandler(options);

            HttpClient client = HttpClientFactory.Create(handler);
            client.DefaultRequestHeaders.Add("X-Request-Header-To-Protect", "secret");

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

            response = client.PostAsJsonAsync(uri, credential.User).Result;
            Console.WriteLine(response.Content.ReadAsStringAsync().Result);

            Console.Read();
        }
    }
}

The full source code of ASP.NET MVC 6 project and the client console app is here.

I have rewritten Hawk library in ASP.NET 5.0 on an experimental basis and the source code is here. Since ASP.NET 5.0 is being actively worked upon at this point in time, there will be changes to this middleware.

Advertisements

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