using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography; using System.Text; using EchoHub.Core.Models; using Microsoft.IdentityModel.Tokens; namespace EchoHub.Server.Auth; public class JwtTokenService { private readonly string _secret; private readonly string _issuer; private readonly string _audience; private static readonly TimeSpan AccessTokenLifetime = TimeSpan.FromMinutes(15); public static readonly TimeSpan RefreshTokenLifetime = TimeSpan.FromDays(30); public JwtTokenService(IConfiguration configuration) { _secret = configuration["Jwt:Secret"] ?? throw new InvalidOperationException("Jwt:Secret is not configured."); _issuer = configuration["Jwt:Issuer"] ?? throw new InvalidOperationException("Jwt:Issuer is not configured."); _audience = configuration["Jwt:Audience"] ?? throw new InvalidOperationException("Jwt:Audience is not configured."); } public (string Token, DateTimeOffset ExpiresAt) GenerateAccessToken(User user) { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secret)); var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var expiresAt = DateTimeOffset.UtcNow.Add(AccessTokenLifetime); Claim[] claims = [ new(JwtRegisteredClaimNames.Sub, user.Id.ToString()), new("username", user.Username), new("display_name", user.DisplayName ?? user.Username), new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), ]; var token = new JwtSecurityToken( issuer: _issuer, audience: _audience, claims: claims, expires: expiresAt.UtcDateTime, signingCredentials: credentials); return (new JwtSecurityTokenHandler().WriteToken(token), expiresAt); } public static string GenerateRefreshToken() { var randomBytes = new byte[64]; using var rng = RandomNumberGenerator.Create(); rng.GetBytes(randomBytes); return Convert.ToBase64String(randomBytes); } public static string HashToken(string token) { var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(token)); return Convert.ToBase64String(bytes); } }