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('/', '_');
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |