using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity.Data; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.RateLimiting; using Nuuru.Server.DTOs.Auth; using Nuuru.Server.Models; using Nuuru.Server.Services; namespace Nuuru.Server.Controllers { [ApiController] [Route("api/[controller]")] [EnableRateLimiting("auth")] public class AuthController : ControllerBase { private readonly IAuthService _authService; private readonly ICaptchaService _captchaService; private readonly ITokenService _tokenService; private readonly ISiteSettingsService _siteSettingsService; public AuthController( IAuthService authService, ICaptchaService captchaService, ITokenService tokenService, ISiteSettingsService siteSettingsService) { _authService = authService; _captchaService = captchaService; _tokenService = tokenService; _siteSettingsService = siteSettingsService; } [HttpPost("register")] public async Task Register([FromBody] Nuuru.Server.Models.Requests.RegisterRequest request) { if (!await _siteSettingsService.IsRegistrationEnabledAsync()) { return StatusCode(StatusCodes.Status403Forbidden, new { error = "Registrations are currently disabled." }); } if (!await _captchaService.ValidateAsync(request.CaptchaToken)) { return BadRequest(new { error = "Invalid CAPTCHA" }); } var clientIp = GetClientIpAddress(); var result = await _authService.RegisterAsync( request.UserName, request.Password, clientIp); if (!result.Success) { return BadRequest(new { error = result.Errors?.FirstOrDefault(), errors = result.Errors, canRetryIpVerification = result.CanRetryIpVerification }); } if (result.RequiresVerification) { HttpContext.Items[AuditLog.UserIdKey] = result.UserId; return Ok(new { requiresVerification = true, verificationAvailableAt = result.VerificationAvailableAt, message = result.Message }); } HttpContext.Items[AuditLog.UserIdKey] = result.UserId; return Ok(new { refreshToken = result.RefreshToken }); } [HttpPost("login")] public async Task Login([FromBody] Nuuru.Server.Models.Requests.LoginRequest request) { var clientIp = GetClientIpAddress(); var result = await _authService.LoginAsync( request.UserName, request.Password, clientIp); if (!result.Success) { return BadRequest(new { error = result.Errors?.FirstOrDefault(), errors = result.Errors, canRetryIpVerification = result.CanRetryIpVerification }); } return Ok(new { refreshToken = result.RefreshToken }); } [HttpPost("refresh")] public async Task Refresh([FromBody] RefreshTokenRequest request) { var tokenResponse = await _tokenService.GetAccessTokenFromRefreshTokenAsync(request.RefreshToken); if (tokenResponse == null) { return Unauthorized(new { error = "Invalid or expired refresh token" }); } return Ok(new { token = tokenResponse.AccessToken, refreshToken = tokenResponse.RefreshToken, expiresAt = tokenResponse.ExpiresAt }); } [HttpPost("revoke")] [Authorize] public async Task Revoke([FromBody] RefreshTokenRequest request) { var result = await _tokenService.RevokeRefreshTokenAsync(request.RefreshToken); if (!result) { return BadRequest(new { error = "Token not found" }); } return Ok(new { message = "Token revoked successfully" }); } [HttpPost("logout")] [Authorize] public async Task Logout([FromBody] RefreshTokenRequest request) { await _tokenService.RevokeRefreshTokenAsync(request.RefreshToken); return Ok(new { message = "Logged out successfully" }); } private string? GetClientIpAddress() { var ipAddress = HttpContext.Connection.RemoteIpAddress; if (ipAddress == null) { return null; } if (ipAddress.IsIPv4MappedToIPv6) { return ipAddress.MapToIPv4().ToString(); } return ipAddress.ToString(); } } }