Files
2025-12-12 15:40:34 -03:00

115 lines
3.3 KiB
C#

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using GestorFacturas.API.Data;
using GestorFacturas.API.Services;
using GestorFacturas.API.Models;
namespace GestorFacturas.API.Controllers;
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly AuthService _authService;
public AuthController(ApplicationDbContext context, AuthService authService)
{
_context = context;
_authService = authService;
}
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginDto login)
{
var usuario = await _context.Usuarios.FirstOrDefaultAsync(u => u.Username == login.Username);
if (usuario == null || !_authService.VerificarPassword(login.Password, usuario.PasswordHash))
{
return Unauthorized(new { mensaje = "Credenciales incorrectas" });
}
// Generar Tokens
var accessToken = _authService.GenerarAccessToken(usuario);
// Pasamos la elección del usuario (true/false)
var refreshToken = _authService.GenerarRefreshToken(usuario.Id, login.RememberMe);
_context.RefreshTokens.Add(refreshToken);
await _context.SaveChangesAsync();
return Ok(new
{
token = accessToken,
refreshToken = refreshToken.Token,
usuario = usuario.Username
});
}
[HttpPost("refresh-token")]
public async Task<IActionResult> RefreshToken([FromBody] RefreshTokenRequest request)
{
var oldRefreshToken = await _context.RefreshTokens
.Include(r => r.Usuario)
.FirstOrDefaultAsync(r => r.Token == request.Token);
if (oldRefreshToken == null || !oldRefreshToken.IsActive)
{
return Unauthorized(new { mensaje = "Token inválido" });
}
// Revocar el anterior
oldRefreshToken.Revoked = DateTime.UtcNow;
// Generamos el nuevo token heredando la persistencia del anterior.
// Si el usuario marcó "Recordarme" hace 20 días, el nuevo token seguirá siendo persistente.
// Si no lo marcó, seguirá siendo de corta duración.
var newRefreshToken = _authService.GenerarRefreshToken(oldRefreshToken.UsuarioId, oldRefreshToken.IsPersistent);
var newAccessToken = _authService.GenerarAccessToken(oldRefreshToken.Usuario!);
_context.RefreshTokens.Add(newRefreshToken);
await _context.SaveChangesAsync();
return Ok(new
{
token = newAccessToken,
refreshToken = newRefreshToken.Token,
usuario = oldRefreshToken.Usuario!.Username
});
}
[HttpPost("revoke")]
public async Task<IActionResult> Revoke([FromBody] RefreshTokenRequest request)
{
var token = request.Token;
// Buscamos el token en la BD
var refreshToken = await _context.RefreshTokens
.FirstOrDefaultAsync(r => r.Token == token);
if (refreshToken == null)
return NotFound(new { mensaje = "Token no encontrado" });
// Lo revocamos inmediatamente
refreshToken.Revoked = DateTime.UtcNow;
_context.Update(refreshToken);
await _context.SaveChangesAsync();
return Ok(new { mensaje = "Sesión cerrada correctamente" });
}
}
// DTOs
public class LoginDto
{
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public bool RememberMe { get; set; } = false;
}
public class RefreshTokenRequest
{
public string Token { get; set; } = string.Empty;
}