Skip to content

Commit 2ebe5e8

Browse files
Can get access and id tokens from the ESBI conformance
1 parent 3e1e9cf commit 2ebe5e8

2 files changed

Lines changed: 146 additions & 10 deletions

File tree

Lines changed: 140 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,35 @@
11
// See https://aka.ms/new-console-template for more information
2+
using Microsoft.IdentityModel.JsonWebTokens;
3+
using Microsoft.IdentityModel.Tokens;
24
using SimpleIdServer.CredentialIssuer.Api.CredentialIssuer;
5+
using SimpleIdServer.Did.Crypto;
6+
using SimpleIdServer.Did.Key;
7+
using System;
8+
using System.Security.Claims;
9+
using System.Security.Cryptography;
10+
using System.Text;
311
using System.Text.Json;
412
using System.Text.Json.Nodes;
513
using System.Web;
614
using static QRCoder.PayloadGenerator;
715

16+
const string url = "https://api-conformance.ebsi.eu/conformance/v3/issuer-mock/.well-known/openid-credential-issuer";
17+
const string publicKey = "z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbpMAoXtZtunruYnM4gCV65AKAUX2AwEReRhEaf3BRQNJArZPwQdmf9ENZcF8VT13a58WsHeVjJtvAKKPYEibaEfdUxvU7sgxEUTJpjEkq6BJKrRV1JQ1CqhYvGbmJ1WyoUQ";
18+
const string did = $"did:key:{publicKey}";
19+
const string refDid = $"{did}#{publicKey}";
20+
821
using (var httpClient = new HttpClient())
922
{
23+
var (challenge, verifier) = PkceGenerate();
1024
var openidCredentialIssuer = GetOpenidCredentialIssuer(httpClient).Result;
11-
var authorizationEndpoint = GetAuthorizationEndpoint(httpClient, openidCredentialIssuer.AuthorizationServer).Result;
12-
ExecuteAuthorizationRequest(httpClient, authorizationEndpoint).Wait();
25+
var configuration = GetAuthorizationEndpoint(httpClient, openidCredentialIssuer.AuthorizationServer).Result;
26+
var parameters = ExecuteAuthorizationRequest(httpClient, configuration.authorizationEndpoint, challenge).Result;
27+
var redirectUri = parameters["redirect_uri"];
28+
var nonce = parameters["nonce"];
29+
var state = parameters["state"];
30+
var postAuthResult = ExecutePostAuthorizationRequest(httpClient, redirectUri, configuration.issuer, nonce, state).Result;
31+
var tokenResult = GetToken(httpClient, configuration.tokenEndpoint, postAuthResult["code"], verifier).Result;
32+
// GET THE CREDENTIAL !!!
1333
}
1434

1535
async Task<ESBICredentialIssuerResult> GetOpenidCredentialIssuer(HttpClient httpClient)
@@ -22,23 +42,56 @@ async Task<ESBICredentialIssuerResult> GetOpenidCredentialIssuer(HttpClient http
2242
return openidCredentialIssuer;
2343
}
2444

25-
async Task<string> GetAuthorizationEndpoint(HttpClient httpClient, string authUrl)
45+
async Task<(string authorizationEndpoint, string issuer, string tokenEndpoint)> GetAuthorizationEndpoint(HttpClient httpClient, string authUrl)
2646
{
2747
var requestMessage = new HttpRequestMessage(HttpMethod.Get, $"{authUrl}/.well-known/openid-configuration");
2848
var httpResult = await httpClient.SendAsync(requestMessage);
2949
var json = await httpResult.Content.ReadAsStringAsync();
30-
return JsonObject.Parse(json)["authorization_endpoint"].ToString();
50+
var jsonObj = JsonObject.Parse(json);
51+
return (jsonObj["authorization_endpoint"].ToString(), jsonObj["issuer"].ToString(), jsonObj["token_endpoint"].ToString());
3152
}
3253

