Files
2024-04-27 22:36:16 +02:00

115 lines
4.2 KiB
C#

using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;
using WatchIt.Database;
using WatchIt.Database.Model.Account;
using WatchIt.WebAPI.Services.Utility.Configuration;
using WatchIt.WebAPI.Services.Utility.Tokens.Exceptions;
namespace WatchIt.WebAPI.Services.Utility.Tokens;
public class TokensService(DatabaseContext database, IConfigurationService configurationService) : ITokensService
{
#region FIELDS
private readonly Configuration.Model.Tokens _tokensConfig = configurationService.Data.Authentication.Tokens;
#endregion
#region PUBLIC METHODS
public async Task<string> CreateRefreshTokenAsync(Account account, bool extendable)
{
int expirationMinutes = extendable ? _tokensConfig.RefreshToken.ExtendedLifetime : _tokensConfig.RefreshToken.NormalLifetime;
DateTime expirationDate = DateTime.UtcNow.AddMinutes(expirationMinutes);
Guid id = Guid.NewGuid();
database.AccountRefreshTokens.Add(new AccountRefreshToken
{
Id = id,
AccountId = account.Id,
ExpirationDate = expirationDate,
IsExtendable = extendable,
});
await database.SaveChangesAsync();
return GenerateRefreshJwt(account, id, expirationDate, extendable);
}
public async Task<string> ExtendRefreshTokenAsync(Account account, Guid id)
{
AccountRefreshToken? token = account.AccountRefreshTokens.FirstOrDefault(x => x.Id == id);
switch (token)
{
case null: throw new TokenNotFoundException();
case { IsExtendable: true }: throw new TokenNotExtendableException();
}
DateTime expirationDate = DateTime.UtcNow.AddMinutes(_tokensConfig.RefreshToken.ExtendedLifetime);
token.ExpirationDate = expirationDate;
await database.SaveChangesAsync();
return GenerateRefreshJwt(account, id, expirationDate, true);
}
public async Task<string> CreateAccessTokenAsync(Account account) => await Task.Run(() => CreateAccessToken(account));
public string CreateAccessToken(Account account)
{
DateTime lifetime = DateTime.Now.AddMinutes(_tokensConfig.AccessToken.NormalLifetime);
Guid id = Guid.NewGuid();
SecurityTokenDescriptor tokenDescriptor = CreateBaseSecurityTokenDescriptor(account, id, lifetime);
tokenDescriptor.Audience = "access";
return TokenToString(tokenDescriptor);
}
#endregion
#region PRIVATE METHODS
protected string GenerateRefreshJwt(Account account, Guid id, DateTime expirationDate, bool extendable)
{
SecurityTokenDescriptor tokenDescriptor = CreateBaseSecurityTokenDescriptor(account, id, expirationDate);
tokenDescriptor.Audience = "refresh";
tokenDescriptor.Subject.AddClaim(new Claim("extend", extendable.ToString()));
return TokenToString(tokenDescriptor);
}
protected SecurityTokenDescriptor CreateBaseSecurityTokenDescriptor(Account account, Guid id, DateTime expirationTime) => new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Jti, id.ToString()),
new Claim(JwtRegisteredClaimNames.Sub, account.Id.ToString()),
new Claim(JwtRegisteredClaimNames.Email, account.Email),
new Claim(JwtRegisteredClaimNames.UniqueName, account.Username),
new Claim(JwtRegisteredClaimNames.Exp, expirationTime.Ticks.ToString()),
new Claim("admin", account.IsAdmin.ToString()),
}),
Expires = expirationTime,
Issuer = configurationService.Data.Authentication.Issuer,
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configurationService.Data.Authentication.Key)), SecurityAlgorithms.HmacSha512)
};
protected string TokenToString(SecurityTokenDescriptor tokenDescriptor)
{
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
handler.InboundClaimTypeMap.Clear();
SecurityToken token = handler.CreateToken(tokenDescriptor);
return handler.WriteToken(token);
}
#endregion
}