using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using T120B165_ImgBoard.Dtos; using T120B165_ImgBoard.Models; using T120B165_ImgBoard.Services; namespace T120B165_ImgBoard.Controllers; [ApiController] [Route("api/auth")] public class AuthController(UserManager userManager, ITokenService tokenService): ControllerBase { /// /// Creates a new user account. /// /// Registration data /// Returns user data /// If user supplied credentials fail validation [HttpPost("register")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)] public async Task> Register(RegisterDto dto) { var user = new User { UserName = dto.UserName, Email = dto.Email, }; var result = await userManager.CreateAsync(user, dto.Password); await userManager.AddToRoleAsync(user, UserRoles.Regular); Dictionary idk = new() { ["errors"] = result.Errors }; if (!result.Succeeded) { return Problem( statusCode: StatusCodes.Status422UnprocessableEntity, extensions: idk ); } return Ok(SlimUserDto.FromUser(user)); } /// /// Authenticates and creates a pair of access and refresh tokens. /// /// Data with refresh token /// Returns refresh and access tokens /// If the credentials are incorrect [HttpPost("login")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task> Login(LoginDto dto) { var user = await userManager.FindByEmailAsync(dto.Email); if (user == null || !await userManager.CheckPasswordAsync(user, dto.Password)) return Unauthorized(); var accessToken = await tokenService.GenerateJwtToken(user); var refreshToken = await tokenService.GenerateRefreshToken(user); return Ok(new TokenDto(AccessToken: accessToken, RefreshToken: refreshToken)); } /// /// Consume refresh token to create new access and refresh tokens. /// /// Data with refresh token /// Returns new refresh and access tokens /// If refresh token is missing or is expired [HttpPost("refresh")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task> Refresh(RefreshDto dto) { var token = await tokenService.GetRefreshTokenByValue(dto.RefreshToken); if (token == null) return Unauthorized(); var user = token.User; await tokenService.InvalidateRefreshToken(token); var accessToken = await tokenService.GenerateJwtToken(user); var newRefreshToken = await tokenService.GenerateRefreshToken(user); return Ok(new TokenDto(AccessToken: accessToken, RefreshToken: newRefreshToken)); } /// /// Revokes the refresh token. /// /// Data with refresh token /// If token was revoked successfully /// If refresh token is missing or is expired [HttpPost("revoke")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task> Revoke(RefreshDto dto) { var token = await tokenService.GetRefreshTokenByValue(dto.RefreshToken); if (token == null) return Unauthorized(); await tokenService.InvalidateRefreshToken(token); return NoContent(); } }