using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; using TimetableDesigner.Backend.Services.Authentication.Database; using TimetableDesigner.Backend.Services.Authentication.Database.Model; using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames; namespace TimetableDesigner.Backend.Services.Authentication.Core.Helpers; public class TokenHelper : ITokenHelper { private readonly IConfiguration _configuration; public TokenHelper(IConfiguration configuration) { _configuration = configuration; } public string GenerateAccessToken(long accountId) { IConfigurationSection accessTokenSettings = _configuration.GetSection("Tokens") .GetSection("AccessToken"); int lifetime = accessTokenSettings.GetSection("Lifetime") .GetValue("Normal"); DateTimeOffset expirationDate = DateTimeOffset.UtcNow.AddMinutes(lifetime); string stringKey = accessTokenSettings.GetValue("Key")!; byte[] encodedKey = Encoding.UTF8.GetBytes(stringKey); SymmetricSecurityKey key = new SymmetricSecurityKey(encodedKey); string algorithm = accessTokenSettings.GetValue("Algorithm")!; SecurityTokenDescriptor descriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity( [ new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Sub, accountId.ToString()), new Claim(JwtRegisteredClaimNames.Exp, expirationDate.UtcTicks.ToString()) ]), Issuer = accessTokenSettings.GetValue("Issuer"), Audience = accessTokenSettings.GetValue("Audience"), SigningCredentials = new SigningCredentials(key, algorithm), Expires = expirationDate.UtcDateTime, }; JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler(); handler.InboundClaimTypeMap.Clear(); SecurityToken token = handler.CreateToken(descriptor); return handler.WriteToken(token); } public bool ValidateExpiredAccessToken(string accessToken) { IConfigurationSection accessTokenSettings = _configuration.GetSection("Tokens") .GetSection("AccessToken"); string stringKey = accessTokenSettings.GetValue("Key")!; byte[] encodedKey = Encoding.UTF8.GetBytes(stringKey); SymmetricSecurityKey key = new SymmetricSecurityKey(encodedKey); string algorithm = accessTokenSettings.GetValue("Algorithm")!; TokenValidationParameters tokenValidation = new TokenValidationParameters { ValidateIssuerSigningKey = true, ValidateAudience = true, ValidateIssuer = true, ValidateLifetime = false, ValidIssuer = accessTokenSettings.GetValue("Issuer"), ValidAudience = accessTokenSettings.GetValue("Audience"), IssuerSigningKey = key, ClockSkew = TimeSpan.FromMinutes(1), }; JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler(); tokenHandler.ValidateToken(accessToken, tokenValidation, out SecurityToken validatedToken); JwtSecurityToken? jwtSecurityToken = validatedToken as JwtSecurityToken; return jwtSecurityToken is not null && jwtSecurityToken.Header.Alg.Equals(algorithm, StringComparison.InvariantCultureIgnoreCase); } public DateTimeOffset CalculateRefreshTokenExpirationDate(bool isExtendable = true) { string lifetimeSection = isExtendable ? "Extended" : "Normal"; int lifetime = _configuration.GetSection("Tokens") .GetSection("RefreshToken") .GetSection("Lifetime") .GetValue(lifetimeSection); return DateTimeOffset.UtcNow.AddMinutes(lifetime); } }