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.IssueTokenPair; using AMREZ.EOP.Domain.Entities.Authentications; using Microsoft.AspNetCore.Http; namespace AMREZ.EOP.Application.UseCases.Authentications; public sealed class IssueTokenPairUseCase : IIssueTokenPairUseCase { private readonly IUserRepository _users; private readonly IJwtFactory _jwt; private readonly IHttpContextAccessor _http; private const int RefreshDays = 14; public IssueTokenPairUseCase( IUserRepository users, IJwtFactory jwt, IHttpContextAccessor http) { _users = users; _jwt = jwt; _http = http; } public async Task ExecuteAsync(IssueTokenPairRequest request, CancellationToken ct = default) { var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext"); var tenantId = request.TenantId; var refreshRaw = Convert.ToBase64String(RandomNumberGenerator.GetBytes(64)); var refreshHash = Sha256(refreshRaw); var now = DateTimeOffset.UtcNow; 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); var tv = await _users.GetTenantTokenVersionAsync(tenantId, ct); var sstamp = await _users.GetUserSecurityStampAsync(request.UserId, ct) ?? string.Empty; var claims = new List { 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) { var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(raw)); return Convert.ToHexString(bytes); } }