ASP.NET Web API 2 - Creating And Validating JWT (JSON Web Token)

Creating & validating JSON Web Tokens is very straightforward in ASP.NET Web API 2. A few packages and lines of code is all we need to create JWT tokens and to validate a JWT bearer tokens.

Why do we need a Token in Restful services?

 
Restful services or Web APIs are stateless by default. Every request is a new request to the server. This makes Web APIs easily scalable. But what if we want to provide some authorization on our Web APIs? We can issue a token to the requester and then the requester can present that token in future requests to authorize itself.
 
Now we have two options,
  1. Create a random but unique token and keep track of that token on the server side. This is how Server Side Session works. We have "Session Id", a unique & random string.
  2. Create a token which contains everything in it and then you don't track anything on server.
The first option is not very scalable but the second option is. Now if our token is going to contain the data in itself, what issues do we see?
  1. What will be format of token & how to represent data in it?
  2. How to secure the content of token so the end user can't read it?
  3. How to detect if token is tempered by end user?
We can develop our own mechanism to 1) Create a token 2) Validate a token and extract information from it when someone presents a token to us. But do we have any solution already available for us? Yes, do.
 
Following are two popular token types for which we currently have support/libraries in ASP.NET,
  1. oAuth Bearer Token

    1. It stores data in the form of claims (key/value pairs)
    2. Token is encrypted.
    3. End User needs algorithm & key to decrypt it.

  2. Json Web Token 

Json Web Tokens (check https://jwt.io/ for example)

  • JWT token is a string and has three parts separated by dot (.) a) Header b) Payload c) Signature 
  • Header & Payload are JSON objects
  • Header contains algorithm & type of token which is jwt
  • Payload contains claims (key/value pairs) + expiration date + aud/issuer etc. 
  • Signature is HASH value computed using Base64(Header) +"." + Base64(Payload).  This information is passed to an algorithm with a secret key.
  • Token structure is base64(header) + "." + base64(payload) + "." + hash
This is a quick workflow using JWT,
  1. Client sends a request to server for token
  2. Server generates a JWT (which contains a hash). Hash is generated using a secret key.
  3. Client receives the token and stores it somewhere locally.
  4. Client sends the token in future requests.
  5. Server gets the token from request header, computes Hash again by using a) Header from token b) payload from token c) secret key which server already has.
  6. If ("newly computed hash" = "hash came in token"), token is valid otherwise it is tempered or not valid. 
User can decode JWT and see what is in header & in payload. Therefore we should not keep any confidential information in token.
 
In this article, we'll learn,
  1. How we can create Json Web Token in ASP.NET Web API
  2. How to validate a JWT bearer token if it comes in a request
  3. How to get claims data 

Creating & Validating JWT in ASP.NET Web API

 
Lets' create an ASP.NET Web API application in Visual Studio 2015 - File -> New Project -> ASP.NET Web Application -> Web API (without  authentication to keep things simple).
 
Creating JWT Token
  1. Add following nuget

    Package System.IdentityModel.Tokens.Jwt 5.5.0 

  2. Open Values Controller (or we may create a new API controller) and add following namespaces

    using Microsoft.IdentityModel.Tokens;
    using System.IdentityModel.Tokens.Jwt;
    using System.Security.Claims;
Add following Action in Values Controller. Normally we'll expose this method with POST verb + we'll receive some credentials for authentication. Once user will be authenticated, token will be generated accordingly. Here we've made it very simple.
  1. [HttpGet]    
  2. public Object GetToken()    
  3. {    
  4.     string key = "my_secret_key_12345"//Secret key which will be used later during validation    
  5.     var issuer = "http://mysite.com";  //normally this will be your site URL    
  6.   
  7.     var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));    
  8.     var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);    
  9.   
  10.     //Create a List of Claims, Keep claims name short    
  11.     var permClaims = new List<Claim>();    
  12.     permClaims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()));    
  13.     permClaims.Add(new Claim("valid""1"));    
  14.     permClaims.Add(new Claim("userid""1"));    
  15.     permClaims.Add(new Claim("name""bilal"));    
  16.   
  17.     //Create Security Token object by giving required parameters    
  18.     var token = new JwtSecurityToken(issuer, //Issure    
  19.                     issuer,  //Audience    
  20.                     permClaims,    
  21.                     expires: DateTime.Now.AddDays(1),    
  22.                     signingCredentials: credentials);    
  23.     var jwt_token = new JwtSecurityTokenHandler().WriteToken(token);    
  24.     return new { data = jwt_token };    
  25. }
This is not required but to use action name in route; we are making the following highlighted change in WebApiConfig file
  1. config.Routes.MapHttpRoute(  
  2.                 name: "DefaultApi",  
  3.                 routeTemplate: "api/{controller}/{action}/{id}",  
  4.                 defaults: new { id = RouteParameter.Optional }  
  5.             );   
