Files
Chatbot-ElDia/ChatbotApi/Controllers/AuthController.cs

124 lines
3.8 KiB
C#
Raw Normal View History

2025-11-18 14:34:26 -03:00
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.ComponentModel.DataAnnotations;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.RateLimiting;
2025-11-18 14:34:26 -03:00
public class LoginRequest
{
[Required]
[MaxLength(100)]
public required string Username { get; set; }
[Required]
[MaxLength(100)]
public required string Password { get; set; }
}
// [SEGURIDAD] LoginResponse ya no es necesario si usamos solo cookies, pero podriamos dejar un mensaje de exito.
public class LoginResponse { public string Message { get; set; } = "Login exitoso"; }
2025-11-18 14:34:26 -03:00
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly IConfiguration _configuration;
private readonly UserManager<IdentityUser> _userManager;
public AuthController(IConfiguration configuration, UserManager<IdentityUser> userManager)
{
_configuration = configuration;
_userManager = userManager;
}
[HttpPost("login")]
[EnableRateLimiting("login-limit")]
2025-11-18 14:34:26 -03:00
public async Task<IActionResult> Login([FromBody] LoginRequest loginRequest)
{
var user = await _userManager.FindByNameAsync(loginRequest.Username);
if (user != null && await _userManager.CheckPasswordAsync(user, loginRequest.Password))
{
var token = GenerateJwtToken(user);
// [SEGURIDAD] Setear Cookie HttpOnly
var cookieOptions = new CookieOptions
{
HttpOnly = true,
2025-12-05 14:20:51 -03:00
Secure = Request.IsHttps, // Dinámico: true si es HTTPS, false si es HTTP
SameSite = SameSiteMode.Lax, // Permite navegación entre puertos/IPs más facil
Expires = DateTime.UtcNow.AddHours(8)
};
Response.Cookies.Append("X-Access-Token", token, cookieOptions);
return Ok(new LoginResponse());
2025-11-18 14:34:26 -03:00
}
return Unauthorized("Credenciales inválidas.");
}
[HttpPost("logout")]
public IActionResult Logout()
{
Response.Cookies.Delete("X-Access-Token");
return Ok(new { message = "Sesión cerrada" });
}
[HttpGet("status")]
[Microsoft.AspNetCore.Authorization.AllowAnonymous]
public IActionResult GetStatus()
{
return Ok(new { isAuthenticated = User.Identity?.IsAuthenticated ?? false });
}
#if DEBUG
// [SEGURIDAD] Endpoint solo para desarrollo
2025-11-18 14:34:26 -03:00
[HttpPost("setup-admin")]
public async Task<IActionResult> SetupAdminUser()
{
var adminUser = await _userManager.FindByNameAsync("admin");
if (adminUser == null)
{
adminUser = new IdentityUser
{
UserName = "admin",
Email = "tecnica@eldia.com",
};
// En producción usar Secrets, no hardcoded
2025-11-18 14:34:26 -03:00
var result = await _userManager.CreateAsync(adminUser, "Diagonal423");
if (result.Succeeded)
{
return Ok("Usuario administrador creado exitosamente.");
}
return BadRequest(result.Errors);
}
return Ok("El usuario administrador ya existe.");
}
#endif
2025-11-18 14:34:26 -03:00
private string GenerateJwtToken(IdentityUser user)
{
var jwtKey = _configuration["Jwt:Key"] ?? throw new InvalidOperationException("La clave JWT no está configurada.");
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName!),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var token = new JwtSecurityToken(
issuer: _configuration["Jwt:Issuer"],
audience: _configuration["Jwt:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddHours(8),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}