11// ------------------------------------------------------------------------------
22// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
33// ------------------------------------------------------------------------------
4+ using Azure ;
45using Azure . Core ;
56using Azure . Core . Diagnostics ;
67using Azure . Core . Pipeline ;
1617using System . IO ;
1718using System . Linq ;
1819using System . Net . Http ;
20+ using System . Net . Http . Headers ;
1921using System . Security . Claims ;
2022using System . Security . Cryptography . X509Certificates ;
2123using System . Text . RegularExpressions ;
@@ -91,7 +93,7 @@ private static bool IsWamSupported()
9193 }
9294
9395 //Check to see if ATPoP is Supported
94- private static bool IsATPoPSupported ( )
96+ public static bool IsATPoPSupported ( )
9597 {
9698 return GraphSession . Instance . GraphOption . EnableATPoPForMSGraph ;
9799 }
@@ -131,10 +133,17 @@ private static async Task<InteractiveBrowserCredential> GetInteractiveBrowserCre
131133 interactiveOptions . AuthorityHost = new Uri ( GetAuthorityUrl ( authContext ) ) ;
132134 interactiveOptions . TokenCachePersistenceOptions = GetTokenCachePersistenceOptions ( authContext ) ;
133135
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+
134143 if ( ! File . Exists ( Constants . AuthRecordPath ) )
135144 {
136145 AuthenticationRecord authRecord ;
137- var interactiveBrowserCredential = new InteractiveBrowserCredential ( interactiveOptions ) ;
146+ // var interactiveBrowserCredential = new InteractiveBrowserCredential(interactiveOptions);
138147 if ( IsWamSupported ( ) )
139148 {
140149 // Adding a scenario to account for Access Token Proof of Possession
@@ -143,45 +152,9 @@ private static async Task<InteractiveBrowserCredential> GetInteractiveBrowserCre
143152 // Logic to implement ATPoP Authentication
144153 authRecord = await Task . Run ( ( ) =>
145154 {
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-
181155 // 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 ) ;
185158 } ) ;
186159 }
187160 else
@@ -508,6 +481,64 @@ public static Task DeleteAuthRecordAsync()
508481 File . Delete ( Constants . AuthRecordPath ) ;
509482 return Task . CompletedTask ;
510483 }
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+ {
511542 }
512543 internal class PopClientOptions : ClientOptions
513544 {
0 commit comments