diff --git a/Endpoint/Common/Services/Security/JwtTokenService.cs b/Endpoint/Common/Services/Security/JwtTokenService.cs new file mode 100644 index 0000000..7c3225f --- /dev/null +++ b/Endpoint/Common/Services/Security/JwtTokenService.cs @@ -0,0 +1,82 @@ +using Microsoft.IdentityModel.Tokens; +using Mirea.Api.Security.Common.Interfaces; +using System; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; + +namespace Mirea.Api.Endpoint.Common.Services.Security; + +public class JwtTokenService : IAccessToken +{ + public required string Issuer { private get; init; } + public required string Audience { private get; init; } + public TimeSpan Lifetime { private get; init; } + + public ReadOnlyMemory EncryptionKey { get; init; } + public ReadOnlyMemory SigningKey { private get; init; } + + public (string Token, DateTime ExpireIn) GenerateToken(string userId) + { + var tokenHandler = new JwtSecurityTokenHandler(); + var signingKey = new SymmetricSecurityKey(SigningKey.ToArray()); + var encryptionKey = new SymmetricSecurityKey(EncryptionKey.ToArray()); + var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha512); + + var expires = DateTime.UtcNow.Add(Lifetime); + + var tokenDescriptor = new SecurityTokenDescriptor + { + Issuer = Issuer, + Audience = Audience, + Expires = expires, + SigningCredentials = signingCredentials, + Subject = new ClaimsIdentity( + [ + new Claim(ClaimTypes.Name, userId), + // todo: get role by userId + new Claim(ClaimTypes.Role, "") + ]), + EncryptingCredentials = new EncryptingCredentials(encryptionKey, SecurityAlgorithms.Aes256KW, SecurityAlgorithms.Aes256CbcHmacSha512) + }; + + var token = tokenHandler.CreateToken(tokenDescriptor); + + return (tokenHandler.WriteToken(token), expires); + } + + public DateTimeOffset GetExpireDateTime(string token) + { + var tokenHandler = new JwtSecurityTokenHandler(); + var signingKey = new SymmetricSecurityKey(SigningKey.ToArray()); + var encryptionKey = new SymmetricSecurityKey(EncryptionKey.ToArray()); + + var tokenValidationParameters = new TokenValidationParameters + { + ValidIssuer = Issuer, + ValidAudience = Audience, + IssuerSigningKey = signingKey, + TokenDecryptionKey = encryptionKey, + ValidateIssuer = true, + ValidateAudience = true, + ValidateIssuerSigningKey = true, + ValidateLifetime = false + }; + + try + { + var claimsPrincipal = tokenHandler.ValidateToken(token, tokenValidationParameters, out _); + + var expClaim = claimsPrincipal.Claims.FirstOrDefault(c => c.Type == "exp"); + + if (expClaim != null && long.TryParse(expClaim.Value, out var expUnix)) + return DateTimeOffset.FromUnixTimeSeconds(expUnix); + } + catch (SecurityTokenException) + { + return DateTimeOffset.MinValue; + } + + return DateTimeOffset.MinValue; + } +} \ No newline at end of file