using DXPBLL.Options; using GB5Shared.Auth.Jwt; using Microsoft.Extensions.Options; namespace DXPBLL.Auth; // Claim names are the ones consumed by DXPSL/Common/DXPBaseEndpoint.cs when reconstructing a // LoginDTO server-side from the validated token — never accept these values from the request // body/headers, only from HttpContext.User.Claims after JWT middleware has verified the // signature (see Part 2 of the DXP plan). public static class DXPClaimTypes { public const string DxpUserId = "dxp_user_id"; public const string DxpPartyId = "dxp_party_id"; public const string TenantId = "tenant_id"; public const string DatabaseName = "database_name"; public const string DatabaseType = "database_type"; public const string LocalPartyId = "local_party_id"; public const string RoleCode = "role"; } // Thin per-module wrapper over the shared GB5Shared.Auth.Jwt issuance mechanics — mirrors // EntitlementBLL.Auth.ClientJwtService's identical shape. The actual HMAC-SHA256 signing and // Vault-backed key resolution now live in GB5Shared (IJwtAccessTokenIssuer/IJwtSigningKeyResolver), // shared with Entitlement's ClientJwtService instead of each hand-rolling its own copy. public class DXPJwtService : IDXPJwtService { // Vault path unchanged from before this pass — only how it's resolved changed (via the // shared IJwtSigningKeyResolver -> GB5Shared.Vault.IVaultService, not a module-local resolver). private const string SigningKeyVaultPath = "dxp/jwt-signing-key"; private readonly IJwtAccessTokenIssuer _Issuer; private readonly DXPJwtOptions _Options; public DXPJwtService(IJwtAccessTokenIssuer issuer, IOptions options) { _Issuer = issuer; _Options = options.Value; } public async Task IssueAccessTokenAsync(DXPAccessTokenClaims claims, CancellationToken ct) { var issued = await _Issuer.IssueAsync( claims, SigningKeyVaultPath, _Options.Issuer, _Options.Audience, _Options.AccessTokenMinutes, ct) .ConfigureAwait(false); return issued.AccessToken; } public async Task IssueTokenPairAsync(DXPAccessTokenClaims claims, CancellationToken ct) { // Both expiries computed from one place (DXPJwtOptions) via the shared issuer's single // internal computation — no second, independently-hardcoded lifetime anywhere anymore. var issued = await _Issuer.IssueAsync( claims, SigningKeyVaultPath, _Options.Issuer, _Options.Audience, _Options.AccessTokenMinutes, ct) .ConfigureAwait(false); var (rawRefresh, _) = RefreshTokenHelper.GenerateRefreshToken(); return new DXPTokenPair { AccessToken = issued.AccessToken, AccessTokenExpiresOn = issued.ExpiresOn, RefreshToken = rawRefresh, RefreshTokenExpiresOn = DateTime.UtcNow.AddDays(_Options.RefreshTokenDays) }; } public (string RawToken, string TokenHash) GenerateRefreshToken() => RefreshTokenHelper.GenerateRefreshToken(); public string HashRefreshToken(string rawToken) => RefreshTokenHelper.HashRefreshToken(rawToken); }