33-
async Task ExecuteAuthorizationRequest(HttpClient httpClient, string url)
54+
async Task<Dictionary<string, string>> ExecuteAuthorizationRequest(HttpClient httpClient, string url, string challenge)
3455
{
3556
var uriBuilder = new UriBuilder(url);
57+
var credentialTypes = new JsonArray
58+
{
59+
"VerifiableCredential",
60+
"VerifiableAttestation",
61+
"CTIssueQualificationCredential"
62+
};
63+
var authorizationDetails = new JsonArray
64+
{
65+
new JsonObject
66+
{
67+
{ "type", "openid_credential" },
68+
{ "format", "jwt_vc" },
69+
{ "types", credentialTypes }
70+
}
71+
};
72+
var clientMetadata = new JsonObject
73+
{
74+
{ "response_types_supported",
75+
new JsonArray
76+
{
77+
"vp_token", "id_token"
78+
}
79+
},
80+
{ "authorization_endpoint", "openid://"}
81+
};
3682
var dic = new Dictionary<string, string>
3783
{
38-
{ "client_id", "did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbpMAoXtZtunruYnM4gCV65AKAUX2AwEReRhEaf3BRQNJArZPwQdmf9ENZcF8VT13a58WsHeVjJtvAKKPYEibaEfdUxvU7sgxEUTJpjEkq6BJKrRV1JQ1CqhYvGbmJ1WyoUQ" },
39-
{ "redirect_uri", "http://localhost:5005" },
84+
{ "response_type", "code" },
4085
{ "scope", "openid" },
41-
{ "response_type", "code" }
86+
// { "issuer_state", "issuer-state" },
87+
{ "state", "client-state" },
88+
{ "client_id", "did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbpMAoXtZtunruYnM4gCV65AKAUX2AwEReRhEaf3BRQNJArZPwQdmf9ENZcF8VT13a58WsHeVjJtvAKKPYEibaEfdUxvU7sgxEUTJpjEkq6BJKrRV1JQ1CqhYvGbmJ1WyoUQ" },
89+
{ "authorization_details", HttpUtility.UrlEncode(authorizationDetails.ToJsonString()) },
90+
{ "redirect_uri", "openid://" },
91+
{ "nonce", "nonce" },
92+
{ "code_challenge", challenge },
93+
{ "code_challenge_method", "S256" },
94+
{ "client_metadata", HttpUtility.UrlEncode(clientMetadata.ToJsonString()) }
4295
};
4396
uriBuilder.Query = string.Join("&", dic.Select(kvp => $"{kvp.Key}={kvp.Value}"));
4497
var requestMessage = new HttpRequestMessage
@@ -47,6 +100,83 @@ async Task ExecuteAuthorizationRequest(HttpClient httpClient, string url)
47100
};
48101
var httpResult = await httpClient.SendAsync(requestMessage);
49102
var result = httpResult.Headers.Location.AbsoluteUri;
50-
var json = await httpResult.Content.ReadAsStringAsync();
103+
uriBuilder = new UriBuilder(result);
104+
var queryParameters = uriBuilder.Query.Trim('?').Split('&').Select(s => s.Split('=')).ToDictionary(arr => arr[0], arr => arr[1]);
105+
return queryParameters;
106+
}
107+
108+
async Task<Dictionary<string, string>> ExecutePostAuthorizationRequest(HttpClient httpClient, string url, string aud, string nonce, string state)
109+
{
110+
var serializedPrivateKey = System.IO.File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "privatekey.json"));
111+
var signatureKey = SignatureKeySerializer.Deserialize(serializedPrivateKey);
112+
var signingCredentials = signatureKey.BuildSigningCredentials(refDid);
113+
var handler = new JsonWebTokenHandler();
114+
var securityTokenDescriptor = new SecurityTokenDescriptor
115+
{
116+
IssuedAt = DateTime.UtcNow,
117+
SigningCredentials = signingCredentials,
118+
Audience = aud
119+
};
120+
var claims = new Dictionary<string, object>
121+
{
122+
{ "nonce", nonce },
123+
{ "iss", did },
124+
{ "sub", did }
125+
};
126+
securityTokenDescriptor.Claims = claims;
127+
var token = handler.CreateToken(securityTokenDescriptor);
128+
var requestMessage = new HttpRequestMessage
129+
{
130+
RequestUri = new Uri(HttpUtility.UrlDecode(url)),
131+
Content = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
132+
{
133+
new KeyValuePair<string, string>("id_token", token),
134+
new KeyValuePair<string, string>("state", state)
135+
}),
136+
Method = HttpMethod.Post
137+
};
138+
var httpResult = await httpClient.SendAsync(requestMessage);
139+
var result = httpResult.Headers.Location.AbsoluteUri;
140+
var uriBuilder = new UriBuilder(result);
141+
var queryParameters = uriBuilder.Query.Trim('?').Split('&').Select(s => s.Split('=')).ToDictionary(arr => arr[0], arr => arr[1]);
142+
return queryParameters;
143+
}
51144

52-
}
145+
async Task<(string accessToken, string idToken)> GetToken(HttpClient httpClient, string url, string authorizationCode, string codeVerifier)
146+
{
147+
var requestMessage = new HttpRequestMessage
148+
{
149+
RequestUri = new Uri(url),
150+
Content = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
151+
{
152+
new KeyValuePair<string, string>("grant_type", "authorization_code"),
153+
new KeyValuePair<string, string>("client_id", did),
154+
new KeyValuePair<string, string>("code", authorizationCode),
155+
new KeyValuePair<string, string>("code_verifier", codeVerifier)
156+
}),
157+
Method = HttpMethod.Post
158+
};
159+
var httpResult = await httpClient.SendAsync(requestMessage);
160+
var content = await httpResult.Content.ReadAsStringAsync();
161+
var jObj = JsonObject.Parse(content);
162+
return (jObj["access_token"].ToString(), jObj["id_token"].ToString());
163+
}
164+
165+
static (string codeChallenge, string verifier) PkceGenerate(int size = 32)
166+
{
167+
using var rng = RandomNumberGenerator.Create();
168+
var randomBytes = new byte[size];
169+
rng.GetBytes(randomBytes);
170+
var verifier = Base64UrlEncode(randomBytes);
171+
172+
var buffer = Encoding.UTF8.GetBytes(verifier);
173+
var hash = SHA256.Create().ComputeHash(buffer);
174+
var challenge = Base64UrlEncode(hash);
175+
176+
return (challenge, verifier);
177+
}
178+
static string Base64UrlEncode(byte[] data) =>
179+
Convert.ToBase64String(data)
180+
.Replace("+", "-")
181+
.Replace("/", "_")
182+
.TrimEnd('=');

src/CredentialIssuer/SimpleIdServer.CredentialIssuer.Console/SimpleIdServer.CredentialIssuer.Console.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
<Nullable>enable</Nullable>
88
</PropertyGroup>
99

10+
<ItemGroup>
11+
<Content Include="..\SimpleIdServer.CredentialIssuer.Startup\privatekey.json" Link="privatekey.json">
12+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
13+
</Content>
14+
</ItemGroup>
15+
1016
<ItemGroup>
1117
<ProjectReference Include="..\SimpleIdServer.CredentialIssuer\SimpleIdServer.CredentialIssuer.csproj" />
1218
</ItemGroup>

0 commit comments

Comments
 (0)