574 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			574 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C#
		
	
	
using Azure.Core;
 | 
						|
using IdentityModel;
 | 
						|
using IdentityModel.Client;
 | 
						|
using IRaCIS.Core.Application.Service.OAuth;
 | 
						|
using MassTransit;
 | 
						|
using Microsoft.AspNetCore.Authorization;
 | 
						|
using Microsoft.AspNetCore.Builder;
 | 
						|
using Microsoft.AspNetCore.Http;
 | 
						|
using Newtonsoft.Json;
 | 
						|
using Newtonsoft.Json.Linq;
 | 
						|
using NPOI.SS.Formula.Functions;
 | 
						|
using Org.BouncyCastle.Utilities.Net;
 | 
						|
using RestSharp;
 | 
						|
using System;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.Linq;
 | 
						|
using System.Net;
 | 
						|
using System.Net.Http;
 | 
						|
using System.Security.Cryptography;
 | 
						|
using System.Text;
 | 
						|
using System.Text.Json;
 | 
						|
using System.Threading.Tasks;
 | 
						|
 | 
						|
namespace IRaCIS.Core.Application.Service
 | 
						|
{
 | 
						|
    public class OAuthService : ServiceBase
 | 
						|
    {
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        #region authorization_code 原生  PKCE 
 | 
						|
 | 
						|
        public IResponseOutput TestPCKEOrgin()
 | 
						|
        {
 | 
						|
            // 1. 生成 code_verifier 和 code_challenge
 | 
						|
            string codeVerifier = "QMSBBxTQrpKPscvNNfmaQfmyk5Wd33GZS1FKSo3Shv8w-59vW1iTSlgAznYojkYv2DgR4XhTqySsBnDPq0";
 | 
						|
 | 
						|
 | 
						|
            string codeChallenge = PkceUtil.GenerateCodeChallenge(codeVerifier);
 | 
						|
 | 
						|
            Console.WriteLine(codeVerifier);
 | 
						|
 | 
						|
            string clientId = "aj34vqrpvz8olsbxwtcog";
 | 
						|
            string redirectUri = "http://localhost:6100/OAuth/TestPKCECallBack";
 | 
						|
            string state = "123456";
 | 
						|
 | 
						|
            // 构造请求的 URL
 | 
						|
            string authorizationUrl = $"https://logto.test.extimaging.com/oidc/auth" +
 | 
						|
                $"?client_id={clientId}" +
 | 
						|
                $"&redirect_uri={Uri.EscapeDataString(redirectUri)}" +
 | 
						|
                $"&response_type=code" +
 | 
						|
                $"&scope=openid profile email phone" +
 | 
						|
                $"&code_challenge={codeChallenge}" +
 | 
						|
                $"&code_challenge_method=S256" +
 | 
						|
                $"&state={state}";
 | 
						|
 | 
						|
            Console.WriteLine("请将以下 URL 复制到浏览器中,完成登录后获取 code:");
 | 
						|
 | 
						|
            Console.WriteLine(authorizationUrl);
 | 
						|
 | 
						|
            return ResponseOutput.Ok();
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        [AllowAnonymous]
 | 
						|
        [RoutePattern(HttpMethod = "Get")]
 | 
						|
        public async Task<IResponseOutput> TestPKCECallBackAsync(string code)
 | 
						|
        {
 | 
						|
            var httpClient = new HttpClient();
 | 
						|
            var disco = await httpClient.GetDiscoveryDocumentAsync("https://logto.test.extimaging.com/oidc");
 | 
						|
            if (disco.IsError)
 | 
						|
            {
 | 
						|
                Console.WriteLine(disco.Error);
 | 
						|
            }
 | 
						|
 | 
						|
            string codeVerifier = "QMSBBxTQrpKPscvNNfmaQfmyk5Wd33GZS1FKSo3Shv8w-59vW1iTSlgAznYojkYv2DgR4XhTqySsBnDPq0";
 | 
						|
            // OIDC 配置,替换为您的 OIDC 提供者的配置
 | 
						|
            string tokenEndpoint = "https://logto.test.extimaging.com/oidc/token"; // 替换为实际 token 端点
 | 
						|
            string clientId = "aj34vqrpvz8olsbxwtcog";
 | 
						|
            string redirectUri = "http://localhost:6100/OAuth/TestPKCECallBack"; // 替换为前端的回调 URL
 | 
						|
 | 
						|
            var baseUrl = "https://logto.test.extimaging.com";
 | 
						|
            var opts = new RestClientOptions(baseUrl);
 | 
						|
            using var client = new RestClient(opts);
 | 
						|
 | 
						|
            //https://blog.logto.io/troubleshoot-invalid-grant-error/
 | 
						|
            var request = new RestRequest("oidc/token", Method.Post)
 | 
						|
            .AddHeader("Content-Type", "application/x-www-form-urlencoded");
 | 
						|
 | 
						|
            request.AddParameter("grant_type", "authorization_code")
 | 
						|
                    .AddParameter("code", code)
 | 
						|
                    .AddParameter("redirect_uri", redirectUri)
 | 
						|
                    .AddParameter("client_id", clientId)
 | 
						|
                    .AddParameter("code_verifier", codeVerifier); // 使用 PKCE
 | 
						|
 | 
						|
 | 
						|
            // 发送请求并获取响应
 | 
						|
            var response = await client.ExecuteAsync<LogtoTokenResponse>(request);
 | 
						|
 | 
						|
            if (response.StatusCode == HttpStatusCode.OK)
 | 
						|
            {
 | 
						|
                var tokenResponse = response.Data;
 | 
						|
 | 
						|
                Console.WriteLine(tokenResponse.ToJsonStr());
 | 
						|
 | 
						|
                var userInfoRequest = new RestRequest($"oidc/me", Method.Get)
 | 
						|
                    .AddHeader("Authorization", $"Bearer {tokenResponse.AccessToken}");
 | 
						|
 | 
						|
                var userResponse = await client.ExecuteAsync(userInfoRequest);
 | 
						|
 | 
						|
                Console.WriteLine(userResponse.Content);
 | 
						|
 | 
						|
                //结束回话
 | 
						|
 | 
						|
                var endUrl = new RequestUrl(disco.EndSessionEndpoint).CreateEndSessionUrl(tokenResponse.IdToken, "http://localhost:6100/OAuth/TestPCKEOrgin");
 | 
						|
 | 
						|
                var _endHttpClient = new HttpClient();
 | 
						|
 | 
						|
                var dd = await _endHttpClient.GetAsync(endUrl);
 | 
						|
            }
 | 
						|
 | 
						|
            return ResponseOutput.Ok();
 | 
						|
        }
 | 
						|
        #endregion
 | 
						|
 | 
						|
 | 
						|
        #region authorization_code OidcClient  PKCE 
 | 
						|
 | 
						|
        [AllowAnonymous]
 | 
						|
        [RoutePattern(HttpMethod = "Get")]
 | 
						|
        public IResponseOutput TestPKCE()
 | 
						|
        {
 | 
						|
            // 1. 生成 code_verifier 和 code_challenge
 | 
						|
            string codeVerifier = "QMSBBxTQrpKPscvNNfmaQfmyk5Wd33GZS1FKSo3Shv8w-59vW1iTSlgAznYojkYv2DgR4XhTqySsBnDPq0";
 | 
						|
 | 
						|
 | 
						|
            string codeChallenge = PkceUtil.GenerateCodeChallenge(codeVerifier);
 | 
						|
 | 
						|
            Console.WriteLine(codeVerifier);
 | 
						|
 | 
						|
            string clientId = "aj34vqrpvz8olsbxwtcog";
 | 
						|
            string redirectUri = "http://localhost:6100/OAuth/TestOidcClientPKCECallBack";
 | 
						|
            string state = "123456";
 | 
						|
 | 
						|
            // 构造请求的 URL
 | 
						|
            string authorizationUrl = $"https://logto.test.extimaging.com/oidc/auth" +
 | 
						|
                $"?client_id={clientId}" +
 | 
						|
                $"&redirect_uri={Uri.EscapeDataString(redirectUri)}" +
 | 
						|
                $"&response_type=code" +
 | 
						|
                $"&scope=openid profile email phone" +
 | 
						|
                $"&code_challenge={codeChallenge}" +
 | 
						|
                $"&code_challenge_method=S256" +
 | 
						|
                $"&state={state}";
 | 
						|
 | 
						|
            Console.WriteLine("请将以下 URL 复制到浏览器中,完成登录后获取 code:");
 | 
						|
 | 
						|
            Console.WriteLine(authorizationUrl);
 | 
						|
 | 
						|
            return ResponseOutput.Ok();
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        [AllowAnonymous]
 | 
						|
        [RoutePattern(HttpMethod = "Get")]
 | 
						|
        public async Task<IResponseOutput> TestOidcClientPKCECallBackAsync(string code)
 | 
						|
        {
 | 
						|
            //使用IdentityModel.OidcClient 测试
 | 
						|
            var client = new HttpClient();
 | 
						|
            var disco = await client.GetDiscoveryDocumentAsync("https://logto.test.extimaging.com/oidc");
 | 
						|
            if (disco.IsError)
 | 
						|
            {
 | 
						|
                Console.WriteLine(disco.Error);
 | 
						|
            }
 | 
						|
 | 
						|
            // OIDC 配置,替换为您的 OIDC 提供者的配置
 | 
						|
            string clientId = "aj34vqrpvz8olsbxwtcog";
 | 
						|
            string codeVerifier = "QMSBBxTQrpKPscvNNfmaQfmyk5Wd33GZS1FKSo3Shv8w-59vW1iTSlgAznYojkYv2DgR4XhTqySsBnDPq0";
 | 
						|
            string redirectUri = "http://localhost:6100/OAuth/TestOidcClientPKCECallBack"; // 替换为前端的回调 URL
 | 
						|
 | 
						|
            var requestBody = new Dictionary<string, string>
 | 
						|
                                {
 | 
						|
                                    { "grant_type", "authorization_code" },
 | 
						|
                                    { "code", code },
 | 
						|
                                    { "redirect_uri", redirectUri },
 | 
						|
                                    { "client_id", clientId },
 | 
						|
                                    { "code_verifier", codeVerifier } // 使用 PKCE
 | 
						|
                                };
 | 
						|
 | 
						|
            var _httpClient = new HttpClient();
 | 
						|
            var content = new FormUrlEncodedContent(requestBody);
 | 
						|
            // 发出 token 请求
 | 
						|
            var response = await _httpClient.PostAsync(disco.TokenEndpoint, content);
 | 
						|
 | 
						|
 | 
						|
            if (response.IsSuccessStatusCode)
 | 
						|
            {
 | 
						|
                var responseBody = await response.Content.ReadAsStringAsync();
 | 
						|
 | 
						|
                // 解析 JSON
 | 
						|
                var jsonObject = JObject.Parse(responseBody);
 | 
						|
 | 
						|
                // 格式化并输出 JSON
 | 
						|
                var formattedJson = jsonObject.ToString(Formatting.Indented);
 | 
						|
 | 
						|
                Console.WriteLine(formattedJson);
 | 
						|
 | 
						|
                var tokenResponse = JsonConvert.DeserializeObject<LogtoTokenResponse>(responseBody);
 | 
						|
 | 
						|
                Console.WriteLine(tokenResponse);
 | 
						|
 | 
						|
                //结束回话
 | 
						|
                var parameters = new Parameters();
 | 
						|
 | 
						|
                parameters.Add("clientId", "aj34vqrpvz8olsbxwtcog");
 | 
						|
                var endUrl = new RequestUrl(disco.EndSessionEndpoint).CreateEndSessionUrl(tokenResponse.IdToken, "http://localhost:6100/OAuth/TestPCKEOrgin", extra: parameters);
 | 
						|
 | 
						|
              
 | 
						|
                Results.Redirect(endUrl);
 | 
						|
                //var _endHttpClient = new HttpClient();
 | 
						|
 | 
						|
                //var dd = await _endHttpClient.GetAsync(endUrl);
 | 
						|
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                var errorContent = await response.Content.ReadAsStringAsync();
 | 
						|
                throw new Exception($"Error: {errorContent}");
 | 
						|
            }
 | 
						|
 | 
						|
            #region 提示必须要Secret
 | 
						|
            //// 准备请求内容
 | 
						|
            //var tokenRequest = new AuthorizationCodeTokenRequest
 | 
						|
            //{
 | 
						|
            //    Address = disco.TokenEndpoint,
 | 
						|
            //    ClientId = clientId,
 | 
						|
            //    Code = code,
 | 
						|
            //    RedirectUri = redirectUri,
 | 
						|
            //    GrantType = "authorization_code",
 | 
						|
            //    CodeVerifier = codeVerifier
 | 
						|
 | 
						|
            //};
 | 
						|
 | 
						|
            //var tokenResponse = await _httpClient.RequestTokenAsync(tokenRequest);
 | 
						|
 | 
						|
            //if (tokenResponse.HttpStatusCode == HttpStatusCode.OK)
 | 
						|
            //{
 | 
						|
            //    var apiClient = new HttpClient();
 | 
						|
            //    apiClient.SetBearerToken(tokenResponse.AccessToken);
 | 
						|
 | 
						|
            //    var response = await apiClient.GetAsync(disco.UserInfoEndpoint);
 | 
						|
            //    if (!response.IsSuccessStatusCode)
 | 
						|
            //    {
 | 
						|
            //        Console.WriteLine(response.StatusCode);
 | 
						|
            //        Console.WriteLine(response.ReasonPhrase);
 | 
						|
            //    }
 | 
						|
            //    else
 | 
						|
            //    {
 | 
						|
            //        var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
 | 
						|
            //        Console.WriteLine(JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true }));
 | 
						|
 | 
						|
            //        //获取刷新token 
 | 
						|
            //        var refreshClient = new HttpClient();
 | 
						|
            //        var refreshRequest = new RefreshTokenRequest
 | 
						|
            //        {
 | 
						|
            //            Address = disco.TokenEndpoint,
 | 
						|
            //            ClientId = clientId,
 | 
						|
            //            RefreshToken = tokenResponse.RefreshToken,
 | 
						|
            //        };
 | 
						|
 | 
						|
            //        var refreshResponse = await refreshClient.RequestRefreshTokenAsync(refreshRequest);
 | 
						|
 | 
						|
            //        if (refreshResponse.IsError)
 | 
						|
            //        {
 | 
						|
            //            Console.WriteLine($"Error: {refreshResponse.Error}");
 | 
						|
            //        }
 | 
						|
            //        else
 | 
						|
            //        {
 | 
						|
            //            Console.WriteLine("获取刷新token 完成");
 | 
						|
 | 
						|
            //            Console.WriteLine("AccessToken:" + refreshResponse.AccessToken);
 | 
						|
 | 
						|
            //            Console.WriteLine("RefreshToken:" + refreshResponse.RefreshToken);
 | 
						|
            //        }
 | 
						|
 | 
						|
            //    }
 | 
						|
            //}
 | 
						|
            #endregion
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
            return ResponseOutput.Ok();
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        #endregion
 | 
						|
 | 
						|
 | 
						|
        #region OIDC  authorization_code  with  client_secret
 | 
						|
 | 
						|
        [AllowAnonymous]
 | 
						|
        public IResponseOutput TestOidcClientWithSecret()
 | 
						|
        {
 | 
						|
 | 
						|
            string clientId = "tl42rjin7obxtwqqgvkti";
 | 
						|
            string redirectUri = "http://localhost:6100/OAuth/TestOidcClientCallBack";
 | 
						|
            string state = "123456";
 | 
						|
 | 
						|
            // 构造请求的 URL
 | 
						|
            string authorizationUrl = $"https://logto.test.extimaging.com/oidc/auth" +
 | 
						|
                $"?client_id={clientId}" +
 | 
						|
                $"&redirect_uri={Uri.EscapeDataString(redirectUri)}" +
 | 
						|
                $"&response_type=code" +
 | 
						|
                $"&scope=openid profile email phone" +
 | 
						|
                $"&state={state}";
 | 
						|
 | 
						|
            Console.WriteLine(authorizationUrl);
 | 
						|
 | 
						|
            return ResponseOutput.Ok();
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        [AllowAnonymous]
 | 
						|
        [RoutePattern(HttpMethod = "Get")]
 | 
						|
        public async Task<IResponseOutput> TestOidcClientCallBackAsync(string code)
 | 
						|
        {
 | 
						|
            //使用IdentityModel.OidcClient 测试
 | 
						|
            var client = new HttpClient();
 | 
						|
            var disco = await client.GetDiscoveryDocumentAsync("https://logto.test.extimaging.com/oidc");
 | 
						|
            if (disco.IsError)
 | 
						|
            {
 | 
						|
                Console.WriteLine(disco.Error);
 | 
						|
            }
 | 
						|
 | 
						|
            // OIDC 配置,替换为您的 OIDC 提供者的配置
 | 
						|
            string clientId = "tl42rjin7obxtwqqgvkti";
 | 
						|
            string clientSecret = "Pu9ig4rz44aLlxb0yKUaOiZaFk6Bcu51";
 | 
						|
            string redirectUri = "http://localhost:6100/OAuth/TestOidcClientCallBack"; // 替换为前端的回调 URL
 | 
						|
            string postLogoutRedirectUri = "http://localhost:6100/OAuth/TestPCKEOrgin"; //退出回话重定向到前端的url
 | 
						|
            // 准备请求内容
 | 
						|
            var tokenRequest = new AuthorizationCodeTokenRequest
 | 
						|
            {
 | 
						|
                Address = disco.TokenEndpoint,
 | 
						|
                ClientId = clientId,
 | 
						|
                ClientSecret = clientSecret,
 | 
						|
                Code = code,
 | 
						|
                RedirectUri = redirectUri,
 | 
						|
                GrantType = "authorization_code",
 | 
						|
 | 
						|
            };
 | 
						|
 | 
						|
            var _httpClient = new HttpClient();
 | 
						|
            // 发出 token 请求
 | 
						|
            var tokenResponse = await _httpClient.RequestAuthorizationCodeTokenAsync(tokenRequest);
 | 
						|
 | 
						|
 | 
						|
            if (tokenResponse.HttpStatusCode == HttpStatusCode.OK)
 | 
						|
            {
 | 
						|
                var apiClient = new HttpClient();
 | 
						|
                apiClient.SetBearerToken(tokenResponse.AccessToken);
 | 
						|
 | 
						|
                var response = await apiClient.GetAsync(disco.UserInfoEndpoint);
 | 
						|
                if (!response.IsSuccessStatusCode)
 | 
						|
                {
 | 
						|
                    Console.WriteLine(response.StatusCode);
 | 
						|
                    Console.WriteLine(response.ReasonPhrase);
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
 | 
						|
                    Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true }));
 | 
						|
 | 
						|
                    //获取刷新token 
 | 
						|
                    var refreshClient = new HttpClient();
 | 
						|
                    var refreshRequest = new RefreshTokenRequest
 | 
						|
                    {
 | 
						|
                        Address = disco.TokenEndpoint,
 | 
						|
                        ClientId = clientId,
 | 
						|
                        ClientSecret = clientSecret,
 | 
						|
                        RefreshToken = tokenResponse.RefreshToken,
 | 
						|
                    };
 | 
						|
 | 
						|
                    var refreshResponse = await refreshClient.RequestRefreshTokenAsync(refreshRequest);
 | 
						|
 | 
						|
                    if (refreshResponse.IsError)
 | 
						|
                    {
 | 
						|
                        Console.WriteLine($"Error: {refreshResponse.Error}");
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        Console.WriteLine("获取刷新token 完成");
 | 
						|
 | 
						|
                        Console.WriteLine("AccessToken:" + refreshResponse.AccessToken);
 | 
						|
 | 
						|
                        Console.WriteLine("RefreshToken:" + refreshResponse.RefreshToken);
 | 
						|
                    }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            return ResponseOutput.Ok();
 | 
						|
        }
 | 
						|
 | 
						|
        #endregion
 | 
						|
 | 
						|
 | 
						|
        #region 客户端凭证
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// 测试客户端凭证代码
 | 
						|
        /// </summary>
 | 
						|
        /// <returns></returns>
 | 
						|
        public async Task<IResponseOutput> TestClientCredentialsAsync()
 | 
						|
        {
 | 
						|
 | 
						|
            #region 使用IdentityModel.OidcClient 测试
 | 
						|
 | 
						|
            // discover endpoints from metadata
 | 
						|
            var client = new HttpClient();
 | 
						|
 | 
						|
            var disco = await client.GetDiscoveryDocumentAsync("https://logto.test.extimaging.com/oidc");
 | 
						|
            if (disco.IsError)
 | 
						|
            {
 | 
						|
                Console.WriteLine(disco.Error);
 | 
						|
            }
 | 
						|
 | 
						|
            // request token
 | 
						|
            var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
 | 
						|
            {
 | 
						|
                Address = disco.TokenEndpoint,
 | 
						|
                ClientId = "v2mr2ndxwkxz0xpsuc1th",
 | 
						|
                ClientSecret = "yq9jUxl70QoOmwHxJ37h1rDoyJ5iz92Q",
 | 
						|
                Resource = new List<string>() { "https://default.logto.app/api" },
 | 
						|
                Scope = "all"
 | 
						|
            });
 | 
						|
 | 
						|
            if (tokenResponse.IsError)
 | 
						|
            {
 | 
						|
                Console.WriteLine(tokenResponse.Error);
 | 
						|
                Console.WriteLine(tokenResponse.ErrorDescription);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                Console.WriteLine(tokenResponse.AccessToken);
 | 
						|
                Console.WriteLine("\n\n");
 | 
						|
 | 
						|
                // call api
 | 
						|
                var apiClient = new HttpClient();
 | 
						|
                apiClient.SetBearerToken(tokenResponse.AccessToken);
 | 
						|
 | 
						|
                var response = await apiClient.GetAsync("https://logto.test.extimaging.com/api/applications");
 | 
						|
                if (!response.IsSuccessStatusCode)
 | 
						|
                {
 | 
						|
                    Console.WriteLine(response.StatusCode);
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
 | 
						|
                    Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true }));
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
 | 
						|
            #endregion
 | 
						|
 | 
						|
            return ResponseOutput.Ok();
 | 
						|
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task<IResponseOutput> TestClientCredentialsOriginAsync()
 | 
						|
        {
 | 
						|
            #region 客户端方式获取logto 里面的信息
 | 
						|
            {
 | 
						|
 | 
						|
                var baseUrl = "https://logto.test.extimaging.com";
 | 
						|
                var appId = "v2mr2ndxwkxz0xpsuc1th";
 | 
						|
                var appSecret = "yq9jUxl70QoOmwHxJ37h1rDoyJ5iz92Q";
 | 
						|
                var apiAddress = "https://default.logto.app/api"; //这里是个坑
 | 
						|
                var scope = "all";
 | 
						|
 | 
						|
                var opts = new RestClientOptions(baseUrl);
 | 
						|
                using var client = new RestClient(opts);
 | 
						|
 | 
						|
                //https://bump.sh/logto/doc/logto-management-api/authentication
 | 
						|
                var request = new RestRequest("oidc/token", Method.Post);
 | 
						|
                request
 | 
						|
                    .AddHeader("Content-Type", "application/x-www-form-urlencoded")
 | 
						|
                    .AddParameter("grant_type", "client_credentials")
 | 
						|
                    .AddParameter("client_id", appId)
 | 
						|
                    .AddParameter("client_secret", appSecret)
 | 
						|
                    .AddParameter("resource", apiAddress) //注意这里默认值地址和api 地址有区别
 | 
						|
                    .AddParameter("scope", scope);
 | 
						|
 | 
						|
 | 
						|
                var response = await client.ExecuteAsync<LogtoTokenResponse>(request);
 | 
						|
 | 
						|
                if (response.StatusCode == HttpStatusCode.OK)
 | 
						|
                {
 | 
						|
                    var tokenResponse = response.Data;
 | 
						|
 | 
						|
                    Console.WriteLine(tokenResponse.ToJsonStr());
 | 
						|
 | 
						|
                    #region 获取应用信息
 | 
						|
 | 
						|
                    var applicationRequest = new RestRequest($"/api/applications", Method.Get)
 | 
						|
                        .AddHeader("Authorization", $"Bearer {tokenResponse.AccessToken}");
 | 
						|
 | 
						|
                    var applicationResponse = await client.ExecuteAsync(applicationRequest);
 | 
						|
                    #endregion
 | 
						|
 | 
						|
                    #region 获取用户信息
 | 
						|
                    //curl \
 | 
						|
                    // -X GET https://[tenant_id].logto.app/api/users/{userId} \
 | 
						|
                    // -H "Authorization: Bearer $ACCESS_TOKEN"
 | 
						|
 | 
						|
                    var userId = "4fqx4cb3438k";
 | 
						|
                    var userInfoRequest = new RestRequest($"api/users/{userId}", Method.Get)
 | 
						|
                        .AddHeader("Authorization", $"Bearer {tokenResponse.AccessToken}");
 | 
						|
 | 
						|
 | 
						|
                    var userResponse = await client.ExecuteAsync<LogtoUser>(userInfoRequest);
 | 
						|
 | 
						|
                    Console.WriteLine(userResponse.Content);
 | 
						|
 | 
						|
                    #endregion
 | 
						|
                }
 | 
						|
 | 
						|
 | 
						|
            }
 | 
						|
            #endregion
 | 
						|
 | 
						|
            return ResponseOutput.Ok();
 | 
						|
        }
 | 
						|
        #endregion
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    public static class PkceUtil
 | 
						|
    {
 | 
						|
        // 生成 code_verifier
 | 
						|
        public static string GenerateCodeVerifier()
 | 
						|
        {
 | 
						|
            var bytes = new byte[64];
 | 
						|
            using (var random = RandomNumberGenerator.Create())
 | 
						|
            {
 | 
						|
                random.GetBytes(bytes);
 | 
						|
                return Convert.ToBase64String(bytes)
 | 
						|
                    .TrimEnd('=')
 | 
						|
                    .Replace('+', '-')
 | 
						|
                    .Replace('/', '_');
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // 生成 code_challenge
 | 
						|
        public static string GenerateCodeChallenge(string codeVerifier)
 | 
						|
        {
 | 
						|
            using (var sha256 = SHA256.Create())
 | 
						|
            {
 | 
						|
                var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
 | 
						|
                return Convert.ToBase64String(bytes)
 | 
						|
                    .TrimEnd('=')
 | 
						|
                    .Replace('+', '-')
 | 
						|
                    .Replace('/', '_');
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |