3
3
using System . CommandLine ;
4
4
using System . Linq ;
5
5
using System . Net . Http ;
6
+ using System . Text . RegularExpressions ;
6
7
using System . Threading . Tasks ;
7
8
using GitCredentialManager ;
8
9
using GitCredentialManager . Authentication ;
@@ -74,10 +75,9 @@ public bool IsSupported(HttpResponseMessage response)
74
75
75
76
public async Task < ICredential > GetCredentialAsync ( InputArguments input )
76
77
{
77
- Uri remoteUri = input . GetRemoteUri ( ) ;
78
-
79
78
if ( UsePersonalAccessTokens ( ) )
80
79
{
80
+ Uri remoteUri = input . GetRemoteUri ( ) ;
81
81
string service = GetServiceName ( remoteUri ) ;
82
82
string account = GetAccountNameForCredentialQuery ( input ) ;
83
83
@@ -104,7 +104,7 @@ public async Task<ICredential> GetCredentialAsync(InputArguments input)
104
104
{
105
105
// Include the username request here so that we may use it as an override
106
106
// for user account lookups when getting Azure Access Tokens.
107
- var azureResult = await GetAzureAccessTokenAsync ( remoteUri , input . UserName ) ;
107
+ var azureResult = await GetAzureAccessTokenAsync ( input ) ;
108
108
return new GitCredential ( azureResult . AccountUpn , azureResult . AccessToken ) ;
109
109
}
110
110
}
@@ -222,8 +222,11 @@ private async Task<ICredential> GeneratePersonalAccessTokenAsync(InputArguments
222
222
return new GitCredential ( result . AccountUpn , pat ) ;
223
223
}
224
224
225
- private async Task < IMicrosoftAuthenticationResult > GetAzureAccessTokenAsync ( Uri remoteUri , string userName )
225
+ private async Task < IMicrosoftAuthenticationResult > GetAzureAccessTokenAsync ( InputArguments input )
226
226
{
227
+ Uri remoteUri = input . GetRemoteUri ( ) ;
228
+ string userName = input . UserName ;
229
+
227
230
// We should not allow unencrypted communication and should inform the user
228
231
if ( StringComparer . OrdinalIgnoreCase . Equals ( remoteUri . Scheme , "http" ) )
229
232
{
@@ -234,14 +237,27 @@ private async Task<IMicrosoftAuthenticationResult> GetAzureAccessTokenAsync(Uri
234
237
Uri orgUri = UriHelpers . CreateOrganizationUri ( remoteUri , out string orgName ) ;
235
238
236
239
_context . Trace . WriteLine ( $ "Determining Microsoft Authentication authority for Azure DevOps organization '{ orgName } '...") ;
237
- string authAuthority = _authorityCache . GetAuthority ( orgName ) ;
238
- if ( authAuthority is null )
240
+ if ( TryGetAuthorityFromHeaders ( input . WwwAuth , out string authAuthority ) )
241
+ {
242
+ _context . Trace . WriteLine ( "Authority was found in WWW-Authenticate headers from Git input." ) ;
243
+ }
244
+ else
239
245
{
240
- // If there is no cached value we must query for it and cache it for future use
241
- _context . Trace . WriteLine ( $ "No cached authority value - querying { orgUri } for authority...") ;
242
- authAuthority = await _azDevOps . GetAuthorityAsync ( orgUri ) ;
243
- _authorityCache . UpdateAuthority ( orgName , authAuthority ) ;
246
+ // Try to get the authority from the cache
247
+ authAuthority = _authorityCache . GetAuthority ( orgName ) ;
248
+ if ( authAuthority is null )
249
+ {
250
+ // If there is no cached value we must query for it and cache it for future use
251
+ _context . Trace . WriteLine ( $ "No cached authority value - querying { orgUri } for authority...") ;
252
+ authAuthority = await _azDevOps . GetAuthorityAsync ( orgUri ) ;
253
+ _authorityCache . UpdateAuthority ( orgName , authAuthority ) ;
254
+ }
255
+ else
256
+ {
257
+ _context . Trace . WriteLine ( "Authority was found in cache." ) ;
258
+ }
244
259
}
260
+
245
261
_context . Trace . WriteLine ( $ "Authority is '{ authAuthority } '.") ;
246
262
247
263
//
@@ -284,6 +300,30 @@ private async Task<IMicrosoftAuthenticationResult> GetAzureAccessTokenAsync(Uri
284
300
return result ;
285
301
}
286
302
303
+ internal /* for testing purposes */ static bool TryGetAuthorityFromHeaders ( IEnumerable < string > headers , out string authority )
304
+ {
305
+ authority = null ;
306
+
307
+ if ( headers is null )
308
+ {
309
+ return false ;
310
+ }
311
+
312
+ var regex = new Regex ( @"authorization_uri=""?(?<authority>.+)""?" , RegexOptions . Compiled | RegexOptions . IgnoreCase ) ;
313
+
314
+ foreach ( string header in headers )
315
+ {
316
+ Match match = regex . Match ( header ) ;
317
+ if ( match . Success )
318
+ {
319
+ authority = match . Groups [ "authority" ] . Value . Trim ( new [ ] { '"' , '\' ' } ) ;
320
+ return true ;
321
+ }
322
+ }
323
+
324
+ return false ;
325
+ }
326
+
287
327
private string GetClientId ( )
288
328
{
289
329
// Check for developer override value
0 commit comments