diff options
Diffstat (limited to 'Timeline/Services')
-rw-r--r-- | Timeline/Services/JwtService.cs | 74 |
1 files changed, 70 insertions, 4 deletions
diff --git a/Timeline/Services/JwtService.cs b/Timeline/Services/JwtService.cs index 1b465dd9..a01f3f2b 100644 --- a/Timeline/Services/JwtService.cs +++ b/Timeline/Services/JwtService.cs @@ -1,17 +1,22 @@ -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using System; -using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; -using System.Threading.Tasks; using Timeline.Configs; using Timeline.Entities; namespace Timeline.Services { + public class TokenValidationResult + { + public bool IsValid { get; set; } + public UserInfo UserInfo { get; set; } + } + public interface IJwtService { /// <summary> @@ -21,16 +26,34 @@ namespace Timeline.Services /// <param name="user">The user to generate token.</param> /// <returns>The generated token or null if <paramref name="user"/> is null.</returns> string GenerateJwtToken(User user); + + /// <summary> + /// Validate a JWT token. + /// Return null is <paramref name="token"/> is null. + /// If token is invalid, return a <see cref="TokenValidationResult"/> with + /// <see cref="TokenValidationResult.IsValid"/> set to false and + /// <see cref="TokenValidationResult.UserInfo"/> set to null. + /// If token is valid, return a <see cref="TokenValidationResult"/> with + /// <see cref="TokenValidationResult.IsValid"/> set to true and + /// <see cref="TokenValidationResult.UserInfo"/> filled with the user info + /// in the token. + /// </summary> + /// <param name="token">The token string to validate.</param> + /// <returns>Null if <paramref name="token"/> is null. Or the result.</returns> + TokenValidationResult ValidateJwtToken(string token); + } public class JwtService : IJwtService { private readonly IOptionsMonitor<JwtConfig> _jwtConfig; private readonly JwtSecurityTokenHandler _tokenHandler = new JwtSecurityTokenHandler(); + private readonly ILogger<JwtService> _logger; - public JwtService(IOptionsMonitor<JwtConfig> jwtConfig) + public JwtService(IOptionsMonitor<JwtConfig> jwtConfig, ILogger<JwtService> logger) { _jwtConfig = jwtConfig; + _logger = logger; } public string GenerateJwtToken(User user) @@ -42,6 +65,7 @@ namespace Timeline.Services var identity = new ClaimsIdentity(); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString())); + identity.AddClaim(new Claim(identity.NameClaimType, user.Username)); identity.AddClaims(user.Roles.Select(role => new Claim(identity.RoleClaimType, role))); var tokenDescriptor = new SecurityTokenDescriptor() @@ -60,5 +84,47 @@ namespace Timeline.Services return tokenString; } + + + public TokenValidationResult ValidateJwtToken(string token) + { + if (token == null) + return null; + + var config = _jwtConfig.CurrentValue; + + try + { + var principal = _tokenHandler.ValidateToken(token, new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateIssuerSigningKey = true, + ValidateLifetime = true, + ValidIssuer = config.Issuer, + ValidAudience = config.Audience, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(config.SigningKey)) + }, out SecurityToken validatedToken); + + var identity = principal.Identity as ClaimsIdentity; + + var userInfo = new UserInfo + { + Username = identity.FindAll(identity.NameClaimType).Select(claim => claim.Value).Single(), + Roles = identity.FindAll(identity.RoleClaimType).Select(claim => claim.Value).ToArray() + }; + + return new TokenValidationResult + { + IsValid = true, + UserInfo = userInfo + }; + } + catch (Exception e) + { + _logger.LogInformation(e, "Token validation failed! Token is {} .", token); + return new TokenValidationResult { IsValid = false }; + } + } } } |