Writing Custom Middleware for ASP.NET

In my last article I decoded a JSON Web Token to get the authorization information. This was a follow on from my prior articles about submitting a JSON Web Token via the Aurelia HTTP Client, authenticating the client side in Aurelia using the Auth0 service, and getting a JSON Web Token from Auth0. However, I left the token decoding a little unfinished. Yes, I decoded a token, but from within an ASP.NET Controller. The normal way to do authorization is with middleware.

Sidestepping – Middleware?

When you do a request to an ASP.NET Web Application (whether it be standalone, MVC or WebAPI), your request goes through a series of software “pipes”. Pipes can handle the request, response or both and modify either. You are already familiar with a pipe – the ASP.NET MVC handler is an example of a pipe. ASP.NET Identity is also a pipe. These pipes are called middleware. They are configured in the Configure method of the Startup.cs like this:

        public void Configure(IApplicationBuilder app)
        {
            app.UseErrorPage(ErrorPageOptions.ShowAll);
            app.UseStaticFiles();
            app.UseJsonWebTokenAuthorization();
            app.UseMvc();
        }

The highlighted line configures the new middleware. It doesn’t exist yet, so expect a red squiggly line.

Injecting Middleware

To inject that JsonWebTokenAuthorization middleware I have to write an extension class that allows me to inject it. I’ve created a folder called Middleware and created a file called JWTExtensions.cs in that folder with the following contents:

using aurelia_2.Middleware;

namespace Microsoft.AspNet.Builder
{
    public static class JWTExtensions
    {
        public static IApplicationBuilder UseJsonWebTokenAuthorization(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<JsonWebTokenAuthorization>();
        }
    }
}

The highlighted line creates an object from the JsonWebTokenAuthorization class (this is our middleware) and tells the ASP.NET pipeline builder to use it as middleware. The JsonWebTokenAuthorization will be underlined in that red squiggly line because we haven’t written any middleware yet.

A Simple Middleware Example

I needed to create a simple middleware example for investigation. What did the middleware get fed? Could I access everything I needed to access? Questions like this are really only answered by setting a breakpoint and looking at the data. I created a JsonWebTokenAuthorization.cs class in the Middleware folder with these contents:

using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;

namespace aurelia_2.Middleware
{
    public class JsonWebTokenAuthorization
    {
        private readonly RequestDelegate next;

        public JsonWebTokenAuthorization(RequestDelegate next)
        {
            this.next = next;
        }

        public Task Invoke(HttpContext context)
        {
            Debug.WriteLine("In JsonWebTokenAuthorization.Invoke");
            return next(context);
        }
    }
}

The Debug.Writeline is only there to set a breakpoint on. In the final version, I’ll remove the System.Diagnostics and Debug.WriteLine lines. This middleware is as basic as it gets. The pipeline calls Invoke with a HttpContext (which is a wrapper for the request, response, identity, etc.) and then you call the next thing in the pipeline.

You can run this application. After clicking Continue a few times, click on the Spells link and you will be able to investigate the request that is important to you:

blog-0714-1

Note the context.Request.Headers contains the information you are after. This gives me enough information to decode the JWT and add it to my context

Decoding The Json Web Token in Middleware

Here is my replacement Invoke method in the JsonWebTokenAuthorization file:

        public Task Invoke(HttpContext context)
        {
            if (context.Request.Headers.ContainsKey("Authorization"))
            {
                var authHeader = context.Request.Headers["Authorization"];
                var authBits = authHeader.Split(' ');
                if (authBits.Length != 2)
                {
                    Debug.WriteLine("[JsonWebTokenAuthorization] Ignoring Bad Authorization Header (count!=2)");
                    return next(context);
                }
                if (!authBits[0].ToLowerInvariant().Equals("bearer"))
                {
                    Debug.WriteLine("[JsonWebTokenAuthorization] Ignoring Bad Authorization Header (type!=bearer)");
                    return next(context);
                }

                string claims;
                try
                {
                    var b64secret = config.Get("Auth0:ClientSecret").Replace('_', '/').Replace('-', '+');
                    var secret = System.Convert.FromBase64String(b64secret);
                    claims = JWT.JsonWebToken.Decode(authBits[1], secret);
                }
                catch (JWT.SignatureVerificationException)
                {
                    Debug.WriteLine("[JsonWebTokenAuthorization] Ignoring Bad Authorization (JWT signature doesn't match)");
                    return next(context);
                }
                catch (FormatException)
                {
                    Debug.WriteLine("[JsonWebTokenAuthorization] Ignoring Bad Client Secret");
                    return next(context);
                }

                Debug.WriteLine(string.Format("[JsonWebTokenAuthorization] JWT Decoded as {0}", claims));
            }
            Debug.WriteLine("In JsonWebTokenAuthorization.Invoke");
            return next(context);
        }

