Skip to content
Snippets Groups Projects
UserController.cs 6.02 KiB
Newer Older
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);            
            }