Hello,
we are currently using angular 8 and msal-angular 1.0.0-beta4 to log into Azure B2C. All works fine the first time we log in: the popup windows appears and we can enter the credentials. Once the token expires (after 1 hour), the token is refreshed silently either by MsalGuard or by the MsalInterceptor. Both call acquireTokenSilent from MsalService, The problem is when the request is done to Azure B2c using https://YOURB2C.b2clogin.cpm/YOURB2C.onmicrosoft.com/YOURPOLICY/oauth2/v2.0/authorize?response_type=token_id&scope=openid%.... , the answer (200 OK) contains the header 'X-Frame-Options' set to 'deny'. This cause the browser to stop with an error telling it can't display the result in a frame.
Has somebody a way to make the silent refresh of the token work? Is there a workaround that is really working?
Thx
Giovanni
@giovannimele Generally speaking, for this error we recommend calling acquireTokenPopup or acquireTokenRedirect to resolve. I realize that isn't possible directly using the built-in Guard and Interceptor, so for now (until we enhance them to provide better error handling), you could look at creating a copy of the existing Guard/Interceptor and having them invoke those methods when there is an error?
@jasonnutter Thank you for your reply. To resolve this problem we already implemented your proposed solution, even if it means that every hour, we have the login popup appearing and disappearing briefly.
Do you have an ETA for the resolution of this problem?
Regards
Giovanni
@giovannimele We don't have an ETA yet, unfortunately.
@tnorling Hello, can you give me the reason why you consider this problem as low priority? For me the silent refresh is totally broken and doesn't work...
I'm having the same issue in React.
I log in the user and when I try to do acquireTokenSilent to get a token for an API I get the x-frame-options = deny error
@giovannimele This is partly due to the fact this is caused by the B2C service, not the library.
@martinlarosa Are you able to invoke acquireTokenPopup or acquireTokenRedirect when this occurs? That is our recommended workaround, currently.
Note, there are some B2C policies (e.g. those that require MFA) that we know will experience this error with acquireTokenSilent. See https://github.com/Azure-Samples/active-directory-b2c-javascript-msal-singlepageapp/issues/49
@jasonnutter Thank you for your reply. To resolve this problem we already implemented your proposed solution, even if it means that every hour, we have the login popup appearing and disappearing briefly.
Do you have an ETA for the resolution of this problem?Regards
Giovanni
Can you publish your solution?
Hi @jasonnutter, thanks for the reply.
We are able to make it work just by forcing acquireToken to occur always with popup.
On the policies side, we have to unfortunately use custom policy to support multitenant AD login. So we have a custom policy providing local accounts as well as AD accounts.
I guess the policies listed in the link you shared are pre-built User Flows, right? Would that make a difference?
Anything we can try to help uncover more information? Or any suggestion for when we are using custom policies?
Thanks!
Hi @jasonnutter, thanks for the reply.
We are able to make it work just by forcing acquireToken to occur always with popup.On the policies side, we have to unfortunately use custom policy to support multitenant AD login. So we have a custom policy providing local accounts as well as AD accounts.
I guess the policies listed in the link you shared are pre-built User Flows, right? Would that make a difference?
Anything we can try to help uncover more information? Or any suggestion for when we are using custom policies?Thanks!
Yeah, I believe custom policies (esp if they require MFA) are likely to run into this limitation.
@jasonnutter I am building a Next JS app.Right now the client side hydrates with msal code, but ran into iframe issues with silent refresh.Would you recommend doing auth via server side flow (authorization code flow I believe) to get around these issues.That also means the Next JS server acts as a proxy in between my client(browser app) and server(APIs).
@jasonnutter @hamiltonha I've commented on #1161 previously and the bug activity appears to have died out on that guy unfortunately. Do your teams (MSAL client or B2C server) know the exact cause of this? During the private preview of 2.0 (using auth code instead), the solution was going to be moving to popup or full page redirect anyway due to browsers such as Safari making 3rd party cookies in IFRAMES a task to re-enable. So I realize this issue may just go away by switching to a popup. However, is this a guaranteed fix? I would hate to burn time on this again only to find implicit-style redirect-based refreshes simply aren't working anywhere (popup, full-page or IFRAME). Just looking for someone to say "yes, definitely move forward with popup refreshes and we guarantee the server will grant a new access token."
With Blazor WASM officially live and generally available, folks are going to run into the exact same issue unless they use the full auth code flow and grant refresh tokens as well (and therefore can use the /token endpoint with their refresh token instead of a redirect to /authorize). In a public untrusted client with no option to use cookies for refresh tokens, this isn't going to past muster for many security teams.
I love what you guys do but I think we're all a tad curious as to the state of this flow for SPAs. Thank you!
@jake-brandt in the spirit of discussion .. Auth0 which is also an identity platform like AD B2C recommends refresh-token-rotation for SPA's and public clients. Below message from https://auth0.com/docs/flows/concepts/auth-code-pkce
Recent advancements in user privacy controls in browsers adversely impact the user experience by preventing access to third-party cookies. Auth0 recommends using Refresh Token Rotation, which provides a secure method for using refresh tokens in SPAs while providing end-users with seamless access to resources without the disruption in UX caused by browser privacy technology like ITP.
Maybe this would be something which MSAL 2.0 could look into.
cc @jasonnutter
@niwsa that's basically what our teams here recommended to do since it's not supported by B2C yet (refresh token rotation). We'd have to stand up our own API endpoints to act as an IDP that passes through to B2C but lets us implement token rotation then. Once B2C added support, we'd be able to go directly to B2C's /token endpoint instead.
So currently our choices are popup refresh (a la implicit style) or creating a rotating token version on our own and talking to B2C from server-to-server then.
My concern with popup refresh is that:
A) it's a UX quirk albeit industry-common at least
B) what if these issues in IFRAMES follow us to the popup version? I don't mean the X-Frame-Options issue but the fact that the redirect isn't even happening. I've looked at the source of that page generating the X-Frame-Options issues and it's (in my case at least) the original interactive page being displayed and its associated 200 status - we don't want a 200 response in an IFRAME or a popup for a refresh; what we want is a 302 redirect since the user already has a session.
So I don't think the real issue is the frame options; but rather the fact that certain policies aren't returning 302 redirects for existing sessions.
Phrased in another way for this thread, the issue is likely not X-Frame-Options and I believe that is a side-effect of the root issue. I believe the root issue is that certain B2C policies (MFA, custom policies, etc.) are not returning a 302 redirect for existing sessions and are instead always trying to return a 200 response with the interactive form. And at that point I do agree that the interactive form should never be allowed within an IFRAME due to clickjacking.
@jmckennon I see Jason was unassigned from this ticket; with my above notes do you think this warrants a second triage for us? Thank you!
@jake-brandt This is probably an issue with the B2C service. Could you file a support ticket following the instructions here? You can link to this issue in that ticket.
@pkanher617 per your guidance I've filed a support ticket on our Azure plan; I'll share any relevant/general information I get back
As that is a private system I was able to include HTTP traces to help illustrate the sequence of events.
Here is a proposal from internal discussions with my team:
If you are already signed in using the default sign up / sign in policy but then need to use a custom policy for further action (such as custom profile management), do not attempt to refresh using a redirect with the latest policy (the custom one). Rather, redirect the user _back_ to the original sign up / sign in policy. As Azure B2C should already have a session established for the user within this policy, it should automatically redirect. Pairing this with a popup redirect as opposed to silent/IFRAME should get around most browser incompatibilities and also address the redirect issue in the process (as now the redirect would be using the original sign in policy again instead).
@jake-brandt In response to your comments, the authorization code flow is our latest recommendation to mitigate Safari ITP. I'm not sure of a solution with Implicit Flow and as protocols similar to ITP are coming to Chromium and Brave I do not think it will be a waste of time to pick up this flow. We are also currently working on design so auth code flow with still work with silent iframe token acquisition, but this is still in early stages. I also acknowledge your point about security implications of using the auth code flow and would say that we do have plans to protect the refresh tokens in the browser in more ways than just limiting the lifetime, but again I can't estimate when this work will be done at the moment.
Haley let your team know we feel the struggle and understand! I'm sure I speak for most of us when I say we've all enjoyed this task a few times with securing public clients / SPAs... Looking forward to what additions come to msal-browser in time.
@jasonnutter Do you have any updates on this issue. We are experiencing a similar issue and neither of the recommended workarounds (acquireTokenPopup or acquireTokenRedirect) work for us.
To reproduce the issue I have done the following:
I followed all steps here: https://github.com/Azure-Samples/active-directory-b2c-javascript-msal-singlepageapp/blob/master/README.md and here: https://github.com/Azure-Samples/active-directory-b2c-javascript-msal-singlepageapp/blob/master/README.md to set up the app and it worked fine without any issues.
I then set up a custom policy with B2C using this guide here: https://docs.microsoft.com/en-us/azure/active-directory-b2c/custom-policy-get-started, which also worked fine.
Finally I followed this guide to add Azure AD as an identity provider: https://docs.microsoft.com/en-us/azure/active-directory-b2c/identity-provider-azure-ad-multi-tenant-custom?tabs=applications#add-a-claims-provider
With other identity providers the acquireTokenSilent call succeeds, however when a user logs in using Azure AD as the identity provider then the acquireTokenSilent method fails with the error described in the initial issue.
With the working providers, the request made by acquireTokenSilent gives a 302 response with the location being the redirect URI and the access token included in it, however for the AAD provider the response is a 302 still but the location is login.microsoftonline.com/common/oauth2... which then causes the iframe error.
acquireTokenPopup is not a valid solution for us because we need it to work in an environment where popups are often blocked.
acquireTokenRedirect does not seem to be working correctly. The redirect flow completes and the access token is passed in the URL, however MSAL.js is not reading the value and the redirect callback is not called, furthermore MSAL.js is triggering an additional redirect, which is causing the value to be lost before we can read it.
acquireTokenPopup is not a valid solution for us because we need it to work in an environment where popups are often blocked.
First, generally speaking you should not rely on acquireTokenSilent always succeeding. You should assume it will potentially fail (i.e. due to factors outside the control of your app or MSAL.js itself), and choose one of the interactive flows as a fallback. I realize both popups and redirects have negative tradeoffs, but they are the only two options we have to resolve silent token acquisition failures.
Note, popups are usually blocked by default only when they are invoked not as a result of user interaction. Furthermore, MSAL will throw an error if it is unable to open a popup, meaning your app can attempt to do acquireTokenPopup, and then fallback to acquireTokenRedirect if that fails.
acquireTokenRedirect does not seem to be working correctly. The redirect flow completes and the access token is passed in the URL, however MSAL.js is not reading the value and the redirect callback is not called, furthermore MSAL.js is triggering an additional redirect, which is causing the value to be lost before we can read it.
I recently heard from another customer about this behavior with acquireTokenRedirect, I haven't had a chance to investigate but I'll follow up.
@jasonnutter Thanks for the response. I understand that we still need an interactive flow as a fallback. The main issue is that this is happening right after log in so the user performs a normal log in to get the ID token, then acquireTokenSilent is called right away, which fails and causes a second interactive log in to get an access token. Ideally we'd like to avoid the user having to log in twice.
One possible solution I've found is changing the initial log in call to use a response_type of id_token token rather than just id_token. Looking here https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-core/src/UserAgentApplication.ts#L506 it seems that the response type is fixed for a login call, it would be great if support could be added for choosing response_type for a login call.
One possible solution I've found is changing the initial log in call to use a
response_typeofid_token tokenrather than justid_token. Looking here https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-core/src/UserAgentApplication.ts#L506 it seems that the response type is fixed for a login call, it would be great if support could be added for choosingresponse_typefor a login call.
This is being addressed in #1628
One possible solution I've found is changing the initial log in call to use a
response_typeofid_token tokenrather than justid_token. Looking here https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-core/src/UserAgentApplication.ts#L506 it seems that the response type is fixed for a login call, it would be great if support could be added for choosingresponse_typefor a login call.This is being addressed in #1628
Ah, brilliant.
@jasonnutter I have been experiencing this same 'X-Frame-Options'='deny' error when trying to acquireTokenSilent I figure I'll post my findings on what the root cause was for me and how I am resolving it. In my case with the identity provider I was using (Google, should be the same for the other providers but haven't tested yet) this error will only occur for me when my browser/cookies are manging multiple active sign-ins for that provider. The scenario is easy to spot when signing into your application (at least when using google provider) select your login provider and you should be presented with multiple accounts/emails to choose from. If multiple accounts pop up to choose from (then you have the potential for this error) and there isn't a 'signed out' label on multiple selections than your browser has multiple active sign-ins. When clicking any of those accounts that are not showing signed out then you are login to the application without needing credentials. When navigating to a protected resource it was failing the acquireTokenSilent because it was unable to determine what active sign-in credentials to be using since my browser had multiple active sign-ins. So to get around this you would have to sign-out of all your accounts for that provider and sign in to a single account or use 'acquireTokenPopup' with token caching for better user experience.
Hope this helps others. Other identity providers/browsers might be harder to determine how many active accounts are signed in. Usually, a good fallback is if that provider provides an email service (Gmail, Hotmail, live, office 365, ...) then navigate to that service to see who is actively logged in for that provider and how easily you can switch accounts without being prompted for credentials (showing that multiple accounts have an active sign in and when getting tokens silently you will have problems).
I am also facing the issue with acquireTokenSilent. When acquireTokenPopup is called popup just comes and closes by itself.
@bkoliba As you mentioned , when multiple accounts are signed in the need for popup is acceptable. But even when only one account is signed in or when login_hint is passed to the provider, the silent call should have passed right?
It's still failing in our case. Any help is appreciated.
This issue is not new, there was an issue that said "With multiple accounts, user is redirected through login experience twice" and they told that it was happening with multiple Google Accounts (https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/842#issuecomment-621531835) but that issue was closed without (aparently) a resolution.
@jasonnutter is there a way to acquireTokenSilent when the user has multiple Google accounts?
Thank you in advance
@JuanGarciaCarmona This is currently not possible on the built in user flows due to an issue with the service where the login_hint is not passed down to the underlying IDP, meaning google doesn't know which account you intend to use. If you're using custom policies there is a fix in flight for this, I will check in with the service team and find out if it has been fully rolled out yet and get back to you with instructions on how to update your policy to support this.
Once everyone is confident in the fix for custom policies it will then be rolled out to basic policies for some Social IDPs, you can expect the most popular IDPs to be included in that list. I don't have an ETA at the moment for when it might be rolled out to basic policies but we'll keep this ticket open until then.
Another note, the first alpha of MSAL Angular v2 is now available: https://github.com/AzureAD/microsoft-authentication-library-for-js/releases/tag/msal-angular-v2.0.0-alpha.0
Given it does not use iframes for most token acquisition scenarios, this issue should be mitigated after upgrading.
@giovannimele Can you please confirm that upgrading to the latest Angular version mentioned above is resolving this issue?
Closing, as this should no longer be an issue in @azure/msal-angular@v2. Please open a new issue if this is still an issue in v2.
Most helpful comment
@tnorling Hello, can you give me the reason why you consider this problem as low priority? For me the silent refresh is totally broken and doesn't work...