.NET Backend Get Noticed 2017 Security

Nancy meets JWT authentication

A few days ago I showed you how to combine Nancy with Autofac and ASP.NET Core IoC. Today’s post will be related to the security and more precisely JWT authentication. Before moving further, I’d like to mention that below text is going to be the last Nancy-related one (at least for now). In a next couple weeks, we’ll explore the world of graph databases with Neo4j and .NET Core, so I hope you’re as excited as I do 馃槈

 

How does JWT authentication work?

JWT stands for JSON Web Token. If you have never heard about this standard, I encourage you to visit this site and read the text. I’m not gonna do this since it’s not the point of this article. What I’d like to present (shortly) is the whole process of using JWT. The below diagram should help to understand it a little bit:

 

https://jwt.io/introduction/

 

Simple is that. What happens when you log in to some web application is you get the JWT token which is then stored inside your browser (etc. local/session storage). The token contains all the data needed to properly authenticate the user, so it might be userId, userName, userEmail or whatever you need to identify and verify. Then when your client side needs some resources from the API it adds the Authorization header to the each request which looks as follows:

Authorization: Bearer <token>

Having the necessary header, the backend can verify the token using implemented logic and decide whether resources should or should not be granted to the caller. If so, you’ll get the response with a body containing needed data, if not you’ll receive a response with 401 status code which means Unauthorized.

 

Installing packages

Knowing the standard and the entire process we can move to code. First thing is installing proper packages for our solution:

 


<PackageReference Include="jose-jwt" Version="2.2.0"/>
<PackageReference Include="Nancy.Authentication.Stateless" Version="2.0.0-clinteastwood"/>

 

First one is going to help us a lot with encoding and decoding token with given algorithm and secret. The second package is an external module for Nancy to add authentication step in the pipeline.

 

Adding JWT authentication to Nancy

Have all the packages restored? Great, let’s do some code! The first thing we need to do is to store the secret used for the encoding/decoding tokens. There are many ways to achieve that such as:

  • appsetings.json
  • SecretManager – introduced in ASP.NET Core as a „proper” way of keeping credentials
  • Lockbox – great and simple library created by Piotr Gankiewicz (the winner of „Get Noticed 2016”). Definitely worth to check this one 馃槈

For the simplicity sake of this article, we’ll stick to the first option. So, the AuthSettings model looks as follows:

 


    internal class AuthSettings
    {
        public string SecretKeyBase64 { get; set; }

        public byte[] SecretKey
        {
            get
            {
                return Convert.FromBase64String(SecretKeyBase64);
            }
        }
    }

 

I’ll skip the registration part here since I did it some time ago in this post, so if you’re not familiar with this topic please read it before moving forward.
Besides the settings we also need some model which then will be stored inside our token and which will contain all necessary data during verification process:

 

    internal sealed class AuthToken
    {
        public Guid UserId { get; set; }
        public string UserName { get; set; }
        public string UserLogin { get; set; }
        public DateTime ExpirationDateTime { get; set; }
    }

 

Having this part done, we should change a little bit Nancy’s bootstrapper by implementing the override for ApplicationStartup method:

 


        protected override void ApplicationStartup(ILifetimeScope container, IPipelines pipelines)
        {
            StatelessAuthentication.Enable();   

            base.ApplicationStartup(container, pipelines);
        }

 

As you see here we are adding the authentication step to Nancy’s pipelines by invoking static Enable method from the StatelessAuthentication class. The method signature looks as presented below:

 


public static void Enable(IPipelines pipelines, StatelessAuthenticationConfiguration configuration);

//config class

public StatelessAuthenticationConfiguration(Func<NancyContext, ClaimsPrincipal> getUserIdentity);


 

