firebase / firebase-js-sdk

Firebase Javascript SDK
https://firebase.google.com/docs/web/setup
Other
4.83k stars 890 forks source link

Google as an Auth provider doesn't work on iOS saved to homescreen #77

Closed Haroenv closed 4 years ago

Haroenv commented 7 years ago

[REQUIRED] Describe your environment

[REQUIRED] Describe the problem

Steps to reproduce:

  1. Save the app on the homescreen on iOS
  2. make a redirect or popup login with google Auth provider
  3. if redirect:
    • webview is not a "valid user agent"
  4. if popup:
    • gets stuck in Safari about:blank, doesn't open the app again to pass on the token

Relevant Code:

import firebase from "firebase";

const config = {
//...
};

firebase.initializeApp(config);

const googleAuthProvider = new firebaseAuth.GoogleAuthProvider();

firebase.auth().signInWithRedirect(googleAuthProvider);
// or
firebase.auth().signInWithRedirect(googleAuthProvider);
Haroenv commented 7 years ago

related issue: https://github.com/firebase/quickstart-js/issues/94

bojeil-google commented 7 years ago

This is a known issue and not really Firebase related (in the case of redirect) which is Google sign-in related. Webviews are insecure and Google sign-in no longer works in that environment anymore. Google sign-in recommends using SFSafariViewController for this but homescreen apps cannot run in a SFSafariViewController, instead they always run in a webview environment. As for popups, they won't work for a different reason since Firebase OAuth popup relies on web storage to pass the result back to the app. However homescreen apps do not share the same web storage as the system browser (which is how the popup is opened).

Haroenv commented 7 years ago

I totally understand that, but saving to home screen isn’t something I have control over as an author. Does this really come down to “it’s impossible”?

bojeil-google commented 7 years ago

Unfortunately, it is currently impossible if you wish to use Google Sign in. If you are using other providers, you can use the recommended signInWithRedirect for that mode (it should work).

Bretto commented 7 years ago

This is a pretty big, deal breaker, any hope/news on this ?

bojeil-google commented 7 years ago

Sorry, it is out of our control. You need to reach out to the Google OAuth folks about this. I don't think there is currently anyway to get Google sign-in to work in a home screen iOS app.

RileyDavidson-Evans commented 7 years ago

Android is also effected by this issue, this is definitely a deal breaker for pwa

zywyz commented 7 years ago

There are still no solution for this problem?

cdharris commented 7 years ago

Have just run into this issue myself, some of our users are trying to launch from iOS bookmarks and are unable to sign in. I appreciate this is not a firebase issue per say but wanted to add my voice here to draw attention that there are more affected users.

I am using firebase web authentication, is there no way to allow login from iOS in this instance?

Thanks

ghost commented 7 years ago

This makes me sad.

bojeil-google commented 7 years ago

Facebook login and other non-Google providers should work in redirect mode only. As I mentioned earlier, it is impossible to get Google sign-in in that iOS homescreen mode. From the Google sign-in perspective, it is coming from an embedded webview which are now blocked.

Bretto commented 7 years ago

Does anyone know where we can follow the progress on this PWA deal breaker ?

mikeatmarvel commented 6 years ago

We would welcome a solution to this problem as well. Unfortunately this is a "deal breaker" for us as well.... I wanted to share on this thread as we are running into this same issue and did not want to be silent about it.

Looking forward to a resolution soon!

codercatdev commented 6 years ago

How do we upvote a solution?

mesqueeb commented 6 years ago

Does anyone know where we can follow information on how this problem is being treated by either Google OAuth, Apple or Firebase?

Is there a way for firebase to detect if the app is launched from the home screen so we can display an explanation for our users?

bojeil-google commented 6 years ago

The iOS embedded webview has a distinct useragent. You can test for that and display a message to the user. Embedded webviews for iOS 9 and above are blocked from accessing the Google OAuth page for security reasons: https://developers.googleblog.com/2016/08/modernizing-oauth-interactions-in-native-apps.html

tcha-tcho commented 6 years ago

Why not just:

  var fbAppId = 'XXXXXXXXXXXXXX';
  var fburl = "https://www.facebook.com/dialog/oauth?";
  fburl += "client_id="+fbAppId;
  fburl += "&redirect_uri="+window.location.href;
  window.location.href = fburl;

When popup is not possible, like in Safari?

henrylearn2rock commented 6 years ago

@RileyDavidson-Evans Look: https://bugs.chromium.org/p/chromium/issues/detail?id=771418 seems to be fixed in Chrome dev 64.0.3254.2

