Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. using Newtonsoft.Json;
  2. using System.Net;
  3. namespace Diligent.WebAPI.Business.Services
  4. {
  5. public class UserService : IUserService
  6. {
  7. private readonly AuthorizationSettings _authSettings;
  8. private readonly UserManager<User> _userManager;
  9. private readonly IMapper _mapper;
  10. private readonly DatabaseContext _databaseContext;
  11. public UserService(IOptions<AuthorizationSettings> authSettings, UserManager<User> userManager, IMapper mapper, DatabaseContext databaseContext)
  12. {
  13. _authSettings = authSettings.Value;
  14. _userManager = userManager;
  15. _mapper = mapper;
  16. _databaseContext = databaseContext;
  17. }
  18. public async Task<IEnumerable<User?>> GetAll() =>
  19. await _userManager.Users.ToListAsync();
  20. public async Task<User?> GetById(int id) =>
  21. await _userManager.FindByIdAsync(id.ToString());
  22. public async Task CreateUser(CreateUserRequestDto model)
  23. {
  24. var user = _mapper.Map<User>(model);
  25. await _userManager.CreateAsync(user, model.Password);
  26. }
  27. private const string GoogleApiTokenInfoUrl = "https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={0}";
  28. private string[] SupportedClientsIds = { "734219382849-nvnulsu7ibfl4bk3n164bgb7c1h5dgca.apps.googleusercontent.com" };
  29. private bool IsTokenValid(string providerToken)
  30. {
  31. var httpClient = new HttpClient();
  32. var requestUri = new Uri(string.Format(GoogleApiTokenInfoUrl, providerToken));
  33. HttpResponseMessage httpResponseMessage;
  34. try
  35. {
  36. httpResponseMessage = httpClient.GetAsync(requestUri).Result;
  37. }
  38. catch (Exception ex)
  39. {
  40. return false;
  41. }
  42. if (httpResponseMessage.StatusCode != HttpStatusCode.OK)
  43. {
  44. return false;
  45. }
  46. var response = httpResponseMessage.Content.ReadAsStringAsync().Result;
  47. var googleApiTokenInfo = JsonConvert.DeserializeObject<GoogleApiTokenInfo>(response);
  48. if (!SupportedClientsIds.Contains(googleApiTokenInfo.aud))
  49. {
  50. return false;
  51. }
  52. return true;
  53. //return new
  54. //{
  55. // Email = googleApiTokenInfo.email,
  56. // FirstName = googleApiTokenInfo.given_name,
  57. // LastName = googleApiTokenInfo.family_name,
  58. // Locale = googleApiTokenInfo.locale,
  59. // Name = googleApiTokenInfo.name,
  60. // ProviderUserId = googleApiTokenInfo.sub
  61. //};
  62. }
  63. public async Task<ServiceResponseDTO<AuthenticateResponseDto>> Authenticate(AuthenticateRequestDto model)
  64. {
  65. var user = await _userManager.FindByNameAsync(model.Username);
  66. // return null if user not found
  67. if (user == null)
  68. {
  69. return new ServiceResponseDTO<AuthenticateResponseDto>
  70. {
  71. IsError = true,
  72. ErrorMessage = "Username is not valid"
  73. };
  74. }
  75. var result = await _userManager.CheckPasswordAsync(user, model.Password);
  76. // password is not correct
  77. if (!result)
  78. {
  79. await _userManager.AccessFailedAsync(user);
  80. return new ServiceResponseDTO<AuthenticateResponseDto>
  81. {
  82. IsError = true,
  83. ErrorMessage = "Password is not correct"
  84. };
  85. }
  86. return await GenerateToken(user);
  87. }
  88. public async Task<ServiceResponseDTO<AuthenticateResponseDto>> Authenticate(GoogleApiModel model)
  89. {
  90. if (!IsTokenValid(model.Token))
  91. {
  92. return new ServiceResponseDTO<AuthenticateResponseDto>
  93. {
  94. IsError = true,
  95. ErrorMessage = "Invalid Google Api Token"
  96. };
  97. }
  98. var user = await _userManager.FindByEmailAsync(model.User.email);
  99. // return null if user not found
  100. if (user == null)
  101. {
  102. return new ServiceResponseDTO<AuthenticateResponseDto>
  103. {
  104. IsError = true,
  105. ErrorMessage = $"User with email {model.User.email} does not exist in database"
  106. };
  107. }
  108. return await GenerateToken(user);
  109. }
  110. private async Task<ServiceResponseDTO<AuthenticateResponseDto>> GenerateToken(User user)
  111. {
  112. var isLocked = await _userManager.IsLockedOutAsync(user);
  113. if (isLocked)
  114. return new ServiceResponseDTO<AuthenticateResponseDto>
  115. {
  116. IsError = true,
  117. ErrorMessage = "The account is locked out"
  118. };
  119. // authentication successful so generate jwt token
  120. var token = await GenerateJwtToken(user, true);
  121. var data = new AuthenticateResponseDto
  122. {
  123. Id = user.Id,
  124. Username = user.UserName,
  125. FirstName = user.FirstName,
  126. LastName = user.LastName,
  127. Token = token,
  128. RefreshToken = token
  129. };
  130. return new ServiceResponseDTO<AuthenticateResponseDto>
  131. {
  132. Data = data
  133. };
  134. }
  135. private async Task<string> GenerateJwtToken(User user, bool authenticate = false)
  136. {
  137. // generate token that is valid for 7 days
  138. var tokenHandler = new JwtSecurityTokenHandler();
  139. var key = Encoding.ASCII.GetBytes(_authSettings.Secret);
  140. var tokenDescriptor = new SecurityTokenDescriptor
  141. {
  142. Subject = new ClaimsIdentity(new[] {
  143. new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
  144. new Claim("id", user.Id.ToString())
  145. }),
  146. Expires = DateTime.UtcNow.AddMinutes(_authSettings.JwtExpiredTime),
  147. SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
  148. };
  149. var token = tokenHandler.CreateToken(tokenDescriptor);
  150. var writedToken = tokenHandler.WriteToken(token);
  151. var refreshToken = new RefreshToken
  152. {
  153. Token = writedToken,
  154. JwtId = token.Id,
  155. UserId = user.Id,
  156. User = user,
  157. CreationDate = DateTime.UtcNow,
  158. ExpiryDate = DateTime.UtcNow.AddMinutes(_authSettings.JwtRefreshExpiredTime)
  159. };
  160. var existRefreshToken = await _databaseContext.RefreshTokens.Where(x => x.Id == user.Id).FirstOrDefaultAsync();
  161. if(existRefreshToken != null)
  162. {
  163. existRefreshToken.Token = writedToken;
  164. existRefreshToken.JwtId = token.Id;
  165. existRefreshToken.CreationDate = DateTime.UtcNow;
  166. existRefreshToken.ExpiryDate = DateTime.UtcNow.AddMinutes(_authSettings.JwtRefreshExpiredTime);
  167. if (authenticate)
  168. {
  169. existRefreshToken.Used = false;
  170. existRefreshToken.Invalidated = false;
  171. }
  172. //_databaseContext.RefreshTokens.Update(existRefreshToken);
  173. await UpdateRefreshToken(existRefreshToken);
  174. }
  175. else
  176. {
  177. await _databaseContext.RefreshTokens.AddAsync(refreshToken);
  178. }
  179. await _databaseContext.SaveChangesAsync();
  180. return writedToken;
  181. }
  182. public async Task<RefreshTokenResultDto> RefreshTokenAsync(RefreshTokenRequestDto model)
  183. {
  184. var validatedToken = GetPrincipalFromToken(model.Token);
  185. if (validatedToken == null)
  186. {
  187. return new RefreshTokenResultDto { Error = "Invalid token" };
  188. }
  189. var expiryDateUnix = long.Parse(validatedToken.Claims.Single(x => x.Type == JwtRegisteredClaimNames.Exp).Value);
  190. var expiryDateTimeUtc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
  191. .AddSeconds(expiryDateUnix);
  192. if (expiryDateTimeUtc > DateTime.UtcNow)
  193. {
  194. return new RefreshTokenResultDto { Error = "This token hasn't expired yet" };
  195. }
  196. var jti = validatedToken.Claims.Single(x => x.Type == JwtRegisteredClaimNames.Jti).Value;
  197. var storedRefreshToken = await _databaseContext.RefreshTokens.SingleOrDefaultAsync(x => x.Token == model.RefreshToken);
  198. if (storedRefreshToken == null)
  199. {
  200. return new RefreshTokenResultDto { Error = "This refresh token does not exist" };
  201. }
  202. if (DateTime.UtcNow > storedRefreshToken.ExpiryDate)
  203. {
  204. return new RefreshTokenResultDto { Error = "This refresh token has expired" };
  205. }
  206. if (storedRefreshToken.Invalidated)
  207. {
  208. return new RefreshTokenResultDto { Error = "This refresh token has been invalidated" };
  209. }
  210. if (storedRefreshToken.Used)
  211. {
  212. return new RefreshTokenResultDto { Error = "This refresh token has been used" };
  213. }
  214. if (storedRefreshToken.JwtId != jti)
  215. {
  216. return new RefreshTokenResultDto { Error = "This refresh token does not match this JWT" };
  217. }
  218. storedRefreshToken.Used = true;
  219. _databaseContext.RefreshTokens.Update(storedRefreshToken);
  220. await _databaseContext.SaveChangesAsync();
  221. var user = await _userManager.FindByIdAsync(validatedToken.Claims.Single(x => x.Type == "id").Value);
  222. var token = await GenerateJwtToken(user);
  223. return new RefreshTokenResultDto
  224. {
  225. Token = token
  226. };
  227. }
  228. private ClaimsPrincipal GetPrincipalFromToken(string token)
  229. {
  230. var tokenHandler = new JwtSecurityTokenHandler();
  231. var key = Encoding.ASCII.GetBytes(_authSettings.Secret);
  232. var tokenValidationParameters = new TokenValidationParameters
  233. {
  234. ValidateIssuerSigningKey = true,
  235. IssuerSigningKey = new SymmetricSecurityKey(key),
  236. ValidateIssuer = false,
  237. ValidateAudience = false,
  238. RequireExpirationTime = false,
  239. ValidateLifetime = true,
  240. // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
  241. //ClockSkew = TimeSpan.Zero
  242. };
  243. try
  244. {
  245. var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out var validatedToken);
  246. if (!IsJwtWithValidSecurityAlgorithm(validatedToken))
  247. {
  248. return null;
  249. }
  250. return principal;
  251. }
  252. catch (Exception ex)
  253. {
  254. return null;
  255. }
  256. }
  257. private bool IsJwtWithValidSecurityAlgorithm(SecurityToken validatedToken)
  258. {
  259. return (validatedToken is JwtSecurityToken jwtSecurityToken) &&
  260. jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256,
  261. StringComparison.InvariantCultureIgnoreCase);
  262. }
  263. public async Task<RefreshToken?> GetRefreshTokenByUserId(int userId)
  264. {
  265. return await _databaseContext.RefreshTokens.Where(x => x.UserId == userId).FirstOrDefaultAsync();
  266. }
  267. public async Task UpdateRefreshToken(RefreshToken refreshToken)
  268. {
  269. _databaseContext.RefreshTokens.Update(refreshToken);
  270. await _databaseContext.SaveChangesAsync();
  271. }
  272. }
  273. }