30

The Facebook OAuth popup is throwing an error in Chrome on iOS only. Both developers.facebook.com and google have turned up nothing about this. Ideas?

screenshot of facebook oath popup on mobile chrome on ios

Daniel Miller
  • 413
  • 1
  • 5
  • 6
  • 3
    This is an issue with how Chrome for iOS handles popup windows: https://code.google.com/p/chromium/issues/detail?id=237084 – Igy May 30 '13 at 18:27
  • 1
    Possibly related [Facebook Login Button: Workaround for bug of Chrome on iOS](http://stackoverflow.com/questions/13603653/facebook-login-button-workaround-for-bug-of-chrome-on-ios). –  Aug 12 '13 at 17:26
  • @Igy fast-forward 2.5 years, [that bug has been fixed](https://code.google.com/p/chromium/issues/detail?id=237084#c24), but the issue remains. – adamdport Dec 23 '15 at 03:20
  • Make that almost 3-years! Bah... – Michael De Silva Feb 08 '16 at 13:32
  • The issue went away in iOS 10, but continues to exist in iOS 9 – felicete Aug 08 '17 at 15:56

7 Answers7

16

You can use the redirection method as follow for this case (by detecting the user agent being chrome ios):

https://www.facebook.com/dialog/oauth?client_id={app-id}&redirect_uri={redirect-uri}

See more info here https://developers.facebook.com/docs/facebook-login/login-flow-for-web-no-jssdk/

Remark: I personnaly use the server OAuth in that case but this should do the trick and is quite simple

Kim D.
  • 806
  • 10
  • 15
  • 2
    Having to detect the user agent is not ideal (I've heard too many arguments that it's unreliable). Is there anything that can be used to feature detect for mobile browsers instead? –  Aug 12 '13 at 15:51
  • 1
    I am not aware of any other viable option in order to detect the user's browser & OS, To me it is better than assuming some functions or variables won't be defined whether you use such or such browser/OS. I've always used the user-agent (and everyone does...) and never had any particular issue with that except that you always need to stay up to date because they are constantly evolving. – Kim D. Aug 12 '13 at 23:23
  • @KimD But that's the fundamental problem: you need to stay up to date, and you're not really detecting what you care about. Feature detection should always be used in preference to user-agent sniffing when possible. The question is really: is it possible here? – Marnen Laibow-Koser Sep 15 '13 at 17:35
  • Has anyone solved the issue here? Im having this issue aswell – Adam G Jan 31 '14 at 05:46
  • 6
    User agent sniffing is generally discouraged in favour of feature detection for that reason - it's not maintainable. However there is nothing wrong or unreliable about user agent sniffing in and of itself. This is a bug related to a particular browser, not a feature set, so browser sniffing is absolutely okay. This answer should be accepted. – Simon Robb Feb 14 '14 at 04:10
11

This is how I did it (fixing iOS chrome specifically)

// fix iOS Chrome
if( navigator.userAgent.match('CriOS') )
    window.open('https://www.facebook.com/dialog/oauth?client_id='+appID+'&redirect_uri='+ document.location.href +'&scope=email,public_profile', '', null);
else
    FB.login(null, {scope: 'email,public_profile'});
vsync
  • 118,978
  • 58
  • 307
  • 400
  • Thank you for that input, this works quite nice. However, facebook displays a warning that the Graph API 1.0 will be disabled april 30 2015. Is there a new version available? – Chris Dec 19 '14 at 16:17
  • 1
    @Chris - this has nothing to do with the question here. if you are using V.1 then use v.2 if this is what they ask you to do, then do it. – vsync Dec 19 '14 at 21:18
  • That answer makes not much sense. So, you say I should open up a new question to ask how to change the oauth-string to v2? – Chris Dec 20 '14 at 23:51
  • @Chris - A quick google gave me this - https://developers.facebook.com/docs/apps/upgrading/#upgrading_v2_0_login – vsync Dec 21 '14 at 13:59
  • In the example above, the 1st argument to FB.login is 'null'... What is the equivalent for CriOS when the 1st argument was an anonymous function that analyzes the login response? – Free Bud Jan 05 '15 at 07:47
  • I'm sorry, I don't understand the question. – vsync Jan 05 '15 at 21:11
  • Free Bud is asking how to analyze the login response from window.open. Presumably because his code has a callback function on the FB.login function. A clean recipe would require looking up the FB login state at a later point in the app, but I'm personally not sure how to do this. – toobulkeh Mar 19 '15 at 14:05
  • For this workaround, do I just need to replace my existing Fb code with this and make no changes to the Fb app itself? – Dano007 Jul 12 '15 at 13:16
5

Here is a complete workaround for your FB JS Auth on Chrome iOS issue http://seanshadmand.com/2015/03/06/facebook-js-login-on-chrome-ios-workaround/

JS functions to check auth, open FB auth page manually and refresh auth tokens on original page once complete:

function openFBLoginDialogManually(){
  // Open your auth window containing FB auth page 
  // with forward URL to your Opened Window handler page (below)

  var redirect_uri = "&redirect_uri=" + ABSOLUTE_URI + "fbjscomplete";
  var scope = "&scope=public_profile,email,user_friends";
  var url = "https://www.facebook.com/dialog/oauth?client_id=" + FB_ID + redirect_uri + scope;

  // notice the lack of other param in window.open
  // for some reason the opener is set to null
  // and the opened window can NOT reference it
  // if params are passed. #Chrome iOS Bug
  window.open(url);

}

function fbCompleteLogin(){

  FB.getLoginStatus(function(response) {
    // Calling this with the extra setting "true" forces
    // a non-cached request and updates the FB cache.
    // Since the auth login elsewhere validated the user
    // this update will now asyncronously mark the user as authed
  }, true);

}

function requireLogin(callback){

    FB.getLoginStatus(function(response) {
        if (response.status != "connected"){
            showLogin();
        }else{
            checkAuth(response.authResponse.accessToken, response.authResponse.userID, function(success){
              // Check FB tokens against your API to make sure user is valid
            });
        }
    });

}

And the Opener Handler that FB auth forwards to and calls a refresh to the main page. Note the window.open in Chrome iOS has bugs too so call it correctly as noted above:

<html>
<head>
<script type="text/javascript">
function handleAuth(){
    // once the window is open 
    window.opener.fbCompleteLogin();
    window.close();    
}
</script>
<body onload="handleAuth();">
    <p>. . . </p>
</body>
</head>
</html>
Sean
  • 2,412
  • 3
  • 25
  • 31
  • 1
    thats a good solution but what is "fbjscomplete" supposed to do in the redirect url. That seems to be redundant. – roopunk Apr 26 '15 at 18:21
  • It updates the local FB cache on the originating page so that the login that was completed in the separate window persists on the first. The key piece of the "fbCompleteLogin" function is the call to "FB.getLoginStatus" with the extra "true" parameter passed. That "true" param forces FB's local cache to refresh the login status (as being logged in) for the page. Without this function the page's FB SDK won't know the user logged in without a page refresh. Does that make sense? – Sean Apr 27 '15 at 23:49
  • @Sean Seems this is what Im looking for, but being new to coding I'm struggling a little. Are you around to ask a few additional questions? – Dano007 Jul 12 '15 at 11:35
  • Sure thing. Happy to help. Shoot. :) – Sean Jul 13 '15 at 21:30
  • @Sean thanks, I actually got it working :-) but only with standard Fb login, not with Parse. You mentioned something in your blog you'd see a solution to this for Parse, where was that? I did'nt see the above answers helping? – Dano007 Jul 19 '15 at 17:00
  • That is a bit of a typo. Actually, I found people claiming to have fixed it using Parse but could not get anything to work. Sorry, I don't have the link history anymore at this point :( Glad you got it working though! Also, You may want to add some notes on what you had a problem with and how you got it working so the next person doesn't have to go through the same ordeal. :) – Sean Jul 22 '15 at 19:54
  • Hi Sean. I have the same issue. But your code is not working for me. you are calling fbCompleteLogin(); which only gets login status. where you are calling other functions? – Irfan TahirKheli Aug 29 '15 at 18:38
  • 2
    The work flow is: Call "openFBLoginDialogManually". That opens the *FB generated* login page. The URL passed to that FB login page includes a redirect to the Opener Handler HTML snippet above. That HTML pages calls the original opener's "fbCompleteLogin". At that point all it does is refresh the local FB login cache with new credentials. The "requireLogin" is unneeded and distracting, sorry about that. Hopefully that helps. If it does not let me know more specifically where you are finding a problem. – Sean Aug 31 '15 at 02:42
