handleRedirectCallback not useful in SPA, documentation misleading
See original GitHub issuePlease follow the issue template below. Failure to do so will result in a delay in answering your question.
Library
msal@1.2.1
Framework
React v16, + parcel bundler
Description
The redirect flow handles the return from a redirect in a way that it does an additional navigation to hide the hash and return the user to the page they were on when they redirected. So far this is good.
However… using the handleRedirectCallback only gets called on the first redirect (with the hash) and on the second redirect (to remove the hash) the response is now removed. This means that on a page where such a second navigation happens, the page is reloaded such that the redirect response is no longer available.
What does work is using the silent token acquisition to get the current token as stored by msal from a redirect (e.g. get it if you have it, even if silent acquisition isn’t supported).
This means the doc indicating handleRedirectCallback is either incomplete or incorrect, because the working sample code doesn’t use it to get a token but instead also uses the silent token acquisition to get the most recent state of a token for that request.
In short, you can’t always use handleRedirectCallback as the documentation indicates because it doesn’t actually work when the msal library does the second redirect, but what does work is silent token acquisition, which isn’t documented for the redirect pattern. I’m not entirely sure why the pattern can’t be this instead in practice as its what happens in the working pattern.
token = userAgent.acquireCachedToken(authenticationParams);
This would also avoid the need to deal with the promise part of acquireTokenSilently.
Security
I don’t believe so
Regression
Don’t know, have only ever used this version.
Configuration
Please provide your MSAL configuration options.
const tenantId = 'e3b48527-4cbe-42a2-b4d2-11b3cc7a86fc';
const clientId = '8b1d6b0d-3777-4099-b6bd-6db1ff2ddb02';
const redirectUri = 'http://localhost:1234/';
const apiId = `062da26d-b94e-424d-b119-b6b12fc46618`
const apiUri = `api://${apiId}`;
const scopes = [`${apiUri}/all:full`];
const accessTokenRequest: MSAL.AuthenticationParameters = {
scopes
};
const msalAppConfig: MSAL.Configuration = {
auth: {
clientId,
authority: `https://login.microsoftonline.com/${tenantId}/`,
redirectUri
},
cache: {
cacheLocation: 'localStorage'
}
}
Reproduction steps
export const AuthProvider = (props: React.PropsWithChildren<{}>) => {
// access token is state as it comes back via a promise.
const [accessToken, setAccessToken] = React.useState<string>();
const userAgent = new MSAL.UserAgentApplication(msalAppConfig);
userAgent.handleRedirectCallback((error, result) => {
// msal requires a redirect callback, even though can't use it to
// get the result as it will redirect again after it has the result
// and not provide the result of the call back on the second
// redirect.
});
function onLogin() {
userAgent.loginRedirect(accessTokenRequest);
}
function onLogout() {
console.log('clear access token');
setAccessToken(undefined);
userAgent.logout();
}
async function updateToken() {
try {
// silent will renew if possible, but also reply with
// a token gotten from a redirect workflow.
const result = await userAgent.acquireTokenSilent(accessTokenRequest);
if (result && result.accessToken) {
console.log(`silently acquired token ${result.accessToken}`);
setAccessToken(result.accessToken);
}
} catch (error) {
console.log(`access token not available`);
}
}
React.useEffect(() => { updateToken(); return; }, []);
const context: AppContext = {
login: onLogin,
logout: onLogout,
accessToken
};
return (
<AppContext.Provider value={context}>
{props.children}
</AppContext.Provider>
);
}
Expected behavior
I expected there to be a clearer way to get the token returned via a redirectLogin that wasn’t invalidated by msal cleaning up the redirect hash and didn’t require redundant caching in the app as its already cached by msal as evident in acquire token silently.
Browsers
Not sure, but does happen in current Chrome. Version 79.0.3945.130 (Official Build) (64-bit)
Issue Analytics
- State:
- Created 4 years ago
- Reactions:4
- Comments:17 (6 by maintainers)

Top Related StackOverflow Question
I read through that quick start, and it got me up and running with the loginpopup method in a couple of hours, but it doesn’t provide any information on how to properly implement the redirect flow.
For instance this diagram is great: https://docs.microsoft.com/en-us/azure/active-directory/develop/media/quickstart-v2-javascript/javascriptspa-intro.svg … but it doesn’t really make sense with a redirect flow given that the token response is delivered to a different instance of your application then the one that requested it.
It also doesn’t tell you the most critical thing about the redirect method which is where you actually need to put each of those function calls in relation to your app’s lifecycle. For instance the way I had implemented it originally worked, but the last update caused all my sites to break and go into infinite redirect loops because of when my functions were being called. I rewrote it and got it working again, but I still don’t know if I did it the way I’m supposed to because I haven’t been able to find a complete redirect example.
@tnorling thanks, just upgraded and this is feeling more intuitive already!