Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
JwtUtil.cs 4.99 KiB
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using Winton.Extensions.Configuration.Consul;

namespace Coscine.ApiClient.Core
{
    public class JwtUtil
    {
        public static JwtConfiguration RetrieveJwtConfiguration(string environment = "development", string? givenConsulUrl = null)
        {
            var consulUrl = givenConsulUrl ?? Environment.GetEnvironmentVariable("CONSUL_URL") ?? "http://localhost:8500";

            var configuration = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
                .AddConsul(
                    "coscine/Coscine.Api/appsettings",
                    options =>
                    {
                        options.ConsulConfigurationOptions =
                            cco => cco.Address = new Uri(consulUrl);
                        options.Optional = true;
                        options.ReloadOnChange = true;
                        options.PollWaitTime = TimeSpan.FromSeconds(5);
                        options.OnLoadException = exceptionContext => exceptionContext.Ignore = true;
                    }
                )
                .AddConsul(
                    $"coscine/Coscine.Api/appsettings.{environment}",
                    options =>
                    {
                        options.ConsulConfigurationOptions =
                            cco => cco.Address = new Uri(consulUrl);
                        options.Optional = true;
                        options.ReloadOnChange = true;
                        options.PollWaitTime = TimeSpan.FromSeconds(5);
                        options.OnLoadException = exceptionContext => exceptionContext.Ignore = true;
                    }
                )
                .AddEnvironmentVariables()
                .Build();
            return RetrieveJwtConfiguration(configuration);
        }

        public static JwtConfiguration RetrieveJwtConfiguration(IConfiguration configuration)
        {
            var jwtConfiguration = new JwtConfiguration();
            configuration.Bind(JwtConfiguration.Section, jwtConfiguration);
            return jwtConfiguration;
        }

        public static string GenerateAdminToken(JwtConfiguration jwtConfiguration, int expiresInDays = 1)
        {
            var jwtSecurityToken = GenerateJwtSecurityToken(
                jwtConfiguration,
                new Dictionary<string, string>
                {
                    [ClaimTypes.Role] = "administrator"
                },
                TimeSpan.FromDays(expiresInDays).TotalMinutes,
                jwtConfiguration.JsonWebKeys.First(x => x.KeySize >= 256)
            );

            return GetJwtStringFromToken(jwtSecurityToken);
        }

        /// <summary>Generates the JWT security token.</summary>
        /// <param name="jwtConfiguration">Jwt Configuration.</param>
        /// <param name="payloadContents">The payload contents.</param>
        /// <param name="expiresInMinutes">The expires in minutes.</param>
        /// <param name="jsonWebKey">The json web key.</param>
        /// <returns>The generated JWT security token.</returns>
        public static JwtSecurityToken GenerateJwtSecurityToken(JwtConfiguration jwtConfiguration, IReadOnlyDictionary<string, string> payloadContents, double expiresInMinutes, JsonWebKey jsonWebKey)
        {
            var now = DateTime.UtcNow;

            // Create claims for the JWT token
            var claims = payloadContents.Select(c => new Claim(c.Key, c.Value)).ToList();
            claims.Add(new Claim("iat", EpochTime.GetIntDate(now).ToString(), ClaimValueTypes.Integer64));

            // Create the security key from the secret key
            var securityKey = new SymmetricSecurityKey(Base64UrlEncoder.DecodeBytes(jsonWebKey.K));

            // Create signing credentials using the security key
            var signingCredentials = new SigningCredentials(securityKey, jsonWebKey.Alg);

            // Create the JWT token
            return new JwtSecurityToken(
                issuer: jwtConfiguration.ValidIssuers[0],
                audience: jwtConfiguration.ValidAudiences[0],
                claims: claims,
                notBefore: now,
                expires: now.AddMinutes(expiresInMinutes), // Token expiration time
                signingCredentials: signingCredentials
            );
        }

        /// <summary>Gets the JWT string from token.</summary>
        /// <param name="token">The token.</param>
        /// <returns>The JWT as a string.</returns>
        public static string GetJwtStringFromToken(JwtSecurityToken token)
        {
            // Generate the encoded JWT token
            var tokenHandler = new JwtSecurityTokenHandler();

            return tokenHandler.WriteToken(token);
        }
    }
}