feat(api): update AuthController for encrypted login
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
using System.Text.Json;
|
||||||
using JdeScoping.Api.Extensions;
|
using JdeScoping.Api.Extensions;
|
||||||
using JdeScoping.Api.Models;
|
|
||||||
using JdeScoping.Core.Interfaces;
|
using JdeScoping.Core.Interfaces;
|
||||||
using JdeScoping.Core.Models;
|
using JdeScoping.Core.Models;
|
||||||
|
using JdeScoping.Core.Models.Auth;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
@@ -20,37 +21,66 @@ namespace JdeScoping.Api.Controllers;
|
|||||||
public class AuthController : ApiControllerBase
|
public class AuthController : ApiControllerBase
|
||||||
{
|
{
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
|
private readonly IRsaKeyService _rsaKeyService;
|
||||||
private readonly ILogger<AuthController> _logger;
|
private readonly ILogger<AuthController> _logger;
|
||||||
|
|
||||||
public AuthController(
|
public AuthController(
|
||||||
IAuthService authService,
|
IAuthService authService,
|
||||||
|
IRsaKeyService rsaKeyService,
|
||||||
ILogger<AuthController> logger)
|
ILogger<AuthController> logger)
|
||||||
{
|
{
|
||||||
_authService = authService;
|
_authService = authService;
|
||||||
|
_rsaKeyService = rsaKeyService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Authenticates a user and creates a session cookie
|
/// Gets the server's RSA public key for encrypting login credentials.
|
||||||
/// </summary>
|
/// </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>
|
/// <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")]
|
[HttpPost("login")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[ProducesResponseType(typeof(UserInfo), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(LoginResultModel), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
[ProducesResponseType(typeof(LoginResultModel), StatusCodes.Status401Unauthorized)]
|
||||||
public async Task<ActionResult<UserInfo>> Login(
|
public async Task<ActionResult<LoginResultModel>> Login(
|
||||||
[FromBody] LoginRequest request,
|
[FromBody] EncryptedLoginRequest request,
|
||||||
CancellationToken ct)
|
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(
|
var result = await _authService.AuthenticateAsync(
|
||||||
request.Username, request.Password, ct);
|
loginModel.Username, loginModel.Password, ct);
|
||||||
|
|
||||||
if (!result.Success)
|
if (!result.Success)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Failed login attempt for user {Username}", request.Username);
|
_logger.LogWarning("Failed login attempt for user {Username}", loginModel.Username);
|
||||||
return Unauthorized(new { message = result.ErrorMessage });
|
return Unauthorized(new LoginResultModel(false, result.ErrorMessage, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign out existing session
|
// Sign out existing session
|
||||||
@@ -66,8 +96,8 @@ public class AuthController : ApiControllerBase
|
|||||||
principal,
|
principal,
|
||||||
new AuthenticationProperties { IsPersistent = false });
|
new AuthenticationProperties { IsPersistent = false });
|
||||||
|
|
||||||
_logger.LogInformation("User {Username} logged in successfully", request.Username);
|
_logger.LogInformation("User {Username} logged in successfully", loginModel.Username);
|
||||||
return Ok(result.User);
|
return Ok(new LoginResultModel(true, null, result.User));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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