Aspnetcore: Blazor webassembly b2c login fails with some identity providers

Created on 29 May 2020  ·  25Comments  ·  Source: dotnet/aspnetcore

Describe the bug

A number of identity providers won't work with the iFrame-based b2c login. For example, Google and Amazon produce these client side browser errors (in the console output):

Refused to display 'https://accounts.google.com/signin/oauth?client_id=....&redirect_uri=https://wickedbyteapps.b2clogin.com/wickedbyteapps.onmicrosoft.com/oauth2/authresp&response_type=code&scope=email+profile&state=StateProperties%3...' in a frame because it set 'X-Frame-Options' to 'deny'.

Refused to display 'https://na.account.amazon.com/ap/oa?arb=...' in a frame because it set 'X-Frame-Options' to 'sameorigin'.

Is there a workaround for this?

To Reproduce

Follow the directions at https://docs.microsoft.com/en-us/aspnet/core/security/blazor/webassembly/hosted-with-azure-active-directory-b2c?view=aspnetcore-3.1

Set up Google or Amazon as the identity provider.

Run the blazor app and log in using the 3rd party identity provider.

Further technical details

  • ASP.NET Core version 3.1
  • Blazor webassembly 3.2.0
  • VS2019 16.5.5
Docs area-security

All 25 comments

@guardrex fyi

Given that safari has now irrevocably broken iframe login in the name of privacy we shouldn't be recommending it any more in docs, but rather switch to using the popups method.

@guardrex can you create a docs issue, or shall I transfer this one?

Transfer this one. I'll put the labels on it.

Nevermind ... we already have one.

https://github.com/dotnet/AspNetCore.Docs/issues/18548

I'll re-open that.

EDIT I've moved this remark over to the PR for discussion. https://github.com/dotnet/AspNetCore.Docs/pull/18678

~IIRC, I read in MSAL docs that one can configure the type of login (popup or redirect) by setting LoginMode to Popup or Redirect in the AD config settings. However, I understand that such a setting might pertain to using MSAL.NET directly. I tried setting it to Redirect in a test app (based on our Azure AD guidance) that was using the popup approach, but it had no effect. I assumed that it was hardwired into the framework at this point ... no dev setting was mentioned when we were putting the docs up.~

~Recently, strange popup/redirect behavior appeared while I was working on restoring state with RemoteAuthenticatorViewCore ... fixing up our coverage and fleshing it out a bit more ...~

Update Blazor WASM security state container
https://github.com/dotnet/AspNetCore.Docs/pull/18652

~The app (standalone WASM with AAD) is now a bit scatterbrained about which approach to use. It's throwing up an empty popup, and the underlying browser window is using the redirect approach. I show a screenshot on that PR of what's happening here. I close the popup, and the app signs in users and behaves normally using the redirect approach. This behavior only comes about due to the changes on that PR with RemoteAuthenticatorViewCore.~

~I need guidance to proceed ...~

  • ~If there's a fix for the behavior, let me know ... I'll write it into the updates on that PR.~
  • ~If it's something that we need to call out in text (e.g., this is a temporary known issue), I'll add a NOTE on it there.~
  • ~If that PR can't proceed without engineering work, then let me know if I need to create an engineering issue. In that case tho, shouldn't we remove that section from the topic and not support restoring state at this time? I don't think we can put up broken guidance _and_ not even have a NOTE about it.~

Given that safari has now irrevocably broken iframe login in the name of privacy we shouldn't be recommending it any more in docs, but rather switch to using the popups method.

@guardrex can you create a docs issue, or shall I transfer this one?

@blowdart

  • How is this issue related to Safari?
  • Normally you can configure the list of allowed origins in OIDC implementation to support silent sign-in an token refresh without an access_token. For example, identity server allows you to configure this.
  • 3rd party apps need to be able to load inside a hidden iframe to perform refresh

This is normally a provider issue/limitation. I don't know the specifics about Google, but AWS is not fully OIDC compliant from what I read dealing with similar issues.

WRT to B2C and Google specifically, I would ask the B2C folks specifically for how to handle this case.

For Google you can configure the list of Authorized JavaScript origins to avoid this issue.

Since that's well-documented configuration in several Google topics, we can close the docs issue+PR. @marshallr12, I'll wait for you to test/confirm before closing the issue.

We still have strange pop-up/redirect behavior on ...

Update Blazor WASM security state container
https://github.com/dotnet/AspNetCore.Docs/pull/18652

