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.ErrorCodes/ErrorCodes.cs | 10 ++ BackEnd/Timeline/Auth/MyAuthenticationHandler.cs | 63 +++++++++- BackEnd/Timeline/Controllers/TokenController.cs | 6 +- BackEnd/Timeline/Services/UserTokenException.cs | 43 ++++--- BackEnd/Timeline/Services/UserTokenHandler.cs | 149 +++++++++++++++++++++++ BackEnd/Timeline/Services/UserTokenManager.cs | 28 +++-- BackEnd/Timeline/Services/UserTokenService.cs | 149 ----------------------- BackEnd/Timeline/Startup.cs | 2 +- 8 files changed, 270 insertions(+), 180 deletions(-) create mode 100644 BackEnd/Timeline/Services/UserTokenHandler.cs delete mode 100644 BackEnd/Timeline/Services/UserTokenService.cs diff --git a/BackEnd/Timeline.ErrorCodes/ErrorCodes.cs b/BackEnd/Timeline.ErrorCodes/ErrorCodes.cs index 4c3b6cd8..87d451f2 100644 --- a/BackEnd/Timeline.ErrorCodes/ErrorCodes.cs +++ b/BackEnd/Timeline.ErrorCodes/ErrorCodes.cs @@ -13,6 +13,7 @@ public const int InvalidModel = 1_000_0001; public const int Forbid = 1_000_0002; public const int UnknownEndpoint = 1_000_0003; + public const int Unauthorized = 1_000_0004; public static class Header { @@ -24,6 +25,15 @@ { public const int TooBig = 1_000_11_01; } + + public static class Token + { + public const int TimeExpired = 1_000_21_01; + public const int VersionExpired = 1_000_21_02; + public const int BadFormat = 1_000_21_03; + public const int UserNotExist = 1_000_21_04; + public const int Unknown = 1_000_21_05; + } } public static class UserCommon diff --git a/BackEnd/Timeline/Auth/MyAuthenticationHandler.cs b/BackEnd/Timeline/Auth/MyAuthenticationHandler.cs index e4122c65..f1f71b20 100644 --- a/BackEnd/Timeline/Auth/MyAuthenticationHandler.cs +++ b/BackEnd/Timeline/Auth/MyAuthenticationHandler.cs @@ -1,13 +1,18 @@ using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Security.Claims; using System.Text.Encodings.Web; +using System.Text.Json; using System.Threading.Tasks; +using Timeline.Models; +using Timeline.Models.Http; using Timeline.Services; using static Timeline.Resources.Authentication.AuthHandler; @@ -30,16 +35,33 @@ namespace Timeline.Auth public class MyAuthenticationHandler : AuthenticationHandler { + private const string TokenErrorCodeKey = "TokenErrorCode"; + + private static CommonResponse CreateChallengeResponseBody(int errorCode) + { + return new CommonResponse(errorCode, errorCode switch + { + ErrorCodes.Common.Token.TimeExpired => "The token is out of date and expired. Please create a new one.", + ErrorCodes.Common.Token.VersionExpired => "The token is of old version and expired. Please create a new one.", + ErrorCodes.Common.Token.BadFormat => "The token is of bad format. It might not be created by this server.", + ErrorCodes.Common.Token.UserNotExist => "The owner of the token does not exist. It might have been deleted.", + _ => "Unknown error." + }); + } + private readonly ILogger _logger; private readonly IUserTokenManager _userTokenManager; private readonly IUserPermissionService _userPermissionService; - public MyAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IUserTokenManager userTokenManager, IUserPermissionService userPermissionService) + private readonly IOptionsMonitor _jsonOptions; + + public MyAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IUserTokenManager userTokenManager, IUserPermissionService userPermissionService, IOptionsMonitor jsonOptions) : base(options, logger, encoder, clock) { _logger = logger.CreateLogger(); _userTokenManager = userTokenManager; _userPermissionService = userPermissionService; + _jsonOptions = jsonOptions; } // return null if no token is found @@ -49,7 +71,7 @@ namespace Timeline.Auth string header = Request.Headers[HeaderNames.Authorization]; if (!string.IsNullOrEmpty(header) && header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { - var token = header.Substring("Bearer ".Length).Trim(); + var token = header["Bearer ".Length..].Trim(); _logger.LogInformation(LogTokenFoundInHeader, token); return token; } @@ -98,8 +120,43 @@ namespace Timeline.Auth catch (Exception e) when (!(e is ArgumentException)) { _logger.LogInformation(e, LogTokenValidationFail); - return AuthenticateResult.Fail(e); + return AuthenticateResult.Fail(e, new AuthenticationProperties(new Dictionary() + { + [TokenErrorCodeKey] = (e switch + { + UserTokenTimeExpiredException => ErrorCodes.Common.Token.TimeExpired, + UserTokenVersionExpiredException => ErrorCodes.Common.Token.VersionExpired, + UserTokenBadFormatException => ErrorCodes.Common.Token.BadFormat, + UserTokenUserNotExistException => ErrorCodes.Common.Token.UserNotExist, + _ => ErrorCodes.Common.Token.Unknown + }).ToString(CultureInfo.InvariantCulture) + })); } } + + protected override async Task HandleChallengeAsync(AuthenticationProperties properties) + { + Response.StatusCode = 401; + + CommonResponse body; + + if (properties.Items.TryGetValue(TokenErrorCodeKey, out var tokenErrorCode)) + { + if (!int.TryParse(tokenErrorCode, out var errorCode)) + errorCode = ErrorCodes.Common.Token.Unknown; + body = CreateChallengeResponseBody(errorCode); + } + else + { + body = new CommonResponse(ErrorCodes.Common.Unauthorized, "You must use a token to authenticate."); + } + + + var bodyData = JsonSerializer.SerializeToUtf8Bytes(body, typeof(CommonResponse), _jsonOptions.CurrentValue.JsonSerializerOptions); + + Response.ContentType = MimeTypes.ApplicationJson; + Response.ContentLength = bodyData.Length; + await Response.Body.WriteAsync(bodyData); + } } } diff --git a/BackEnd/Timeline/Controllers/TokenController.cs b/BackEnd/Timeline/Controllers/TokenController.cs index b3675aad..3ff8acf5 100644 --- a/BackEnd/Timeline/Controllers/TokenController.cs +++ b/BackEnd/Timeline/Controllers/TokenController.cs @@ -117,12 +117,12 @@ namespace Timeline.Controllers User = await _userMapper.MapToHttp(result, Url) }; } - catch (UserTokenTimeExpireException e) + catch (UserTokenTimeExpiredException e) { LogFailure(LogVerifyExpire, e, ("Expire Time", e.ExpireTime), ("Verify Time", e.VerifyTime)); return BadRequest(ErrorResponse.TokenController.Verify_TimeExpired()); } - catch (UserTokenBadVersionException e) + catch (UserTokenVersionExpiredException e) { LogFailure(LogVerifyOldVersion, e, ("Token Version", e.TokenVersion), ("Required Version", e.RequiredVersion)); return BadRequest(ErrorResponse.TokenController.Verify_OldVersion()); @@ -133,7 +133,7 @@ namespace Timeline.Controllers LogFailure(LogVerifyBadFormat, e); return BadRequest(ErrorResponse.TokenController.Verify_BadFormat()); } - catch (UserNotExistException e) + catch (UserTokenUserNotExistException e) { LogFailure(LogVerifyUserNotExist, e); return BadRequest(ErrorResponse.TokenController.Verify_UserNotExist()); diff --git a/BackEnd/Timeline/Services/UserTokenException.cs b/BackEnd/Timeline/Services/UserTokenException.cs index d25fabb3..398da41f 100644 --- a/BackEnd/Timeline/Services/UserTokenException.cs +++ b/BackEnd/Timeline/Services/UserTokenException.cs @@ -20,14 +20,14 @@ namespace Timeline.Services [Serializable] - public class UserTokenTimeExpireException : UserTokenException + public class UserTokenTimeExpiredException : UserTokenException { - public UserTokenTimeExpireException() : base(Resources.Services.Exception.UserTokenTimeExpireException) { } - public UserTokenTimeExpireException(string message) : base(message) { } - public UserTokenTimeExpireException(string message, Exception inner) : base(message, inner) { } - public UserTokenTimeExpireException(string token, DateTime expireTime, DateTime verifyTime) : base(token, Resources.Services.Exception.UserTokenTimeExpireException) { ExpireTime = expireTime; VerifyTime = verifyTime; } - public UserTokenTimeExpireException(string token, DateTime expireTime, DateTime verifyTime, Exception inner) : base(token, Resources.Services.Exception.UserTokenTimeExpireException, inner) { ExpireTime = expireTime; VerifyTime = verifyTime; } - protected UserTokenTimeExpireException( + public UserTokenTimeExpiredException() : base(Resources.Services.Exception.UserTokenTimeExpireException) { } + public UserTokenTimeExpiredException(string message) : base(message) { } + public UserTokenTimeExpiredException(string message, Exception inner) : base(message, inner) { } + public UserTokenTimeExpiredException(string token, DateTime expireTime, DateTime verifyTime) : base(token, Resources.Services.Exception.UserTokenTimeExpireException) { ExpireTime = expireTime; VerifyTime = verifyTime; } + public UserTokenTimeExpiredException(string token, DateTime expireTime, DateTime verifyTime, Exception inner) : base(token, Resources.Services.Exception.UserTokenTimeExpireException, inner) { ExpireTime = expireTime; VerifyTime = verifyTime; } + protected UserTokenTimeExpiredException( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } @@ -37,14 +37,14 @@ namespace Timeline.Services } [Serializable] - public class UserTokenBadVersionException : UserTokenException + public class UserTokenVersionExpiredException : UserTokenException { - public UserTokenBadVersionException() : base(Resources.Services.Exception.UserTokenBadVersionException) { } - public UserTokenBadVersionException(string message) : base(message) { } - public UserTokenBadVersionException(string message, Exception inner) : base(message, inner) { } - public UserTokenBadVersionException(string token, long tokenVersion, long requiredVersion) : base(token, Resources.Services.Exception.UserTokenBadVersionException) { TokenVersion = tokenVersion; RequiredVersion = requiredVersion; } - public UserTokenBadVersionException(string token, long tokenVersion, long requiredVersion, Exception inner) : base(token, Resources.Services.Exception.UserTokenBadVersionException, inner) { TokenVersion = tokenVersion; RequiredVersion = requiredVersion; } - protected UserTokenBadVersionException( + public UserTokenVersionExpiredException() : base(Resources.Services.Exception.UserTokenBadVersionException) { } + public UserTokenVersionExpiredException(string message) : base(message) { } + public UserTokenVersionExpiredException(string message, Exception inner) : base(message, inner) { } + public UserTokenVersionExpiredException(string token, long tokenVersion, long requiredVersion) : base(token, Resources.Services.Exception.UserTokenBadVersionException) { TokenVersion = tokenVersion; RequiredVersion = requiredVersion; } + public UserTokenVersionExpiredException(string token, long tokenVersion, long requiredVersion, Exception inner) : base(token, Resources.Services.Exception.UserTokenBadVersionException, inner) { TokenVersion = tokenVersion; RequiredVersion = requiredVersion; } + protected UserTokenVersionExpiredException( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } @@ -53,6 +53,21 @@ namespace Timeline.Services public long RequiredVersion { get; set; } } + + [Serializable] + public class UserTokenUserNotExistException : UserTokenException + { + const string message = "The owner of the token does not exist."; + + public UserTokenUserNotExistException() : base(message) { } + public UserTokenUserNotExistException(string token) : base(token, message) { } + public UserTokenUserNotExistException(string token, Exception inner) : base(token, message, inner) { } + + protected UserTokenUserNotExistException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } + [Serializable] public class UserTokenBadFormatException : UserTokenException { diff --git a/BackEnd/Timeline/Services/UserTokenHandler.cs b/BackEnd/Timeline/Services/UserTokenHandler.cs new file mode 100644 index 00000000..c24a8d47 --- /dev/null +++ b/BackEnd/Timeline/Services/UserTokenHandler.cs @@ -0,0 +1,149 @@ +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 IUserTokenHandler + { + /// + /// 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 JwtUserTokenHandler : IUserTokenHandler + { + private const string VersionClaimType = "timeline_version"; + + private readonly IOptionsMonitor _jwtConfig; + private readonly IClock _clock; + + private readonly JwtSecurityTokenHandler _tokenHandler = new JwtSecurityTokenHandler(); + private SymmetricSecurityKey _tokenSecurityKey; + + public JwtUserTokenHandler(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); + } + } + } +} diff --git a/BackEnd/Timeline/Services/UserTokenManager.cs b/BackEnd/Timeline/Services/UserTokenManager.cs index 78aa0b1f..898e4d6d 100644 --- a/BackEnd/Timeline/Services/UserTokenManager.cs +++ b/BackEnd/Timeline/Services/UserTokenManager.cs @@ -34,10 +34,10 @@ namespace Timeline.Services /// The token. /// The user stored in token. /// Thrown when is null. - /// Thrown when the token is expired. - /// Thrown when the token is of bad version. + /// Thrown when the token is expired. + /// Thrown when the token is of bad version. /// Thrown when the token is of bad format. - /// Thrown when the user specified by the token does not exist. Usually the user had been deleted after the token was issued. + /// Thrown when the user specified by the token does not exist. Usually the user had been deleted after the token was issued. public Task VerifyToken(string token); } @@ -46,10 +46,10 @@ namespace Timeline.Services private readonly ILogger _logger; private readonly IUserService _userService; private readonly IUserCredentialService _userCredentialService; - private readonly IUserTokenService _userTokenService; + private readonly IUserTokenHandler _userTokenService; private readonly IClock _clock; - public UserTokenManager(ILogger logger, IUserService userService, IUserCredentialService userCredentialService, IUserTokenService userTokenService, IClock clock) + public UserTokenManager(ILogger logger, IUserService userService, IUserCredentialService userCredentialService, IUserTokenHandler userTokenService, IClock clock) { _logger = logger; _userService = userService; @@ -86,15 +86,23 @@ namespace Timeline.Services { var currentTime = _clock.GetCurrentTime(); if (tokenInfo.ExpireAt < currentTime) - throw new UserTokenTimeExpireException(token, tokenInfo.ExpireAt.Value, currentTime); + throw new UserTokenTimeExpiredException(token, tokenInfo.ExpireAt.Value, currentTime); } - var user = await _userService.GetUser(tokenInfo.Id); + try + { + var user = await _userService.GetUser(tokenInfo.Id); + + if (tokenInfo.Version < user.Version) + throw new UserTokenVersionExpiredException(token, tokenInfo.Version, user.Version); - if (tokenInfo.Version < user.Version) - throw new UserTokenBadVersionException(token, tokenInfo.Version, user.Version); + return user; - return user; + } + catch (UserNotExistException e) + { + throw new UserTokenUserNotExistException(token, e); + } } } } 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); - } - } - } -} diff --git a/BackEnd/Timeline/Startup.cs b/BackEnd/Timeline/Startup.cs index 932e03c3..c2134a94 100644 --- a/BackEnd/Timeline/Startup.cs +++ b/BackEnd/Timeline/Startup.cs @@ -111,7 +111,7 @@ namespace Timeline services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddUserAvatarService(); -- cgit v1.2.3