using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; namespace SpaceGame.Api.Auth.Simulation; public sealed class JwtTokenService( IOptions jwtOptions, RefreshTokenFactory refreshTokenFactory) : ITokenService { public (string Token, DateTimeOffset ExpiresAtUtc) CreateAccessToken(UserAccount user) { var options = jwtOptions.Value; var expiresAtUtc = DateTimeOffset.UtcNow.AddMinutes(Math.Max(options.AccessTokenLifetimeMinutes, 5)); var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(options.SigningKey)); var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var claims = new[] { new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()), new Claim(JwtRegisteredClaimNames.Email, user.Email), new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Email, user.Email), }.ToList(); foreach (var role in user.Roles) { claims.Add(new Claim(ClaimTypes.Role, role)); claims.Add(new Claim("role", role)); } var token = new JwtSecurityToken( issuer: options.Issuer, audience: options.Audience, claims: claims, notBefore: DateTime.UtcNow, expires: expiresAtUtc.UtcDateTime, signingCredentials: credentials); return (new JwtSecurityTokenHandler().WriteToken(token), expiresAtUtc); } public (string Token, string TokenHash, DateTimeOffset ExpiresAtUtc) CreateRefreshToken() { var token = refreshTokenFactory.CreateToken(); var tokenHash = refreshTokenFactory.HashToken(token); var expiresAtUtc = DateTimeOffset.UtcNow.AddDays(Math.Max(jwtOptions.Value.RefreshTokenLifetimeDays, 1)); return (token, tokenHash, expiresAtUtc); } }