Since the first parameter is self-explained we should concentrate more on the second one. If it’s not clear for you, we need to deliver a function which will take the NancyContext and return ClaimsPrincipal (this one represents our authenticated user). Now, we could write some lambda inside the bootstrapper but that would look ugly. That’s why I suggest creating independent service for this purpose. Mine called IdentityProvider looks as shown below:

 


    public interface IIdentityProvider
    {
         ClaimsPrincipal GetUserIdentity(NancyContext context);
    }

    internal sealed class IdentityProvider : IIdentityProvider
    {
        private readonly AuthSettings _authSettings;
        private const string _bearerDeclaration = "Bearer ";

        public IdentityProvider(AuthSettings authSettings)
        {
            _authSettings = authSettings;
        }

        public ClaimsPrincipal GetUserIdentity(NancyContext context)
        {
            try
            {
                var authorizationHeader = context.Request.Headers.Authorization;
                var jwt = authorizationHeader.Substring(_bearerDeclaration.Length);

                var authToken = Jose.JWT.Decode<AuthToken>(jwt, _authSettings.SecretKey, JwsAlgorithm.HS256);

                if(authToken.ExpirationDateTime < DateTime.UtcNow)
                    return null;
                
                var authUser = new AuthUser(authToken.UserName, authToken.UserLogin, authToken.UserId);   
                return new ClaimsPrincipal(authUser);             
            }
            catch(Exception)
            {
                return null;
            }
        }
    }

 

The first thing you’ve probably spotted is the constructor. Since we’ll need to decode the JWT we need secret used for its encryption. The easiest way is obviously to use dependency injection since we’ve already registered settings model. Moving to the GetUserIdentity method we can see the entire logic. As wrote before our function needs NancyContext as the parameter. Then the whole „magic” happens. The first step is to get the Authorization header from the request and pull out the token. Next, we need to decode it using secret and algorithm used for encoding. In this case, it’s HS256. After decoding we get the AuthModel with data need to create ClaimsPricipal. But before creation it’s a good practice to check whether to token expiration date didn’t pass. If so, we should not let him get the resources. Assuming that everything looks fine we are ready to create the ClaimsPrincipal. On of its constructor takes the IIdentity object as a parameter. Therefore we need to create a class that will implement that interface. And that’s where AuthUser comes into play:

 

    internal class AuthUser : IAuthUser
    {
        public string AuthenticationType => "Test";
        public bool IsAuthenticated { get; }
        public string Name { get; }
        public string Login { get; }
        public Guid Id { get; }

        public AuthUser(string name, string login, Guid id)
        {
            Name = name;
            Login = login;
            Id = id;
            IsAuthenticated = true;
        }
    }

 

Moving back to the IdentityProvider we create the new instance of the AuthUser and finally create the expected ClaimsPrincipal. Notice, that the entire code has been wrapped by the try-catch. So, if some failure would happen like decode would fail or there would be no Authorization header we return null. This is a sign for Nancy that user has not been authenticated and it should return the 401 response.
Having this code done we can now come back to the bootstrapper to finish our work:

 


        protected override void ApplicationStartup(ILifetimeScope container, IPipelines pipelines)
        {
            var identityProvider = BootstraperLifetimeScope.Resolve<IIdentityProvider>();
            var statelessAuthConfig = new StatelessAuthenticationConfiguration(identityProvider.GetUserIdentity);

            StatelessAuthentication.Enable(pipelines, statelessAuthConfig);   

            base.ApplicationStartup(container, pipelines);
        }

 

And that’s it! Everything should work like a charm. If you’re confused in some way about the flow, here’s how does it work:

  1. Client sends request with proper Authorization header.
  2. Nancy receives the request and uses given a function for creating ClaimsPrincipal.
  3. If the function returns a proper object, it’s assigned to NancyContext.CurrentUser and the proper Module is invoked.
  4. If the function returns null, the 401 request is sent back to the client.

Now, the last question for today is: How do we mark the Module as protected (which means that we require a token to get the resources). The example below shows that:

 


    public class HomeModule : NancyModule
    {
        public HomeModule(IServiceBus serviceBus)
        {
            this.RequiresAuthentication();
            Get("/", args => "Hello world!");
        }
    }

 

It’s worth to mention that you can enable that for the entire Nancy module or just for the selected actions, so you have the flexibility in that case. If you want to see this implementation in action, I encourage you to visit gifty’s GitHub 馃槈

  • Pingback: dotnetomaniak.pl()

  • Lukasz Lysik

    Hi Dariusz. Thanks for this great article. It helped me a lot.

    Btw, in IdentityProvider you have:

    var jwt = authorizationHeader.Substring(0, _bearerDeclaration.Length);

    should be:

    var jwt = authorizationHeader.Substring(_bearerDeclaration.Length);

Don’t miss new posts!

If you enjoy reading my blog, follow me on Twitter or leave a like on Facebook. It costs nothing and will let you be up to date with new posts 馃檪