JAELYS 4 роки тому
батько
коміт
2b20520e02

+ 171 - 0
TEAMModelOS.SDK/Extension/CoreTokenExtensions.cs

@@ -0,0 +1,171 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using Microsoft.IdentityModel.Tokens;
+using Microsoft.Identity.Client;
+using System.ComponentModel;
+using System.Threading.Tasks;
+using Azure.Security.KeyVault.Secrets;
+using Azure.Core;
+using Azure.Identity;
+using System.Net.Http;
+using System.Collections.Concurrent;
+using System.Diagnostics;
+
+namespace TEAMModelOS.SDK.Extension
+{
+    public static class CoreTokenExtensions
+    {       //var issuer = Configuration.GetValue<string>("JwtSettings:Issuer");
+            //var signKey = Configuration.GetValue<string>("JwtSettings:SignKey");
+        private const string issuer = "account.teammodel";
+        //Azure AD 租用戶識別碼(國際、大陸)
+        private static List<string> tenantids = new List<string> { "73a2bcc5-fe99-4566-aa8a-07e7bb287df1", "4807e9cf-87b8-4174-aa5b-e76497d7392b" };
+        private static ConcurrentDictionary<string, KeyVaultSecret> KeyVaultSecrets { get; } = new ConcurrentDictionary<string, KeyVaultSecret>();
+              
+
+        #region  Access Token
+        /// <summary>
+        /// 產生AccessToken
+        /// </summary>
+        /// <param name="clientID"></param>
+        /// <param name="location">服務位置,Global or China ...</param>
+        /// <returns></returns>
+        public static async ValueTask<AuthenticationResult> CreateAccessToken(string clientID, string clientSecret, string location)
+        {
+            //從金鑰庫取出秘密,此作法讓所有端直接刷新金鑰,無需傳送秘密,SPA更適用
+            var secret = clientSecret ?? (await GetClientIDSecret(clientID, location)).Value;
+
+            var sts = Enum.Parse<STSEndpoint>(location, true);
+
+            IConfidentialClientApplication app;
+            app = ConfidentialClientApplicationBuilder.Create(clientID)
+                                                      .WithClientSecret(secret)
+                                                      .WithAuthority(new Uri(sts.GetDescriptionText()))
+                                                      .Build();
+            var scope = ((STSScope)sts).GetDescriptionText();
+            var result = await app.AcquireTokenForClient(new[] { scope }).ExecuteAsync();
+            return result;
+        }
+
+        /// <summary>
+        /// 驗證是否為公司Azure發行金鑰,支援大陸國際
+        /// </summary>
+        /// <param name="token"></param>
+        /// <returns></returns>
+        public static bool ValidateAccessToken(JwtSecurityToken token)
+        {
+            try
+            {
+                if (token.Payload.TryGetValue("tid", out var value) && value is string tokenTenantId)
+                {
+                    return tenantids.Contains(tokenTenantId);
+                }
+                return false;
+            }
+            catch (Exception)
+            {
+                return false;
+            }
+        }
+
+        public static bool ValidateIdToken(string token, string salt)
+        {
+            try
+            {
+                var handler = new JwtSecurityTokenHandler();
+                var validationParameters = new TokenValidationParameters
+                {
+                    RequireExpirationTime = true,
+                    ValidateIssuer = false,
+                    ValidateAudience = false,
+                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)),
+                    ValidateLifetime = false,
+                    //LifetimeValidator = LifetimeValidator,
+                    ClockSkew = TimeSpan.Zero                    
+                };
+                ClaimsPrincipal principal = handler.ValidateToken(token, validationParameters, out SecurityToken securityToken);
+                return true;
+            }
+            catch(Exception ex)
+            {
+                Trace.WriteLine(ex.Message);
+                return false;
+            }
+        }
+        #endregion
+
+        private static async ValueTask<KeyVaultSecret> GetClientIDSecret(string clientID, string location)
+        {   //Azure 金鑰庫處理
+            var s = await Task.Run(() =>
+            {
+                var secret = KeyVaultSecrets.GetOrAdd(clientID, (x) =>
+                {
+                    try
+                    {
+                        var sts = Enum.Parse<CoreServiceClient>(location, true);
+                        var scrtetstring = sts.GetDescriptionText().Split(",");
+                        //TODO 之後驗證端點用KnownAuthorityHosts取代,此SDK版本無支援
+                        var secret = new ClientSecretCredential(scrtetstring[0], scrtetstring[1], scrtetstring[2], new TokenCredentialOptions() { AuthorityHost = new Uri(scrtetstring[3]) });
+                        var client = new SecretClient(new Uri(((KeyVaultEndpoint)sts).GetDescriptionText()), secret);
+                        var clientSecret = client.GetSecretAsync(clientID).ConfigureAwait(false);
+                        return clientSecret.GetAwaiter().GetResult();
+                    }
+                    catch
+                    {
+                        return null;
+                    }
+                });
+                return secret;
+            });
+            return s;
+        }
+
+        public static bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
+        {
+            return true;
+            //if (expires != null)
+            //{
+            //    if (DateTime.UtcNow < expires)
+            //    {
+            //        return true;
+            //    }
+            //}
+
+            //return false;
+        }
+
+        private enum STSEndpoint
+        {
+            [Description("https://login.chinacloudapi.cn/4807e9cf-87b8-4174-aa5b-e76497d7392b")]
+            China,
+            [Description("https://login.microsoftonline.com/73a2bcc5-fe99-4566-aa8a-07e7bb287df1")]
+            Global
+        }
+
+        private enum STSScope
+        {
+            [Description("api://72643704-b2e7-4b26-b881-bd5865e7a7a5/.default")]
+            China,
+            [Description("api://8768b06f-c5c5-4b0c-abfb-d7ded354626d/.default")]
+            Global
+        }
+
+        private enum KeyVaultEndpoint
+        {
+            [Description("https://corekeyvaultcn.vault.azure.cn/")]
+            China,
+            [Description("https://corekeyvaultjp.vault.azure.net/")]
+            Global
+        }
+
+        private enum CoreServiceClient
+        {
+            [Description("4807e9cf-87b8-4174-aa5b-e76497d7392b,72643704-b2e7-4b26-b881-bd5865e7a7a5,tRYbDXtotEOe2Bbmo=[3h9Hbu_Trt:c6,https://login.partner.microsoftonline.cn")]
+            China,
+            [Description("73a2bcc5-fe99-4566-aa8a-07e7bb287df1,8768b06f-c5c5-4b0c-abfb-d7ded354626d,7=O./yws0L89WcEsece:9/4deJHP4E=F,https://login.microsoftonline.com/")]
+            Global
+        }
+    }
+}

+ 3 - 0
TEAMModelOS.SDK/TEAMModelOS.SDK.csproj

@@ -13,7 +13,9 @@
   <ItemGroup>
     <PackageReference Include="AspectCore.Extensions.Reflection" Version="2.2.0" />
     <PackageReference Include="Azure.Cosmos" Version="4.0.0-preview3" />
+    <PackageReference Include="Azure.Identity" Version="1.4.0" />
     <PackageReference Include="Azure.Messaging.ServiceBus" Version="7.1.1" />
+    <PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.1.0" />
     <PackageReference Include="Azure.Storage.Blobs.Batch" Version="12.5.1" />
     <PackageReference Include="Azure.Storage.Queues" Version="12.6.1" />
     <PackageReference Include="ClouDASLibx" Version="1.2.7" />
@@ -22,6 +24,7 @@
     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.10" />
     <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
     <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="5.0.4" />
+    <PackageReference Include="Microsoft.Identity.Client" Version="4.32.1" />
     <PackageReference Include="StackExchange.Redis" Version="2.2.4" />
     <PackageReference Include="SvgNet" Version="2.1.1" />
     <PackageReference Include="System.Drawing.Common" Version="5.0.2" />