Now run the application and test the following in browser/postman (considering http://localhost:1234 is base URL of our application). 
 
http://localhost:1234/api/values/gettoken
 
Response in browser should be something like this. "data" contains the token.
  1. {"data":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI3ODZlZmY0OS00OWQ2LTQ4YjQtODM4NC0yYTA5NDYxODJmN2YiLCJ2YWxpZCI6IjEiLCJ1c2VyaWQiOiIxIiwibmFtZSI6ImJpbGFsIiwiZXhwIjoxNTcwNjMwMzMwLCJpc3MiOiJodHRwOi8vbXlzaXRlLmNvbSIsImF1ZCI6Imh0dHA6Ly9teXNpdGUuY29tIn0.06vzYfiSpj1X9s0-CL2nE7NH4LloASMikZCNfHIJ8tY"}  
You may copy token from here and decode it on http://jwt.io to see what it contains.
 

How to Validate JWT Token?

 
Add the following nuget packages,
 
Microsoft.Owin.Security.Jwt 4.0.1
Microsoft.AspNet.WebApi.Owin 5.2.3
Microsoft.Owin.Host.SystemWeb 4.0.1
 
Create Owin Statup class -> Right click on Web Project -> Add -> Owin Startup Class. Replace class with the following. Note we are using same parameters that we used while creating token.
  1. public class Startup  
  2.     {  
  3.         public void Configuration(IAppBuilder app)  
  4.         {  
  5.             app.UseJwtBearerAuthentication(  
  6.                 new JwtBearerAuthenticationOptions  
  7.                 {  
  8.                     AuthenticationMode = AuthenticationMode.Active,  
  9.                     TokenValidationParameters = new TokenValidationParameters()  
  10.                     {  
  11.                         ValidateIssuer = true,  
  12.                         ValidateAudience = true,  
  13.                         ValidateIssuerSigningKey = true,  
  14.                         ValidIssuer = "http://mysite.com", //some string, normally web url,  
  15.                         ValidAudience = "http://mysite.com",  
  16.                         IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("my_secret_key_12345"))  
  17.                     }  
  18.                 });  
  19.         }  
  20.     }  
Update WebApiConfig.Register method with highlighted code. This is to enable oAuth authentication
  1. public static void Register(HttpConfiguration config) {  
  2.  // Web API configuration and services    
  3.  config.SuppressDefaultHostAuthentication();  
  4.  config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));  
  5.   
  6.  // Web API routes    
  7.  config.MapHttpAttributeRoutes();  
  8.   
  9.  config.Routes.MapHttpRoute(  
  10.   name: "DefaultApi",  
  11.   routeTemplate: "api/{controller}/{action}/{id}",  
  12.   defaults: new {  
  13.    id = RouteParameter.Optional  
  14.   }  
  15.  );  
  16. }  
Add the following Actions in API Controller (e.g. Values) for testing.
 
GetName1
 
It has no authorization enabled on it. This function will be called whether we've received a token or not but we are checking if user is authenticated (means a valid token has been received) inside the function. User.Identity contains the claims (which are constructed from token)
 
GetName2
 
It has Authorize attribute. This function will not be called if a valid token is not received.
  1. [HttpPost]  
  2. public String GetName1() {  
  3.  if (User.Identity.IsAuthenticated) {  
  4.   var identity = User.Identity as ClaimsIdentity;  
  5.   if (identity != null) {  
  6.    IEnumerable < Claim > claims = identity.Claims;  
  7.   }  
  8.   return "Valid";  
  9.  } else {  
  10.   return "Invalid";  
  11.  }  
  12. }  
  13.   
  14. [Authorize]  
  15. [HttpPost]  
  16. public Object GetName2() {  
  17.  var identity = User.Identity as ClaimsIdentity;  
  18.  if (identity != null) {  
  19.   IEnumerable < Claim > claims = identity.Claims;  
  20.   var name = claims.Where(p => p.Type == "name").FirstOrDefault() ? .Value;  
  21.   return new {  
  22.    data = name  
  23.   };  
  24.   
  25.  }  
  26.  return null;  
  27. } 
Now let's run the application and test it using Postman (https://getpostman.com) by providing token, by providing invalid token, without token etc.
 
Method Type: POST
 
URL: http://localhost:1234/api/values/getname1
 
Headers
 
Authorization: Bearer <token> 
Content-Type: application/json
 
Body
 
Select "raw"
Select "JSON" from last dropdown
 
Provide data in JSON format if any.
 
Note
Requester/Consumer of token can be browser/desktop app/mobile app/postman etc. as long it allows creating HTTP requests. 
 

Summary

 
JSON web tokens have got quite popular and there are reasons for this popularity. The main reason is its simplicity. End application/consumer should consider security of tokens as important as login/password security. JWT are not encrypted, but rather encoded. It means anyone who has access to JWT can decode and get information from it. Confidential data should not be part of it or it should be encrypted if it is required. Size of payload should be small. Keep only required claims with small names.