using System.Security.Claims; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.RateLimiting; /// /// JWT authentication, token refresh, logout, and current user info. /// [ApiController] [Route("api/v1/auth")] [Produces("application/json")] public class AuthController : ControllerBase { private readonly IAuthService _auth; public AuthController(IAuthService auth) => _auth = auth; /// /// Authenticates a user and returns a JWT access token, refresh token, and profile. /// [HttpPost("login")] [AllowAnonymous] [EnableRateLimiting("auth")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status422UnprocessableEntity)] [ProducesResponseType(StatusCodes.Status429TooManyRequests)] public async Task Login([FromBody] LoginRequest req) { var result = await _auth.LoginAsync(req); return Ok(ApiResponse.Ok(result)); } /// /// Rotates the refresh token and issues a new access token. /// [HttpPost("refresh")] [AllowAnonymous] [EnableRateLimiting("auth")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status422UnprocessableEntity)] [ProducesResponseType(StatusCodes.Status429TooManyRequests)] public async Task Refresh([FromBody] RefreshRequest req) { var result = await _auth.RefreshAsync(req); return Ok(ApiResponse.Ok(result)); } /// /// Revokes the refresh token server-side. /// [HttpPost("logout")] [AllowAnonymous] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status422UnprocessableEntity)] public async Task Logout([FromBody] LogoutRequest req) { await _auth.LogoutAsync(req); return NoContent(); } /// /// Returns the current authenticated user's profile. /// [HttpGet("me")] [Authorize] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status401Unauthorized)] public async Task Me() { var userId = Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!); var user = await _auth.GetCurrentUserAsync(userId); return Ok(ApiResponse.Ok(new UserProfileResponse( user.Id, user.Username, user.FullName, user.Role.ToDbString()))); } }