1
1
using System ;
2
2
using System . Collections . Generic ;
3
3
using System . IO ;
4
+ using System . Linq ;
4
5
using System . Net . Http ;
5
6
using System . Threading . Tasks ;
6
7
using GitCredentialManager . Interop . Windows . Native ;
@@ -78,7 +79,7 @@ public async Task<IMicrosoftAuthenticationResult> GetTokenAsync(
78
79
bool hasExistingUser = ! string . IsNullOrWhiteSpace ( userName ) ;
79
80
if ( hasExistingUser )
80
81
{
81
- result = await GetAccessTokenSilentlyAsync ( app , scopes , userName ) ;
82
+ result = await GetAccessTokenSilentlyAsync ( app , scopes , userName , msaPt ) ;
82
83
}
83
84
84
85
//
@@ -116,7 +117,7 @@ public async Task<IMicrosoftAuthenticationResult> GetTokenAsync(
116
117
// account then the user may become stuck in a loop of authentication failures.
117
118
if ( ! hasExistingUser && Context . Settings . UseMsAuthDefaultAccount )
118
119
{
119
- result = await GetAccessTokenSilentlyAsync ( app , scopes , null ) ;
120
+ result = await GetAccessTokenSilentlyAsync ( app , scopes , null , msaPt ) ;
120
121
121
122
if ( result is null || ! await UseDefaultAccountAsync ( result . Account . Username ) )
122
123
{
@@ -281,7 +282,8 @@ internal MicrosoftAuthenticationFlowType GetFlowType()
281
282
/// <summary>
282
283
/// Obtain an access token without showing UI or prompts.
283
284
/// </summary>
284
- private async Task < AuthenticationResult > GetAccessTokenSilentlyAsync ( IPublicClientApplication app , string [ ] scopes , string userName )
285
+ private async Task < AuthenticationResult > GetAccessTokenSilentlyAsync (
286
+ IPublicClientApplication app , string [ ] scopes , string userName , bool msaPt )
285
287
{
286
288
try
287
289
{
@@ -295,9 +297,27 @@ private async Task<AuthenticationResult> GetAccessTokenSilentlyAsync(IPublicClie
295
297
{
296
298
Context . Trace . WriteLine ( $ "Attempting to acquire token silently for user '{ userName } '...") ;
297
299
298
- // We can either call `app.GetAccountsAsync` and filter through the IAccount objects for the instance with the correct user name,
299
- // or we can just pass the user name string we have as the `loginHint` and let MSAL do exactly that for us instead!
300
- return await app . AcquireTokenSilent ( scopes , loginHint : userName ) . ExecuteAsync ( ) ;
300
+ // Enumerate all accounts and find the one matching the user name
301
+ IEnumerable < IAccount > accounts = await app . GetAccountsAsync ( ) ;
302
+ IAccount account = accounts . FirstOrDefault ( x => StringComparer . OrdinalIgnoreCase . Equals ( x . Username , userName ) ) ;
303
+ if ( account is null )
304
+ {
305
+ Context . Trace . WriteLine ( $ "No cached account found for user '{ userName } '...") ;
306
+ return null ;
307
+ }
308
+
309
+ var atsBuilder = app . AcquireTokenSilent ( scopes , account ) ;
310
+
311
+ // Is we are operating with an MSA passthrough app we need to ensure that we target the
312
+ // special MSA 'transfer' tenant explicitly. This is a workaround for MSAL issue:
313
+ // https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/3077
314
+ if ( msaPt && Guid . TryParse ( account . HomeAccountId . TenantId , out Guid homeTenantId ) &&
315
+ homeTenantId == Constants . MsaHomeTenantId )
316
+ {
317
+ atsBuilder = atsBuilder . WithTenantId ( Constants . MsaTransferTenantId . ToString ( "D" ) ) ;
318
+ }
319
+
320
+ return await atsBuilder . ExecuteAsync ( ) ;
301
321
}
302
322
}
303
323
catch ( MsalUiRequiredException )
0 commit comments