using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography; using System.Text; using AMREZ.EOP.Abstractions.Applications.Tenancy; using AMREZ.EOP.Abstractions.Security; using AMREZ.EOP.Infrastructures.Options; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; namespace AMREZ.EOP.Infrastructures.Security; public sealed class JwtFactory : IJwtFactory { private readonly ITenantResolver _resolver; private readonly IHttpContextAccessor _http; private const string Issuer = "amrez.eop"; private const string Audience = "amrez.eop.clients"; private const int AccessMinutes = 10; public JwtFactory(ITenantResolver resolver, IHttpContextAccessor http) { _resolver = resolver; _http = http; } public (string token, DateTimeOffset expiresAt) CreateAccessToken(IEnumerable claims) { var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext"); var tenant = _resolver.Resolve(http) ?? throw new InvalidOperationException("No tenant context"); var material = !string.IsNullOrWhiteSpace(tenant.Id) ? tenant.Id! : tenant.TenantKey!; var keyBytes = SHA256.HashData(Encoding.UTF8.GetBytes(material)); var cred = new SigningCredentials(new SymmetricSecurityKey(keyBytes), SecurityAlgorithms.HmacSha256); var now = DateTimeOffset.UtcNow; var exp = now.AddMinutes(AccessMinutes); var jwt = new JwtSecurityToken( issuer: Issuer, audience: Audience, claims: claims, notBefore: now.UtcDateTime, expires: exp.UtcDateTime, signingCredentials: cred); return (new JwtSecurityTokenHandler().WriteToken(jwt), exp); } }