Unfortunately (as with most things auth-related), there are some gotcha’s to be aware of. One relates to how ADAL obtains refresh tokens in this crazy world of implicit auth.
Implicit Auth Flow
Implicit auth allows for the application developer to not have to host their own token authentication service. The ADAL.js and the Azure AD auth endpoint do all the heavy lifting:
It’s the bottom third of the diagram (after the token expires) that causes the issue I am addressing in this post. This is where ADAL.js creates a hidden iframe (browser fragment) that sends a request to get a fresh token. This will show up in the DOM (if you inspect in the browser dev tools) as an iframe element with an ID of “adalRenewFrame” followed by the endpoint that it is renewing the token for (in the below example this is https://graph.microsoft.com).
What’s the problem?
What’s the solution?
The ADAL.js team recommend a couple of different approaches to getting around this issue in their FAQ page on github. The simpler solution is to control how your app is bootstrapped so that it only loads if the window === window.parent (i.e. it isn’t in an iframe), which is fine if you have this kind of control over how your app starts (like with AngularJS or React). But this won’t always suit.
The other option is to have an alternative redirect page that is targeted after the iframe renews the token (by specifying it in the ADAL config with the redirectUri property). N.B. you have to specify the exact url in the Azure AD app settings for your app as well.
Suffice it to say, just pointing to an empty page doesn’t do the trick and there is a bunch of hassle with getting everything working (see the comments on this gist for the full adventure), but to cut a long story short – here’s what worked for me.
The redirect page itself redirects back to our SPA (in this case, the root of the web app) only if the window === window.parent (not an iframe) and passes the token etc in the window.location.hash as well. See the below example.
Hope this saves you some time/grief with getting this all working. I haven’t seen a satisfactory write up of a solution to this issue before (hence this post).
Any questions, suggested improvements, please let me know in the comments!
‘Till next time…