1 | using System;
|
---|
2 | using System.ComponentModel.DataAnnotations;
|
---|
3 | using System.Security.Claims;
|
---|
4 | using System.Text.Json.Serialization;
|
---|
5 | using System.Threading.Tasks;
|
---|
6 | using FarmatikoData.Models;
|
---|
7 | using FarmatikoServices.Auth;
|
---|
8 | using FarmatikoServices.FarmatikoServiceInterfaces;
|
---|
9 | using FarmatikoServices.Infrastructure;
|
---|
10 | using Microsoft.AspNetCore.Authentication;
|
---|
11 | using Microsoft.AspNetCore.Authorization;
|
---|
12 | using Microsoft.AspNetCore.Http;
|
---|
13 | using Microsoft.AspNetCore.Mvc;
|
---|
14 | using Microsoft.Extensions.Logging;
|
---|
15 | using Microsoft.IdentityModel.Tokens;
|
---|
16 |
|
---|
17 | namespace Farmatiko.Controllers
|
---|
18 | {
|
---|
19 | [ApiController]
|
---|
20 | public class LoginController : ControllerBase
|
---|
21 | {
|
---|
22 | private readonly ILogger<LoginController> _logger;
|
---|
23 | private readonly IAuthService _authService;
|
---|
24 | private readonly IJwtAuthManager _jwtAuthManager;
|
---|
25 | private readonly IPHService _pHService;
|
---|
26 |
|
---|
27 | public LoginController(ILogger<LoginController> logger, IAuthService authService, IJwtAuthManager jwtAuthManager, IPHService pHService)
|
---|
28 | {
|
---|
29 | _logger = logger;
|
---|
30 | _authService = authService;
|
---|
31 | _jwtAuthManager = jwtAuthManager;
|
---|
32 | _pHService = pHService;
|
---|
33 | }
|
---|
34 |
|
---|
35 | [AllowAnonymous]
|
---|
36 | [HttpPost("api/pharmacyhead/login")]
|
---|
37 | public ActionResult Login([FromBody] LoginRequest request)
|
---|
38 | {
|
---|
39 | if (!ModelState.IsValid)
|
---|
40 | {
|
---|
41 | return BadRequest();
|
---|
42 | }
|
---|
43 |
|
---|
44 | if (!_authService.IsValidUserCredentials(request.UserName, request.Password))
|
---|
45 | {
|
---|
46 | return Unauthorized();
|
---|
47 | }
|
---|
48 |
|
---|
49 | var role = _authService.GetUserRole(request.UserName);
|
---|
50 | var claims = new[]
|
---|
51 | {
|
---|
52 | new Claim(ClaimTypes.Name,request.UserName),
|
---|
53 | new Claim(ClaimTypes.Role, role)
|
---|
54 | };
|
---|
55 |
|
---|
56 | var jwtResult = _jwtAuthManager.GenerateTokens(request.UserName, claims, DateTime.Now);
|
---|
57 | _logger.LogInformation($"User [{request.UserName}] logged in the system.");
|
---|
58 | return Ok(new LoginResult
|
---|
59 | {
|
---|
60 | UserName = request.UserName,
|
---|
61 | Role = role,
|
---|
62 | AccessToken = jwtResult.AccessToken,
|
---|
63 | RefreshToken = jwtResult.RefreshToken.TokenString,
|
---|
64 | Head = _pHService.GetPharmacyHead(request.UserName)
|
---|
65 | });
|
---|
66 | }
|
---|
67 |
|
---|
68 | [HttpGet("api/pharmacyhead/user")]
|
---|
69 | [Authorize]
|
---|
70 | public ActionResult GetCurrentUser()
|
---|
71 | {
|
---|
72 | return Ok(new LoginResult
|
---|
73 | {
|
---|
74 | UserName = User.Identity.Name,
|
---|
75 | Role = User.FindFirst(ClaimTypes.Role)?.Value ?? string.Empty,
|
---|
76 | OriginalUserName = User.FindFirst("OriginalUserName")?.Value
|
---|
77 | });
|
---|
78 | }
|
---|
79 |
|
---|
80 | [HttpPost("api/pharmacyhead/logout")]
|
---|
81 | [Authorize]
|
---|
82 | public ActionResult Logout()
|
---|
83 | {
|
---|
84 | // optionally "revoke" JWT token on the server side --> add the current token to a block-list
|
---|
85 | // https://github.com/auth0/node-jsonwebtoken/issues/375
|
---|
86 |
|
---|
87 | var userName = User.Identity.Name;
|
---|
88 | _jwtAuthManager.RemoveRefreshTokenByUserName(userName);
|
---|
89 | _logger.LogInformation($"User [{userName}] logged out the system.");
|
---|
90 | return Ok();
|
---|
91 | }
|
---|
92 |
|
---|
93 | [HttpPost("api/pharmacyhead/refresh-token")]
|
---|
94 | [Authorize]
|
---|
95 | public async Task<ActionResult> RefreshToken([FromBody] RefreshTokenRequest request)
|
---|
96 | {
|
---|
97 | try
|
---|
98 | {
|
---|
99 | var userName = User.Identity.Name;
|
---|
100 | _logger.LogInformation($"User [{userName}] is trying to refresh JWT token.");
|
---|
101 |
|
---|
102 | if (string.IsNullOrWhiteSpace(request.RefreshToken))
|
---|
103 | {
|
---|
104 | return Unauthorized();
|
---|
105 | }
|
---|
106 |
|
---|
107 | var accessToken = await HttpContext.GetTokenAsync("Bearer", "access_token");
|
---|
108 | var jwtResult = _jwtAuthManager.Refresh(request.RefreshToken, accessToken, DateTime.Now);
|
---|
109 | _logger.LogInformation($"User [{userName}] has refreshed JWT token.");
|
---|
110 | return Ok(new LoginResult
|
---|
111 | {
|
---|
112 | UserName = userName,
|
---|
113 | Role = User.FindFirst(ClaimTypes.Role)?.Value ?? string.Empty,
|
---|
114 | AccessToken = jwtResult.AccessToken,
|
---|
115 | RefreshToken = jwtResult.RefreshToken.TokenString
|
---|
116 | });
|
---|
117 | }
|
---|
118 | catch (SecurityTokenException e)
|
---|
119 | {
|
---|
120 | return Unauthorized(e.Message); // return 401 so that the client side can redirect the user to login page
|
---|
121 | }
|
---|
122 | }
|
---|
123 |
|
---|
124 | [HttpPost("api/pharmacyhead/impersonation")]
|
---|
125 | [Authorize(Roles = UserRoles.Admin)]
|
---|
126 | public ActionResult Impersonate([FromBody] ImpersonationRequest request)
|
---|
127 | {
|
---|
128 | var userName = User.Identity.Name;
|
---|
129 | _logger.LogInformation($"User [{userName}] is trying to impersonate [{request.UserName}].");
|
---|
130 |
|
---|
131 | var impersonatedRole = _authService.GetUserRole(request.UserName);
|
---|
132 | if (string.IsNullOrWhiteSpace(impersonatedRole))
|
---|
133 | {
|
---|
134 | _logger.LogInformation($"User [{userName}] failed to impersonate [{request.UserName}] due to the target user not found.");
|
---|
135 | return BadRequest($"The target user [{request.UserName}] is not found.");
|
---|
136 | }
|
---|
137 | if (impersonatedRole == UserRoles.Admin)
|
---|
138 | {
|
---|
139 | _logger.LogInformation($"User [{userName}] is not allowed to impersonate another Admin.");
|
---|
140 | return BadRequest("This action is not supported.");
|
---|
141 | }
|
---|
142 |
|
---|
143 | var claims = new[]
|
---|
144 | {
|
---|
145 | new Claim(ClaimTypes.Name,request.UserName),
|
---|
146 | new Claim(ClaimTypes.Role, impersonatedRole),
|
---|
147 | new Claim("OriginalUserName", userName)
|
---|
148 | };
|
---|
149 |
|
---|
150 | var jwtResult = _jwtAuthManager.GenerateTokens(request.UserName, claims, DateTime.Now);
|
---|
151 | _logger.LogInformation($"User [{request.UserName}] is impersonating [{request.UserName}] in the system.");
|
---|
152 | return Ok(new LoginResult
|
---|
153 | {
|
---|
154 | UserName = request.UserName,
|
---|
155 | Role = impersonatedRole,
|
---|
156 | OriginalUserName = userName,
|
---|
157 | AccessToken = jwtResult.AccessToken,
|
---|
158 | RefreshToken = jwtResult.RefreshToken.TokenString
|
---|
159 | });
|
---|
160 | }
|
---|
161 |
|
---|
162 | [HttpPost("api/pharmacyhead/stop-impersonation")]
|
---|
163 | public ActionResult StopImpersonation()
|
---|
164 | {
|
---|
165 | var userName = User.Identity.Name;
|
---|
166 | var originalUserName = User.FindFirst("OriginalUserName")?.Value;
|
---|
167 | if (string.IsNullOrWhiteSpace(originalUserName))
|
---|
168 | {
|
---|
169 | return BadRequest("You are not impersonating anyone.");
|
---|
170 | }
|
---|
171 | _logger.LogInformation($"User [{originalUserName}] is trying to stop impersonate [{userName}].");
|
---|
172 |
|
---|
173 | var role = _authService.GetUserRole(originalUserName);
|
---|
174 | var claims = new[]
|
---|
175 | {
|
---|
176 | new Claim(ClaimTypes.Name,originalUserName),
|
---|
177 | new Claim(ClaimTypes.Role, role)
|
---|
178 | };
|
---|
179 |
|
---|
180 | var jwtResult = _jwtAuthManager.GenerateTokens(originalUserName, claims, DateTime.Now);
|
---|
181 | _logger.LogInformation($"User [{originalUserName}] has stopped impersonation.");
|
---|
182 | return Ok(new LoginResult
|
---|
183 | {
|
---|
184 | UserName = originalUserName,
|
---|
185 | Role = role,
|
---|
186 | OriginalUserName = null,
|
---|
187 | AccessToken = jwtResult.AccessToken,
|
---|
188 | RefreshToken = jwtResult.RefreshToken.TokenString
|
---|
189 | });
|
---|
190 | }
|
---|
191 | }
|
---|
192 |
|
---|
193 | public class LoginRequest
|
---|
194 | {
|
---|
195 | [Required]
|
---|
196 | [JsonPropertyName("username")]
|
---|
197 | public string UserName { get; set; }
|
---|
198 |
|
---|
199 | [Required]
|
---|
200 | [JsonPropertyName("password")]
|
---|
201 | public string Password { get; set; }
|
---|
202 | }
|
---|
203 |
|
---|
204 | public class LoginResult
|
---|
205 | {
|
---|
206 | [JsonPropertyName("username")]
|
---|
207 | public string UserName { get; set; }
|
---|
208 |
|
---|
209 | [JsonPropertyName("role")]
|
---|
210 | public string Role { get; set; }
|
---|
211 |
|
---|
212 | [JsonPropertyName("originalUserName")]
|
---|
213 | public string OriginalUserName { get; set; }
|
---|
214 |
|
---|
215 | [JsonPropertyName("accessToken")]
|
---|
216 | public string AccessToken { get; set; }
|
---|
217 |
|
---|
218 | [JsonPropertyName("refreshToken")]
|
---|
219 | public string RefreshToken { get; set; }
|
---|
220 | [JsonPropertyName("head")]
|
---|
221 | public PharmacyHead Head { get; set; }
|
---|
222 | }
|
---|
223 |
|
---|
224 | public class RefreshTokenRequest
|
---|
225 | {
|
---|
226 | [JsonPropertyName("refreshToken")]
|
---|
227 | public string RefreshToken { get; set; }
|
---|
228 | }
|
---|
229 |
|
---|
230 | public class ImpersonationRequest
|
---|
231 | {
|
---|
232 | [JsonPropertyName("username")]
|
---|
233 | public string UserName { get; set; }
|
---|
234 | }
|
---|
235 | }
|
---|
236 |
|
---|