Actually, I tried https://fir-ui-demo-84a6c.firebaseapp.com/ posted in the thread above with just the latest stable Chrome and couldn't reproduce the endless loop. Maybe someone fixed something behind the scene already?

nicolasgarnier commented 6 years ago

For me it looks like redirect auth works on https://friendly-pix.com/ on iOS homescreen apps with Google sign-in. But perhaps, since this is an old app, I was grandfathered into an exception.

Popup auth works on Android Homescreen apps.

This is how I implemented an exception just for iOS homescreen apps:

  var signInFlow = 'popup';
  // For iOS full screen apps we use the redirect auth mode.
  if (('standalone' in window.navigator)
      && window.navigator.standalone){
    signInFlow = 'redirect';
  }

  // FirebaseUI config.
  var uiConfig = {
    'signInFlow': signInFlow,
    'signInOptions': [
      firebase.auth.GoogleAuthProvider.PROVIDER_ID,
      firebase.auth.FacebookAuthProvider.PROVIDER_ID
    ]
  };
  var firebaseUi = new firebaseui.auth.AuthUI(firebase.auth());
mesqueeb commented 6 years ago

When I tested today it worked without problems. Was this problem fixed on Apple's side (related to webviews) or Google OAuth side or Firebase side?

jcyh0120 commented 6 years ago

Any update for this feature? I want to give up using PWA because my user cannot login.

RileyDavidson-Evans commented 6 years ago

This may have changed but about 6 months ago when I was attempting this I discovered apple has deemed WebViews insecure and thus, secure login methods through PWA's by design will not work.

mesqueeb commented 6 years ago

Today this issue is fixed to my findings! Does anyone still experience the inability to log in?

jcyh0120 commented 6 years ago

How do you solve this? @mesqueeb

mesqueeb commented 6 years ago

@jcyh0120 It suddenly works for my app! The google auth login in my app at https://listo.lucaban.com works now even within the safari webview. You can try it out by adding it on the homescreen and login.

jcyh0120 commented 6 years ago

@mesqueeb What kind of oauth did you use? SigninWithPopup or SignInWithRedirect?

mesqueeb commented 6 years ago

@jcyh0120 I use redirect. Whereas even with redirect it did not work before on iPhone when saving the website on the homescreen and opening as PWA.

jcyh0120 commented 6 years ago

I got this error with my last build using login with redirect.

I got this error too with your https://listo.lucaban.com PWA on iOS.

https://user-images.githubusercontent.com/29078867/27479671-c5b7c88a-580c-11e7-9df8-be1016e2b106.PNG

mesqueeb commented 6 years ago

@jcyh0120 That's weird. I don't get that bug at all. I can log in without problems. Maybe it's iOS version related? I'm on iOS 11.2.5. You?

jcyh0120 commented 6 years ago

@mesqueeb I use ios 9.x.x and ios 10.x.x. Both can not do Google OAuth with Add to home screen.

johnozbay commented 6 years ago

We're developing a pinned-home-screen mobile web app, and now have the same issue. @mesqueeb and @jcyh0120 you're both correct.

I started testing different iOS versions to try to isolate the versions impacted by this. So far what I've found is :

iOS v9.3.5 - Not Working _(disallowed_useragent error) iOS v10.3.2 - Not Working _(disallowed_useragent error) iOS v11.2 - Working iOS v11.2.5 - Working

johnozbay commented 6 years ago

I think I solved the auth problem for ios home screen web applications entirely, and it seems to work for all iOS Devices & Versions.

TLDR; Here is the github gist for the code :)

Solution First, you'll need to lead your users to login in Safari, then add your webapp to their home screen.

Once your users are logged in, and you got your user object in firebase.auth().onAuthStateChanged Get everything from localStorage, convert it to a base64 string and append it to the end of the URL with a parameter using window.history.pushState.

When your users pin your website to their homescreen, check this parameter, and if there's a base64 version of the localStorage in the URL, set the localStorage from the base64 and reload the page with window.location.reload(false).

This tactic basically allows you to port everything from Safari's localStorage to the pinned web app's local storage using URL parameters.

gist for the code

bojeil-google commented 6 years ago

Please do not rely on hacking localStorage. It is an implementation detail which we have the right to change. We have plans to migrate from it to indexedDB. In addition, I would not recommend keeping your Auth state (which contains the indefinite refresh token, opening a large window of attack) in the URL parameters at all times hoping a user would add your app to the homescreen.

johnozbay commented 6 years ago

