feat(api): update AuthController for encrypted login
This commit is contained in:
@@ -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<AuthController> _logger;
|
||||
|
||||
public AuthController(
|
||||
IAuthService authService,
|
||||
IRsaKeyService rsaKeyService,
|
||||
ILogger<AuthController> logger)
|
||||
{
|
||||
_authService = authService;
|
||||
_rsaKeyService = rsaKeyService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates a user and creates a session cookie
|
||||
/// Gets the server's RSA public key for encrypting login credentials.
|
||||
/// </summary>
|
||||
/// <param name="request">Login credentials</param>
|
||||
[HttpGet("public-key")]
|
||||
[AllowAnonymous]
|
||||
[ProducesResponseType(typeof(PublicKeyResponse), StatusCodes.Status200OK)]
|
||||
public ActionResult<PublicKeyResponse> GetPublicKey()
|
||||
{
|
||||
var publicKeyPem = _rsaKeyService.GetPublicKeyPem();
|
||||
return Ok(new PublicKeyResponse(publicKeyPem));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates a user with encrypted credentials and creates a session cookie.
|
||||
/// </summary>
|
||||
/// <param name="request">Encrypted login credentials</param>
|
||||
/// <param name="ct">Cancellation token</param>
|
||||
/// <returns>User info on success, 401 on failure</returns>
|
||||
/// <returns>Login result with user info on success</returns>
|
||||
[HttpPost("login")]
|
||||
[AllowAnonymous]
|
||||
[ProducesResponseType(typeof(UserInfo), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
public async Task<ActionResult<UserInfo>> Login(
|
||||
[FromBody] LoginRequest request,
|
||||
[ProducesResponseType(typeof(LoginResultModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(LoginResultModel), StatusCodes.Status401Unauthorized)]
|
||||
public async Task<ActionResult<LoginResultModel>> Login(
|
||||
[FromBody] EncryptedLoginRequest request,
|
||||
CancellationToken ct)
|
||||
{
|
||||
LoginModel loginModel;
|
||||
try
|
||||
{
|
||||
var ciphertext = Convert.FromBase64String(request.EncryptedData);
|
||||
var plaintext = _rsaKeyService.Decrypt(ciphertext);
|
||||
loginModel = JsonSerializer.Deserialize<LoginModel>(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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace JdeScoping.Api.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Login request payload
|
||||
/// </summary>
|
||||
public class LoginRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Username for authentication
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Username is required")]
|
||||
public string Username { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Password for authentication
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Password is required")]
|
||||
public string Password { get; set; } = string.Empty;
|
||||
}
|
||||
Reference in New Issue
Block a user