Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to validate a JWT token

Tags:

c#

security

jwt

I'm trying to use JWT tokens. I managed to generate a valid JWTTokenString and validated it on the JWT debugger but I'm having an impossible time validating the token in .Net. Here's the code I have so far:

class Program {

    static string key = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";

    static void Main(string[] args) {
        var stringToken = GenerateToken();
        ValidateToken(stringToken);
    }

    private static string GenerateToken() {
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));

        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var header = new JwtHeader(credentials);

        var payload = new JwtPayload {
           { "some ", "hello "},
           { "scope", "world"},
        };

        var secToken = new JwtSecurityToken(header, payload);
        var handler = new JwtSecurityTokenHandler();

        return handler.WriteToken(secToken);

    }

    private static bool ValidateToken(string authToken) {
        var tokenHandler = new JwtSecurityTokenHandler();
        var validationParameters = GetValidationParameters();

        SecurityToken validatedToken;
        IPrincipal principal = tokenHandler.ValidateToken(authToken, validationParameters, out validatedToken);
        Thread.CurrentPrincipal = principal;
        return true;
    }

    private static TokenValidationParameters GetValidationParameters() {
        return new TokenValidationParameters() {
            //NOT A CLUE WHAT TO PLACE HERE
        };
    }
}

All I want is a function that receives a token and returns true or false based on its validity. From research I've seen people use IssuerSigningToken to assign the validation key. But when I try to use it, it doesn't seem to exist. Could anyone give me a hand on validating the token?

like image 807
Aterin Avatar asked May 06 '18 21:05

Aterin


2 Answers

You must use the same key to validate the token as the one you use to generate it. Also you need to disable some validations such as expiration, issuer and audiance, because the token you generate doesn't have these information (or you can add these information). Here's a working example:

class Program
{
    static string key = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";

    static void Main(string[] args)
    {
        var stringToken = GenerateToken();
        ValidateToken(stringToken);
    }

    private static string GenerateToken()
    {
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var secToken = new JwtSecurityToken(
            signingCredentials: credentials,
            issuer: "Sample",
            audience: "Sample",
            claims: new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, "meziantou")
            },
            expires: DateTime.UtcNow.AddDays(1));

        var handler = new JwtSecurityTokenHandler();
        return handler.WriteToken(secToken);
    }

    private static bool ValidateToken(string authToken)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var validationParameters = GetValidationParameters();

        SecurityToken validatedToken;
        IPrincipal principal = tokenHandler.ValidateToken(authToken, validationParameters, out validatedToken);
        return true;
    }

    private static TokenValidationParameters GetValidationParameters()
    {
        return new TokenValidationParameters()
        {
            ValidateLifetime = false, // Because there is no expiration in the generated token
            ValidateAudience = false, // Because there is no audiance in the generated token
            ValidateIssuer = false,   // Because there is no issuer in the generated token
            ValidIssuer = "Sample",
            ValidAudience = "Sample",
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)) // The same key as the one that generate the token
        };
    }
}
like image 151
meziantou Avatar answered Nov 19 '22 18:11

meziantou


In my case I only want to validate if the signature is correct. Most likely you will probably want to use @meziantou answer. But if you only want to verify that the message has not been tampered with here is an example. Lastly since I am the only person generating this tokens and I know I will be generating them using HmacSha256 I am using this approach.

using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;

class Program
{
    static readonly byte[] key = Encoding.UTF8.GetBytes("f645b33ef0d04cbe859777ac6f46226d");

    // use this algorithm for example to work
    static readonly string securityAlgorithm = SecurityAlgorithms.HmacSha256;

    static void Main()
    {
        var token = GenerateToken();
        var isTokenValid = IsJwtTokenValid(token);
        if (isTokenValid)
            Console.WriteLine(true);
    }

    /// <summary>
    ///     This method assumes token has been hashed using HMACSHA256 algorithm!
    /// </summary>
    private static bool IsJwtTokenValid(string token)
    {
        // example of token:
        //                  header                              payload                                      signature
        // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb29AZ21haWwuY29tIiwiZXhwIjoxNjQ1NzM1MDU2fQ.Gtrm2G_35ynyNd1-CjZ1HsvvFFItEsXPvwhaOsN81HQ

        // from JWT spec
        static string Base64UrlEncode(byte[] input)
        {
            var output = Convert.ToBase64String(input);
            output = output.Split('=')[0]; // Remove any trailing '='s
            output = output.Replace('+', '-'); // 62nd char of encoding
            output = output.Replace('/', '_'); // 63rd char of encoding
            return output;
        }

        try
        {
            // position of second period in order to split header+payload and signature
            int index = token.IndexOf('.', token.IndexOf('.') + 1);

            // Example: Gtrm2G_35ynyNd1-CjZ1HsvvFFItEsXPvwhaOsN81HQ
            string signature = token[(index + 1)..];

            // Bytes of header + payload
            // In other words bytes of: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb29AZ21haWwuY29tIiwiZXhwIjoxNjQ1NzM1MDU2fQ
            byte[] bytesToSign = Encoding.UTF8.GetBytes(token[..index]);

            // compute hash
            var hash = new HMACSHA256(key).ComputeHash(bytesToSign);
            var computedSignature = Base64UrlEncode(hash);

            // make sure that signatures match
            return computedSignature.Length == signature.Length 
                && computedSignature.SequenceEqual(signature);
        }
        catch
        {
            return false;
        }
    }

    private static string GenerateToken()
    {
        var securityKey = new SymmetricSecurityKey(key);
        var credentials = new SigningCredentials(securityKey, securityAlgorithm);

        var secToken = new JwtSecurityToken(
            signingCredentials: credentials,
            claims: new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, "[email protected]")
            },
            expires: DateTime.UtcNow.AddDays(1));

        var handler = new JwtSecurityTokenHandler();
        return handler.WriteToken(secToken);
    }
    
}
like image 1
Tono Nam Avatar answered Nov 19 '22 16:11

Tono Nam