This commit is contained in:
2026-07-16 14:23:23 +05:30
parent 582782b0fe
commit 7c5faabc2d
26 changed files with 1225 additions and 18 deletions
@@ -0,0 +1,41 @@
using ERPCore.Dtos.Auth;
using ERPCore.Infra.Auth.AuthHex;
using ERPCore.Services.Interfaces;
using ERPCore.System.Errors;
namespace ERPCore.Services.Auth;
/// <inheritdoc cref="IAuthAltService"/>
public sealed class AuthAltService : IAuthAltService
{
private readonly IAuthHexClient _authHex;
public AuthAltService(IAuthHexClient authHex) => _authHex = authHex;
public Task<IsAvailableResponse> IsAvailableAsync(IsAvailableRequest request, CancellationToken ct = default)
=> _authHex.IsAvailableAsync(request, ct);
public Task<SendOtpResponse> SendOtpAsync(SendOtpRequest request, CancellationToken ct = default)
=> _authHex.SendOtpAsync(request, ct);
public async Task<OtpAuthSessionResult> VerifyOtpAsync(VerifyAltOtpRequest request, CancellationToken ct = default)
{
var result = await _authHex.VerifyAltOtpAsync(request, ct);
if (result is null || string.IsNullOrEmpty(result.AccessToken) || string.IsNullOrEmpty(result.RefreshToken))
throw new DomainException(ErrorCodes.AuthUpstreamError, "The authentication service did not return a session.", 502);
return new OtpAuthSessionResult
{
AccessToken = result.AccessToken,
RefreshToken = result.RefreshToken,
Body = new OtpLoginVerifiedResponse
{
ReferenceNumber = result.ReferenceNumber,
UserId = result.UserId,
Verified = result.Verified ?? true,
User = result.User,
ExpiresIn = result.ExpiresIn
}
};
}
}
@@ -0,0 +1,25 @@
using ERPCore.Dtos.Auth;
using ERPCore.Infra.Auth.AuthHex;
using ERPCore.Services.Interfaces;
namespace ERPCore.Services.Auth;
/// <inheritdoc cref="IAuthRecoveryService"/>
public sealed class AuthRecoveryService : IAuthRecoveryService
{
private readonly IAuthHexClient _authHex;
public AuthRecoveryService(IAuthHexClient authHex) => _authHex = authHex;
public Task<ForgotPasswordResponse> ForgotPasswordAsync(ForgotPasswordRequest request, CancellationToken ct = default)
=> _authHex.ForgotPasswordAsync(request, ct);
public Task<VerifyRecoveryOtpResponse> VerifyOtpAsync(VerifyRecoveryOtpRequest request, CancellationToken ct = default)
=> _authHex.VerifyRecoveryOtpAsync(request, ct);
public Task ResetPasswordAsync(ResetPasswordRequest request, CancellationToken ct = default)
=> _authHex.ResetPasswordAsync(request, ct);
public Task ResetPasswordWithTokenAsync(ResetPasswordWithTokenRequest request, CancellationToken ct = default)
=> _authHex.ResetPasswordWithTokenAsync(request, ct);
}
@@ -0,0 +1,25 @@
using ERPCore.Dtos.Auth;
namespace ERPCore.Services.Auth;
/// <summary>
/// Carries a freshly issued AuthHex session from a Services/Auth method back to
/// AuthController. Never serialized directly — the controller pulls
/// AccessToken/RefreshToken into httpOnly cookies (see AuthCookieWriter) and
/// returns only <see cref="Body"/> in the response.
/// </summary>
public sealed class AuthSessionResult
{
public required string AccessToken { get; init; }
public required string RefreshToken { get; init; }
public required AuthSessionResponse Body { get; init; }
}
/// <summary>Same purpose as <see cref="AuthSessionResult"/>, for the two OTP-verify
/// flows whose body also carries ReferenceNumber/Verified alongside the user/session.</summary>
public sealed class OtpAuthSessionResult
{
public required string AccessToken { get; init; }
public required string RefreshToken { get; init; }
public required OtpLoginVerifiedResponse Body { get; init; }
}
@@ -0,0 +1,111 @@
using ERPCore.Dtos.Auth;
using ERPCore.Infra.Auth.AuthHex;
using ERPCore.Services.Interfaces;
using ERPCore.System.Errors;
namespace ERPCore.Services.Auth;
/// <inheritdoc cref="IAuthUserService"/>
public sealed class AuthUserService : IAuthUserService
{
private readonly IAuthHexClient _authHex;
public AuthUserService(IAuthHexClient authHex) => _authHex = authHex;
public async Task<AuthSessionResult> RegisterAsync(RegisterRequest request, CancellationToken ct = default)
{
request.UserId ??= Guid.NewGuid();
var result = await _authHex.RegisterAsync(request, ct);
return ToSessionResult(result);
}
public async Task<AuthSessionResult> LoginAsync(LoginRequest request, CancellationToken ct = default)
{
var result = await _authHex.LoginAsync(request, ct);
return ToSessionResult(result);
}
public async Task<OtpAuthSessionResult> VerifyOtpForLoginAsync(VerifyOtpForLoginRequest request, CancellationToken ct = default)
{
var result = await _authHex.VerifyOtpForLoginAsync(request, ct);
return ToOtpSessionResult(result);
}
public async Task<AuthSessionResult> RefreshTokenAsync(string refreshToken, RefreshTokenRequest request, CancellationToken ct = default)
{
var result = await _authHex.RefreshTokenAsync(refreshToken, request.DeviceName, ct);
return ToSessionResult(result);
}
public Task<GetUserDetailsResponse> GetUserDetailsAsync(Guid userId, CancellationToken ct = default)
=> _authHex.GetUserDetailsAsync(userId, ct);
public Task<List<SessionDto>> GetUserSessionsAsync(string bearerToken, CancellationToken ct = default)
=> _authHex.GetUserSessionsAsync(bearerToken, ct);
public Task ChangeUserStatusAsync(ChangeUserStatusRequest request, string bearerToken, CancellationToken ct = default)
=> _authHex.ChangeUserStatusAsync(request.IsActive, bearerToken, ct);
public Task LockUserAccountAsync(LockUserAccountRequest request, string bearerToken, CancellationToken ct = default)
=> _authHex.LockUserAccountAsync(request.IsLocked, bearerToken, ct);
public Task ChangeUserPasswordAsync(ChangeUserPasswordRequest request, string bearerToken, CancellationToken ct = default)
=> _authHex.ChangeUserPasswordAsync(request, bearerToken, ct);
public Task<VerifyPasswordResponse> VerifyPasswordAsync(VerifyPasswordRequest request, string bearerToken, CancellationToken ct = default)
=> _authHex.VerifyPasswordAsync(request, bearerToken, ct);
public Task LogoutUserAsync(LogoutRequest request, CancellationToken ct = default)
=> _authHex.LogoutUserAsync(request.UserId, ct);
public Task<UserSummaryDto?> UpdateUserAsync(UpdateUserRequest request, string bearerToken, CancellationToken ct = default)
=> _authHex.UpdateUserAsync(request, bearerToken, ct);
public Task<TwoFaSetupResponse> InitiateTwoFaSetupAsync(string bearerToken, CancellationToken ct = default)
=> _authHex.InitiateTwoFaSetupAsync(bearerToken, ct);
public Task<CompleteTwoFaSetupResponse> CompleteTwoFaSetupAsync(CompleteTwoFaSetupRequest request, string bearerToken, CancellationToken ct = default)
=> _authHex.CompleteTwoFaSetupAsync(request, bearerToken, ct);
public Task VerifyTwoFaAsync(VerifyTwoFaRequest request, string bearerToken, CancellationToken ct = default)
=> _authHex.VerifyTwoFaAsync(request, bearerToken, ct);
public Task DisableTwoFaAsync(DisableTwoFaRequest request, string bearerToken, CancellationToken ct = default)
=> _authHex.DisableTwoFaAsync(request, bearerToken, ct);
public Task<TwoFaStatusResponse> GetTwoFaStatusAsync(string bearerToken, CancellationToken ct = default)
=> _authHex.GetTwoFaStatusAsync(bearerToken, ct);
private static AuthSessionResult ToSessionResult(AuthHexSessionResult? result)
{
if (result is null || string.IsNullOrEmpty(result.AccessToken) || string.IsNullOrEmpty(result.RefreshToken))
throw new DomainException(ErrorCodes.AuthUpstreamError, "The authentication service did not return a session.", 502);
return new AuthSessionResult
{
AccessToken = result.AccessToken,
RefreshToken = result.RefreshToken,
Body = new AuthSessionResponse { User = result.User, ExpiresIn = result.ExpiresIn }
};
}
private static OtpAuthSessionResult ToOtpSessionResult(AuthHexSessionResult? result)
{
if (result is null || string.IsNullOrEmpty(result.AccessToken) || string.IsNullOrEmpty(result.RefreshToken))
throw new DomainException(ErrorCodes.AuthUpstreamError, "The authentication service did not return a session.", 502);
return new OtpAuthSessionResult
{
AccessToken = result.AccessToken,
RefreshToken = result.RefreshToken,
Body = new OtpLoginVerifiedResponse
{
ReferenceNumber = result.ReferenceNumber,
UserId = result.UserId,
Verified = result.Verified ?? true,
User = result.User,
ExpiresIn = result.ExpiresIn
}
};
}
}
@@ -0,0 +1,12 @@
using ERPCore.Dtos.Auth;
using ERPCore.Services.Auth;
namespace ERPCore.Services.Interfaces;
/// <summary>AltOptionManager proxy business logic (API_REFERENCE.md §5), fronting AuthHex.</summary>
public interface IAuthAltService
{
Task<IsAvailableResponse> IsAvailableAsync(IsAvailableRequest request, CancellationToken ct = default);
Task<SendOtpResponse> SendOtpAsync(SendOtpRequest request, CancellationToken ct = default);
Task<OtpAuthSessionResult> VerifyOtpAsync(VerifyAltOtpRequest request, CancellationToken ct = default);
}
@@ -0,0 +1,12 @@
using ERPCore.Dtos.Auth;
namespace ERPCore.Services.Interfaces;
/// <summary>RecoveryManager proxy business logic (API_REFERENCE.md §4), fronting AuthHex.</summary>
public interface IAuthRecoveryService
{
Task<ForgotPasswordResponse> ForgotPasswordAsync(ForgotPasswordRequest request, CancellationToken ct = default);
Task<VerifyRecoveryOtpResponse> VerifyOtpAsync(VerifyRecoveryOtpRequest request, CancellationToken ct = default);
Task ResetPasswordAsync(ResetPasswordRequest request, CancellationToken ct = default);
Task ResetPasswordWithTokenAsync(ResetPasswordWithTokenRequest request, CancellationToken ct = default);
}
@@ -0,0 +1,26 @@
using ERPCore.Dtos.Auth;
using ERPCore.Services.Auth;
namespace ERPCore.Services.Interfaces;
/// <summary>UserManager proxy business logic (API_REFERENCE.md §3), fronting AuthHex.</summary>
public interface IAuthUserService
{
Task<AuthSessionResult> RegisterAsync(RegisterRequest request, CancellationToken ct = default);
Task<AuthSessionResult> LoginAsync(LoginRequest request, CancellationToken ct = default);
Task<OtpAuthSessionResult> VerifyOtpForLoginAsync(VerifyOtpForLoginRequest request, CancellationToken ct = default);
Task<AuthSessionResult> RefreshTokenAsync(string refreshToken, RefreshTokenRequest request, CancellationToken ct = default);
Task<GetUserDetailsResponse> GetUserDetailsAsync(Guid userId, CancellationToken ct = default);
Task<List<SessionDto>> GetUserSessionsAsync(string bearerToken, CancellationToken ct = default);
Task ChangeUserStatusAsync(ChangeUserStatusRequest request, string bearerToken, CancellationToken ct = default);
Task LockUserAccountAsync(LockUserAccountRequest request, string bearerToken, CancellationToken ct = default);
Task ChangeUserPasswordAsync(ChangeUserPasswordRequest request, string bearerToken, CancellationToken ct = default);
Task<VerifyPasswordResponse> VerifyPasswordAsync(VerifyPasswordRequest request, string bearerToken, CancellationToken ct = default);
Task LogoutUserAsync(LogoutRequest request, CancellationToken ct = default);
Task<UserSummaryDto?> UpdateUserAsync(UpdateUserRequest request, string bearerToken, CancellationToken ct = default);
Task<TwoFaSetupResponse> InitiateTwoFaSetupAsync(string bearerToken, CancellationToken ct = default);
Task<CompleteTwoFaSetupResponse> CompleteTwoFaSetupAsync(CompleteTwoFaSetupRequest request, string bearerToken, CancellationToken ct = default);
Task VerifyTwoFaAsync(VerifyTwoFaRequest request, string bearerToken, CancellationToken ct = default);
Task DisableTwoFaAsync(DisableTwoFaRequest request, string bearerToken, CancellationToken ct = default);
Task<TwoFaStatusResponse> GetTwoFaStatusAsync(string bearerToken, CancellationToken ct = default);
}