From 4b6460e7d564eebff7a79df475a702dbd3f009b9 Mon Sep 17 00:00:00 2001 From: crupest Date: Fri, 23 Apr 2021 19:06:11 +0800 Subject: feat: Fix #51. --- BackEnd/Timeline/Services/UserTokenService.cs | 149 -------------------------- 1 file changed, 149 deletions(-) delete mode 100644 BackEnd/Timeline/Services/UserTokenService.cs (limited to 'BackEnd/Timeline/Services/UserTokenService.cs') diff --git a/BackEnd/Timeline/Services/UserTokenService.cs b/BackEnd/Timeline/Services/UserTokenService.cs deleted file mode 100644 index 86f3a0f7..00000000 --- a/BackEnd/Timeline/Services/UserTokenService.cs +++ /dev/null @@ -1,149 +0,0 @@ -using Microsoft.Extensions.Options; -using Microsoft.IdentityModel.Tokens; -using System; -using System.Globalization; -using System.IdentityModel.Tokens.Jwt; -using System.Linq; -using System.Security.Claims; -using Timeline.Configs; -using Timeline.Entities; - -namespace Timeline.Services -{ - public class UserTokenInfo - { - public long Id { get; set; } - public long Version { get; set; } - public DateTime? ExpireAt { get; set; } - } - - public interface IUserTokenService - { - /// - /// Create a token for a given token info. - /// - /// The info to generate token. - /// Return the generated token. - /// Thrown when is null. - string GenerateToken(UserTokenInfo tokenInfo); - - /// - /// Verify a token and get the saved info. - /// - /// The token to verify. - /// The saved info in token. - /// Thrown when is null. - /// Thrown when the token is of bad format. - /// - /// If this method throw , it usually means the token is not created by this service. - /// - UserTokenInfo VerifyToken(string token); - } - - public class JwtUserTokenService : IUserTokenService - { - private const string VersionClaimType = "timeline_version"; - - private readonly IOptionsMonitor _jwtConfig; - private readonly IClock _clock; - - private readonly JwtSecurityTokenHandler _tokenHandler = new JwtSecurityTokenHandler(); - private SymmetricSecurityKey _tokenSecurityKey; - - public JwtUserTokenService(IOptionsMonitor jwtConfig, IClock clock, DatabaseContext database) - { - _jwtConfig = jwtConfig; - _clock = clock; - - var key = database.JwtToken.Select(t => t.Key).SingleOrDefault(); - - if (key == null) - { - throw new InvalidOperationException(Resources.Services.UserTokenService.JwtKeyNotExist); - } - - _tokenSecurityKey = new SymmetricSecurityKey(key); - } - - public string GenerateToken(UserTokenInfo tokenInfo) - { - if (tokenInfo == null) - throw new ArgumentNullException(nameof(tokenInfo)); - - var config = _jwtConfig.CurrentValue; - - var identity = new ClaimsIdentity(); - identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, tokenInfo.Id.ToString(CultureInfo.InvariantCulture.NumberFormat), ClaimValueTypes.Integer64)); - identity.AddClaim(new Claim(VersionClaimType, tokenInfo.Version.ToString(CultureInfo.InvariantCulture.NumberFormat), ClaimValueTypes.Integer64)); - - var tokenDescriptor = new SecurityTokenDescriptor() - { - Subject = identity, - Issuer = config.Issuer, - Audience = config.Audience, - SigningCredentials = new SigningCredentials(_tokenSecurityKey, SecurityAlgorithms.HmacSha384), - IssuedAt = _clock.GetCurrentTime(), - Expires = tokenInfo.ExpireAt.GetValueOrDefault(_clock.GetCurrentTime().AddSeconds(config.DefaultExpireOffset)), - NotBefore = _clock.GetCurrentTime() // I must explicitly set this or it will use the current time by default and mock is not work in which case test will not pass. - }; - - var token = _tokenHandler.CreateToken(tokenDescriptor); - var tokenString = _tokenHandler.WriteToken(token); - - return tokenString; - } - - - public UserTokenInfo VerifyToken(string token) - { - if (token == null) - throw new ArgumentNullException(nameof(token)); - - var config = _jwtConfig.CurrentValue; - try - { - var principal = _tokenHandler.ValidateToken(token, new TokenValidationParameters - { - ValidateIssuer = true, - ValidateAudience = true, - ValidateIssuerSigningKey = true, - ValidateLifetime = false, - ValidIssuer = config.Issuer, - ValidAudience = config.Audience, - IssuerSigningKey = _tokenSecurityKey - }, out var t); - - var idClaim = principal.FindFirstValue(ClaimTypes.NameIdentifier); - if (idClaim == null) - throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.NoIdClaim); - if (!long.TryParse(idClaim, out var id)) - throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.IdClaimBadFormat); - - var versionClaim = principal.FindFirstValue(VersionClaimType); - if (versionClaim == null) - throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.NoVersionClaim); - if (!long.TryParse(versionClaim, out var version)) - throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.VersionClaimBadFormat); - - var decodedToken = (JwtSecurityToken)t; - var exp = decodedToken.Payload.Exp; - DateTime? expireAt = null; - if (exp.HasValue) - { - expireAt = EpochTime.DateTime(exp.Value); - } - - return new UserTokenInfo - { - Id = id, - Version = version, - ExpireAt = expireAt - }; - } - catch (Exception e) when (e is SecurityTokenException || e is ArgumentException) - { - throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.Other, e); - } - } - } -} -- cgit v1.2.3