0

This is a very common issue which all developers have faced while implementing the FB login feature. I have tried most of the Internet solutions but none of them worked. Either window.opener do not work in Chrome iOS or sometime FB object is not loaded while using /dialog/oauth.

Finally I solved this by myself after trying all the hacks!

function loginWithFacebook()
{
  if( navigator.userAgent.match('CriOS') )
  {
    var redirect_uri = document.location.href;
    if(redirect_uri.indexOf('?') !== -1)
    {
      redirect_uri += '&back_from_fb=1';
    }
    else
    {
      redirect_uri += '?back_from_fb=1';
    }
    var url = 'https://www.facebook.com/dialog/oauth?client_id=[app-id]&redirect_uri='+redirect_uri+'&scope=email,public_profile';
    var win = window.open(url, '_self');
  }
  else
  {
    FB.login(function(response)
      {
        checkLoginState();
      },
      {
        scope:'public_profile,email,user_friends,user_photos'
      });
  }
}

Notice that above I'm passing an extra param to the redirect url so that once the new window opens with above redirect uri I could read the values and can say yes this call is from Chrome iOS window. Also make sure this code runs on page load.

if (document.URL.indexOf('back_from_fb=1') != -1 && document.URL.indexOf('code=') != -1)
{
  pollingInterval = setInterval(function()
    {
      if(typeof FB != "undefined")
      {
        FB.getLoginStatus(function(response) {}, true);
        checkLoginState();
      }
      else
      {
        alert("FB is not responding, Please try again!", function()
          {
            return true;
          });
      }
    }, 1000);
}
Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Abhishek
  • 1,558
  • 16
  • 28
