简体   繁体   中英

ADB2C - Handling the "Null user was passed in AcquiretokenSilent API" error when token cache expires

I've managed to configure my application to authenticate using ADB2C, and it seems to work fine. The ADB2C code implemented is a tweak of one of Microsoft's samples, in which they use a SessionTokenCache class to manage instances of TokenCache. In my application, I retrieve the access token as follows:

private async Task<string> _getAccessToken(IConfidentialClientCredentials credentials)
        {
            if (this.HasCredentials())
            {
                var clientCredential = new ClientCredential(credentials.ClientSecret);
                var userId = this._getUserIdClaimValue();

                var tokenCache = new SessionTokenCache(_httpContextResolver.Context, userId);                

                var confidentialClientApplication = new ConfidentialClientApplication(
                    credentials.ClientId,
                    credentials.Authority,
                    credentials.RedirectUri,
                    clientCredential,
                    tokenCache.GetInstance(),
                    null);

                IAccount account = confidentialClientApplication.GetAccountsAsync().Result.FirstOrDefault();
                if (account == null)
                {
                    return "";
                }

                var authenticationResult = await confidentialClientApplication.AcquireTokenSilentAsync(
                    credentials.ApiScopes.Split(' '),
                    account,
                    credentials.Authority,
                    false);

                return authenticationResult.AccessToken;
            }
            else
            {
                return "";
            }
        }

This method is used to get the access token and pass it in the request header of an HttpClient as follows:

...

 using (var request = new HttpRequestMessage(HttpMethod.Get, address.AbsoluteUri))
            {
                if (this.HasCredentials())
                {
                    string accessToken = await this._getAccessToken(_confidentialClientCredentials);

                    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
                }

                using (HttpResponseMessage response = await this.SendAsync(request))
                {
                    //result-processing logic
                }
...

The problem is that when the app is restarted, the user remains authenticated through the ADB2C cookie, but confidentialClientApplication.GetAccountsAsync().Result.FirstOrDefault(); returns null. This probably happens because the token cache is destroyed on app restart, so I can probably use a Redis cache to fix.

My main issue however is how to handle the situation of having a null Account but being "authenticated" at the same time. How are my requests to my website being authenticated even though I have a null Account? Shouldn't it fail and redirect me to login page, for example?

I tried looking into Authorization filters, and using the following code to hook up to the auth process and validate if user is null there, but to no avail. The following events are not being called ever (this is in ConfigureServices):

services.AddAuthentication(AzureADB2CDefaults.AuthenticationScheme)            
            .AddAzureADB2C(options => Configuration.Bind("ActiveDirectoryB2C", options))
            .AddAzureADB2CBearer(options => Configuration.Bind("ActiveDirectoryB2C", options))
            .AddCookie((options) => new CookieAuthenticationOptions
            {
                Events = new CookieAuthenticationEvents
                {
                    OnValidatePrincipal = context =>
                    {
                        // context.Principal gives you access to the logged-in user
                        // context.Properties.GetTokens() gives you access to all the tokens

                        return Task.CompletedTask;
                    },
                    OnSignedIn = context =>
                    {
                        return Task.CompletedTask;
                    }
                }
            });

It all feels a bit too abstracted for me to make any sense of what's going on. Either that, or I'm missing something fundamental.

Note: The error " Null user was passed in AcquiretokenSilent API. Pass in a user object or call acquireToken authenticate. " is thrown if I try to pass the null account to the confidentialClientApplication.AcquireTokenSilentAsync() method.

I solved with this code:

protected override void OnException(ExceptionContext filterContext)
{
 if (filterContext.Exception is Microsoft.Identity.Client.MsalUiRequiredException)
 {
   RedirectToAction("SignIn", "Account");
 }
 else {
    //Do your logging
    // ...
 }
}

I'll search for a better solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM