How To Enable JwtBearer Authentication In Your NancyFx Project

Introduction

In this article, you will learn how to enable JwtBearer authentication when you are building APIs through NancyFx and learn how to implement the JwtBearer Authentication. This is also an open source project on Github.

Background

When I build some RESTful APIs using NancyFx, I need to verify the request's validity. And, I don't want to use the default authentication type (Basic,Forms and Stateless) provided by Nancy project.

Based on this document API-Security-Checklist, I prefer to use the JwtBearer(using json web token) which we often use in Web API and it also fits me very well.

So, I created an open source project named Nancy.Authentication.JwtBearer to solve this problem on my Github page.

You also can install this package through NuGet. I have uploaded this package to the NuGet. Now, I will show you how to use this package with a easy sample and how to implement the JwtBearer Authentication.


How to Use

First of all, let's create a new empty ASP.NET Core Web application.

Open Source

Secondly, open the Package Manager Console and execute the below commands one by one.

  1. Install-Package Microsoft.AspNetCore.Owin -Version 1.1.2   
  2. Install-Package Nancy -Pre  
  3. Install-Package Nancy.Authentication.JwtBearer  

Thirdly, in order to enable Nancy framework, we need to modify the method Configure in Startup class, as demonstrated by the following code.

  1. public class Startup  
  2. {          
  3.     public void Configure(IApplicationBuilder app)  
  4.     {              
  5.         app.UseOwin(x=>x.UseNancy());  
  6.     }  
  7. }  

Fourthly, create a module class to ensure that our Nancy project can run well.

  1. public class MainModule : NancyModule  
  2. {  
  3.     public MainModule()  
  4.     {  
  5.         Get("/",_=>   
  6.         {  
  7.             return "test";  
  8.         });  
  9.     }  
  10. }  

After the previous 4 steps, we can run this project well!

Open Source

Now, we need to create a bootstrapper so that we can enable the JwtBearer authentication for the sample project.

  1. public class DemoBootstrapper :  DefaultNancyBootstrapper  
  2. {  
  3.     protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)  
  4.     {  
  5.         base.ApplicationStartup(container, pipelines);  
  6.   
  7.         var keyByteArray = Encoding.ASCII.GetBytes("Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==");  
  8.         var signingKey = new SymmetricSecurityKey(keyByteArray);  
  9.   
  10.         var tokenValidationParameters = new TokenValidationParameters  
  11.         {  
  12.             // The signing key must match!  
  13.             ValidateIssuerSigningKey = true,  
  14.             IssuerSigningKey = signingKey,  
  15.             // Validate the JWT Issuer (iss) claim  
  16.             ValidateIssuer = true,  
  17.             ValidIssuer = "http://www.c-sharpcorner.com/members/catcher-wong",  
  18.             // Validate the JWT Audience (aud) claim  
  19.             ValidateAudience = true,  
  20.             ValidAudience = "Catcher Wong",  
  21.             // Validate the token expiry  
  22.             ValidateLifetime = true,  
  23.             ClockSkew = TimeSpan.Zero  
  24.         };  
  25.   
  26.         var configuration = new JwtBearerAuthenticationConfiguration  
  27.         {  
  28.             TokenValidationParameters = tokenValidationParameters  
  29.         };  
  30.           
  31.         //enable the JwtBearer authentication  
  32.         pipelines.EnableJwtBearerAuthentication(configuration);  
  33.     }  
  34. }  

As you can see, I constructed a new instance of JwtBearerAuthenticationConfiguration, and used this instance to enable the JwtBearer authentication.

However, we need to construct an instance of Token Validation Parameters that is in the Microsoft.IdentityModel. Tokens namespace at first! Because this is the most important property of JwtBearerAuthenticationConfiguration.

So far, we have finished the configuration of the authentication. And, we need to verify whether the authentication takes effect.

Create a new module class to use the authentication.

  1. public class SecurityModule : NancyModule  
  2. {  
  3.     public SecurityModule() : base("/demo")  
  4.     {  
  5.         //important  
  6.         this.RequiresAuthentication();  
  7.   
  8.         Get("/",_=>   
  9.         {  
  10.             return "JwtBearer authentication";  
  11.         });  
  12.     }      
  13. }  

At this time, we accomplished the task!

When we visit this secure route, it will tell us "The requested resource requires user authentication".

Open Source

Now, let's create a valid JSON Web Token and use this token to send a request using Fiddler.

Here is a method that we can use to create a JSON Web Token:

  1. private string GetJwt(string client_id)  
  2. {  
  3.     var now = DateTime.UtcNow;  
  4.   
  5.     var claims = new Claim[]  
  6.     {  
  7.         new Claim(JwtRegisteredClaimNames.Sub, client_id),  
  8.         new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),  
  9.         new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64)  
  10.     };  
  11.       
  12.     //must the same as your setting in your boostrapper class  
  13.     var symmetricKeyAsBase64 = "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==";  
  14.     var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);  
  15.     var signingKey = new SymmetricSecurityKey(keyByteArray);  
  16.   
  17.     var jwt = new JwtSecurityToken(  
  18.         issuer: "http://www.c-sharpcorner.com/members/catcher-wong",  
  19.         audience: "Catcher Wong",  
  20.         claims: claims,  
  21.         notBefore: now,  
  22.         expires: now.Add(TimeSpan.FromMinutes(10)),  
  23.         signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256));  
  24.     var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);  
  25.   
  26.     var response = new  
  27.     {  
  28.         access_token = encodedJwt,  
  29.         expires_in = (int)TimeSpan.FromMinutes(10).TotalSeconds  
  30.     };  
  31.   
  32.     return JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting = Formatting.Indented });  
  33. }  

