mirror of
https://github.com/Shchoholiev/shopping-assistant-api.git
synced 2025-04-03 16:19:46 +00:00
127 lines
4.7 KiB
C#
127 lines
4.7 KiB
C#
using System.IdentityModel.Tokens.Jwt;
|
|
using System.Security.Claims;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using MongoDB.Bson;
|
|
using ShoppingAssistantApi.Application.IRepositories;
|
|
using ShoppingAssistantApi.Application.IServices.Identity;
|
|
using ShoppingAssistantApi.Application.Models.Identity;
|
|
|
|
namespace ShoppingAssistantApi.Infrastructure.Services.Identity;
|
|
|
|
public class TokensService : ITokensService
|
|
{
|
|
private readonly IConfiguration _configuration;
|
|
|
|
private readonly IUsersRepository _usersRepository;
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
public TokensService(IConfiguration configuration, IUsersRepository usersRepository,
|
|
ILogger<TokensService> logger)
|
|
{
|
|
this._configuration = configuration;
|
|
this._usersRepository = usersRepository;
|
|
this._logger = logger;
|
|
}
|
|
|
|
public async Task<TokensModel> RefreshUserAsync(TokensModel tokensModel, CancellationToken cancellationToken)
|
|
{
|
|
var principal = this.GetPrincipalFromExpiredToken(tokensModel.AccessToken);
|
|
|
|
var userId = principal.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
|
|
if (!ObjectId.TryParse(userId, out var objectId))
|
|
{
|
|
throw new InvalidDataException("Provided id is invalid.");
|
|
}
|
|
|
|
var user = await this._usersRepository.GetUserAsync(objectId, cancellationToken);
|
|
if (user == null || user?.RefreshToken != tokensModel.RefreshToken
|
|
|| user?.RefreshTokenExpiryDate <= DateTime.UtcNow)
|
|
{
|
|
throw new SecurityTokenExpiredException();
|
|
}
|
|
|
|
var newAccessToken = this.GenerateAccessToken(principal.Claims);
|
|
var newRefreshToken = this.GenerateRefreshToken();
|
|
user.RefreshToken = newRefreshToken;
|
|
user.RefreshTokenExpiryDate = DateTime.UtcNow.AddDays(30);
|
|
await this._usersRepository.UpdateUserAsync(user, cancellationToken);
|
|
|
|
this._logger.LogInformation($"Refreshed user tokens.");
|
|
|
|
return new TokensModel
|
|
{
|
|
AccessToken = newAccessToken,
|
|
RefreshToken = newRefreshToken
|
|
};
|
|
}
|
|
|
|
public string GenerateAccessToken(IEnumerable<Claim> claims)
|
|
{
|
|
var tokenOptions = GetTokenOptions(claims);
|
|
var tokenString = new JwtSecurityTokenHandler().WriteToken(tokenOptions);
|
|
|
|
this._logger.LogInformation($"Generated new access token.");
|
|
|
|
return tokenString;
|
|
}
|
|
|
|
public string GenerateRefreshToken()
|
|
{
|
|
var randomNumber = new byte[32];
|
|
using (var rng = RandomNumberGenerator.Create())
|
|
{
|
|
rng.GetBytes(randomNumber);
|
|
var refreshToken = Convert.ToBase64String(randomNumber);
|
|
|
|
this._logger.LogInformation($"Generated new refresh token.");
|
|
|
|
return refreshToken;
|
|
}
|
|
}
|
|
|
|
private ClaimsPrincipal GetPrincipalFromExpiredToken(string token)
|
|
{
|
|
var tokenValidationParameters = new TokenValidationParameters
|
|
{
|
|
ValidateAudience = false,
|
|
ValidateIssuer = false,
|
|
ValidateIssuerSigningKey = true,
|
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
|
|
_configuration.GetValue<string>("JsonWebTokenKeys:IssuerSigningKey"))),
|
|
ValidateLifetime = false
|
|
};
|
|
var tokenHandler = new JwtSecurityTokenHandler();
|
|
SecurityToken securityToken;
|
|
var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out securityToken);
|
|
var jwtSecurityToken = securityToken as JwtSecurityToken;
|
|
if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256,
|
|
StringComparison.InvariantCultureIgnoreCase))
|
|
throw new SecurityTokenException("Invalid token");
|
|
|
|
this._logger.LogInformation($"Returned data from expired access token.");
|
|
|
|
return principal;
|
|
}
|
|
|
|
private JwtSecurityToken GetTokenOptions(IEnumerable<Claim> claims)
|
|
{
|
|
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
|
|
_configuration.GetValue<string>("JsonWebTokenKeys:IssuerSigningKey")));
|
|
var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
|
|
|
|
var tokenOptions = new JwtSecurityToken(
|
|
issuer: _configuration.GetValue<string>("JsonWebTokenKeys:ValidIssuer"),
|
|
audience: _configuration.GetValue<string>("JsonWebTokenKeys:ValidAudience"),
|
|
expires: DateTime.UtcNow.AddMinutes(5),
|
|
claims: claims,
|
|
signingCredentials: signinCredentials
|
|
);
|
|
|
|
return tokenOptions;
|
|
}
|
|
} |