115 lines
4.2 KiB
C#
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
|
|||
|
|
}
|