Inconvenient ADAL JS Angular with simultaneous CORS requests continued
Recently I wrote about the issue with issuing multiple CORS requests using the ADAL JS library. It turns out that there is even more to it.
Inconvenient issuing multiple CORS requests simultaneously with the ADAL JS Angular library
The ADAL JS Angular library simplifies building Single Page Applications that communicate with Azure and Office 365. Unfortunately, if you’re issuing multiple CORS request to the same resources simultaneously only one of them will get resolved. In my recent post I shared a solution that you can use to work around this issue.
In my solution I suggested using a custom function to check whether a request for the particular access token has already been issued and if so, delay executing the request until the request for the access token has been resolved. Following was the custom function responsible for checking the access token request status:
function accessTokenRequestInProgress(endpoint, adal) {
var requestInProgress = false;
var resource = adal.getResourceForEndpoint(endpoint);
var keysString = adal._getItem(adal.CONSTANTS.STORAGE.TOKEN_KEYS) || '';
if (keysString.indexOf(resource + adal.CONSTANTS.RESOURCE_DELIMETER) > -1) {
var tokenStored = adal.getCachedToken(resource);
if (tokenStored === null) {
requestInProgress = true;
}
}
return requestInProgress;
}
While it seems to work at first, it turns out that there is a scenario which this function doesn’t take into account.
Refreshing expired access tokens
Access token are valid only for a certain period of time. After that they expire and need to be refreshed. If you are using the ADAL JS Angular library in your Single Page Application it will automatically check on each request if the access token that you have is still valid and will retrieve a new one if necessary. Unfortunately the function I built to allow multiple simultaneous CORS requests disallows refreshing access tokens.
While ADAL JS manages access tokens for you it does a poor job of communicating the status of each token and any requests that might be in progress. I based my workaround on the assumption that ADAL JS is requesting a token when the key of the related resource has been added to the list of resource keys but no access token is available for that resource yet. In such case I delay executing the request that needs the particular access token. What I’ve found out is that when an access token expires, ADAL JS clears the expired access token, sets its expiration time to 0 but keeps the resource key in the resource keys list – which is exactly the same state as when it’s retrieving an access token! As you can imagine this causes the SPA to wait infinitely delaying the request which normally would get a fresh access token.
Yet another workaround
ADAL JS doesn’t communicate the state of the different access token and related requests. With that it’s hard to determine what your application should do if no valid access token is available without modifying the ADAL JS library. A possible workaround, that I am testing at the moment, is to parse ADAL JS’ logs to see if it recently issued a request for an access token and if so delay your request to wait for the access token to be retrieved. Following is the extended version of the function I’ve built before:
function accessTokenRequestInProgress(endpoint, adal) {
var requestInProgress = false;
var resource = adal.getResourceForEndpoint(endpoint);
var keysString = adal._getItem(adal.CONSTANTS.STORAGE.TOKEN_KEYS) || '';
if (keysString.length > 0 &&
(keysString === resource ||
keysString.indexOf(resource + adal.CONSTANTS.RESOURCE_DELIMETER) > -1)) {
var tokenStored = adal.getCachedToken(resource);
if (tokenStored === null &&
isTokenRetrievalStarted(resource, adal)) {
requestInProgress = true;
}
}
return requestInProgress;
}
function isTokenRetrievalStarted(resource, adal) {
var isTokenRetrievalStarted = false;
var log = adal._getItem(adal.CONSTANTS.LOG_ENTRY + resource);
if (log) {
var logEntries = log.split(';');
var lastEntry = logEntries[logEntries.length-2];
var entryTime = new Date(lastEntry.substr(0, lastEntry.indexOf('GMT:')+3));
var now = new Date();
isTokenRetrievalStarted = now.getTime() < entryTime.getTime() + 10000;
}
return isTokenRetrievalStarted;
}
In the accessTokenRequestInProgress function we now not only check whether the particular access token is null but also if a token retrieval request has been started recently. We determine that by reading the time of last log entry for the particular resource. If the entry has been logged within the last 10 seconds we assume the request is still in progress. The 10 seconds time span is an assumption to cater for possible network or Azure AD delays.
So far this workaround has been working allowing both for initially retrieving access tokens and refreshing expired tokens but as you can see the process is far from being robust. It would be helpful if the future versions of the ADAL JS and ADAL JS Angular libraries provided us with a more reliable way of dealing with multiple simultaneous CORS requests. In the meanwhile this solution might help you get the job done.
Summary
The ADAL JS Angular library simplifies building Single Page Applications that communicate with Azure and Office 365. Unfortunately, if you’re issuing multiple CORS request to the same resources simultaneously only one of them will get resolved. Neither ADAL JS nor ADAL JS Angular library offer any functionality to manage such requests and require a custom mechanism if you want to support such scenario. When implementing your workaround you should ensure that you not only support initial retrieval of access tokens but also allow for refreshing expired tokens.