Thanks for the quick response @bojeil-google! and I appreciate the heads up. ✌🏻

For my personal education, two questions :

1) Wouldn't appending the token to the URL only using javascript (and not involving any servers or anything, purely on the client side with pushState) be as safe as localStorage? They're both only accessible on the client side via javascript, and if one has a way of executing any malicious code, wouldn't this be as bad for keeping things in localStorage too?

2) Is there anything else we can use/do to move a temporary token to the web app, through the URL to achieve the same results in a less hacky way?

In my specific case it's not hoping the user would add the app to the home screen. 😕 It's the expected behavior, so I need a way around this if I don't want to exclude all users between iOS9 up to iOS11.

nicolasgarnier commented 6 years ago

@bojeil-google I'm guessing the only way to truly fix this is to work on fixing the popup flow to work on iOS homescreen apps. Not saying it's easy or even possible but if there is any way to communicate between the popup and the app that works it might be worth a hack... (I know, no-one likes hacks...)

henrylearn2rock commented 6 years ago

Has anyone tried https://github.com/firebase/firebaseui-web?

Demo: https://fir-ui-demo-84a6c.firebaseapp.com/

Looks like it supports the latest google login method inside an animated container, that does not use redirect nor popup. Looks like it's inside an iframe. https://developers.google.com/identity/smartlock-passwords/android/associate-apps-and-sites

Does it have the same issue on iOS?

bojeil-google commented 6 years ago

Hey @nicolasgarnier, I don't know of any way to pass the result securely enough that we can provide as a built in solution in our SDK. The issue with embedded webviews (iOS homescreen apps use those) is that they are insecure. The user has no way to tell which website they are signing in to as there is no indicator of the URL identifying the page they are entering their credentials to. Ideally, this should be implemented with SFSafariViewController or SFAuthenticationSession but iOS does not support those in homescreen apps. Chrome homescreen apps on the other hand, support Chrome custom tabs which have same security as SFSafariViewController and SFAuthenticationSession and even share same storage as the homescreen app itself.

Regarding @johnozbay's suggestions: From my understanding, you don't know if the user will actually add your app to homescreen. This action has to be taken outside the web app which means you have to keep the Auth state in the URL the whole time. If this is a one time operation and you know when it will happen, you could try to pass the ID token instead and then exchange it for a custom token in your homescreen app (detect it, send it to server, verify it, mint custom token and return it to sign in with ) after you verify the auth_time in the ID token is recent (user recently signed in) to minimize token theft risks.

Also if you are appending the auth state in the URL, it will end up getting logged on your server, other web analytics tracking tools, etc. Embedded in that link is the user's Firebase refresh token. So if these logs are compromised, the refresh tokens of all these users could be decoded and stolen no matter how old they are (refresh tokens are indefinite unless revoked).

johnozbay commented 6 years ago

Thanks a million @bojeil-google for taking the time for the detailed explanation. I now have a much clearer understanding of what's going on, and Google Auth's perspective on why WebViews are insecure. (regarding the lack of URL visibility)

In my specific case, we're deploying a cross-platform mobile web-app, and it's intended specifically to be used on mobile devices as a pinned app, and we're directing users to do so with specific UX. It is a one time operation and we can even direct the users to make it on a specific page.

I'll pass the ID token and do the custom token exchange & verify auth_time instead 👍🏻

Plus side of using Firebase is that we have no servers, nor any analytics or tracking tools. Static HTML hosted on Firebase Hosting, and some Cloud Functions here and there. So there should be no logs in our specific case.

Again many thanks for the help & clarifications.

mesqueeb commented 6 years ago

Dear @bojeil-google You said:

Ideally, this should be implemented with SFSafariViewController or SFAuthenticationSession but iOS does not support those in homescreen apps.

But since the problem does not occur anymore after iOS v11.2, do you think Apple already changed that behaviour?

And also, should we change this issue from: Google as an Auth provider doesn't work on iOS saved to homescreen to: Google as an Auth provider doesn't work on iOS saved to homescreen before iOS v11.2?

bojeil-google commented 6 years ago

Hey @mesqueeb, I don't know the answer to that. iOS v11.2 is fairly new. I haven't had a chance to test it yet. Will try to get my hands on a device running that version of iOS and test it.

henrylearn2rock commented 6 years ago

I may be wrong but looks like google-api-javascript-client uses SessionStorage https://github.com/google/google-api-javascript-client/issues/260

https://pwa-directory.appspot.com/ This PWA uses google-api-javascript-client and google login works as expected in Android. I'm not sure about iOS.

