CoreTokenExtensions.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.IdentityModel.Tokens.Jwt;
  5. using System.Security.Claims;
  6. using Microsoft.IdentityModel.Tokens;
  7. using Microsoft.Identity.Client;
  8. using System.ComponentModel;
  9. using System.Threading.Tasks;
  10. using Azure.Security.KeyVault.Secrets;
  11. using Azure.Core;
  12. using Azure.Identity;
  13. using System.Net.Http;
  14. using System.Collections.Concurrent;
  15. using System.Diagnostics;
  16. namespace TEAMModelOS.SDK.Extension
  17. {
  18. public static class CoreTokenExtensions
  19. { //var issuer = Configuration.GetValue<string>("JwtSettings:Issuer");
  20. //var signKey = Configuration.GetValue<string>("JwtSettings:SignKey");
  21. private const string issuer = "account.teammodel";
  22. //Azure AD 租用戶識別碼(國際、大陸)
  23. private static List<string> tenantids = new List<string> { "73a2bcc5-fe99-4566-aa8a-07e7bb287df1", "4807e9cf-87b8-4174-aa5b-e76497d7392b" };
  24. private static ConcurrentDictionary<string, KeyVaultSecret> KeyVaultSecrets { get; } = new ConcurrentDictionary<string, KeyVaultSecret>();
  25. #region Access Token
  26. /// <summary>
  27. /// 產生AccessToken
  28. /// </summary>
  29. /// <param name="clientID"></param>
  30. /// <param name="location">服務位置,Global or China ...</param>
  31. /// <returns></returns>
  32. public static async ValueTask<AuthenticationResult> CreateAccessToken(string clientID, string clientSecret, string location)
  33. {
  34. //從金鑰庫取出秘密,此作法讓所有端直接刷新金鑰,無需傳送秘密,SPA更適用
  35. var secret = clientSecret ?? (await GetClientIDSecret(clientID, location)).Value;
  36. var sts = Enum.Parse<STSEndpoint>(location, true);
  37. IConfidentialClientApplication app;
  38. app = ConfidentialClientApplicationBuilder.Create(clientID)
  39. .WithClientSecret(secret)
  40. .WithAuthority(new Uri(sts.GetDescriptionText()))
  41. .Build();
  42. var scope = ((STSScope)sts).GetDescriptionText();
  43. var result = await app.AcquireTokenForClient(new[] { scope }).ExecuteAsync();
  44. return result;
  45. }
  46. /// <summary>
  47. /// 驗證是否為公司Azure發行金鑰,支援大陸國際
  48. /// </summary>
  49. /// <param name="token"></param>
  50. /// <returns></returns>
  51. public static bool ValidateAccessToken(JwtSecurityToken token)
  52. {
  53. try
  54. {
  55. if (token.Payload.TryGetValue("tid", out var value) && value is string tokenTenantId)
  56. {
  57. return tenantids.Contains(tokenTenantId);
  58. }
  59. return false;
  60. }
  61. catch (Exception)
  62. {
  63. return false;
  64. }
  65. }
  66. public static bool ValidateIdToken(string token, string salt)
  67. {
  68. try
  69. {
  70. var handler = new JwtSecurityTokenHandler();
  71. var validationParameters = new TokenValidationParameters
  72. {
  73. RequireExpirationTime = true,
  74. ValidateIssuer = false,
  75. ValidateAudience = false,
  76. IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)),
  77. ValidateLifetime = false,
  78. //LifetimeValidator = LifetimeValidator,
  79. ClockSkew = TimeSpan.Zero
  80. };
  81. ClaimsPrincipal principal = handler.ValidateToken(token, validationParameters, out SecurityToken securityToken);
  82. return true;
  83. }
  84. catch(Exception ex)
  85. {
  86. Trace.WriteLine(ex.Message);
  87. return false;
  88. }
  89. }
  90. #endregion
  91. private static async ValueTask<KeyVaultSecret> GetClientIDSecret(string clientID, string location)
  92. { //Azure 金鑰庫處理
  93. var s = await Task.Run(() =>
  94. {
  95. var secret = KeyVaultSecrets.GetOrAdd(clientID, (x) =>
  96. {
  97. try
  98. {
  99. var sts = Enum.Parse<CoreServiceClient>(location, true);
  100. var scrtetstring = sts.GetDescriptionText().Split(",");
  101. //TODO 之後驗證端點用KnownAuthorityHosts取代,此SDK版本無支援
  102. var secret = new ClientSecretCredential(scrtetstring[0], scrtetstring[1], scrtetstring[2], new TokenCredentialOptions() { AuthorityHost = new Uri(scrtetstring[3]) });
  103. var client = new SecretClient(new Uri(((KeyVaultEndpoint)sts).GetDescriptionText()), secret);
  104. var clientSecret = client.GetSecretAsync(clientID).ConfigureAwait(false);
  105. return clientSecret.GetAwaiter().GetResult();
  106. }
  107. catch
  108. {
  109. return null;
  110. }
  111. });
  112. return secret;
  113. });
  114. return s;
  115. }
  116. public static bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
  117. {
  118. return true;
  119. //if (expires != null)
  120. //{
  121. // if (DateTime.UtcNow < expires)
  122. // {
  123. // return true;
  124. // }
  125. //}
  126. //return false;
  127. }
  128. private enum STSEndpoint
  129. {
  130. [Description("https://login.chinacloudapi.cn/4807e9cf-87b8-4174-aa5b-e76497d7392b")]
  131. China,
  132. [Description("https://login.microsoftonline.com/73a2bcc5-fe99-4566-aa8a-07e7bb287df1")]
  133. Global
  134. }
  135. private enum STSScope
  136. {
  137. [Description("api://72643704-b2e7-4b26-b881-bd5865e7a7a5/.default")]
  138. China,
  139. [Description("api://8768b06f-c5c5-4b0c-abfb-d7ded354626d/.default")]
  140. Global
  141. }
  142. private enum KeyVaultEndpoint
  143. {
  144. [Description("https://corekeyvaultcn.vault.azure.cn/")]
  145. China,
  146. [Description("https://corekeyvaultjp.vault.azure.net/")]
  147. Global
  148. }
  149. private enum CoreServiceClient
  150. {
  151. [Description("4807e9cf-87b8-4174-aa5b-e76497d7392b,72643704-b2e7-4b26-b881-bd5865e7a7a5,tRYbDXtotEOe2Bbmo=[3h9Hbu_Trt:c6,https://login.partner.microsoftonline.cn")]
  152. China,
  153. [Description("73a2bcc5-fe99-4566-aa8a-07e7bb287df1,8768b06f-c5c5-4b0c-abfb-d7ded354626d,7=O./yws0L89WcEsece:9/4deJHP4E=F,https://login.microsoftonline.com/")]
  154. Global
  155. }
  156. }
  157. }