using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Security.Claims; using UserMicroservice.Models; using UserMicroservice.Services; namespace UserMicroservice.Controllers { [ApiController] [Route("api/[controller]")] public class UserController : ControllerBase { private readonly IUserService _userService; private readonly IAuthService _authService; public UserController(IUserService userService, IAuthService authService) { _userService = userService; _authService = authService; } #region Auth Endpoints // POST: api/User/register [HttpPost("register")] public IActionResult Register([FromBody] RegisterModel model) { try { User user = _userService.CreateUser(model.Email, model.Username, model.Password, model.UserType); if (user == null) return BadRequest(); setAuthCookies(user.Id); return Ok(new { user.Id, user.Username, user.Email, user.Type }); } catch (InvalidOperationException ex) { return BadRequest(ex.Message); } } // POST: api/User/authorize [HttpPost("authorize")] public IActionResult Authorize() { string? refreshToken = Request.Cookies["RefreshToken"]; if (string.IsNullOrEmpty(refreshToken)) return BadRequest("Refresh token is missing."); if (!_authService.ValidateRefreshToken(refreshToken)) return Unauthorized("Invalid or expired refresh token."); string? userIdString = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; if(userIdString == null) return BadRequest(); if (!int.TryParse(userIdString, out int userId)) return BadRequest("User ID is invalid."); User? user = _userService.GetUser(userId); if(user == null) return Unauthorized(); setAuthCookies(userId); return Ok(new { user.Id, user.Username, user.Email, user.Type }); } // POST: api/User/login [HttpPost("login")] public IActionResult Login([FromBody] LoginModel model) { User? user = _userService.GetUser(model.Username, model.Password); if(user == null) return Unauthorized(); setAuthCookies(user.Id); return Ok(new { user.Id, user.Username, user.Email, user.Type }); } private void setAuthCookies(int userId) { AuthTokenPair authToken = _authService.AuthenticateUser(userId); if (authToken == null) throw new ArgumentNullException(nameof(authToken)); // Set the access token as an HttpOnly cookie Response.Cookies.Append("AccessToken", authToken.AccessToken, new CookieOptions { HttpOnly = true, Secure = true, SameSite = SameSiteMode.Strict, Expires = DateTimeOffset.UtcNow.AddMinutes(30) }); // Set the refresh token as an HttpOnly cookie Response.Cookies.Append("RefreshToken", authToken.RefreshToken, new CookieOptions { HttpOnly = true, Secure = true, SameSite = SameSiteMode.Strict, Expires = DateTimeOffset.UtcNow.AddDays(2) }); } // POST: api/User/logout [Authorize] [HttpPost("logout")] public IActionResult Logout() { string? refreshToken = Request.Cookies["RefreshToken"]; if(string.IsNullOrEmpty(refreshToken)) return BadRequest("Refresh token is missing."); if (!_authService.ValidateRefreshToken(refreshToken)) return Unauthorized("Invalid or expired refresh token."); _authService.RevokeRefreshToken(refreshToken); // Clear the access token cookie and set it to expire immediately Response.Cookies.Append("AccessToken", string.Empty, new CookieOptions { HttpOnly = true, Secure = true, Expires = DateTimeOffset.UtcNow.AddSeconds(-1) }); // Clear the refresh token cookie and set it to expire immediately Response.Cookies.Append("RefreshToken", string.Empty, new CookieOptions { HttpOnly = true, Secure = true, Expires = DateTimeOffset.UtcNow.AddSeconds(-1) }); return Ok(); } #endregion // GET: api/User [Authorize] [HttpGet()] public IActionResult GetUsers() { List<User> users = _userService.GetUsers(); if(users == null) return BadRequest(); return Ok(users); } // GET: api/User/{id} [Authorize] [HttpGet("{id}")] public IActionResult GetUser(int id) { User? user = _userService.GetUser(id); if(user == null) return NotFound($"User with {id} doesnt exist"); return Ok(new{ user.Id, user.Username, user.Email, user.Type }); } // PUT: api/User/{id} [Authorize] [HttpPatch("{id}")] public IActionResult UpdateUser(int id, [FromBody] UpdateUserModel model) { try { _userService.UpdateUser(id, model.Username, model.Email, model.Password); return Ok(); } catch (KeyNotFoundException exception) { return NotFound(exception.Message); } catch (DbUpdateException exception) { return BadRequest(exception.Message); } } } }