Journal: Software Development

Email: mail@appsoftware.com

Software development notes and articles

A journal for sharing all things software development related


Json Web Tokens (JWTs)

Mon, 15 Jul 2019 07:54 UTC

Discussion of pros / cons

https://developer.okta.com/blog/2017/08/17/why-jwts-suck-as-session-tokens

Basics and how to implement:

https://auth0.com/learn/json-web-tokens/

Revoking JSON web tokens:

https://stackoverflow.com/questions/31919067/how-can-i-revoke-a-jwt-token

Blacklisting JWTs and unique identifiers (JTIs)

https://auth0.com/blog/blacklist-json-web-token-api-keys/

Debugging / Decoding

https://jwt.io/

Bearer Token Spec

https://tools.ietf.org/html/rfc6750

https://stackoverflow.com/questions/33265812/best-http-authorization-header-type-for-jwt

Where to stor JWT's

https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage

Cookies are more secure than HTML 5 local storage so long as other appropriate security controls are considered.

Code Examples (.NET Core)

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

namespace ConsoleApp1
{
    class Program
    {
        // Requires reference to Microsoft.AspNetCore.App

        // Adapted from https://stackoverflow.com/questions/50204844/how-to-validate-a-jwt-token

        static string key = "averylongkeygoeshere";

        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, "test_value")
                },
                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();

            validationParameters.ClockSkew = new TimeSpan(0, 2, 0);

            SecurityToken validatedToken;

            // Throws Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException
            // on signature validation failure

            IPrincipal principal = tokenHandler.ValidateToken(authToken, validationParameters, out validatedToken);

            return true;
        }

        private static TokenValidationParameters GetValidationParameters()
        {
            return new TokenValidationParameters()
            {
                ValidateLifetime = true, // Because there is no expiration in the generated token .. appears that there is
                ValidateAudience = true, // Because there is no audiance in the generated token .. appears that there is
                ValidateIssuer = true,   // Because there is no issuer in the generated token .. appears that there is
                ValidIssuer = "Sample",
                ValidAudience = "Sample",
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)) // The same key as the one that generate the token
            };
        }
    }
}

The information on this site is provided “AS IS” and without warranties of any kind either express or implied. To the fullest extent permissible pursuant to applicable laws, the author disclaims all warranties, express or implied, including, but not limited to, implied warranties of merchantability, non-infringement and suitability for a particular purpose.