using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Azaion.Common.Configs; using Azaion.Common.Entities; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; namespace Azaion.Services; public interface IAuthService { Task GetCurrentUser(); /// /// AZ-531 / AZ-532 — mint a 15-minute ES256 access token. /// is stamped as the sid claim (logout / family-revocation key in AZ-535) /// and is the per-token unique id (AZ-535 access denylist). /// AccessToken CreateToken(User user, Guid sessionId, Guid jti); } public sealed record AccessToken(string Jwt, DateTime ExpiresAt); public class AuthService( IHttpContextAccessor httpContextAccessor, IOptions jwtConfig, IJwtSigningKeyProvider signingKeys, IUserService userService) : IAuthService { private readonly JwtConfig _jwt = jwtConfig.Value; private string? GetCurrentUserEmail() { var claims = httpContextAccessor.HttpContext?.User.Claims.ToDictionary(x => x.Type); return claims?[ClaimTypes.Name].Value; } public async Task GetCurrentUser() { var email = GetCurrentUserEmail(); return await userService.GetByEmail(email); } public AccessToken CreateToken(User user, Guid sessionId, Guid jti) { var active = signingKeys.Active; var signingCredentials = new SigningCredentials(active.SecurityKey, SecurityAlgorithms.EcdsaSha256); var expires = DateTime.UtcNow.AddMinutes(_jwt.AccessTokenLifetimeMinutes); var tokenHandler = new JwtSecurityTokenHandler(); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity([ new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.Email), new Claim(ClaimTypes.Role, user.Role.ToString()), new Claim(JwtRegisteredClaimNames.Sid, sessionId.ToString()), new Claim(JwtRegisteredClaimNames.Jti, jti.ToString()) ]), Expires = expires, Issuer = _jwt.Issuer, Audience = _jwt.Audience, SigningCredentials = signingCredentials }; var token = tokenHandler.CreateToken(tokenDescriptor); return new AccessToken(tokenHandler.WriteToken(token), expires); } }