0

My 2 cents on this as noone of the answers were clear to me. Im firing the login js dialog on a button click, so now when it's chrome ios I check first if the user it's logged into facebook and if not I send them to the login window. The problem with this is that chome ios users needs to click connect button twice if they are not logged into facebook. If they are logged into facebook one click is enough.

 $( 'body' ).on( 'click', '.js-fbl', function( e ) {
        e.preventDefault();

        if( navigator.userAgent.match('CriOS') ) {
            // alert users they will need to click again, don't use alert or popup will be blocked
            $('<p class="fbl_error">MESSAGE HERE</p>').insertAfter( $(this));
            FB.getLoginStatus( handleResponse );
        } else {
            // regular users simple login
            try {
                FB.login( handleResponse , {
                    scope: fbl.scopes,
                    return_scopes: true,
                    auth_type: 'rerequest'
                });
            } catch (err) {
                $this.removeClass('fbl-loading');

            }
        }
    });

That bit of code make it works for chrome ios users. On handle response I simple take care of fb response and send it to my website backend for login/register users.

var handleResponse = function( response ) {
    var $form_obj       = window.fbl_button.parents('.flp_wrapper').find('form') || false,
        $redirect_to    = $form_obj.find('input[name="redirect_to"]').val() || window.fbl_button.data('redirect');
    /**
     * If we get a successful authorization response we handle it
     */
    if (response.status == 'connected') {

        var fb_response = response;

        /**
         * Make an Ajax request to the "facebook_login" function
         * passing the params: username, fb_id and email.
         *
         * @note Not all users have user names, but all have email
         * @note Must set global to false to prevent gloabl ajax methods
         */
        $.ajax({...});

    } else {
         //if in first click user is not logged into their facebook we then show the login window
         window.fbl_button.removeClass('fbl-loading');
        if( navigator.userAgent.match('CriOS') )
            window.open('https://www.facebook.com/dialog/oauth?client_id=' + fbl.appId + '&redirect_uri=' + document.location.href + '&scope=email,public_profile', '', null);
    }
};

Hope it helps!

chifliiiii
  • 2,231
  • 3
  • 28
  • 37
0

Not a real answer but based on this thread worth noting that is started working for our app, on Chrome when on the iPhone we did General>Reset>Reset Location & Privacy

akotian
  • 3,885
  • 1
  • 33
  • 44
0

I got a solution for ios facebook website login in google chrome . Actually the issue was with google chrome in ios when we click on facebook login button it give internally null to the window.open in ios chrome .
There are two solution either to check it is chrome in ios(chrios) and then generate custom login screen ( still not chance that it will we correct ).
Second what i have used. Is to use facebook login from backhand create a api hit will populate facebook screen and then when login is done it will redirect to your server from where you will redirect to your website page with facebook data.
one other benefit of it is that you create 2 website for same website owner you can not set two website url in facebook developer account .In this way you can create many website facebook login with same facebook appid .

Himanshu sharma
  • 7,487
  • 4
  • 42
  • 75