bojeil-google commented 6 years ago

I am not sure what you are trying to say. This issue is specific to iOS. I also have no idea why it matters that they use sessionStorage. We use sessionStorage too for redirect mode.

mikkopaderes commented 6 years ago

Not sure if this can be considered as related but Google sign in still doesn't work in Safari Webview for apps like Facebook and Facebook Messenger even in iOS 11.2.

I mentioned it here because I think those have the same setup as a homescreen app.

mesqueeb commented 6 years ago

Then I must warn my users if I share my app through facebook with something like: "It seems the app is open inside Facebook. Please open it in Safari."

@rmmmp Do you know how recognise with JS if it's inside Facebook messenger or not?

mikkopaderes commented 6 years ago

@mesqueeb - I'm not sure how to specifically detect if in Facebook. But you can detect if it's in Safari Webview mode. See here.

JaySunSyn commented 6 years ago

@johnozbay does iOS v11.3 work for you? (does not for me)

dyegolara commented 6 years ago

it does not work for me on iOS 11.3 with redirect nor pop-up

johnozbay commented 6 years ago

Unfortunately it doesn't @JaySunSyn. 🙁 All that being said, I absolutely understand google's point of view on this issue. It is an inherent security risk, and it's something either Apple or we should take action on. I wouldn't want my Google creds stolen either. Looks like Apple brought PWA support in iOS 11.3 ~ so it's only more of a reason for them to fix.

To work around this I've implemented two workarounds based on the conversations up in this chain. Neither are good solutions, but depending on your situation, they could solve your problem. First one is all local on the device, and can be broken if Apple decides to change things up. Second involves setting up a cloud function chain.

Solution 1) Create a controlled flow with good design & UX and make sure the user can only "Add to Home Screen" after they're logged in to your service in Safari, auth-tokenize the URL there (with a 5min expiry timing) only on a single specific page, and ask the users to add to home screen & launch immediately. This relies on iOS not stripping URL parameters when adding to home screen, and can be broken if Apple changes this. For one specific use case, this worked quite well for me. However it still allows for an auth-tokened URL to exist for 5 minutes, hence the danger and second solution.

Solution 2) This one relies on the fact that you can open URLs in Safari from pinned home screen applications, but not the other way around. Flow goes like this :

In Pinned App User presses sign in / sign up with Google in your pinned home screen app, Ping a cloud function to get a UUID, Save this UUID to localstorage, then open a URL in Safari like : myapp.com/auth?uuid=12345

Now In Safari The Safari auth page gets the UUID from the URL parameter, saves UUID to localstorage temporarily, Then triggers Google's redirect auth, prompts user to login using Google. After a successful signup/signin, you got the ID token, Safari sends this token & UUID it had in localstorage to a cloud function Cloud Function saves the token & UUID & token creation time to your db temporarily. Once token is successfully saved to DB, and function returns a happy 200 status code, Safari prompts the user to go back to the pinned app ~ we did something like this :

screen shot 2018-04-16 at 20 01 08

Once back in the pinned app, the app checks localstorage for a UUID, If there's one, pings the cloud function with the UUID. Cloud Function delivers the ID token (and deletes both UUID & Token from the database immediately) Finally, you call signinUsingToken with this token in the pinned web app, and open up a bottle of champagne.

I'd say this is a slightly better of a solution than the first one. Takes more steps, but feels much more safer.

I'd be happy and curious to hear the google team's thoughts on this as well. ✌🏻

socceroos commented 6 years ago

My goodness. So much pain to merely log in a user to a pinned PWA.

What about using postMessage?

johnozbay commented 6 years ago

@socceroos the solution proposed at the url relies on window.open() which in iOS pinned home screen application results in navigating away from your current page (so same behavior as redirect). And this issue is specific to Google's auth flow, which is different than Spotify's.

The first post in this issue talks about the results of both redirects and popups. Shortly,

If you do a plain redirect, you'll get a Webview is not a "valid user agent" error due to google's restrictions (Which are btw perfectly legitimate and understandable. Highly recommend that you read the reasons in this thread).

If you use a popup: you'll get stuck in Safari about:blank, and won't open the app again to pass on the token, due to Firebase OAuth popup relying on web storage to pass the result back to the app and homescreen apps not sharing the same web storage as the system browser.

The only OS & browser version agnostic workaround I could find is the one I listed above, which is to instruct the user to press the "< Back to App" button on the top left in iOS Safari and handle the rest with some cloud functions sorcery.