using EntitlementBLL.Auth; using EntitlementSL.Common; using FastEndpoints; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Framework.ResponseStandard; using GB5Shared.FastEndPoint; using Microsoft.AspNetCore.RateLimiting; using ErrorResponse = GB5Shared.Resource.Response.ErrorResponse; using static GB5Shared.GB5Constant.Constant; namespace EntitlementSL.Endpoints.ClientAuth; // AllowAnonymous — the signed token itself is the credential; there is no separate caller // identity to check first (same trust model as RefreshToken.cs). // // ClientAuthBLL.ResetPasswordAsync validates the token via IDirectActionTokenService/ // IDirectActionTokenDAL directly — NOT the generic /Action/Execute path (that path's NEEDSINPUT=1 // flow would persist the new password in TDIRECTACTIONTOKEN.REMARKS in plaintext, which is // unacceptable for a password value). Every rejection reason (bad signature, expired, already // used, ClientUserId not found/mismatched/deleted) collapses to the same generic response — // mirrors RefreshToken.cs's Invalid-vs-ReuseDetected non-disclosure posture. public class ResetPassword : BaseEndpoint> { private readonly IClientAuthBLL _ClientAuthBLL; public ResetPassword(IClientAuthBLL clientAuthBLL) => _ClientAuthBLL = clientAuthBLL; public override void Configure() { Post("/lic/ClientAuth.svc/ResetPassword"); AllowAnonymous(); Options(x => x.RequireRateLimiting(ClientAuthRateLimitPolicies.LoginAndRefresh)); } public record Body(string Token, string NewPassword); public record Params( [property: FromHeader] string Login, [property: FromBody] Body RequestBody ); protected override string? GetCacheKey(Params req, LoginDTO loginDTO) => null; protected override async Task> ExecuteAsync(Params req, LoginDTO loginDTO, CancellationToken ct) { var result = await _ClientAuthBLL.ResetPasswordAsync(req.RequestBody.Token, req.RequestBody.NewPassword, loginDTO, ct); if (result.Status != ClientPasswordResetResultStatus.Success) return await GB5Shared.ResponseStandard.Response.CreateErrorResponse( ErrorResponse.InvalidOrExpiredSessionMessage, CacheKeyLevel.NOT_REQUIRED, loginDTO, statusCode: 400); return await GB5Shared.ResponseStandard.Response.CreateSuccessResponse(result.Message, CacheKeyLevel.NOT_REQUIRED, loginDTO); } }