< Summary

Information
Class: ArturRios.Jwt.JwtHandler
Assembly: ArturRios.Jwt
File(s): D:\Repositories\dotnet-jwt\src\JwtHandler.cs
Line coverage
100%
Covered lines: 42
Uncovered lines: 0
Coverable lines: 42
Total lines: 92
Line coverage: 100%
Branch coverage
75%
Covered branches: 6
Total branches: 8
Branch coverage: 75%
Method coverage
100%
Covered methods: 5
Fully covered methods: 2
Total methods: 5
Method coverage: 100%
Full method coverage: 40%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
CreateToken(...)50%22100%
GetUserIdFromToken(...)100%22100%
IsTokenValidAsync()50%22100%
ReadToken(...)100%22100%

File(s)

D:\Repositories\dotnet-jwt\src\JwtHandler.cs

#LineLine coverage
 1using System.IdentityModel.Tokens.Jwt;
 2using System.Security.Claims;
 3using System.Text;
 4using Microsoft.IdentityModel.Tokens;
 5
 6namespace ArturRios.Jwt;
 7
 8/// <summary>
 9/// Creates and validates JSON Web Tokens (JWTs) based on a <see cref="JwtConfiguration"/>.
 10/// </summary>
 11public class JwtHandler
 12{
 613    private readonly JwtSecurityTokenHandler _handler = new();
 14
 15    /// <summary>
 16    /// Creates a signed JWT (using HMAC-SHA256) from the given configuration.
 17    /// </summary>
 18    /// <param name="configuration">The issuer, audience, expiration, signing secret and claims to embed in the token.</
 19    /// <returns>The serialized, signed JWT.</returns>
 20    public string CreateToken(JwtConfiguration configuration)
 521    {
 522        var key = string.IsNullOrWhiteSpace(configuration.Secret) ? [] : Encoding.ASCII.GetBytes(configuration.Secret);
 23
 1024        var claimsList = configuration.Claims.Select(c => new Claim(c.Key, c.Value)).ToList();
 25
 526        ClaimsIdentity identity = new(claimsList);
 27
 528        var creationDate = DateTime.Now;
 529        var expirationDate = creationDate + TimeSpan.FromSeconds(configuration.ExpirationInSeconds);
 30
 531        JwtSecurityTokenHandler handler = new();
 32
 533        var token = handler.CreateToken(new SecurityTokenDescriptor
 534        {
 535            Issuer = configuration.Issuer,
 536            Audience = configuration.Audience,
 537            SigningCredentials =
 538                new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
 539            Subject = identity,
 540            NotBefore = creationDate,
 541            Expires = expirationDate
 542        });
 43
 544        return handler.WriteToken(token);
 545    }
 46
 47    /// <summary>
 48    /// Reads a JWT and extracts the user id from its "id" claim, without validating the token's signature.
 49    /// </summary>
 50    /// <param name="token">The JWT to read.</param>
 51    /// <returns>The user id from the token's "id" claim, or <see langword="null"/> if the token cannot be read.</return
 52    public int? GetUserIdFromToken(string token)
 253    {
 254        if (ReadToken(token) is not JwtSecurityToken jwtToken)
 155        {
 156            return null;
 57        }
 58
 259        return int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value);
 260    }
 61
 62    /// <summary>
 63    /// Validates a JWT's signature against the given secret.
 64    /// </summary>
 65    /// <param name="token">The JWT to validate.</param>
 66    /// <param name="secret">The secret key expected to have been used to sign the token.</param>
 67    /// <returns><see langword="true"/> if the token's signature is valid; otherwise, <see langword="false"/>.</returns>
 68    public async Task<bool> IsTokenValidAsync(string token, string secret)
 369    {
 370        var key = string.IsNullOrWhiteSpace(secret) ? [] : Encoding.ASCII.GetBytes(secret);
 71
 372        var output = await _handler.ValidateTokenAsync(token,
 373            new TokenValidationParameters
 374            {
 375                ValidateIssuerSigningKey = true,
 376                IssuerSigningKey = new SymmetricSecurityKey(key),
 377                ValidateIssuer = false,
 378                ValidateAudience = false,
 379                ClockSkew = TimeSpan.Zero
 380            });
 81
 382        return output.IsValid;
 383    }
 84
 85    /// <summary>
 86    /// Reads a JWT without validating its signature, returning <see langword="null"/> if it cannot be read.
 87    /// </summary>
 88    private SecurityToken? ReadToken(string token)
 289    {
 290        return _handler.CanReadToken(token) ? _handler.ReadToken(token) : null;
 291    }
 92}