A log of this code is identical to the code I wrote in the last article. I’ve somewhat tightened it up by writing out error messages to the log (instead of sending an error to the user) and trapping exceptions that library routines generate on a bad Authorization header or configuration. I’ve also adjusted the constructor for this class as follows:

        private readonly RequestDelegate next;
        private readonly IConfiguration config;

        public JsonWebTokenAuthorization(RequestDelegate next)
        {
            this.next = next;
            this.config = Startup.Configuration;
        }

This is most definitely not the best way to configure an ASP.NET middleware class. This works for now and I’ll get to configuration another time. Note that the decode will fail if the JWT is expired, so I don’t have to worry about checking for an expiry time.

Creating an Identity

Eventually, I want my Controller to have an [Authorize] decorator on it. This tells the ASP.NET MVC system that it needs to authorize the user and not call the method if the user is not authorized. The Authorize decorator is actually defined in the class AuthorizeAttribute. That’s a complicated beast, able to handle users, roles and ad-hoc policies and is contained within the Microsoft.AspNet.Authorization namespace. Check out the source code. The basic premise we are after is contained in the DenyAnonymousAuthorizationRequirement.cs class. That class is actually fairly reasonable and it basically says “the user is authorized if any Identity object in the Identities list is authenticated.

That means what I have to do is create an Identity with a specific claim (pulled from the sub field of the JWT claim) and then set the IsAuthenticated flag on that Identity. Finally, I need to add the Identity to the list of Identities in the request. Here is the code:

                var identity = new ClaimsIdentity(
                    new[]
                    {
                        new Claim(ClaimTypes.NameIdentifier, claims, xmlString, "JWT-Issuer"),
                        new Claim(ClaimTypes.Name, claims, xmlString, "JWT-Issuer"),
                    },
                    "JWT-Issuer",
                    ClaimsIdentity.DefaultNameClaimType,
                    ClaimsIdentity.DefaultRoleClaimType);
                context.User.AddIdentity(identity);

I copied this code from the Twitter code within the AspNet.Security package. XmlString is a constant that I’ve defined at the top of the class to be http://www.w3.org/2001/XMLSchema#string.

Now I can change the code in SpellsController to this:

        [Authorize]
        [Route("")]
        public string GetAll()
        {
            return "{id:1}";
        }

Running the project will do two things:

  1. If I’m signed out, the WebAPI call will return a 401 Unauthenticated response code – I can use this in my Aurelia app to trigger an authentication.
  2. If I’m signed in, the WebAPI call will return the expected JSON string.

The “claim” is in the Name of the identity and that’s a composite JSON object. You can see it using the JSON Visualizer in Visual Studio. Set a breakpoint on the Debug.WriteLine that says JWT Decoded. When it is hit, go to the Locals tab and expand the identity. Click on the down-arrow on the right hand side of the row that shows the Name property, then select JSON Visualizer. You will get something like this:

blog-0715-1

Decoding the JSON Response

I really want to have the JWT-Issuer replaced by the iss (or Issuer) field and the name replaced by the sub (or Subject) field. Now that I have a plaintext token that I have verified, I can trust that it has not been mutilated in transit. This allows me to use a standard mechanism to decode it:

                var jwt = JsonConvert.DeserializeObject<JsonWebToken>(claims,new JsonSerializerSettings
                {
                    MissingMemberHandling = MissingMemberHandling.Ignore
                });

                var identity = new ClaimsIdentity(
                    new[]
                    {
                        new Claim(ClaimTypes.NameIdentifier, jwt.Subject, xmlString, jwt.Issuer),
                        new Claim(ClaimTypes.Name, jwt.Subject, xmlString, jwt.Issuer),
                        new Claim(ClaimTypes.UserData, claims, xmlString, jwt.Issuer)
                    },
                    jwt.Issuer,
                    ClaimsIdentity.DefaultNameClaimType,
                    ClaimsIdentity.DefaultRoleClaimType);
                context.User.AddIdentity(identity);

I’ve created a new class – JsonWebToken.cs – to hold the claim:

using Newtonsoft.Json;

namespace aurelia_2.Middleware
{
    public class JsonWebToken
    {
        [JsonProperty("iss")]
        public string Issuer { get; set; }

        [JsonProperty("sub")]
        public string Subject { get; set; }

        [JsonProperty("aud")]
        public string Audience { get; set; }

        [JsonProperty("exp")]
        public long Expiry { get; set; }

        [JsonProperty("iat")]
        public long IssuedAt { get; set; }
    }
}

Now my issuer is my Auth0 domain instead of the custom issuer string and my name (which I will use as a unique identifier for the user) is the JWT subject field.

Wrap Up

That’s it for my investigation into authentication with ASP.NET MVC6 WebAPI. To re-cap:

  1. I followed the Aurelia tutorial, but adjusted for TypeScript and ASP.NET
  2. I added an Auth0 pop-up to authenticate using a service
  3. I used that authentication to affect routing on the client side
  4. I added the authorization JWT to a WebAPI request
  5. I decoded the JWT in a Controller
  6. I made the JWT an authorization middleware (this article)

That’s a lot of code and I hope you enjoy it. The code is on my GitHub Repository.