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;
public JwtService(RSA rsa, JwtOptions options)
{
_rsa = rsa;
_options = options;
}
///
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 = DateTime.UtcNow;
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);
}
}