using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using System; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using Timeline.Configs; using Timeline.Entities; namespace Timeline.Services { public interface IJwtService { /// /// Create a JWT token for a given user. /// Return null if is null. /// /// The user to generate token. /// The generated token or null if is null. string GenerateJwtToken(User user); /// /// Validate a JWT token. /// Return null is is null. /// If token is invalid, return a with /// set to false and /// set to null. /// If token is valid, return a with /// set to true and /// filled with the user info /// in the token. /// /// The token string to validate. /// Null if is null. Or the result. TokenValidationResponse ValidateJwtToken(string token); } public class JwtService : IJwtService { private readonly IOptionsMonitor _jwtConfig; private readonly JwtSecurityTokenHandler _tokenHandler = new JwtSecurityTokenHandler(); private readonly ILogger _logger; public JwtService(IOptionsMonitor jwtConfig, ILogger logger) { _jwtConfig = jwtConfig; _logger = logger; } public string GenerateJwtToken(User user) { if (user == null) return null; var jwtConfig = _jwtConfig.CurrentValue; 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() { Subject = identity, Issuer = jwtConfig.Issuer, Audience = jwtConfig.Audience, SigningCredentials = new SigningCredentials( new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtConfig.SigningKey)), SecurityAlgorithms.HmacSha384), IssuedAt = DateTime.Now, Expires = DateTime.Now.AddDays(1) }; var token = _tokenHandler.CreateToken(tokenDescriptor); var tokenString = _tokenHandler.WriteToken(token); return tokenString; } public TokenValidationResponse 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 TokenValidationResponse { IsValid = true, UserInfo = userInfo }; } catch (Exception e) { _logger.LogInformation(e, "Token validation failed! Token is {} .", token); return new TokenValidationResponse { IsValid = false }; } } } }