11using System ;
22using System . Collections . Generic ;
33using System . IO ;
4+ using System . Linq ;
45using System . Net . Http ;
56using System . Threading . Tasks ;
67using GitCredentialManager . Interop . Windows . Native ;
@@ -78,7 +79,7 @@ public async Task<IMicrosoftAuthenticationResult> GetTokenAsync(
7879 bool hasExistingUser = ! string . IsNullOrWhiteSpace ( userName ) ;
7980 if ( hasExistingUser )
8081 {
81- result = await GetAccessTokenSilentlyAsync ( app , scopes , userName ) ;
82+ result = await GetAccessTokenSilentlyAsync ( app , scopes , userName , msaPt ) ;
8283 }
8384
8485 //
@@ -116,7 +117,7 @@ public async Task<IMicrosoftAuthenticationResult> GetTokenAsync(
116117 // account then the user may become stuck in a loop of authentication failures.
117118 if ( ! hasExistingUser && Context . Settings . UseMsAuthDefaultAccount )
118119 {
119- result = await GetAccessTokenSilentlyAsync ( app , scopes , null ) ;
120+ result = await GetAccessTokenSilentlyAsync ( app , scopes , null , msaPt ) ;
120121
121122 if ( result is null || ! await UseDefaultAccountAsync ( result . Account . Username ) )
122123 {
@@ -281,7 +282,8 @@ internal MicrosoftAuthenticationFlowType GetFlowType()
281282 /// <summary>
282283 /// Obtain an access token without showing UI or prompts.
283284 /// </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 )
285287 {
286288 try
287289 {
@@ -295,9 +297,27 @@ private async Task<AuthenticationResult> GetAccessTokenSilentlyAsync(IPublicClie
295297 {
296298 Context . Trace . WriteLine ( $ "Attempting to acquire token silently for user '{ userName } '...") ;
297299
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 ( ) ;
301321 }
302322 }
303323 catch ( MsalUiRequiredException )
0 commit comments