The question in posted in StackOverflow is as follows
We have a multi-tenant Web app (ASP.NET MVC 5.2.2) which is protected by Azure AD for user authentication and Web app calls the Backend Rest APIs (ASP.NET Web API 5.2.3) which is also protected VIA OAuth 2.0 bearer token. We are using Open-ID Connect protocol in Web app using Open-ID Connect OWIN module.
We have a requirement to get the tenant’s Azure AD directory users and groups into the application store using Graph API version 1.5 .We are using Microsoft ADAL 2.0 to get access token and refresh token and store them in the ADAL Token Cache Which is extended to Redis Cache.
The design is such a way that the Web App passes the user context to the Web API which includes SignInUserId, ObjectId ,TenantId and Web Api uses this context along with Web App identity to read the Access Token which already stored in TokenCache (if expired to refresh the access token) and to get tenant AD data using this token.
// get a token for the Graph without triggering any user interaction (from the cache, via multi-resource refresh token, etc)
ClientCredential clientcred = new ClientCredential(clientId, appKey);
// initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's EF DB
AuthenticationContext authContext = new AuthenticationContext(string.Format("https://login.microsoftonline.com/{0}", tenantID), new CustomTokenCache(signedInUserID));
AuthenticationResult result = await authContext.AcquireTokenSilentAsync(graphResourceID, clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
return result.AccessToken;
When reading the token the FailedToRefreshAccessToken exception is thrown even if the token is accessed immediately from cache .
Any help would be appreciated.
Code for custom token cache
public class PerUserCache
{
public string userUniqueId { get; set; }
public byte[] cacheBits { get; set; }
public DateTime LastWrite { get; set; }
}
public class CustomTokenCache : TokenCache
{
string userID;
PerUserCache Cache;
ICache database = CacheFactory.GetCacheInstance();
/// <summary>
///
/// </summary>
/// <param name="userID"></param>
public CustomTokenCache(string userID)
{
// associate the cache to the web api
this.userID = userID;
this.AfterAccess = AfterAccessNotification;
this.BeforeAccess = BeforeAccessNotification;
this.BeforeWrite = BeforeWriteNotification;
// look up the entry in the DB
Cache = database.Get<PerUserCache>(this.userID);
// place the entry in memory
this.Deserialize((Cache == null) ? null : Cache.cacheBits);
}
// clean up the DB
public override void Clear()
{
base.Clear();
}
enter code here
// Notification raised before ADAL accesses the cache.
// This is your chance to update the in-memory copy from the DB, if the in-memory version is stale
void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
if (Cache == null)
{
// first time access
Cache = database.Get<PerUserCache>(userID);
}`enter code here`
else
{ // retrieve last write from the DB
var status = database.Get<PerUserCache>(userID).LastWrite;
// if the in-memory copy is older than the persistent copy
if (status > Cache.LastWrite)
//// read from from storage, update in-memory copy
{
Cache = database.Get<PerUserCache>(userID);
}
}
this.Deserialize((Cache == null) ? null : Cache.cacheBits);
}
// Notification raised after ADAL accessed the cache.
// If the HasStateChanged flag is set, ADAL changed the content of the cache
void AfterAccessNotification(TokenCacheNotificationArgs args)
{
// if state changed
if (this.HasStateChanged)
{
Cache = new PerUserCache
{
userUniqueId = userID,
cacheBits = this.Serialize(),
LastWrite = DateTime.Now
};
//// update the DB and the lastwrite
database.Set<PerUserCache>(userID, Cache,null);
this.HasStateChanged = false;
}
}
void BeforeWriteNotification(TokenCacheNotificationArgs args)
{
// if you want to ensure that no concurrent write take place, use this notification to place a lock on the entry
}
}
}


FailedToRefreshAccessTokenexception? And it is also helpful to share the code aboutCustomTokenCacheclass. - Fei Xue - MSFT