'How to validate a JWT token

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?



Solution 1:[1]

Validate Token in Jwt Middleware Class that Invoke Method fire in every request for Authorization

JwtMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly TokenValidationParameters _tokenValidationParams;
        public JwtMiddleware(RequestDelegate next, TokenValidationParameters 
        tokenValidationParams)
        {
            _next = next;
            _tokenValidationParams = tokenValidationParams;
        }

    

    public async Task Invoke(HttpContext context)
            {
            try{
                var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
    
                var jwtTokenHandler = new JwtSecurityTokenHandler();
                // Validation 1 - Validation JWT token format
                var tokenInVerification = jwtTokenHandler.ValidateToken(token, _tokenValidationParams, out var validatedToken);
    
                if (validatedToken is JwtSecurityToken jwtSecurityToken)
                {
                    var result = jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase);
    
                    if (result == false)
                    {
                        Error Invalid = new Error()
                        {
                            Success = false,
                            Errors = "Token is Invalid"
                        };
    
                        context.Items["Error"] = Invalid;
                    }
                }
           }
           catch (Exception ex)
            {
                Error Invalid = new Error()
                {
                    Success = false,
                    Errors = "Token does not match or may expired."
                };
                context.Items["Error"] = Invalid ; // userService.GetById(userId);
            }
                await _next(context);
        }
    }

Solution 2:[2]

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);
    }
    
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Md. Nazmul Nadim
Solution 2 Tono Nam