using System; using System.ComponentModel.DataAnnotations; using System.Security.Claims; using System.Text.Json.Serialization; using System.Threading.Tasks; using FarmatikoData.Models; using FarmatikoServices.Auth; using FarmatikoServices.FarmatikoServiceInterfaces; using FarmatikoServices.Infrastructure; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; namespace Farmatiko.Controllers { [ApiController] public class LoginController : ControllerBase { private readonly ILogger _logger; private readonly IAuthService _authService; private readonly IJwtAuthManager _jwtAuthManager; private readonly IPHService _pHService; public LoginController(ILogger logger, IAuthService authService, IJwtAuthManager jwtAuthManager, IPHService pHService) { _logger = logger; _authService = authService; _jwtAuthManager = jwtAuthManager; _pHService = pHService; } [AllowAnonymous] [HttpPost("api/pharmacyhead/login")] public ActionResult Login([FromBody] LoginRequest request) { if (!ModelState.IsValid) { return BadRequest(); } if (!_authService.IsValidUserCredentials(request.UserName, request.Password)) { return Unauthorized(); } var role = _authService.GetUserRole(request.UserName); var claims = new[] { new Claim(ClaimTypes.Name,request.UserName), new Claim(ClaimTypes.Role, role) }; var jwtResult = _jwtAuthManager.GenerateTokens(request.UserName, claims, DateTime.Now); _logger.LogInformation($"User [{request.UserName}] logged in the system."); return Ok(new LoginResult { UserName = request.UserName, Role = role, AccessToken = jwtResult.AccessToken, RefreshToken = jwtResult.RefreshToken.TokenString, Head = _pHService.GetPharmacyHead(request.UserName) }); } [HttpGet("api/pharmacyhead/user")] [Authorize] public ActionResult GetCurrentUser() { return Ok(new LoginResult { UserName = User.Identity.Name, Role = User.FindFirst(ClaimTypes.Role)?.Value ?? string.Empty, OriginalUserName = User.FindFirst("OriginalUserName")?.Value }); } [HttpPost("api/pharmacyhead/logout")] [Authorize] public ActionResult Logout() { var userName = User.Identity.Name; _jwtAuthManager.RemoveRefreshTokenByUserName(userName); _logger.LogInformation($"User [{userName}] logged out the system."); return Ok(); } [HttpPost("api/pharmacyhead/refresh-token")] [Authorize] public async Task RefreshToken([FromBody] RefreshTokenRequest request) { try { var userName = User.Identity.Name; _logger.LogInformation($"User [{userName}] is trying to refresh JWT token."); if (string.IsNullOrWhiteSpace(request.RefreshToken)) { return Unauthorized(); } var accessToken = await HttpContext.GetTokenAsync("Bearer", "access_token"); var jwtResult = _jwtAuthManager.Refresh(request.RefreshToken, accessToken, DateTime.Now); _logger.LogInformation($"User [{userName}] has refreshed JWT token."); return Ok(new LoginResult { /*UserName = userName, Role = User.FindFirst(ClaimTypes.Role)?.Value ?? string.Empty, AccessToken = jwtResult.AccessToken, RefreshToken = jwtResult.RefreshToken.TokenString*/ UserName = userName, Role = User.FindFirst(ClaimTypes.Role)?.Value ?? string.Empty, AccessToken = jwtResult.AccessToken, RefreshToken = jwtResult.RefreshToken.TokenString, Head = _pHService.GetPharmacyHead(userName) }); } catch (SecurityTokenException e) { return Unauthorized(e.Message); // return 401 so that the client side can redirect the user to login page } } [HttpPost("api/pharmacyhead/impersonation")] [Authorize(Roles = UserRoles.Admin)] public ActionResult Impersonate([FromBody] ImpersonationRequest request) { var userName = User.Identity.Name; _logger.LogInformation($"User [{userName}] is trying to impersonate [{request.UserName}]."); var impersonatedRole = _authService.GetUserRole(request.UserName); if (string.IsNullOrWhiteSpace(impersonatedRole)) { _logger.LogInformation($"User [{userName}] failed to impersonate [{request.UserName}] due to the target user not found."); return BadRequest($"The target user [{request.UserName}] is not found."); } if (impersonatedRole == UserRoles.Admin) { _logger.LogInformation($"User [{userName}] is not allowed to impersonate another Admin."); return BadRequest("This action is not supported."); } var claims = new[] { new Claim(ClaimTypes.Name,request.UserName), new Claim(ClaimTypes.Role, impersonatedRole), new Claim("OriginalUserName", userName) }; var jwtResult = _jwtAuthManager.GenerateTokens(request.UserName, claims, DateTime.Now); _logger.LogInformation($"User [{request.UserName}] is impersonating [{request.UserName}] in the system."); return Ok(new LoginResult { UserName = request.UserName, Role = impersonatedRole, OriginalUserName = userName, AccessToken = jwtResult.AccessToken, RefreshToken = jwtResult.RefreshToken.TokenString, Head = _pHService.GetPharmacyHead(userName) }); } [HttpPost("api/pharmacyhead/stop-impersonation")] public ActionResult StopImpersonation() { var userName = User.Identity.Name; var originalUserName = User.FindFirst("OriginalUserName")?.Value; if (string.IsNullOrWhiteSpace(originalUserName)) { return BadRequest("You are not impersonating anyone."); } _logger.LogInformation($"User [{originalUserName}] is trying to stop impersonate [{userName}]."); var role = _authService.GetUserRole(originalUserName); var claims = new[] { new Claim(ClaimTypes.Name,originalUserName), new Claim(ClaimTypes.Role, role) }; var jwtResult = _jwtAuthManager.GenerateTokens(originalUserName, claims, DateTime.Now); _logger.LogInformation($"User [{originalUserName}] has stopped impersonation."); return Ok(new LoginResult { UserName = originalUserName, Role = role, OriginalUserName = null, AccessToken = jwtResult.AccessToken, RefreshToken = jwtResult.RefreshToken.TokenString, Head = _pHService.GetPharmacyHead(userName) }); } } public class LoginRequest { [Required] [JsonPropertyName("username")] public string UserName { get; set; } [Required] [JsonPropertyName("password")] public string Password { get; set; } } public class LoginResult { [JsonPropertyName("userName")] public string UserName { get; set; } [JsonPropertyName("role")] public string Role { get; set; } [JsonPropertyName("originalUserName")] public string OriginalUserName { get; set; } [JsonPropertyName("accessToken")] public string AccessToken { get; set; } [JsonPropertyName("refreshToken")] public string RefreshToken { get; set; } [JsonPropertyName("head")] public object Head { get; set; } } public class RefreshTokenRequest { [JsonPropertyName("refreshToken")] public string RefreshToken { get; set; } } public class ImpersonationRequest { [JsonPropertyName("userName")] public string UserName { get; set; } } }