TokenAuthController.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. using Abp.Runtime.Security;
  2. using Abp.UI;
  3. using Microsoft.AspNetCore.Identity;
  4. using Microsoft.AspNetCore.Mvc;
  5. using System.IdentityModel.Tokens.Jwt;
  6. using System.Security.Claims;
  7. using VberAdmin.Authentication.External;
  8. using VberAdmin.Authentication.JwtBearer;
  9. using VberAdmin.Authorization;
  10. using VberAdmin.Authorization.Users;
  11. using VberAdmin.Models.TokenAuth;
  12. using VberZero.AppService.Authorization;
  13. using VberZero.Authorization;
  14. using VberZero.Authorization.Users;
  15. using VberZero.BaseSystem.Users;
  16. using VberZero.MultiTenancy;
  17. namespace VberAdmin.Controllers;
  18. [Route("api/[controller]/[action]")]
  19. public class TokenAuthController : VberAdminControllerBase
  20. {
  21. private readonly LogInManager _logInManager;
  22. private readonly ITenantCache _tenantCache;
  23. private readonly VzLoginResultTypeHelper _vzLoginResultTypeHelper;
  24. private readonly TokenAuthConfiguration _configuration;
  25. private readonly IExternalAuthConfiguration _externalAuthConfiguration;
  26. private readonly IExternalAuthManager _externalAuthManager;
  27. private readonly UserRegistrationManager _userRegistrationManager;
  28. private readonly UserManager _userManager;
  29. public TokenAuthController(
  30. LogInManager logInManager,
  31. ITenantCache tenantCache,
  32. VzLoginResultTypeHelper vzLoginResultTypeHelper,
  33. TokenAuthConfiguration configuration,
  34. IExternalAuthConfiguration externalAuthConfiguration,
  35. IExternalAuthManager externalAuthManager,
  36. UserRegistrationManager userRegistrationManager, UserManager userManager)
  37. {
  38. _logInManager = logInManager;
  39. _tenantCache = tenantCache;
  40. _vzLoginResultTypeHelper = vzLoginResultTypeHelper;
  41. _configuration = configuration;
  42. _externalAuthConfiguration = externalAuthConfiguration;
  43. _externalAuthManager = externalAuthManager;
  44. _userRegistrationManager = userRegistrationManager;
  45. _userManager = userManager;
  46. }
  47. [HttpPost]
  48. public async Task<AuthenticateResultModel> Authenticate([FromBody] AuthenticateModel model)
  49. {
  50. var tenancyName = await _userManager.GetTenancyNameAsync(model.UsernameOrEmailOrPhone);
  51. var loginResult = await GetLoginResultAsync(
  52. model.UsernameOrEmailOrPhone,
  53. model.Password,
  54. tenancyName
  55. );
  56. var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
  57. return new AuthenticateResultModel
  58. {
  59. AccessToken = accessToken,
  60. EncryptedAccessToken = GetEncryptedAccessToken(accessToken),
  61. ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds,
  62. UserId = loginResult.User.Id
  63. };
  64. }
  65. [HttpGet]
  66. public List<ExternalLoginProviderInfoModel> GetExternalAuthenticationProviders()
  67. {
  68. return ObjectMapper.Map<List<ExternalLoginProviderInfoModel>>(_externalAuthConfiguration.Providers);
  69. }
  70. [HttpPost]
  71. public async Task<ExternalAuthenticateResultModel> ExternalAuthenticate([FromBody] ExternalAuthenticateModel model)
  72. {
  73. var externalUser = await GetExternalUserInfo(model);
  74. var loginResult = await _logInManager.LoginAsync(new UserLoginInfo(model.AuthProvider, model.ProviderKey, model.AuthProvider), GetTenancyNameOrNull());
  75. switch (loginResult.Result)
  76. {
  77. case VzLoginResultType.Success:
  78. {
  79. var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
  80. return new ExternalAuthenticateResultModel
  81. {
  82. AccessToken = accessToken,
  83. EncryptedAccessToken = GetEncryptedAccessToken(accessToken),
  84. ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds
  85. };
  86. }
  87. case VzLoginResultType.UnknownExternalLogin:
  88. {
  89. var newUser = await RegisterExternalUserAsync(externalUser);
  90. if (!newUser.IsActive)
  91. {
  92. return new ExternalAuthenticateResultModel
  93. {
  94. WaitingForActivation = true
  95. };
  96. }
  97. // Try to login again with newly registered user!
  98. loginResult = await _logInManager.LoginAsync(new UserLoginInfo(model.AuthProvider, model.ProviderKey, model.AuthProvider), GetTenancyNameOrNull());
  99. if (loginResult.Result != VzLoginResultType.Success)
  100. {
  101. throw _vzLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(
  102. loginResult.Result,
  103. model.ProviderKey,
  104. GetTenancyNameOrNull()
  105. );
  106. }
  107. var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
  108. return new ExternalAuthenticateResultModel
  109. {
  110. AccessToken = accessToken,
  111. EncryptedAccessToken = GetEncryptedAccessToken(accessToken),
  112. ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds
  113. };
  114. }
  115. default:
  116. {
  117. throw _vzLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(
  118. loginResult.Result,
  119. model.ProviderKey,
  120. GetTenancyNameOrNull()
  121. );
  122. }
  123. }
  124. }
  125. private async Task<User> RegisterExternalUserAsync(ExternalAuthUserInfo externalUser)
  126. {
  127. var user = await _userRegistrationManager.RegisterAsync(
  128. externalUser.Name,
  129. externalUser.Surname,
  130. externalUser.EmailAddress,
  131. externalUser.EmailAddress,
  132. VberZero.BaseSystem.Users.User.CreateRandomPassword(),
  133. true
  134. );
  135. user.Logins = new List<UserLogin>
  136. {
  137. new UserLogin
  138. {
  139. LoginProvider = externalUser.Provider,
  140. ProviderKey = externalUser.ProviderKey,
  141. TenantId = user.TenantId
  142. }
  143. };
  144. await CurrentUnitOfWork.SaveChangesAsync();
  145. return user;
  146. }
  147. private async Task<ExternalAuthUserInfo> GetExternalUserInfo(ExternalAuthenticateModel model)
  148. {
  149. var userInfo = await _externalAuthManager.GetUserInfo(model.AuthProvider, model.ProviderAccessCode);
  150. if (userInfo.ProviderKey != model.ProviderKey)
  151. {
  152. throw new UserFriendlyException(L("CouldNotValidateExternalUser"));
  153. }
  154. return userInfo;
  155. }
  156. private string GetTenancyNameOrNull()
  157. {
  158. if (!AbpSession.TenantId.HasValue)
  159. {
  160. return null;
  161. }
  162. return _tenantCache.GetOrNull(AbpSession.TenantId.Value)?.TenancyName;
  163. }
  164. private async Task<AbpLoginResult> GetLoginResultAsync(string usernameOrEmailAddress, string password, string tenancyName)
  165. {
  166. var loginResult = await _logInManager.LoginAsync(usernameOrEmailAddress, password, tenancyName);
  167. switch (loginResult.Result)
  168. {
  169. case VzLoginResultType.Success:
  170. return loginResult;
  171. default:
  172. throw _vzLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(loginResult.Result, usernameOrEmailAddress, tenancyName);
  173. }
  174. }
  175. private string CreateAccessToken(IEnumerable<Claim> claims, TimeSpan? expiration = null)
  176. {
  177. var now = DateTime.UtcNow;
  178. var jwtSecurityToken = new JwtSecurityToken(
  179. issuer: _configuration.Issuer,
  180. audience: _configuration.Audience,
  181. claims: claims,
  182. notBefore: now,
  183. expires: now.Add(expiration ?? _configuration.Expiration),
  184. signingCredentials: _configuration.SigningCredentials
  185. );
  186. return new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
  187. }
  188. private static List<Claim> CreateJwtClaims(ClaimsIdentity identity)
  189. {
  190. var claims = identity.Claims.ToList();
  191. var nameIdClaim = claims.First(c => c.Type == ClaimTypes.NameIdentifier);
  192. // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
  193. claims.AddRange(new[]
  194. {
  195. new Claim(JwtRegisteredClaimNames.Sub, nameIdClaim.Value),
  196. new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
  197. new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.Now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
  198. });
  199. return claims;
  200. }
  201. private string GetEncryptedAccessToken(string accessToken)
  202. {
  203. return SimpleStringCipher.Instance.Encrypt(accessToken);
  204. }
  205. }