@@ -11,10 +11,13 @@ namespace DocuSign.CodeExamples
11
11
using System . Net . Http ;
12
12
using System . Net . Http . Headers ;
13
13
using System . Security . Claims ;
14
+ using System . Security . Cryptography ;
15
+ using System . Text ;
14
16
using System . Text . Json ;
15
17
using System . Text . RegularExpressions ;
16
18
using System . Threading . Tasks ;
17
19
using System . Web ;
20
+ using Azure . Core ;
18
21
using DocuSign . CodeExamples . Common ;
19
22
using DocuSign . CodeExamples . Models ;
20
23
using DocuSign . Rooms . Api ;
@@ -158,54 +161,79 @@ public void ConfigureServices(IServiceCollection services)
158
161
options . TokenEndpoint = this . Configuration [ "DocuSign:TokenEndpoint" ] ;
159
162
options . UserInformationEndpoint = this . Configuration [ "DocuSign:UserInformationEndpoint" ] ;
160
163
161
- string codeVerifier = GenerateCodeVerifier ( ) ;
162
- string codeChallenge = GenerateCodeChallenge ( codeVerifier ) ;
164
+ foreach ( var apiType in this . apiTypes )
165
+ {
166
+ foreach ( var scope in apiType . Value )
167
+ {
168
+ if ( ! options . Scope . Contains ( scope . ToLower ( ) ) )
169
+ {
170
+ options . Scope . Add ( scope ) ;
171
+ }
172
+ }
173
+ }
174
+
175
+ options . SaveTokens = true ;
176
+ options . ClaimActions . MapJsonKey ( ClaimTypes . NameIdentifier , "sub" ) ;
177
+ options . ClaimActions . MapJsonKey ( ClaimTypes . Name , "name" ) ;
178
+ options . ClaimActions . MapJsonKey ( "accounts" , "accounts" ) ;
179
+ options . ClaimActions . MapCustomJson ( "account_id" , obj => this . ExtractDefaultAccountValue ( obj , "account_id" ) ) ;
180
+ options . ClaimActions . MapCustomJson ( "account_name" , obj => this . ExtractDefaultAccountValue ( obj , "account_name" ) ) ;
181
+ options . ClaimActions . MapCustomJson ( "base_uri" , obj => this . ExtractDefaultAccountValue ( obj , "base_uri" ) ) ;
182
+ options . ClaimActions . MapJsonKey ( "access_token" , "access_token" ) ;
183
+ options . ClaimActions . MapJsonKey ( "refresh_token" , "refresh_token" ) ;
184
+ options . ClaimActions . MapJsonKey ( "expires_in" , "expires_in" ) ;
163
185
164
186
options . Events = new OAuthEvents
165
187
{
166
188
OnRedirectToAuthorizationEndpoint = redirectContext =>
167
189
{
168
190
List < string > scopesForCurrentApi = this . apiTypes . GetValueOrDefault ( Enum . Parse < ExamplesApiType > ( this . Configuration [ "API" ] ) ) ;
191
+ redirectContext . RedirectUri = this . UpdateRedirectUriScopes ( redirectContext . RedirectUri , scopesForCurrentApi ) ;
169
192
170
- var redirectUri = this . UpdateRedirectUriScopes ( redirectContext . RedirectUri , scopesForCurrentApi ) ;
171
-
172
- var pkceQuery = $ "&code_challenge={ codeChallenge } &code_challenge_method=S256";
173
- redirectContext . RedirectUri = redirectUri + pkceQuery ;
174
-
175
- redirectContext . HttpContext . Session . SetString ( "code_verifier" , codeVerifier ) ;
193
+ redirectContext . Options . UsePkce = this . Configuration [ "PkceFailed" ] == null ;
176
194
195
+ this . Configuration [ "RedirectUrl" ] = redirectContext . RedirectUri ;
177
196
redirectContext . HttpContext . Response . Redirect ( redirectContext . RedirectUri ) ;
178
197
return Task . FromResult ( 0 ) ;
179
198
} ,
180
199
OnCreatingTicket = async context =>
181
200
{
182
- string codeVerifier = context . HttpContext . Session . GetString ( "code_verifier" ) ;
183
-
184
- var tokenRequestParams = new Dictionary < string , string >
185
- {
186
- { "grant_type" , "authorization_code" } ,
187
- { "code" , context . ProtocolMessage . Code } ,
188
- { "redirect_uri" , context . Properties . RedirectUri } ,
189
- { "client_id" , options . ClientId } ,
190
- { "code_verifier" , codeVerifier } ,
191
- } ;
192
-
193
- var requestContent = new FormUrlEncodedContent ( tokenRequestParams ) ;
194
- var requestMessage = new HttpRequestMessage ( HttpMethod . Post , options . TokenEndpoint )
195
- {
196
- Content = requestContent
197
- } ;
198
- var response = await context . Backchannel . SendAsync ( requestMessage , HttpCompletionOption . ResponseHeadersRead , context . HttpContext . RequestAborted ) ;
201
+ var request = new HttpRequestMessage ( HttpMethod . Get , context . Options . UserInformationEndpoint ) ;
202
+ request . Headers . Accept . Add ( new MediaTypeWithQualityHeaderValue ( "application/json" ) ) ;
203
+ request . Headers . Authorization = new AuthenticationHeaderValue ( "Bearer" , context . AccessToken ) ;
204
+ var response = await context . Backchannel . SendAsync ( request , HttpCompletionOption . ResponseHeadersRead , context . HttpContext . RequestAborted ) ;
199
205
response . EnsureSuccessStatusCode ( ) ;
200
-
201
- var payload = JsonDocument . Parse ( await response . Content . ReadAsStringAsync ( ) ) ;
202
- context . RunClaimActions ( payload . RootElement ) ;
206
+ var user = JObject . Parse ( await response . Content . ReadAsStringAsync ( ) ) ;
207
+ user . Add ( "access_token" , context . AccessToken ) ;
208
+ user . Add ( "refresh_token" , context . RefreshToken ) ;
209
+ user . Add ( "expires_in" , DateTime . Now . Add ( context . ExpiresIn . Value ) . ToString ( ) ) ;
210
+ using ( JsonDocument payload = JsonDocument . Parse ( user . ToString ( ) ) )
211
+ {
212
+ context . RunClaimActions ( payload . RootElement ) ;
213
+ }
203
214
} ,
204
- OnRemoteFailure = context =>
215
+ OnRemoteFailure = async context =>
205
216
{
206
- context . HandleResponse ( ) ;
207
- context . Response . Redirect ( "/Home/Error?message=" + context . Failure ? . Message ) ;
208
- return Task . FromResult ( 0 ) ;
217
+ if ( this . Configuration [ "PkceFailed" ] != null )
218
+ {
219
+ context . HandleResponse ( ) ;
220
+ context . Response . Redirect ( "/Home/Error?message=" + context . Failure ? . Message ) ;
221
+ }
222
+ else
223
+ {
224
+ var redirectContext = new RedirectContext < OAuthOptions > (
225
+ context . HttpContext ,
226
+ context . Scheme ,
227
+ options ,
228
+ context . Properties ,
229
+ this . Configuration [ "RedirectUrl" ] ) ;
230
+
231
+ this . Configuration [ "PkceFailed" ] = "true" ;
232
+
233
+ await options . Events . OnRedirectToAuthorizationEndpoint ( redirectContext ) ;
234
+
235
+ context . HandleResponse ( ) ;
236
+ }
209
237
} ,
210
238
} ;
211
239
} ) ;
@@ -261,33 +289,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
261
289
} ) ;
262
290
}
263
291
264
- private string GenerateCodeVerifier ( )
265
- {
266
- using ( var rng = new RNGCryptoServiceProvider ( ) )
267
- {
268
- var bytes = new byte [ 32 ] ;
269
- rng . GetBytes ( bytes ) ;
270
- return Base64UrlEncode ( bytes ) ;
271
- }
272
- }
273
-
274
- private string GenerateCodeChallenge ( string codeVerifier )
275
- {
276
- using ( var sha256 = SHA256 . Create ( ) )
277
- {
278
- var hash = sha256 . ComputeHash ( Encoding . UTF8 . GetBytes ( codeVerifier ) ) ;
279
- return Base64UrlEncode ( hash ) ;
280
- }
281
- }
282
-
283
- private string Base64UrlEncode ( byte [ ] input )
284
- {
285
- return Convert . ToBase64String ( input )
286
- . Replace ( "+" , "-" )
287
- . Replace ( "/" , "_" )
288
- . Replace ( "=" , "" ) ;
289
- }
290
-
291
292
private string UpdateRedirectUriScopes ( string uri , List < string > wantedScopes )
292
293
{
293
294
const string pattern = @"(?:&|\?)scope=([^&]+)" ;
0 commit comments