Fix Access/Refres Token

This commit is contained in:
Thanakarn Klangkasame
2025-10-05 17:24:30 +07:00
parent d266463c9f
commit ad0d9e41ba
12 changed files with 191 additions and 143 deletions

View File

@@ -15,92 +15,67 @@ namespace AMREZ.EOP.Application.UseCases.Authentications;
public sealed class IssueTokenPairUseCase : IIssueTokenPairUseCase
{
private readonly ITenantResolver _tenantResolver; // ctx/key for UoW
private readonly ITenantRepository _tenants; // get TenantId (Guid)
private readonly IUserRepository _users;
private readonly IJwtFactory _jwt;
private readonly IHttpContextAccessor _http;
private readonly IUnitOfWork _uow;
private const int RefreshDays = 14;
public IssueTokenPairUseCase(
ITenantResolver resolver,
ITenantRepository tenants,
IUserRepository users,
IJwtFactory jwt,
IHttpContextAccessor http,
IUnitOfWork uow)
IHttpContextAccessor http)
{
_tenantResolver = resolver;
_tenants = tenants;
_users = users;
_jwt = jwt;
_http = http;
_uow = uow;
}
public async Task<IssueTokenPairResponse> ExecuteAsync(IssueTokenPairRequest request, CancellationToken ct = default)
{
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
var tenantCtx = _tenantResolver.Resolve(http, request);
if (tenantCtx is null) throw new InvalidOperationException("Cannot resolve tenant context");
var tenantId = request.TenantId;
await _uow.BeginAsync(tenantCtx, IsolationLevel.ReadCommitted, ct);
var refreshRaw = Convert.ToBase64String(RandomNumberGenerator.GetBytes(64));
var refreshHash = Sha256(refreshRaw);
var now = DateTimeOffset.UtcNow;
try
var session = await _users.CreateSessionAsync(new UserSession
{
var tn = await _tenants.GetAsync(tenantCtx.Id, ct);
var tenantId = tn.TenantId;
Id = Guid.NewGuid(),
TenantId = tenantId,
UserId = request.UserId,
RefreshTokenHash = refreshHash,
IssuedAt = now,
ExpiresAt = now.AddDays(RefreshDays),
DeviceId = http.Request.Headers["X-Device-Id"].FirstOrDefault(),
UserAgent = http.Request.Headers["User-Agent"].FirstOrDefault(),
IpAddress = http.Connection.RemoteIpAddress?.ToString()
}, ct);
var refreshRaw = Convert.ToBase64String(RandomNumberGenerator.GetBytes(64));
var refreshHash = Sha256(refreshRaw);
var now = DateTimeOffset.UtcNow;
var tv = await _users.GetTenantTokenVersionAsync(tenantId, ct);
var sstamp = await _users.GetUserSecurityStampAsync(request.UserId, ct) ?? string.Empty;
var session = await _users.CreateSessionAsync(new UserSession
{
Id = Guid.NewGuid(),
TenantId = tenantId,
UserId = request.UserId,
RefreshTokenHash = refreshHash,
IssuedAt = now,
ExpiresAt = now.AddDays(RefreshDays),
DeviceId = http.Request.Headers["X-Device-Id"].FirstOrDefault(),
UserAgent = http.Request.Headers["User-Agent"].FirstOrDefault(),
IpAddress = http.Connection.RemoteIpAddress?.ToString()
}, ct);
// 6) tv / sstamp
var tv = await _users.GetTenantTokenVersionAsync(tenantId, ct);
var sstamp = await _users.GetUserSecurityStampAsync(request.UserId, ct) ?? string.Empty;
var claims = new List<Claim>
{
new("sub", request.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);
await _uow.CommitAsync(ct);
return new IssueTokenPairResponse
{
AccessToken = access,
AccessExpiresAt = accessExp,
RefreshToken = refreshRaw, // raw ออกให้ client
RefreshExpiresAt = session.ExpiresAt
};
}
catch
var claims = new List<Claim>
{
await _uow.RollbackAsync(ct);
throw;
}
new("sub", request.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);
return new IssueTokenPairResponse
{
AccessToken = access,
AccessExpiresAt = accessExp,
RefreshToken = refreshRaw, // ส่ง raw กลับให้ client
RefreshExpiresAt = session.ExpiresAt
};
}
private static string Sha256(string raw)