177 lines
6.1 KiB
C#
177 lines
6.1 KiB
C#
using System.IdentityModel.Tokens.Jwt;
|
|
using System.Security.Claims;
|
|
using System.Security.Cryptography;
|
|
|
|
namespace qtc_api.Services.TokenService
|
|
{
|
|
public class TokenService : ITokenService
|
|
{
|
|
private readonly IConfiguration _configuration;
|
|
private readonly DataContext _dataContext;
|
|
|
|
public TokenService(IConfiguration configuration, DataContext dataContext)
|
|
{
|
|
_configuration = configuration;
|
|
_dataContext = dataContext;
|
|
}
|
|
|
|
public async Task<ServiceResponse<string>> GenerateAccessTokenAndRefreshToken(User user, bool generateRefToken, bool remember)
|
|
{
|
|
var serviceResponse = new ServiceResponse<string>();
|
|
|
|
// Generate JWT Access Token
|
|
|
|
List<Claim> claims = new List<Claim>()
|
|
{
|
|
new Claim(ClaimTypes.Hash, user.Id),
|
|
new Claim(ClaimTypes.Name, user.Username),
|
|
new Claim(ClaimTypes.Email, user.Email),
|
|
new Claim(ClaimTypes.Role, user.Role)
|
|
};
|
|
|
|
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration.GetSection("Jwt:Key").Value!));
|
|
var issuer = _configuration["Jwt:Issuer"];
|
|
var audience = _configuration["Jwt:Audience"];
|
|
|
|
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
|
|
|
var token = new JwtSecurityToken(
|
|
issuer: issuer,
|
|
audience: audience,
|
|
claims: claims,
|
|
expires: DateTime.UtcNow.AddHours(1),
|
|
signingCredentials: creds
|
|
);
|
|
|
|
var jwt = new JwtSecurityTokenHandler().WriteToken(token);
|
|
|
|
serviceResponse.Data = jwt;
|
|
|
|
// Generate and Store Refresh Token
|
|
|
|
if (generateRefToken)
|
|
{
|
|
var random = new byte[32];
|
|
using (var rng = RandomNumberGenerator.Create())
|
|
{
|
|
rng.GetBytes(random);
|
|
}
|
|
|
|
RefreshToken refToken = new RefreshToken()
|
|
{
|
|
ID = LongRandom(1, 900000000000000000, new Random()).ToString(),
|
|
UserID = user.Id,
|
|
Token = Convert.ToBase64String(random)
|
|
};
|
|
|
|
if (remember) refToken.Expires = DateTime.UtcNow.AddDays(7);
|
|
else refToken.Expires = DateTime.UtcNow.AddDays(1);
|
|
|
|
_dataContext.ValidRefreshTokens.Add(refToken);
|
|
|
|
await _dataContext.SaveChangesAsync();
|
|
|
|
serviceResponse.Message = refToken.Token;
|
|
}
|
|
|
|
serviceResponse.Success = true;
|
|
return serviceResponse;
|
|
}
|
|
|
|
public async Task<ServiceResponse<string>> ValidateRefreshToken(string refreshToken)
|
|
{
|
|
var serviceResponse = new ServiceResponse<string>();
|
|
|
|
var dbRefresh = await _dataContext.ValidRefreshTokens.FirstOrDefaultAsync(x => x.Token == refreshToken);
|
|
|
|
if (dbRefresh != null)
|
|
{
|
|
if (dbRefresh.Expires < DateTime.UtcNow)
|
|
{
|
|
serviceResponse.Success = false;
|
|
serviceResponse.Message = "Refresh Token Expired.";
|
|
|
|
// Handle Expired Refresh Token
|
|
|
|
_dataContext.ValidRefreshTokens.Remove(dbRefresh);
|
|
await _dataContext.SaveChangesAsync();
|
|
|
|
return serviceResponse;
|
|
}
|
|
|
|
var user = await _dataContext.Users.FirstOrDefaultAsync(x => x.Id == dbRefresh.UserID);
|
|
|
|
if (user != null && dbRefresh.UserID == user.Id)
|
|
{
|
|
var token = await GenerateAccessTokenAndRefreshToken(user, false, false);
|
|
|
|
if (token != null)
|
|
{
|
|
serviceResponse.Success = true;
|
|
serviceResponse.Data = token.Data;
|
|
}
|
|
} else
|
|
{
|
|
serviceResponse.Success = false;
|
|
serviceResponse.Message = "Requesting User ID and the associated Refresh Token's User ID does not match.";
|
|
}
|
|
} else
|
|
{
|
|
serviceResponse.Success = false;
|
|
serviceResponse.Message = "Invalid Refresh Token.";
|
|
}
|
|
|
|
return serviceResponse;
|
|
}
|
|
|
|
public async Task<ServiceResponse<bool>> ValidateAccessToken(string accessToken)
|
|
{
|
|
var serviceResponse = new ServiceResponse<bool>();
|
|
|
|
var tokenHandler = new JwtSecurityTokenHandler();
|
|
var validationParams = GetValidationParams();
|
|
|
|
TokenValidationResult result = await tokenHandler.ValidateTokenAsync(accessToken, validationParams.Data);
|
|
|
|
if (result.IsValid)
|
|
{
|
|
serviceResponse.Success = true;
|
|
serviceResponse.Data = true;
|
|
return serviceResponse;
|
|
}
|
|
else
|
|
{
|
|
serviceResponse.Success = true;
|
|
serviceResponse.Data = false;
|
|
return serviceResponse;
|
|
}
|
|
}
|
|
|
|
public ServiceResponse<TokenValidationParameters> GetValidationParams()
|
|
{
|
|
var serviceResponse = new ServiceResponse<TokenValidationParameters>();
|
|
|
|
serviceResponse.Data = new TokenValidationParameters
|
|
{
|
|
ValidateIssuer = true,
|
|
ValidateAudience = true,
|
|
ValidateLifetime = true,
|
|
ValidateIssuerSigningKey = true,
|
|
ValidIssuer = _configuration["Jwt:Issuer"],
|
|
ValidAudience = _configuration["Jwt:Audience"],
|
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]!))
|
|
};
|
|
|
|
return serviceResponse;
|
|
}
|
|
|
|
private long LongRandom(long min, long max, Random rnd)
|
|
{
|
|
long result = rnd.Next((int)(min >> 32), (int)(max >> 32));
|
|
result = result << 32;
|
|
result = result | (long)rnd.Next((int)min, (int)max);
|
|
return result;
|
|
}
|
|
}
|
|
}
|