After executing this request in Fiddler, we may get the below result.

Open Source

And, here is the token information of this sample.

Open Source

Note

We need to add this token to the request header(Authorization) , and the value is combine the string Bearer and the token with a space.

Up to here , we have finished the sample and show you how to use this provider.

The next time , I will show you how to implement this provider.

How to Implement

Before I introduce how to implement this provider, I assume that you have already learned about the pipelines of Nancy , which play a important role at the implementation.

By the way, I used the Nancy Style to write the code, which you may find similar to other authentication types in Nancy project.

The code is very easy !

As in the previous sample, we have an entrance to enable the JwtBearer authentication. And, this entrance is an extension method of IPipelines.

  1. /// <summary>  
  2. /// Module requires JwtBearer authentication  
  3. /// </summary>  
  4. /// <param name="pipeline">Bootstrapper to enable</param>  
  5. /// <param name="configuration">JwtBearer authentication configuration</param>  
  6. public static void EnableJwtBearerAuthentication(this IPipelines pipeline, JwtBearerAuthenticationConfiguration configuration)  
  7. {  
  8.     JwtBearerAuthentication.Enable(pipeline, configuration);  
  9. }  

In the extension method, I called JwtBearerAuthentication's Enable method to finish this work.

Let's take a look at the Enable method.

  1. /// <summary>  
  2. /// Enables JwtBearer authentication for the application  
  3. /// </summary>  
  4. /// <param name="pipelines">Pipelines to add handlers to (usually "this")</param>  
  5. /// <param name="configuration">JwtBearer authentication configuration</param>  
  6. public static void Enable(IPipelines pipelines, JwtBearerAuthenticationConfiguration configuration)  
  7. {  
  8.     if (pipelines == null)  
  9.     {  
  10.         throw new ArgumentNullException("pipelines");  
  11.     }  
  12.   
  13.     if (configuration == null)  
  14.     {  
  15.         throw new ArgumentNullException("configuration");  
  16.     }  
  17.     pipelines.BeforeRequest.AddItemToStartOfPipeline(GetLoadAuthenticationHook(configuration));    
  18.     pipelines.AfterRequest.AddItemToEndOfPipeline(GetAuthenticationPromptHook(configuration));  
  19. }  

The BeforeRequest and the AfterRequest are the main characters .

Requests will enter the before pipeline first , then you need to handle the requests , and at last the after pipeline will response something for the request.

So we need to verify the token on the before request ,but how can we do that?

The following code shows you how to do it!

  1. private static Func<NancyContext, Response> GetLoadAuthenticationHook(JwtBearerAuthenticationConfiguration configuration)  
  2. {  
  3.     return context =>   
  4.     {  
  5.         Validate(context,configuration);  
  6.         return null;  
  7.     };  
  8. }  

This method just returns a value whose type is Func<NancyContext, Response> ,and the core of this method is to call the Validate method which is used to handle the token.

Here is the code to handle the token . And the verification is based on JwtSecurityTokenHandler which you can find in the namespace System.IdentityModel.Tokens.Jwt.

  1. private static void Validate(NancyContext context, JwtBearerAuthenticationConfiguration configuration)  
  2. {              
  3.     //get the token from request header  
  4.     var jwtToken = context.Request.Headers["Authorization"].FirstOrDefault() ?? string.Empty;  
  5.      
  6.     //whether the token value start with the Bearer  
  7.     if (jwtToken.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))  
  8.     {  
  9.         jwtToken = jwtToken.Substring("Bearer ".Length);  
  10.     }  
  11.     else  
  12.     {                                  
  13.         return;  
  14.     }  
  15.       
  16.     //verify the token  
  17.     if (!string.IsNullOrWhiteSpace(jwtToken))  
  18.     {  
  19.         try  
  20.         {  
  21.             SecurityToken validatedToken;  
  22.             var tokenHandler = new JwtSecurityTokenHandler();  
  23.             var validatedClaims = tokenHandler.ValidateToken(jwtToken, configuration.TokenValidationParameters, out validatedToken);  
  24.             //var jwtSecurityToken = validatedToken as JwtSecurityToken;  
  25.             context.CurrentUser = validatedClaims;  
  26.         }  
  27.         catch (Exception)  
  28.         {                                    
  29.         }                                                                         
  30.     }  
  31. }  

We often use Bearer as the prefix of the value of the header - Authorization , however , we also can use other words to replace the default value which you can specify in your configuration.

And the most important thing is to assign the validated claims to the NancyContext's CurrentUser property.

If the token is not validated , we do nothing , Nancy will return Unauthorized to the client.

And what do we need to do after a request ? We need to deal with the scenario that Nancy returns Unauthorized . Because we need to tell the client the requested resource requires user authentication and which type of authentication the requested resource needs.

  1. private static Action<NancyContext> GetAuthenticationPromptHook(JwtBearerAuthenticationConfiguration configuration)  
  2. {  
  3.     return context =>  
  4.     {  
  5.         if (context.Response.StatusCode == HttpStatusCode.Unauthorized)  
  6.         {  
  7.             //add a response header   
  8.             context.Response.WithHeader(JwtBearerDefaults.WWWAuthenticate, configuration.Challenge);  
  9.         }  
  10.     };  
  11. }  

OK! That's the core of how to impement the provider.

Summary

This article introduced how to use a provider to enable the JwtBearer authentication when we use Nancy and showed how to implement this provider.


Similar Articles