AzureAD/azure-activedirectory-library-for-dotnet

Refreshing Token using AcquireTokenSilentAsync fails only in Service API but works on a Console App

haricr opened this issue · 1 comments

If you think that your issue falls into the above categories, please fill in the form below.

Which Version of ADAL are you using ?
Microsoft.IdentityModel.Clients.ActiveDirectory.4.5.1

Which platform has the issue?
net45

What authentication flow has the issue?

  • Web API - OBO

Is this a new or existing app?
This is a new app

Repro
I wrote to library that exposes following API
a. perform OBO for user token. Serialize and save the accessToken and TokenCache
b. refresh the token by deseriazling the cache

When i test this library using console App everything works fine

Then i exposed this library as a Service API. In the service API, the refresh always fails with error

Microsoft.IdentityModel.Clients.ActiveDirectory.AdalSilentTokenAcquisitionException: Failed to acquire token silently as no token was found in the cache. Call method AcquireToken
at Microsoft.IdentityModel.Clients.ActiveDirectory.Internal.Flows.AcquireTokenSilentHandler.SendTokenRequestAsync()

I am calling the refresh 2 mins after the token generation, I validated with logs that the token cache is getting deserialized.

Code to refresh
RefreshTokenCache tokenCache = new RefreshTokenCache(tokenCacheData, this.Logger);

                AuthenticationContext context = new AuthenticationContext(authority.AbsoluteUri, tokenCache);
                ClientAssertionCertificate credential = new ClientAssertionCertificate(this.AppId, this.Certificate);
                AuthenticationResult authResult = await context.AcquireTokenSilentAsync(this.Audience, credential, new UserIdentifier(oid, UserIdentifierType.UniqueId));

Token Cache
public class RefreshTokenCache : TokenCache
    {
        public string SerializedCache { get; set; }
        public RefreshTokenCache(string cache)
        {
            this.AfterAccess = AfterAccessCacheNotification;
            this.BeforeAccess = BeforeAccessCacheNotification;
            this.SerializedCache = cache;
        }

        public void BeforeAccessCacheNotification(TokenCacheNotificationArgs args)
        {
            if (!string.IsNullOrEmpty(this.SerializedCache))
            {         
                char[] cacheData = this.SerializedCache.ToCharArray();
                this.Deserialize(Convert.FromBase64CharArray(cacheData, 0, cacheData.Length));
            }
        }
        
        public void AfterAccessCacheNotification(TokenCacheNotificationArgs args)
        {
            this.SerializedCache = Convert.ToBase64String(this.Serialize());
        }
    }

Expected behavior
I am not sure what is the difference between running in Console App and exposing it as Service APIs

Actual behavior
Microsoft.IdentityModel.Clients.ActiveDirectory.AdalSilentTokenAcquisitionException: Failed to acquire token silently as no token was found in the cache. Call method AcquireToken
at Microsoft.IdentityModel.Clients.ActiveDirectory.Internal.Flows.AcquireTokenSilentHandler.SendTokenRequestAsync()

@haricr, did you read https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/wiki/Token-cache-serialization#custom-token-cache-serialization-in-web-applications--web-api ? the article in general explains the differences between a token cache for public client applications and for confidential client applications.