@@ -11,10 +11,13 @@ namespace DocuSign.CodeExamples
1111 using System . Net . Http ;
1212 using System . Net . Http . Headers ;
1313 using System . Security . Claims ;
14+ using System . Security . Cryptography ;
15+ using System . Text ;
1416 using System . Text . Json ;
1517 using System . Text . RegularExpressions ;
1618 using System . Threading . Tasks ;
1719 using System . Web ;
20+ using Azure . Core ;
1821 using DocuSign . CodeExamples . Common ;
1922 using DocuSign . CodeExamples . Models ;
2023 using DocuSign . Rooms . Api ;
@@ -158,54 +161,79 @@ public void ConfigureServices(IServiceCollection services)
158161 options . TokenEndpoint = this . Configuration [ "DocuSign:TokenEndpoint" ] ;
159162 options . UserInformationEndpoint = this . Configuration [ "DocuSign:UserInformationEndpoint" ] ;
160163
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" ) ;
163185
164186 options . Events = new OAuthEvents
165187 {
166188 OnRedirectToAuthorizationEndpoint = redirectContext =>
167189 {
168190 List < string > scopesForCurrentApi = this . apiTypes . GetValueOrDefault ( Enum . Parse < ExamplesApiType > ( this . Configuration [ "API" ] ) ) ;
191+ redirectContext . RedirectUri = this . UpdateRedirectUriScopes ( redirectContext . RedirectUri , scopesForCurrentApi ) ;
169192
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 ;
176194
195+ this . Configuration [ "RedirectUrl" ] = redirectContext . RedirectUri ;
177196 redirectContext . HttpContext . Response . Redirect ( redirectContext . RedirectUri ) ;
178197 return Task . FromResult ( 0 ) ;
179198 } ,
180199 OnCreatingTicket = async context =>
181200 {
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 ) ;
199205 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+ }
203214 } ,
204- OnRemoteFailure = context =>
215+ OnRemoteFailure = async context =>
205216 {
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+ }
209237 } ,
210238 } ;
211239 } ) ;
@@ -261,33 +289,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
261289 } ) ;
262290 }
263291
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-
291292 private string UpdateRedirectUriScopes ( string uri , List < string > wantedScopes )
292293 {
293294 const string pattern = @"(?:&|\?)scope=([^&]+)" ;
0 commit comments