Decoding and verifying JWT token using System.IdentityModel.Tokens.Jwt

Within the package there is a class called JwtSecurityTokenHandler which derives from System.IdentityModel.Tokens.SecurityTokenHandler. In WIF this is the core class for deserialising and serialising security tokens.

The class has a ReadToken(String) method that will take your base64 encoded JWT string and returns a SecurityToken which represents the JWT.

The SecurityTokenHandler also has a ValidateToken(SecurityToken) method which takes your SecurityToken and creates a ReadOnlyCollection<ClaimsIdentity>. Usually for JWT, this will contain a single ClaimsIdentity object that has a set of claims representing the properties of the original JWT.

JwtSecurityTokenHandler defines some additional overloads for ValidateToken, in particular, it has a ClaimsPrincipal ValidateToken(JwtSecurityToken, TokenValidationParameters) overload. The TokenValidationParameters argument allows you to specify the token signing certificate (as a list of X509SecurityTokens). It also has an overload that takes the JWT as a string rather than a SecurityToken.

The code to do this is rather complicated, but can be found in the Global.asax.cx code (TokenValidationHandler class) in the developer sample called "ADAL - Native App to REST service - Authentication with ACS via Browser Dialog", located at

http://code.msdn.microsoft.com/AAL-Native-App-to-REST-de57f2cc

Alternatively, the JwtSecurityToken class has additional methods that are not on the base SecurityToken class, such as a Claims property that gets the contained claims without going via the ClaimsIdentity collection. It also has a Payload property that returns a JwtPayload object that lets you get at the raw JSON of the token. It depends on your scenario which approach it most appropriate.

The general (i.e. non JWT specific) documentation for the SecurityTokenHandler class is at

http://msdn.microsoft.com/en-us/library/system.identitymodel.tokens.securitytokenhandler.aspx

Depending on your application, you can configure the JWT handler into the WIF pipeline exactly like any other handler.

There are 3 samples of it in use in different types of application at

http://code.msdn.microsoft.com/site/search?f%5B0%5D.Type=SearchText&f%5B0%5D.Value=aal&f%5B1%5D.Type=User&f%5B1%5D.Value=Azure%20AD%20Developer%20Experience%20Team&f%5B1%5D.Text=Azure%20AD%20Developer%20Experience%20Team

Probably, one will suite your needs or at least be adaptable to them.


I had version issues between System.IdentityModel.Tokens and System.IdentityModel.Tokens.Jwt, which is a known issue after version 5.0.0.0 of Jwt. So, instead I downloaded the latest release of Microsoft.IdentityModel.Tokens - note Microsoft - and all worked fine. Here is a nice snippet I made for validating and decoding a custom-generated JWT token and parsing its JSON content.

using System.Collections.Generic;
using System.Linq;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

public static void Main()
{
    var key = "qwertyuiopasdfghjklzxcvbnm123456";
    var securityKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(key));

    string token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NDA0MDY1MjIsImV4cCI6MTY3MTk0MjUyMiwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsImZvbyI6ImJhciJ9.QqcxZWEUt5YLraLRg5550Ls7aMVqm7aCUcbU7uB1qgY";

    TokenValidationParameters tokenValidationParameters = new TokenValidationParameters
    {
        IssuerSigningKey = securityKey,
        RequireExpirationTime = true,
        ValidateLifetime = true,
        ValidateAudience = true,
        ValidateIssuer = true,
        ValidIssuer = "Online JWT Builder",
        ValidAudience = "www.example.com"
    };

    if (ValidateToken(token, tokenValidationParameters))
    {
        var TokenInfo = new Dictionary<string, string>();
        var handler = new JwtSecurityTokenHandler();
        var jwtSecurityToken = handler.ReadJwtToken(token);
        var claims = jwtSecurityToken.Claims.ToList();

        foreach (var claim in claims)
        {
            TokenInfo.Add(claim.Type, claim.Value);
        }

        string sub = jwtSecurityToken.Subject;
        string iss = jwtSecurityToken.Issuer;
        DateTime iat = jwtSecurityToken.IssuedAt;
        List<string> audiences = new List<string>(jwtSecurityToken.Audiences);
        DateTime exp = jwtSecurityToken.ValidTo;
        string bar;
        bool ifBar = TokenInfo.TryGetValue("foo", out bar);
        Console.WriteLine("Subject: " + sub);
        Console.WriteLine("Issuer: " + iss);
        Console.WriteLine("Issued At: " + iat);
        foreach (var member in audiences)
        {
            Console.WriteLine("Audience: " + member);
        }
        Console.WriteLine("Expiration: " + exp);
        Console.WriteLine("foo: " + bar);
    }
    Console.ReadLine();
}

private static bool ValidateToken(string token, TokenValidationParameters tvp)
{
    try
    {
        var handler = new JwtSecurityTokenHandler();
        SecurityToken securityToken;
        ClaimsPrincipal principal = handler.ValidateToken(token, tvp, out securityToken);
        return true;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        return false;
    }
}

Output

Subject: [email protected]
Issuer: Online JWT Builder
Issued At: 12/25/2022 4:28:42 AM
Audience: www.example.com
Expiration: 12/25/2022 4:28:42 AM
foo: bar

I am just wondering why to use some libraries for JWT token decoding and verification at all.

Encoded JWT token can be created using following pseudocode

var headers = base64URLencode(myHeaders);
var claims = base64URLencode(myClaims);
var payload = header + "." + claims;

var signature = base64URLencode(HMACSHA256(payload, secret));

var encodedJWT = payload + "." + signature;

It is very easy to do without any specific library. Using following code:

using System;
using System.Text;
using System.Security.Cryptography;

public class Program
{   
    // More info: https://stormpath.com/blog/jwt-the-right-way/
    public static void Main()
    {           
        var header = "{\"typ\":\"JWT\",\"alg\":\"HS256\"}";
        var claims = "{\"sub\":\"1047986\",\"email\":\"[email protected]\",\"given_name\":\"John\",\"family_name\":\"Doe\",\"primarysid\":\"b521a2af99bfdc65e04010ac1d046ff5\",\"iss\":\"http://example.com\",\"aud\":\"myapp\",\"exp\":1460555281,\"nbf\":1457963281}";

        var b64header = Convert.ToBase64String(Encoding.UTF8.GetBytes(header))
            .Replace('+', '-')
            .Replace('/', '_')
            .Replace("=", "");
        var b64claims = Convert.ToBase64String(Encoding.UTF8.GetBytes(claims))
            .Replace('+', '-')
            .Replace('/', '_')
            .Replace("=", "");

        var payload = b64header + "." + b64claims;
        Console.WriteLine("JWT without sig:    " + payload);

        byte[] key = Convert.FromBase64String("mPorwQB8kMDNQeeYO35KOrMMFn6rFVmbIohBphJPnp4=");
        byte[] message = Encoding.UTF8.GetBytes(payload);

        string sig = Convert.ToBase64String(HashHMAC(key, message))
            .Replace('+', '-')
            .Replace('/', '_')
            .Replace("=", "");

        Console.WriteLine("JWT with signature: " + payload + "." + sig);        
    }

    private static byte[] HashHMAC(byte[] key, byte[] message)
    {
        var hash = new HMACSHA256(key);
        return hash.ComputeHash(message);
    }
}

The token decoding is reversed version of the code above.To verify the signature you will need to the same and compare signature part with calculated signature.

UPDATE: For those how are struggling how to do base64 urlsafe encoding/decoding please see another SO question, and also wiki and RFCs

Tags:

.Net

Wif

Jwt