auth token endpoint finished
This commit is contained in:
@@ -10,13 +10,13 @@ public class AuthPasswordHandler : IRequestHandler<AuthPasswordCommand, AuthPass
|
|||||||
{
|
{
|
||||||
private readonly DatabaseContext _databaseContext;
|
private readonly DatabaseContext _databaseContext;
|
||||||
private readonly IPasswordHasher _passwordHasher;
|
private readonly IPasswordHasher _passwordHasher;
|
||||||
private readonly IAccessTokenGenerator _accessTokenGenerator;
|
private readonly ITokenHelper _tokenHelper;
|
||||||
|
|
||||||
public AuthPasswordHandler(DatabaseContext databaseContext, IPasswordHasher passwordHasher, IAccessTokenGenerator accessTokenGenerator)
|
public AuthPasswordHandler(DatabaseContext databaseContext, IPasswordHasher passwordHasher, ITokenHelper tokenHelper)
|
||||||
{
|
{
|
||||||
_databaseContext = databaseContext;
|
_databaseContext = databaseContext;
|
||||||
_passwordHasher = passwordHasher;
|
_passwordHasher = passwordHasher;
|
||||||
_accessTokenGenerator = accessTokenGenerator;
|
_tokenHelper = tokenHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AuthPasswordResult> Handle(AuthPasswordCommand request, CancellationToken cancellationToken)
|
public async Task<AuthPasswordResult> Handle(AuthPasswordCommand request, CancellationToken cancellationToken)
|
||||||
@@ -33,10 +33,17 @@ public class AuthPasswordHandler : IRequestHandler<AuthPasswordCommand, AuthPass
|
|||||||
return AuthPasswordResult.Failure();
|
return AuthPasswordResult.Failure();
|
||||||
}
|
}
|
||||||
|
|
||||||
string accessToken = _accessTokenGenerator.GenerateAccessToken(account);
|
string accessToken = _tokenHelper.GenerateAccessToken(account.Id);
|
||||||
RefreshToken refreshToken = _accessTokenGenerator.GenerateRefreshToken(request.RememberMe);
|
|
||||||
|
|
||||||
account.RefreshTokens.Add(refreshToken);
|
RefreshToken refreshToken = new RefreshToken
|
||||||
|
{
|
||||||
|
Token = Guid.NewGuid(),
|
||||||
|
IsExtendable = request.RememberMe,
|
||||||
|
AccountId = account.Id,
|
||||||
|
ExpirationDate = _tokenHelper.CalculateRefreshTokenExpirationDate(request.RememberMe),
|
||||||
|
};
|
||||||
|
|
||||||
|
await _databaseContext.RefreshTokens.AddAsync(refreshToken, cancellationToken);
|
||||||
await _databaseContext.SaveChangesAsync(cancellationToken);
|
await _databaseContext.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
return AuthPasswordResult.Success(accessToken, refreshToken.Token.ToString());
|
return AuthPasswordResult.Success(accessToken, refreshToken.Token.ToString());
|
||||||
|
|||||||
@@ -9,31 +9,32 @@ namespace TimetableDesigner.Backend.Services.Authentication.Core.Commands.AuthTo
|
|||||||
public class AuthTokenHandler : IRequestHandler<AuthTokenCommand, AuthTokenResult>
|
public class AuthTokenHandler : IRequestHandler<AuthTokenCommand, AuthTokenResult>
|
||||||
{
|
{
|
||||||
private readonly DatabaseContext _databaseContext;
|
private readonly DatabaseContext _databaseContext;
|
||||||
private readonly IAccessTokenGenerator _accessTokenGenerator;
|
private readonly ITokenHelper _tokenHelper;
|
||||||
|
|
||||||
public AuthTokenHandler(DatabaseContext databaseContext, IAccessTokenGenerator accessTokenGenerator)
|
public AuthTokenHandler(DatabaseContext databaseContext, ITokenHelper tokenHelper)
|
||||||
{
|
{
|
||||||
_databaseContext = databaseContext;
|
_databaseContext = databaseContext;
|
||||||
_accessTokenGenerator = accessTokenGenerator;
|
_tokenHelper = tokenHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AuthTokenResult> Handle(AuthTokenCommand request, CancellationToken cancellationToken)
|
public async Task<AuthTokenResult> Handle(AuthTokenCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
RefreshToken? token = await _databaseContext.RefreshTokens
|
RefreshToken? refreshToken = await _databaseContext.RefreshTokens
|
||||||
.Include(x => x.Account)
|
.Include(x => x.Account)
|
||||||
.FirstOrDefaultAsync(x => x.Token == Guid.Parse(request.RefreshToken), cancellationToken);
|
.FirstOrDefaultAsync(x => x.Token == Guid.Parse(request.RefreshToken), cancellationToken);
|
||||||
if (token is null || token.ExpirationDate < DateTimeOffset.UtcNow || !_accessTokenGenerator.ValidateExpiredAccessToken(request.AccessToken))
|
if (refreshToken is null || refreshToken.ExpirationDate < DateTimeOffset.UtcNow || !_tokenHelper.ValidateExpiredAccessToken(request.AccessToken))
|
||||||
{
|
{
|
||||||
return AuthTokenResult.Failure();
|
return AuthTokenResult.Failure();
|
||||||
}
|
}
|
||||||
|
|
||||||
string accessToken = _accessTokenGenerator.GenerateAccessToken(token.Account);
|
string accessToken = _tokenHelper.GenerateAccessToken(refreshToken.Account.Id);
|
||||||
if (token.IsExtendable)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
if (refreshToken.IsExtendable)
|
||||||
|
{
|
||||||
|
refreshToken.ExpirationDate = _tokenHelper.CalculateRefreshTokenExpirationDate();
|
||||||
|
await _databaseContext.SaveChangesAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return AuthTokenResult.Success(accessToken, refreshToken.Token.ToString());
|
||||||
return AuthTokenResult.Success(refreshToken, accessToken);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace TimetableDesigner.Backend.Services.Authentication.Core.Helpers;
|
namespace TimetableDesigner.Backend.Services.Authentication.Core.Helpers;
|
||||||
|
|
||||||
public interface IAccessTokenGenerator
|
public interface ITokenHelper
|
||||||
{
|
{
|
||||||
string GenerateAccessToken(Account account);
|
string GenerateAccessToken(long accountId);
|
||||||
RefreshToken GenerateRefreshToken(bool isExtendable);
|
|
||||||
bool ValidateExpiredAccessToken(string accessToken);
|
bool ValidateExpiredAccessToken(string accessToken);
|
||||||
|
DateTimeOffset CalculateRefreshTokenExpirationDate(bool isExtendable = true);
|
||||||
}
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace TimetableDesigner.Backend.Services.Authentication.Core.Helpers;
|
|
||||||
|
|
||||||
public record PasswordHashData(
|
|
||||||
byte[] Hash,
|
|
||||||
string Salt
|
|
||||||
);
|
|
||||||
@@ -37,3 +37,8 @@ public class PasswordHasher : IPasswordHasher
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record PasswordHashData(
|
||||||
|
byte[] Hash,
|
||||||
|
string Salt
|
||||||
|
);
|
||||||
@@ -9,18 +9,16 @@ using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegiste
|
|||||||
|
|
||||||
namespace TimetableDesigner.Backend.Services.Authentication.Core.Helpers;
|
namespace TimetableDesigner.Backend.Services.Authentication.Core.Helpers;
|
||||||
|
|
||||||
public class AccessTokenGenerator : IAccessTokenGenerator
|
public class TokenHelper : ITokenHelper
|
||||||
{
|
{
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
private readonly DatabaseContext _databaseContext;
|
|
||||||
|
|
||||||
public AccessTokenGenerator(IConfiguration configuration, DatabaseContext databaseContext)
|
public TokenHelper(IConfiguration configuration)
|
||||||
{
|
{
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_databaseContext = databaseContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GenerateAccessToken(Account account)
|
public string GenerateAccessToken(long accountId)
|
||||||
{
|
{
|
||||||
IConfigurationSection accessTokenSettings = _configuration.GetSection("Tokens")
|
IConfigurationSection accessTokenSettings = _configuration.GetSection("Tokens")
|
||||||
.GetSection("AccessToken");
|
.GetSection("AccessToken");
|
||||||
@@ -40,7 +38,7 @@ public class AccessTokenGenerator : IAccessTokenGenerator
|
|||||||
Subject = new ClaimsIdentity(
|
Subject = new ClaimsIdentity(
|
||||||
[
|
[
|
||||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||||
new Claim(JwtRegisteredClaimNames.Sub, account.Id.ToString()),
|
new Claim(JwtRegisteredClaimNames.Sub, accountId.ToString()),
|
||||||
new Claim(JwtRegisteredClaimNames.Exp, expirationDate.UtcTicks.ToString())
|
new Claim(JwtRegisteredClaimNames.Exp, expirationDate.UtcTicks.ToString())
|
||||||
]),
|
]),
|
||||||
Issuer = accessTokenSettings.GetValue<string>("Issuer"),
|
Issuer = accessTokenSettings.GetValue<string>("Issuer"),
|
||||||
@@ -56,25 +54,6 @@ public class AccessTokenGenerator : IAccessTokenGenerator
|
|||||||
return handler.WriteToken(token);
|
return handler.WriteToken(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RefreshToken GenerateRefreshToken(bool isExtendable)
|
|
||||||
{
|
|
||||||
string lifetimeSection = isExtendable ? "Extended" : "Normal";
|
|
||||||
int lifetime = _configuration.GetSection("Tokens")
|
|
||||||
.GetSection("RefreshToken")
|
|
||||||
.GetSection("Lifetime")
|
|
||||||
.GetValue<int>(lifetimeSection);
|
|
||||||
|
|
||||||
Guid guid = Guid.NewGuid();
|
|
||||||
DateTimeOffset expirationDate = DateTimeOffset.UtcNow.AddMinutes(lifetime);
|
|
||||||
|
|
||||||
return new RefreshToken
|
|
||||||
{
|
|
||||||
Token = guid,
|
|
||||||
ExpirationDate = expirationDate,
|
|
||||||
IsExtendable = isExtendable,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ValidateExpiredAccessToken(string accessToken)
|
public bool ValidateExpiredAccessToken(string accessToken)
|
||||||
{
|
{
|
||||||
IConfigurationSection accessTokenSettings = _configuration.GetSection("Tokens")
|
IConfigurationSection accessTokenSettings = _configuration.GetSection("Tokens")
|
||||||
@@ -104,4 +83,14 @@ public class AccessTokenGenerator : IAccessTokenGenerator
|
|||||||
|
|
||||||
return jwtSecurityToken is not null && jwtSecurityToken.Header.Alg.Equals(algorithm, StringComparison.InvariantCultureIgnoreCase);
|
return jwtSecurityToken is not null && jwtSecurityToken.Header.Alg.Equals(algorithm, StringComparison.InvariantCultureIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DateTimeOffset CalculateRefreshTokenExpirationDate(bool isExtendable = true)
|
||||||
|
{
|
||||||
|
string lifetimeSection = isExtendable ? "Extended" : "Normal";
|
||||||
|
int lifetime = _configuration.GetSection("Tokens")
|
||||||
|
.GetSection("RefreshToken")
|
||||||
|
.GetSection("Lifetime")
|
||||||
|
.GetValue<int>(lifetimeSection);
|
||||||
|
return DateTimeOffset.UtcNow.AddMinutes(lifetime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ public static class Program
|
|||||||
private static IServiceCollection AddHelpers(this IServiceCollection services)
|
private static IServiceCollection AddHelpers(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddSingleton<IPasswordHasher, PasswordHasher>();
|
services.AddSingleton<IPasswordHasher, PasswordHasher>();
|
||||||
services.AddScoped<IAccessTokenGenerator, AccessTokenGenerator>();
|
services.AddScoped<ITokenHelper, TokenHelper>();
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user