using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using Managing.Domain.Users; using Microsoft.IdentityModel.Tokens; namespace Managing.Api.Authorization; public interface IJwtUtils { public string GenerateJwtToken(User user, string publicAddress); public string ValidateJwtToken(string token); } public class JwtUtils : IJwtUtils { private readonly string _secret; private readonly string? _issuer; private readonly string? _audience; public JwtUtils(IConfiguration config) { _secret = config.GetValue("Jwt:Secret") ?? throw new InvalidOperationException("JWT secret is not configured."); _issuer = config.GetValue("Authentication:Schemes:Bearer:ValidIssuer"); _audience = config.GetValue("Authentication:Schemes:Bearer:ValidAudiences"); } public string GenerateJwtToken(User user, string publicAddress) { // Generate token that is valid for 15 days (as per original implementation) var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.UTF8.GetBytes(_secret); // Use UTF8 consistently with Program.cs var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim("address", publicAddress) }), Expires = DateTime.UtcNow.AddDays(15), Issuer = _issuer, // Include issuer if configured Audience = _audience, // Include audience if configured SigningCredentials = new SigningCredentials( new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); return tokenHandler.WriteToken(token); } public string ValidateJwtToken(string token) { if (token == null || string.IsNullOrEmpty(token)) return null; var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.UTF8.GetBytes(_secret); // Use UTF8 consistently with Program.cs try { tokenHandler.ValidateToken(token, new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key), ValidateIssuer = false, ValidateAudience = false, // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later) ClockSkew = TimeSpan.Zero }, out SecurityToken validatedToken); var jwtToken = (JwtSecurityToken)validatedToken; var address = jwtToken.Claims.First(x => x.Type == "address").Value; // return user id from JWT token if validation successful return address; } catch { // return null if validation fails return null; } } }