diff --git a/NEW/src/JdeScoping.Api/Controllers/AuthController.cs b/NEW/src/JdeScoping.Api/Controllers/AuthController.cs index f837a40..2a62491 100644 --- a/NEW/src/JdeScoping.Api/Controllers/AuthController.cs +++ b/NEW/src/JdeScoping.Api/Controllers/AuthController.cs @@ -1,8 +1,9 @@ using System.Security.Claims; +using System.Text.Json; using JdeScoping.Api.Extensions; -using JdeScoping.Api.Models; using JdeScoping.Core.Interfaces; using JdeScoping.Core.Models; +using JdeScoping.Core.Models.Auth; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; @@ -20,37 +21,66 @@ namespace JdeScoping.Api.Controllers; public class AuthController : ApiControllerBase { private readonly IAuthService _authService; + private readonly IRsaKeyService _rsaKeyService; private readonly ILogger _logger; public AuthController( IAuthService authService, + IRsaKeyService rsaKeyService, ILogger logger) { _authService = authService; + _rsaKeyService = rsaKeyService; _logger = logger; } /// - /// Authenticates a user and creates a session cookie + /// Gets the server's RSA public key for encrypting login credentials. /// - /// Login credentials + [HttpGet("public-key")] + [AllowAnonymous] + [ProducesResponseType(typeof(PublicKeyResponse), StatusCodes.Status200OK)] + public ActionResult GetPublicKey() + { + var publicKeyPem = _rsaKeyService.GetPublicKeyPem(); + return Ok(new PublicKeyResponse(publicKeyPem)); + } + + /// + /// Authenticates a user with encrypted credentials and creates a session cookie. + /// + /// Encrypted login credentials /// Cancellation token - /// User info on success, 401 on failure + /// Login result with user info on success [HttpPost("login")] [AllowAnonymous] - [ProducesResponseType(typeof(UserInfo), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - public async Task> Login( - [FromBody] LoginRequest request, + [ProducesResponseType(typeof(LoginResultModel), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(LoginResultModel), StatusCodes.Status401Unauthorized)] + public async Task> Login( + [FromBody] EncryptedLoginRequest request, CancellationToken ct) { + LoginModel loginModel; + try + { + var ciphertext = Convert.FromBase64String(request.EncryptedData); + var plaintext = _rsaKeyService.Decrypt(ciphertext); + loginModel = JsonSerializer.Deserialize(plaintext) + ?? throw new InvalidOperationException("Deserialization returned null"); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Failed to decrypt login request"); + return BadRequest(new LoginResultModel(false, "Invalid encrypted payload", null)); + } + var result = await _authService.AuthenticateAsync( - request.Username, request.Password, ct); + loginModel.Username, loginModel.Password, ct); if (!result.Success) { - _logger.LogWarning("Failed login attempt for user {Username}", request.Username); - return Unauthorized(new { message = result.ErrorMessage }); + _logger.LogWarning("Failed login attempt for user {Username}", loginModel.Username); + return Unauthorized(new LoginResultModel(false, result.ErrorMessage, null)); } // Sign out existing session @@ -66,8 +96,8 @@ public class AuthController : ApiControllerBase principal, new AuthenticationProperties { IsPersistent = false }); - _logger.LogInformation("User {Username} logged in successfully", request.Username); - return Ok(result.User); + _logger.LogInformation("User {Username} logged in successfully", loginModel.Username); + return Ok(new LoginResultModel(true, null, result.User)); } /// diff --git a/NEW/src/JdeScoping.Api/Models/LoginRequest.cs b/NEW/src/JdeScoping.Api/Models/LoginRequest.cs deleted file mode 100644 index b7ae179..0000000 --- a/NEW/src/JdeScoping.Api/Models/LoginRequest.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace JdeScoping.Api.Models; - -/// -/// Login request payload -/// -public class LoginRequest -{ - /// - /// Username for authentication - /// - [Required(ErrorMessage = "Username is required")] - public string Username { get; set; } = string.Empty; - - /// - /// Password for authentication - /// - [Required(ErrorMessage = "Password is required")] - public string Password { get; set; } = string.Empty; -}