microsoft-authentication-library-for-dotnet icon indicating copy to clipboard operation
microsoft-authentication-library-for-dotnet copied to clipboard

[Bug] GetAccount returns null in AuthCode flow in ASP.NET OWIN app

Open alfaromeo90210 opened this issue 2 years ago • 1 comments

Logs and network traces Error Code: user_null Error Message: No account or login hint was passed to the AcquireTokenSilent call. I have also noticed that this happens under the following scenarios:

Which version of MSAL.NET are you using? MSAL.NET 4.46.1.0

Platform .NET 4.8

What authentication flow has the issue?

  • Desktop / Mobile
    • [ ] Interactive
    • [ ] Integrated Windows Authentication
    • [ ] Username Password
    • [ ] Device code flow (browserless)
  • Web app
    • [x] Authorization code
    • [ ] On-Behalf-Of
  • Daemon app
    • [x] Service to Service calls

Other?

Is this a new or existing app? a. The app is in production, and I have upgraded to a new version of MSAL.

Repro

public async Task<string> GetAccessTokenAsync()
{
    string accessToken;
    UserExternalApp.Scope = string.IsNullOrWhiteSpace(UserExternalApp.Scope) ? "" : UserExternalApp.Scope;

    // Load the app config from web.config
    var microsoftScopes = UserExternalApp.Scope.Replace(' ', ',').SplitAndTrim(new char[] { ',' }).ToList();
    var accountID = UserExternalApp.ExternalUserAccountID;

    var app = ConfidentialClientApplicationBuilder.Create(ClientID)
        .WithRedirectUri(DefaultRedirectUrl) // https:\//mywebsite.com
        .WithClientSecret(Secret)
        .Build();

    app.AddDistributedTokenCache(services =>
    {
        services.AddDistributedSqlServerCache(options =>
        {
            options.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Connection"].ConnectionString;
            options.SchemaName = "dbo";
            options.TableName = "TokenCache";
            options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
        });
    });

    try
    {
        var account = await app.GetAccountAsync(accountID);
        var query = app.AcquireTokenSilent(microsoftScopes, account); // This is where the error is thrown
        var acquireTokenSilent = await query.ExecuteAsync();

        accessToken = acquireTokenSilent.AccessToken;
    }
    catch
    {
        // This is the error thrown:
        // Exception Type: MsalUiRequiredException
        // Error code: user_null
        // Exception Details: No account or login hint was passed to the AcquireTokenSilent call.  
        throw;
    }
    return accessToken;
}

Expected behavior A valid account should be provided by GetAccountAsync(). Using this, AcquireTokenSilent() should get the access token

Actual behavior Account returned by GetAccountAsync() is null. As a result, subsequent call to AcquireTokenSilent() fails.

Update I resolved this particular issue by creating an implementation of IAccount and populating the homeAccountId field. Interestingly, in my case, the above error went away when I implemented this workaround. I feel that this is a bug that the team should investigate.

alfaromeo90210 avatar Aug 30 '22 21:08 alfaromeo90210

@alfaromeo90210 Sorry for the late reply. In your workaround do you set homeAccountId to UserExternalApp.ExternalUserAccountID or how do you construct it? Is your app multi tenant? MSAL needs the home tenant of the user, so for guest accounts, if you pass tenant id of that guest tenant, then MSAL won't find the account in the cache. For reference, there's this PR that shows how get the home account id by passing the client_info flag.

pmaytak avatar Sep 17 '22 06:09 pmaytak

Closing as duplicate of https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/3642 - see workarounds there.

bgavrilMS avatar Dec 21 '22 13:12 bgavrilMS