1
1
// ------------------------------------------------------------------------------
2
2
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
3
3
// ------------------------------------------------------------------------------
4
+ using Azure ;
4
5
using Azure . Core ;
5
6
using Azure . Core . Diagnostics ;
6
7
using Azure . Core . Pipeline ;
16
17
using System . IO ;
17
18
using System . Linq ;
18
19
using System . Net . Http ;
20
+ using System . Net . Http . Headers ;
19
21
using System . Security . Claims ;
20
22
using System . Security . Cryptography . X509Certificates ;
21
23
using System . Text . RegularExpressions ;
@@ -91,7 +93,7 @@ private static bool IsWamSupported()
91
93
}
92
94
93
95
//Check to see if ATPoP is Supported
94
- private static bool IsATPoPSupported ( )
96
+ public static bool IsATPoPSupported ( )
95
97
{
96
98
return GraphSession . Instance . GraphOption . EnableATPoPForMSGraph ;
97
99
}
@@ -131,10 +133,17 @@ private static async Task<InteractiveBrowserCredential> GetInteractiveBrowserCre
131
133
interactiveOptions . AuthorityHost = new Uri ( GetAuthorityUrl ( authContext ) ) ;
132
134
interactiveOptions . TokenCachePersistenceOptions = GetTokenCachePersistenceOptions ( authContext ) ;
133
135
136
+ var interactiveBrowserCredential = new InteractiveBrowserCredential ( interactiveOptions ) ;
137
+ if ( IsATPoPSupported ( ) )
138
+ {
139
+ GraphSession . Instance . GraphRequestProofofPossession . PopTokenContext = CreatePopTokenRequestContext ( authContext ) ;
140
+ GraphSession . Instance . GraphRequestProofofPossession . BrowserCredential = interactiveBrowserCredential ;
141
+ }
142
+
134
143
if ( ! File . Exists ( Constants . AuthRecordPath ) )
135
144
{
136
145
AuthenticationRecord authRecord ;
137
- var interactiveBrowserCredential = new InteractiveBrowserCredential ( interactiveOptions ) ;
146
+ // var interactiveBrowserCredential = new InteractiveBrowserCredential(interactiveOptions);
138
147
if ( IsWamSupported ( ) )
139
148
{
140
149
// Adding a scenario to account for Access Token Proof of Possession
@@ -143,45 +152,9 @@ private static async Task<InteractiveBrowserCredential> GetInteractiveBrowserCre
143
152
// Logic to implement ATPoP Authentication
144
153
authRecord = await Task . Run ( ( ) =>
145
154
{
146
- // Creating a Request to retrieve nonce value
147
- string popNonce = null ;
148
- var popNonceToken = "nonce=\" " ;
149
- Uri resourceUri = new Uri ( "https://canary.graph.microsoft.com/beta/me" ) ; //PPE (https://graph.microsoft-ppe.com) or Canary (https://canary.graph.microsoft.com) or (https://20.190.132.47/beta/me)
150
- HttpClient httpClient = new ( new HttpClientHandler { ServerCertificateCustomValidationCallback = ( _ , _ , _ , _ ) => true } ) ;
151
- HttpResponseMessage response = httpClient . SendAsync ( new HttpRequestMessage ( HttpMethod . Get , resourceUri ) ) . Result ;
152
-
153
- // Find the WWW-Authenticate header in the response.
154
- var popChallenge = response . Headers . WwwAuthenticate . First ( wa => wa . Scheme == "PoP" ) ;
155
- var nonceStart = popChallenge . Parameter . IndexOf ( popNonceToken ) + popNonceToken . Length ;
156
- var nonceEnd = popChallenge . Parameter . IndexOf ( '"' , nonceStart ) ;
157
- popNonce = popChallenge . Parameter . Substring ( nonceStart , nonceEnd - nonceStart ) ;
158
-
159
- // Refresh token logic --- start
160
- var popTokenAuthenticationPolicy = new PopTokenAuthenticationPolicy ( interactiveBrowserCredential as ISupportsProofOfPossession , $ "https://graph.microsoft.com/.default") ;
161
- var pipelineOptions = new HttpPipelineOptions ( new PopClientOptions ( )
162
- {
163
- Diagnostics =
164
- {
165
- IsLoggingContentEnabled = true ,
166
- LoggedHeaderNames = { "Authorization" }
167
- } ,
168
- } ) ;
169
- pipelineOptions . PerRetryPolicies . Add ( popTokenAuthenticationPolicy ) ;
170
-
171
- var _pipeline = HttpPipelineBuilder . Build ( pipelineOptions , new HttpPipelineTransportOptions { ServerCertificateCustomValidationCallback = ( _ ) => true } ) ;
172
-
173
- using var request = _pipeline . CreateRequest ( ) ;
174
- request . Method = RequestMethod . Get ;
175
- request . Uri . Reset ( resourceUri ) ;
176
-
177
- // Manually invoke the authentication policy's process method
178
- popTokenAuthenticationPolicy . ProcessAsync ( new HttpMessage ( request , new ResponseClassifier ( ) ) , ReadOnlyMemory < HttpPipelinePolicy > . Empty ) ;
179
- // Refresh token logic --- end
180
-
181
155
// Run the thread in MTA.
182
- var popContext = new PopTokenRequestContext ( authContext . Scopes , isProofOfPossessionEnabled : true , proofOfPossessionNonce : popNonce , request : request ) ;
183
- //var token = interactiveBrowserCredential.GetToken(popContext, cancellationToken);
184
- return interactiveBrowserCredential . Authenticate ( popContext , cancellationToken ) ;
156
+ //GraphSession.Instance.GraphRequestProofofPossession.AccessToken = interactiveBrowserCredential.GetTokenAsync(GraphSession.Instance.GraphRequestProofofPossession.PopTokenContext, cancellationToken).Result;
157
+ return interactiveBrowserCredential . AuthenticateAsync ( GraphSession . Instance . GraphRequestProofofPossession . PopTokenContext , cancellationToken ) ;
185
158
} ) ;
186
159
}
187
160
else
@@ -508,6 +481,64 @@ public static Task DeleteAuthRecordAsync()
508
481
File . Delete ( Constants . AuthRecordPath ) ;
509
482
return Task . CompletedTask ;
510
483
}
484
+
485
+ public static PopTokenRequestContext CreatePopTokenRequestContext ( IAuthContext authContext )
486
+ {
487
+ // Creating a httpclient that would handle all pop calls
488
+ Uri popResourceUri = GraphSession . Instance . GraphRequestProofofPossession . Uri ?? new Uri ( "https://canary.graph.microsoft.com/beta/me" ) ; //PPE (https://graph.microsoft-ppe.com) or Canary (https://canary.graph.microsoft.com) or (https://20.190.132.47/beta/me)
489
+ HttpClient popHttpClient = new ( new HttpClientHandler { ServerCertificateCustomValidationCallback = ( _ , _ , _ , _ ) => true } ) ;
490
+
491
+ // Find the WWW-Authenticate header in the response.
492
+ var popMethod = GraphSession . Instance . GraphRequestProofofPossession . HttpMethod ?? HttpMethod . Get ;
493
+ var popResponse = popHttpClient . SendAsync ( new HttpRequestMessage ( popMethod , popResourceUri ) ) . Result ;
494
+ var popChallenge = popResponse . Headers . WwwAuthenticate . First ( wa => wa . Scheme == "PoP" ) ;
495
+ var nonceStart = popChallenge . Parameter . IndexOf ( "nonce=\" " ) + "nonce=\" " . Length ;
496
+ var nonceEnd = popChallenge . Parameter . IndexOf ( '"' , nonceStart ) ;
497
+ GraphSession . Instance . GraphRequestProofofPossession . ProofofPossessionNonce = popChallenge . Parameter . Substring ( nonceStart , nonceEnd - nonceStart ) ;
498
+
499
+ // Refresh token logic --- start
500
+ var popPipelineOptions = new HttpPipelineOptions ( new PopClientOptions ( )
501
+ {
502
+
503
+ } ) ;
504
+
505
+ var _popPipeline = HttpPipelineBuilder . Build ( popPipelineOptions , new HttpPipelineTransportOptions { ServerCertificateCustomValidationCallback = ( _ ) => true } ) ;
506
+ GraphSession . Instance . GraphRequestProofofPossession . Request = _popPipeline . CreateRequest ( ) ;
507
+ GraphSession . Instance . GraphRequestProofofPossession . Request . Method = ConvertToAzureRequestMethod ( popMethod ) ;
508
+ GraphSession . Instance . GraphRequestProofofPossession . Request . Uri . Reset ( popResourceUri ) ;
509
+
510
+ // Refresh token logic --- end
511
+ var popContext = new PopTokenRequestContext ( authContext . Scopes , isProofOfPossessionEnabled : true , proofOfPossessionNonce : GraphSession . Instance . GraphRequestProofofPossession . ProofofPossessionNonce , request : GraphSession . Instance . GraphRequestProofofPossession . Request ) ;
512
+ return popContext ;
513
+ }
514
+ public static RequestMethod ConvertToAzureRequestMethod ( HttpMethod httpMethod )
515
+ {
516
+ // Mapping known HTTP methods
517
+ switch ( httpMethod . Method . ToUpper ( ) )
518
+ {
519
+ case "GET" :
520
+ return RequestMethod . Get ;
521
+ case "POST" :
522
+ return RequestMethod . Post ;
523
+ case "PUT" :
524
+ return RequestMethod . Put ;
525
+ case "DELETE" :
526
+ return RequestMethod . Delete ;
527
+ case "HEAD" :
528
+ return RequestMethod . Head ;
529
+ case "OPTIONS" :
530
+ return RequestMethod . Options ;
531
+ case "PATCH" :
532
+ return RequestMethod . Patch ;
533
+ case "TRACE" :
534
+ return RequestMethod . Trace ;
535
+ default :
536
+ throw new ArgumentException ( $ "Unsupported HTTP method: { httpMethod . Method } ") ;
537
+ }
538
+ }
539
+ }
540
+ internal class PopClientOptions : ClientOptions
541
+ {
511
542
}
512
543
internal class PopClientOptions : ClientOptions
513
544
{
0 commit comments