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 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 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 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; }