react-aad
react-aad copied to clipboard
ClientAuthError: Token calls are blocked in hidden iframes
Library versions
-
react-aad-msal
: 2.3.1 -
msal
:1.2.0
Describe the bug
Sometimes the following error is displayed in Chrome Developer tools console.
index.js:1406 [ERROR] ClientAuthError: Token calls are blocked in hidden iframe
It appears 500+ times and then
MsalAuthProvider.js:75 Uncaught (in promise) RangeError: Maximum call stack size exceeded
Usually happens after a page reload or when coming back after a while of inactivity on the page.
The react application is not rendered in an iframe, it's a default create-react-app. However everything still seems to work fine after it happens.
Expected behavior No error messages in the console.
Desktop (please complete the following information):
- OS: Windows 10
- Browser: Chrome
- Version: 79.0.3945.117 (Official Build) (64-bit)
We also are seeing this behavior in react-aad-msal
2.3.2
+1 for
"msal": "1.2.0", "react": "^16.9.0", "react-aad-msal": "2.3.2",
Same here
"msal": "1.2.0", "react": "^16.12.0", "react-aad-msal": "2.3.1",
Error is being caught here: https://github.com/syncweek-react-aad/react-aad/blob/8db0b8cd9293f1667b71ef9351de22bb2cd41ac1/packages/react-aad-msal/src/MsalAuthProvider.ts#L152
Saw this thread in msal
https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/1156
I reverted back to older versions until the problem went away. This is what is working for me now.
"msal": "1.1.3", "react": "^16.12.0", "react-aad-msal": "1.1.3"
Took me a while, but through process of elimination worked out that you need auth.html
in the public folder.
Error Message:
Without it:
Fix:
Add this to your public folder:
Package.json:
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"axios": "^0.19.1",
"msal": "^1.2.1",
"react": "^16.12.0",
"react-aad-msal": "^2.3.2",
"react-dom": "^16.12.0",
"react-redux": "^7.1.3",
"react-scripts": "3.3.0",
"redux": "^4.0.5",
"typescript": "^3.7.5"
},
Nexith, in your case, you're probably have some sort of infinite authProvider.somthing()
loop because it is inside an <AzureAD>authProvider.somthing()</AzureAD>
Provider.
so it's like this
-
<AzureAD>
load - authProvider.somthing()
-
<AzureAD>
detect change, que load -
<AzureAD>
load - authProvider.somthing()
-
<AzureAD>
detect change, que load -
<AzureAD>
load - ...
And example that is most likely related to yours is:
getAccessToken() is called inside <AzureAD>
without an check to see if it has already been run.
Therefore causing an infinite refresh loop.
Solution:
<AzureAD provider={authProvider}>
{
(adProps: IAzureADFunctionProps) => {
const isAuthenticated = adProps.authenticationState === AuthenticationState.Authenticated;
const isUnauthenticated = adProps.authenticationState === AuthenticationState.Unauthenticated;
if (adProps.error) {
console.log(adProps.error.errorMessage);
}
if (isAuthenticated && adProps.accountInfo) {
if (adProps.accountInfo && adProps.accountInfo.jwtAccessToken === undefined && getAccessTokenCallback == null) {
getAccessTokenCallback = authProvider.getAccessToken().then(() => {
someGlobalVariable Set();
getAccessTokenCallback == null;
}
)
}
You'll need to probably store getAccessTokenCallback in some sort of global env.. setState usually forces an refresh too. I have an AuthRequestStore class that handles all of this refresh stuff.
See the sample js react app for an implementation that seems to have no errors.
Took me a while, but through process of elimination worked out that you need
auth.html
in the public folder.
I tried this and updated to msal 1.2.1
and react-aad-msal 2.3.2
adding in the option 'tokenRefreshUri'
const options = {
loginType: LoginType.Redirect,
tokenRefreshUri: window.location.origin + '/auth.html',
}
and now I get the following error instead: Unsafe JavaScript attempt to initiate navigation for frame with origin 'https://localhost:3000' from frame with URL 'https://*'. The frame attempting navigation of the top-level window is sandboxed, but the flag of 'allow-top-navigation' or 'allow-top-navigation-by-user-activation' is not set.
We do however call getAccessToken within the <AzureAD>
, shouldn't that be handle with with the cache config set automatically and check if it's already been run?
const config = {
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
}
https://github.com/syncweek-react-aad/react-aad#refreshing-access-tokens
Took me a while, but through process of elimination worked out that you need
auth.html
in the public folder.
Error Message:
Without it:
Fix:
Add this to your public folder:
Package.json:
"dependencies": { "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", "axios": "^0.19.1", "msal": "^1.2.1", "react": "^16.12.0", "react-aad-msal": "^2.3.2", "react-dom": "^16.12.0", "react-redux": "^7.1.3", "react-scripts": "3.3.0", "redux": "^4.0.5", "typescript": "^3.7.5" },
Using this approach works for us. Thank you!
At first I was experiencing the same issue but then I implemented @PathToLife solution and the problem seemed to go away, but I still have issues. Problem is that I still get this error ClientAuthError: Token calls are blocked in hidden iframe
sometimes. I notice that when the token expires and I need to fetch a new token, I will actually get a CORS exception and I will have to refresh the page a few times before the errors go away. Here's how I'm experiencing the issue:
I've written a function inside my authProvider
file that takes a partially applied (via lodash) axios request, completes it with the rawIdToken
in the header, and returns the result.
export const makeRequestWithToken = async (request) => {
try {
const token = await authProvider.acquireTokenSilent(authenticationParameters);
const res = await request({headers: { 'Authorization': `Bearer ${token.idToken.rawIdToken}`}});
return res;
}
catch (error) {
console.log(error);
}
}
Preliminary thoughts are that @PathToLife is correct and the problem is related to aquireTokenSilent
triggering <AzureAD>
to load and the loop occurring. He suggests putting it into the global env but I'm a bit confused. ~~Do we store the token in the env? If so, I'll still have the issue of having to refresh an expired token. Can someone help me spell it out for me? ~~ So I think what this means is to put some kind of flag in the global env to let the app know not to call the the acquireTokenSilent
function again.
and now I get the following error instead:
Unsafe JavaScript attempt to initiate navigation for frame with origin 'https://localhost:3000' from frame with URL 'https://*'. The frame attempting navigation of the top-level window is sandboxed, but the flag of 'allow-top-navigation' or 'allow-top-navigation-by-user-activation' is not set.
Can you share the full text from this error? I know it's unclear, but MSAL is trying to redirect to an error page that usually references some issue in your configuration. If you navigate to the second URL it should give you a more descriptive error.
@rosenjcb, It's a very useful clue that you're still seeing intermittent issues. I just pushed a release with a fix which resolves an issue where an incorrect redirectUri
was sometimes being used due to some bad reference issues. I suspect this could have been causing you to hit some edge cases that results in this error, even though you're correctly using the refreshTokenUri
and the blank auth.html
file.
and now I get the following error instead:
Unsafe JavaScript attempt to initiate navigation for frame with origin 'https://localhost:3000' from frame with URL 'https://*'. The frame attempting navigation of the top-level window is sandboxed, but the flag of 'allow-top-navigation' or 'allow-top-navigation-by-user-activation' is not set.
Can you share the full text from this error? I know it's unclear, but MSAL is trying to redirect to an error page that usually references some issue in your configuration. If you navigate to the second URL it should give you a more descriptive error.
There was a missing port number for localhost in the configuration for the Redirect URIs that seemed that have caused the error. Now running [email protected]
and [email protected]
with the auth.html
file and tokenRefreshUri
option pointing towards it, everything works fine.
EDIT: Also needed to add https://localhost:3000/auth.html
as a Redirect URI.
So with @AndrewCraswell update of 2.3.3
I have experienced a significant drop in errors. The only point in which I see any ClientAuthError
or CORS Exception is when a login is in progress (this basically only happens when a token expires and it attempts to fetch a new one). This is what my stacktrace looks like:
XHRClient.ts:42 GET https://login.microsoftonline.com/mycompany.onmicrosoft.com/v2.0/.well-known/openid-configuration net::ERR_FAILED
(index):1 Access to XMLHttpRequest at 'https://login.microsoftonline.com/mycompany.onmicrosoft.com/v2.0/.well-known/openid-configuration' from origin 'https://my-app.azurewebsites.net' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Logger.ts:19 [ERROR] ClientAuthError: Error: could not resolve endpoints. Please check network and try again. Details: function toString() { [native code] }
XHRClient.ts:42 GET https://login.microsoftonline.com/mycompany.onmicrosoft.com/v2.0/.well-known/openid-configuration net::ERR_FAILED
The work around behind this is to render a page saying during the InProgress
state which basically states "Hey, I'm logging you on right now. Try refreshing in a minute" but this obviously isn't ideal. I was hoping it'd just redirect the user to the app once it exits that InProgress
state and the user is authenticated.
I've experienced the same errors as those mentioned above and added everything mentioned as required to get rid of the errors. Now I'm with the following versions: "react-aad-msal": "^2.3.3" and "msal": "^1.2.1". The only error that I'm receiving is during the Login process: "[ERROR] ClientAuthError: Token calls are blocked in hidden iframes". However, it logs in but it's still annoying to have the error message appearing in the console.
If my log helps in anyways to understand the issue,
versions: "react-aad-msal": "2.3.3" and "msal": "1.2.1".
Not sure if this helps. https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/1194
I was seeing the same error. I was getting this error in the iFrame.
AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application:
Turns out the redirectUrl being sent in the iFrame included /auth.html. I added http://{domain}.com/auth.html to the list of accepted redirectURLs in App registration on Azure.
Another issue when we exclude the
"[ERROR] ClientAuthError: Tokew calls are blocked in hidden iframes"
is when I set the auth options field tokenRefreshUri: window.location.origin + '/auth.html' . Then the error is the following:
The frame attempting navigation of the top-level window is sandboxed, but the flag of 'allow-top-navigation' or 'allow-top-navigation-by-user-activation' is not set.
@KonstantinDinev Can you post the full text of the error, including the URLs? The error you're seeing is saying that there was an error, and AAD was trying to redirect to the error page but MSAL prevented it. If you click on the second URL it will take you to the error page so you can get the error description. Usually this is caused by a configuration issue.
@AndrewCraswell this is the screenshot of the error message. It is very similar to @Nexith 's problem but if I set the redirecUri to auth.html as he does, it returns a blank page without redirecting to the right page.

@KonstantinDinev, I've added both https://localhost:3000
and https://localhost:3000/auth.html
to the Azure Auth config for allowed callback urls.
Currently my application config looks like this for authProvider.js:
import { MsalAuthProvider, LoginType } from 'react-aad-msal';
const config = {
auth: {
authority: 'https://login.microsoftonline.com/**************',
clientId: '**************',
redirectUri: 'https://' + window.location.host
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
const authenticationParameters = {
scopes: [
'**************'
]
};
const options = {
loginType: LoginType.Redirect,
tokenRefreshUri: window.location.origin + "/auth.html"
};
export const authProvider = new MsalAuthProvider(config, authenticationParameters, options);
Still seeing intermittent errors where it get's blocked in the iFrame where you have to stop the page and then click refresh for it to login, but it happens very rarely and only in our dev/prod environment. We are still in development and testing phase so it's not an issue for us at the moment.
@Nexith thank you very much! I had a configuration issue for the local environment as what @AndrewCraswell pointed earlier. I was targeting http instead of https for the redirect. However, I had to start the project with HTTPS=true npm start
and it worked with /auth.html
included but the console was still firing [ERROR] ClientAuthError: Token calls are blocked in hidden iframes
plus err_cert_invalid
.
My work around for Production eliminated the issues. I will post my code:
authProvider.js
import { MsalAuthProvider, LoginType } from 'react-aad-msal';
import Constants from '../api/constants';
const config = {
auth: {
authority: Constants.AZURE_AD.authority,
clientId: Constants.AZURE_AD.clientId,
redirectUri: Constants.SYSTEM.frontend,
postLogoutRedirectUri: Constants.SYSTEM.frontend,
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
const options = {
loginType: LoginType.Redirect,
tokenRefreshUri: Constants.AZURE_AD.refreshTokenUri
};
const authenticationParameters = {
scopes: [***********]
};
export const authProvider = new MsalAuthProvider(config, authenticationParameters, options);
Constants.js
const { selectedServer, selectedClientId } = config;
const isDevelopment = (process.env.NODE_ENV === 'development');
const FRONTEND_ENV = (isDevelopment) ? 'http://localhost:3000' :
`https://${selectedServer}/`;
const postReditect = (isDevelopment) ?
'http%3A%2F%2Flocalhost%3A3000' :
`https%3A%2F%2F${selectedServer}`;
const Constants = {
AZURE_AD: {
url: 'https://login.microsoftonline.com',
tenant: '******************************',
clientId: `${selectedClientId}`,
redirect: `?post_logout_redirect_uri=${postReditect}`,
get authority() {
return `${this.url}/${this.tenant}`
},
get logout() {
return `${this.url}/${this.tenant}/oauth2/logout${this.redirect}`
},
get refreshTokenUri() {
return (isDevelopment) ? FRONTEND_ENV : `${FRONTEND_ENV}/auth.html`
}
}
};
This works great with no console errors for Production. I decided to leave it using http for local development without the /auth.html
in tokenRefreshUri. For local development it still shows [ERROR] ClientAuthError: Token calls are blocked in hidden iframes
but it works.
Thanks!
Did as @Nexith on this, adding http://localhost:3000/auth.html to the Redirect URI for the app registration resolved the issue.