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 SuccessResponse = GB5Shared.Resource.Response.SuccessResponse; using static GB5Shared.GB5Constant.Constant; namespace EntitlementSL.Endpoints.ClientAuth; // AllowAnonymous — this IS the credential-recovery bootstrapping endpoint; there is no caller // identity to check yet. Design decision: ClientId is required alongside Email, same // disambiguation reasoning as Login.cs (MENTITLEMENTCLIENTUSER.EMAIL is unique only per // (CLIENTID, EMAIL), not globally). // // ALWAYS returns the exact same generic acknowledgement regardless of whether an account was // actually found — ClientAuthBLL.ForgotPasswordAsync deliberately returns nothing to branch on, // so there is no code path here that could accidentally leak account existence via a different // response shape, status code, or (to the extent this framework controls it) timing. // // Rate-limited under the same policy as Login/RefreshToken (see ClientAuthRateLimitOptions) — a // forgot-password endpoint is just as attractive a target for enumeration/spam abuse (mass- // triggering reset emails) as the credential-guessing surfaces item 7 protected. public class ForgotPassword : BaseEndpoint> { private readonly IClientAuthBLL _ClientAuthBLL; public ForgotPassword(IClientAuthBLL clientAuthBLL) => _ClientAuthBLL = clientAuthBLL; public override void Configure() { Post("/lic/ClientAuth.svc/ForgotPassword"); AllowAnonymous(); Options(x => x.RequireRateLimiting(ClientAuthRateLimitPolicies.LoginAndRefresh)); } public record Body(int ClientId, string Email); 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) { await _ClientAuthBLL.ForgotPasswordAsync(req.RequestBody.ClientId, req.RequestBody.Email, loginDTO, ct); return await GB5Shared.ResponseStandard.Response.CreateSuccessResponse( SuccessResponse.ForgotPasswordAcknowledgementMessage, CacheKeyLevel.NOT_REQUIRED, loginDTO); } }