PKCE 対応に苦労したので、サンプルコードをメモしておく。
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using IdentityModel;
using IdentityModel.Client;
namespace SampleApiClient
{
class Program
{
const string IdentityServerAddress = "IdentityServerのアドレス";
const string WebApiAddress = "Web API のアドレス";
const string ClientId = "クライアント ID";
const string ClientSecret = "クライアントシークレット";
const string Scope = "API スコープ";
const string RedirectUrl = "http://localhost";
static async Task Main(string[] args)
{
var tokenClient = new HttpClient
{
BaseAddress = new Uri(IdentityServerAddress),
};
var disco = await tokenClient.GetDiscoveryDocumentAsync();
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
var codeVerifier = CryptoRandom.CreateUniqueId();
string challenge;
using (var sha256 = SHA256.Create())
{
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
challenge = Base64Url.Encode(challengeBytes);
}
var nonce = CryptoRandom.CreateUniqueId();
var request = new RequestUrl(disco.AuthorizeEndpoint);
var authorizeUrl = request.CreateAuthorizeUrl(
clientId: ClientId,
responseType: OidcConstants.ResponseTypes.Code,
scope: Scope,
redirectUri: RedirectUrl,
nonce: nonce,
codeChallenge: challenge,
codeChallengeMethod: OidcConstants.CodeChallengeMethods.Sha256);
var escapedUrl = authorizeUrl.Replace("&", "^&");
Process.Start(new ProcessStartInfo("cmd", $"/c start {escapedUrl}")
{
CreateNoWindow = true,
});
Console.Write("AuthorizationCode:");
var code = Console.ReadLine();
var codeRequest = new AuthorizationCodeTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = ClientId,
ClientSecret = ClientSecret,
Code = code,
RedirectUri = RedirectUrl,
CodeVerifier = codeVerifier,
};
var tokenResponse = await tokenClient.RequestAuthorizationCodeTokenAsync(codeRequest);
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
var apiClient = new HttpClient
{
BaseAddress = new Uri(WebApiAddress),
};
apiClient.SetBearerToken(tokenResponse.AccessToken);
var response = await apiClient.GetAsync(
"/api/products");
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
Console.WriteLine("Enter で終了");
Console.ReadLine();
}
}
}