11// See https://aka.ms/new-console-template for more information
2+ using Microsoft . IdentityModel . JsonWebTokens ;
3+ using Microsoft . IdentityModel . Tokens ;
24using 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 ;
311using System . Text . Json ;
412using System . Text . Json . Nodes ;
513using System . Web ;
614using 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+
821using ( 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
1535async 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 ( '=' ) ;
0 commit comments