using EntitlementBLL.Auth; using EntitlementSL.Common; using FastEndpoints; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Framework.ResponseStandard; using GB5Shared.FastEndPoint; using ErrorResponse = GB5Shared.Resource.Response.ErrorResponse; using static GB5Shared.GB5Constant.Constant; namespace EntitlementSL.Endpoints.ClientAuth; // The acceptance side of the individual-user first-login gate (tracker §50.3/§51.7) — the // authenticated caller accepts whatever AgreementVersionIds Login's own PendingAgreementVersionIds // told the FE about. ClientUserId/Role come ONLY from verified JWT claims (ClientCallerContext), // never from the request body — mirrors GetMySubscription.cs's own posture, since accepting an // agreement on someone else's behalf would be a real impersonation risk, not just bad practice. public class AcceptAgreements : BaseEndpoint> { private const string ClientJwtSchemeName = "ClientJwtBearer"; private readonly IClientAuthBLL _ClientAuthBLL; public AcceptAgreements(IClientAuthBLL clientAuthBLL) => _ClientAuthBLL = clientAuthBLL; public override void Configure() { Post("/lic/ClientAuth.svc/AcceptAgreements"); AuthSchemes(ClientJwtSchemeName); Roles(ClientRoleCodes.ClientAdmin, ClientRoleCodes.ClientUser); } public record Body(int[] AgreementVersionIds); public record Params( [property: FromHeader] string Login, [property: FromBody] Body RequestBody ); /// NOT_REQUIRED — a one-time acceptance action, never cached. protected override string? GetCacheKey(Params req, LoginDTO loginDTO) => null; protected override async Task> ExecuteAsync(Params req, LoginDTO loginDTO, CancellationToken ct) { var caller = ClientCallerContext.FromClaims(HttpContext.User.Claims); if (caller.ClientUserId <= 0) return await GB5Shared.ResponseStandard.Response.CreateErrorResponse( ErrorResponse.AccessDeniedMessage, CacheKeyLevel.NOT_REQUIRED, loginDTO, statusCode: 403); var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString(); var userAgent = HttpContext.Request.Headers.UserAgent.ToString(); await _ClientAuthBLL.AcceptPendingAgreementsAsync( caller.ClientUserId, caller.Role, req.RequestBody.AgreementVersionIds, ipAddress, userAgent, loginDTO, ct); return await GB5Shared.ResponseStandard.Response.CreateSuccessResponse("Accepted.", CacheKeyLevel.NOT_REQUIRED, loginDTO); } }