Add Login Module
This commit is contained in:
102
AMREZ.EOP.Application/UseCases/Authentications/RefreshUseCase.cs
Normal file
102
AMREZ.EOP.Application/UseCases/Authentications/RefreshUseCase.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System.Data;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Applications.UseCases.Authentications;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Common;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Security;
|
||||
using AMREZ.EOP.Contracts.DTOs.Authentications.Refresh;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace AMREZ.EOP.Application.UseCases.Authentications;
|
||||
|
||||
public sealed class RefreshUseCase : IRefreshUseCase
|
||||
{
|
||||
private readonly ITenantResolver _resolver;
|
||||
private readonly ITenantRepository _tenants;
|
||||
private readonly IUserRepository _users;
|
||||
private readonly IJwtFactory _jwt;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
private readonly IUnitOfWork _uow;
|
||||
|
||||
private const int RefreshDays = 14;
|
||||
|
||||
public RefreshUseCase(
|
||||
ITenantResolver resolver,
|
||||
ITenantRepository tenants,
|
||||
IUserRepository users,
|
||||
IJwtFactory jwt,
|
||||
IHttpContextAccessor http,
|
||||
IUnitOfWork uow)
|
||||
{ _resolver = resolver; _tenants = tenants; _users = users; _jwt = jwt; _http = http; _uow = uow; }
|
||||
|
||||
public async Task<RefreshResponse?> ExecuteAsync(RefreshRequest request, CancellationToken ct = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.RefreshToken)) return null;
|
||||
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tenantCtx = _resolver.Resolve(http, request);
|
||||
if (tenantCtx is null) return null;
|
||||
|
||||
await _uow.BeginAsync(tenantCtx, IsolationLevel.ReadCommitted, ct);
|
||||
try
|
||||
{
|
||||
var tn = await _tenants.GetAsync(tenantCtx.TenantKey, ct);
|
||||
var tenantId = tn.TenantId;
|
||||
|
||||
var hash = Sha256(request.RefreshToken);
|
||||
var session = await _users.FindSessionByRefreshHashAsync(tenantId, hash, ct);
|
||||
if (session is null || session.RevokedAt.HasValue ||
|
||||
(session.ExpiresAt.HasValue && session.ExpiresAt.Value <= DateTimeOffset.UtcNow))
|
||||
{ await _uow.RollbackAsync(ct); return null; }
|
||||
|
||||
if (!await _users.IsSessionActiveAsync(session.UserId, session.Id, ct))
|
||||
{ await _uow.RollbackAsync(ct); return null; }
|
||||
|
||||
var tv = await _users.GetTenantTokenVersionAsync(tenantId, ct);
|
||||
var sstamp = await _users.GetUserSecurityStampAsync(session.UserId, ct) ?? string.Empty;
|
||||
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new("sub", session.UserId.ToString()),
|
||||
new("tenant_id", tenantId.ToString()),
|
||||
new("sid", session.Id.ToString()),
|
||||
new("jti", Guid.NewGuid().ToString("N")),
|
||||
new("tv", tv),
|
||||
new("sstamp", sstamp)
|
||||
};
|
||||
var (access, accessExp) = _jwt.CreateAccessToken(claims);
|
||||
|
||||
var newRaw = Convert.ToBase64String(RandomNumberGenerator.GetBytes(64));
|
||||
var newHash = Sha256(newRaw);
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var newExp = now.AddDays(RefreshDays);
|
||||
|
||||
var ok = await _users.RotateSessionRefreshAsync(tenantId, session.Id, newHash, now, newExp, ct);
|
||||
if (!ok) { await _uow.RollbackAsync(ct); return null; }
|
||||
|
||||
await _uow.CommitAsync(ct);
|
||||
|
||||
return new RefreshResponse
|
||||
{
|
||||
AccessToken = access,
|
||||
AccessExpiresAt = accessExp,
|
||||
RefreshToken = newRaw,
|
||||
RefreshExpiresAt = newExp
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
await _uow.RollbackAsync(ct);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static string Sha256(string raw)
|
||||
{
|
||||
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(raw));
|
||||
return Convert.ToHexString(bytes);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user