... which we should try to resolve quickly given that what we've published doesn't seem to work (e.g., setKey/getKey 😕). I put something on the PR that _almost_ works (OnLogOutSucceeded and other non-sign-in actions aren't working for me yet).

  • Google login seems to be working today with my Blazor client side app. without my changing anything. I already had https://[mydomain].b2clogin.com added to the Google "Authorized Javascript Origins," so this was already working with a Blazor Server Side app. I don't know why Blazor client side Google login is working now.

  • Using Amazon as the identity provider with Chrome causes the app to hang at "checking login state," with a console warning "A cookie associated with a cross-site resource at http://amazon.com/ was set without the SameSite attribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set with SameSite=None and Secure. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032."

There is also a console error: "Refused to display 'https://na.account.amazon.com/ap/oa?arb=33766bd4-cb9b-4039-95b3-571028751338' in a frame because it set 'X-Frame-Options' to 'sameorigin'." If I refresh the browser at this point, then I will be logged in.

There is also a console error: "Refused to frame 'https://github.com/' because an ancestor violates the following Content Security Policy directive: "frame-ancestors 'none'". If I refresh the browser at this point, then I will be logged in.

  • The LinkedIn identity provider worked successfully.

  • Microsoft Account identity provider worked successfully.

  • B2C organizational domain identity provider worked successfully.

I will investigate more with Github and Amazon.

Thanks @marshallr12 ... super helpful to hear about these scenarios. I haven't had a lick of time to work with the 3rd party providers.

After final configurations are clarified, I'll just need to know what docs should say/link to in order to avoid gotchas. I'll keep an :ear: on this issue. I might close your docs issue and open a new one for 3rd party shortly.

... and I still would like to hear back from @blowdart on that iframe discussion and understand where that's all heading. I'll close the iframe PR today even if we keep an issue open for 3rd party details/notes/gotchas.

btw @marshallr12 ...

I don't know why Blazor client side Google login is working now.

Are you using a fresh browser (inprivate/incognito) session each run? It has been enormously helpful for those _Wait! What! ... This was working/not working five minutes ago!_ :rage: scenarios. I was so happy with the approach that I wrote it into the whole node of topics as an INCLUDE file (e.g., https://docs.microsoft.com/aspnet/core/security/blazor/webassembly/standalone-with-azure-active-directory-b2c#cookies-and-site-data).

Google still works with an incognito session (a bit more work to test that way because of 2-stage authentication). Maybe Google wasn't working before until some cookies expired in my normal browser session.

Rubber :duck: says that a lot of these ✨ _now it works, now it doesn't (or vice-versa)_ ✨ scenarios are related to lingering or foul cookies. Inprivate/incognito for _every single test run for every single change anywhere (provider-side or app-side)_ is the 🐝's knees for me now. I only test this way now, and my stress level has dropped quite a bit. 😀

Thanks for that advice! I set up Visual Studio to start debugging with Chrome in incognito mode by default.

I followed the same instructions as the original post above using Blazor wasm ASP.NET hosted and azure B2C with google auth. What I noticed was when I open the app in chrome where I have multiple google accounts then I encounter the same error.

image

What's the best way to get around this error?

Is there any functional work-around for this in the interim while awaiting a more permanent plan?

Ie. is there a way to disable 'silent' refresh and just redirect the browser to the OIDC provider (Cognito in my case). It would bounce the user between a couple of sites when things are loading just to refresh the token, but it would be significantly faster overall as currently the AuthorizeView requires to wait 2x for the X-Frame-Options error during it's verification/refresh process which is really slow every time someone loads up the blazor page..

There's a lot of classes in the Authorization process so if someone knows the "oh, you override ___ or implement ___ and add it via DI and voila!" workaround it would be much appreciated.

We looked at this a couple of weeks ago. We determined this is because B2C and MSAL.JS don't work well with anything that is not a "redirect" login, since the login flow tries to take you to the 3rd party provider to validate the login status.

This is something that we think has been fixed in msal.js 2.0 which switches to use code+PKCE. Unfortunately, there is no good way to achieve this with the current library other than replacing the AuthenticationService.js file that does all the interop work.

We've updated our authentication package to MSAL.js 2.0 as part of our 5.0 release, and this should be fixed at that point. We suggest as the next step to upgrade to 5.0 once the RC release it is available, to at least validate the changes.

I don't think "just wait for .net 5" is good enough. I spent a lot of time yesterday trying to go to .net 5 and ended up reverting. There are a lot of breaking changes, the current wasm hosted b2c template doesn't work ('input' parameter can't be null - the app goes straight to a debug halt), you have to use vs2019 preview, I had to download the .net 5 sdk on my phone, because windows10 refused ... In the meantime we are telling folks to use EDGE because it's the one browser that still works for google signins. Incognito mode in chrome doesn't work for me, not today anyways. I really like azure b2c, msal, and blazor - but this is a big roadblock. Thanks, Gregor

Spent the evening upgrading my project to .NET 5.0 RC2 (5.0.0-rc.2.20475.17) based on @javiercn 's comment and can confirm it still does not work. I'm still getting Refused to display 'https://*****' in a frame because it set 'X-Frame-Options' to 'deny'.

A workaround would be appreciated.

@TrieBr Are you by any chance still using implicit flow? (Not sure how)

We upgraded to Code+PKCE (You likely had to change the URL in the portal) and I believe that doesn't use a hidden iframe at all.

@javiercn I was never using implicit flow (in fact implicit flow is disabled on my oidc provider (AWS Cognito)). I'm using code grant type. Looking at the chrome console, I can see various code_challenge parameters, so it looks like its using Code+PKCE but it still attempts to embed an iframe a couple of times before redirecting to the oidc provider login.

What about adding an option to disable silent sign in altogether (somewhere around https://github.com/dotnet/aspnetcore/blob/648c15dbe90fb8c113f7c6b4adeb40d9e10494f6/src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/AuthenticationService.ts#L91) or an option to use signinPopup from IdentityModel/oidc-client-js instead of signinSilent?

Just wanted to note a workaround that worked for me. I made a few adjustments to AuthenticationService.ts to comment out calls to signinSilent or otherwise force a redirection. Re-built the .js file and threw it in my project. Worked great, now my login is almost instant

Did you need to include a custom-build of the library or only the JS file? (if only the JS file, where did you put it in the project?)

Just the JS file is needed. Run yarn build in aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Interop and then place the compiled .js file somewhere in your wwwroot directory and reference it from index.html

Example of what I did:

<script src="js/AuthenticationService.js"></script>
<script src="_framework/blazor.webassembly.js"></script>

Any chance you can shoot me the final .js file so I don't have to figure out all the quirks to getting the entire aspnetcore project compiling locally?

Was this page helpful?
0 / 5 - 0 ratings