using System.Security.Claims; using System.Security.Cryptography; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using SIGCM2.Application.Abstractions.Security; using SIGCM2.Domain.Entities; namespace SIGCM2.Infrastructure.Security; public sealed class JwtService : IJwtService { private readonly RSA _rsa; private readonly JwtOptions _options; private readonly TimeProvider _timeProvider; public JwtService(RSA rsa, JwtOptions options, TimeProvider timeProvider) { _rsa = rsa; _options = options; _timeProvider = timeProvider; } /// public ClaimsPrincipal GetPrincipalFromExpiredToken(string accessToken) { var parameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = _options.Issuer, ValidateAudience = true, ValidAudience = _options.Audience, ValidateIssuerSigningKey = true, IssuerSigningKey = new RsaSecurityKey(_rsa), ValidateLifetime = false, // Key: accept expired tokens in refresh flow ClockSkew = TimeSpan.Zero, }; var handler = new JwtSecurityTokenHandler(); var principal = handler.ValidateToken(accessToken, parameters, out var securityToken); if (securityToken is not JwtSecurityToken jwt || !jwt.Header.Alg.Equals(SecurityAlgorithms.RsaSha256, StringComparison.OrdinalIgnoreCase)) throw new SecurityTokenException("Invalid token algorithm"); return principal; } /// /// UDT-009: Generates an access token with minimal claims. /// Claim 'permisos' has been removed — authorization handler resolves permissions /// from DB per-request using IUsuarioRepository + PermisoResolver. /// Token claims: sub, jti, name, rol (+ standard iat/exp/nbf). /// public string GenerateAccessToken(Usuario usuario) { var signingKey = new RsaSecurityKey(_rsa); var credentials = new SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256); var claims = new List { new(JwtRegisteredClaimNames.Sub, usuario.Id.ToString()), new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new("name", usuario.Username), new("rol", usuario.Rol), }; var now = _timeProvider.GetUtcNow().UtcDateTime; var descriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(claims), Issuer = _options.Issuer, Audience = _options.Audience, IssuedAt = now, Expires = now.AddMinutes(_options.AccessTokenMinutes), SigningCredentials = credentials }; var handler = new JwtSecurityTokenHandler(); var token = handler.CreateToken(descriptor); return handler.WriteToken(token); } }