react-google-login icon indicating copy to clipboard operation
react-google-login copied to clipboard

onSuccess and onFailure callbacks not triggering

Open brunobmello25 opened this issue 3 years ago • 11 comments

Description

onSuccess and onFailure callbacks should be called after login popup is closed but that doesn't happen. This happens both on a project with nextjs (created with create-next-app) and normal react (created with create-react-app)

Steps to reproduce the issue:

  • clone this project: https://github.com/brunobmello25/broken-google-login
  • install dependencies with node 14+
  • run project
  • click on login button and log in

Expected behaviour

should have triggered either onSuccess or onFailure callbacks and logged something on browser console

Actual behaviour

does not trigger any of the expected callbacks

brunobmello25 avatar May 22 '21 23:05 brunobmello25

Hi @brunobmello25, I found your issue because I had the same problem and after some more searching I found this which fixed the problem for me (I had to disable PrivacyBadger browser addon): https://github.com/anthonyjgrove/react-google-login/issues/278#issuecomment-651293828

Hopefully, it will help you as well. 😎

mrtnzlml avatar Jun 01 '21 19:06 mrtnzlml

Same issue here, on chrome mobile, sometimes, onSuccess is not triggered...

felixmeziere avatar Jun 04 '21 10:06 felixmeziere

Ok, I'm investigating this. It seems that SOMETIMES, on devices where it won't work (and mysteriously, works fine on my own), google returns a `"error": "popup_closed_by_user" in the failure callback. This is a lead.

People talk about this all over the internet:

Not sure why yet that it's intermittent or only on some devices. I'm going to keep looking.

ariccio avatar Jun 09 '21 21:06 ariccio

Also, when setting a breakpoint in the gapi userLoggedOut handler, it hits sometimes on clicking sign in?

We should also document some of the other errors returned so we can provide better info to users. I'm also seeing popup_blocked_by_browser sometimes.

The source also seems to have other error constants:

  • wrong_response_type
  • unknown_error
  • immediate_failed (error_subtype: no_user_bound, access_denied)
  • idpiframe_initialization_failed (for incognito, or other places where cookies are blocked, will come with field: details: "Cookies are not enabled in current environment.")

...not sure what those mean. I'll handle them in the meantime so there's some useful feedback for users.

ariccio avatar Jun 09 '21 21:06 ariccio

Ok, last post for now. I'm more confused than when I started.

By single stepping through the gapi source, I can see that the user_logged_out error originates when the iframe tries and fails to match this static string in the cookie (WTF?):

R.fc = "APISID";
R.ec = "SAPISID";
R.cc = "__Secure-3PAPISID";
R.$ = function(a) {
    a = encodeURIComponent(a);
    var b = R.Cd();
    if (b && (a = b.match("(^|;) ?" + a + "=([^;]*)(;|$)")) && 2 < a.length && (a = a[2]))
        return decodeURIComponent(a)
}
;
R.sc = function(a) {
    var b;
    (a = R.$(a)) && (b = String(bh(a)));
    return b
}
;
R.Cd = function() {
    return document.cookie
}
;
R.ig = function(a) {
    document.cookie = a
}

...which is called from this check:

var rh = function() {
    return R.sc(R.fc) || R.sc(R.ec) || R.sc(R.cc)
};

...inside this godforsaken check function:

m.kf = function(a) {
    var b = a.params || {}
      , c = this
      , d = function(q) {
        X(c.h, a.id, q)
    }
      , e = b.clientId
      , f = b.loginHint
      , g = b.request
      , h = b.sessionSelector;
    g.client_id = e;
    g.login_hint = f;
    g.ss_domain = h.domain;
    var k = rh();
    if (k) {
        var l = !!g.enable_serial_consent
          , r = function(q) {
            q && !q.error && q.login_hint ? (q.first_issued_at = (new Date).getTime(),
            q.expires_at = q.first_issued_at + 1E3 * q.expires_in,
            q.session_state || (q.session_state = {}),
            l || q.scope || (q.scope = g.scope),
            b.skipCache ? ni(c.ba, e, q, k, function() {
                d(q)
            }) : ci(c.s, k, e, g.response_type, q, b.id, function() {
                ni(c.ba, e, q, k, function() {
                    d(q)
                })
            })) : (q = q || {},
            d(q))
        };
        b.forceRefresh ? Bh(this.X, g, r) : bi(this.s, f, e, g.response_type, g.scope, b.id, function(q) {
            q && 18E4 < q.expires_at - (new Date).getTime() ? ni(c.ba, e, q, k, function() {
                d(q)
            }) : Bh(c.X, g, r)
        })
    } else
        X(c.h, a.id, {
            error: "user_logged_out"
        }),
        b.userInteracted && (f = ri(),
        si(f).Bc(),
        f.flush())
}

...the devtools give a hint as to what some of the parameters are, like a:

id: "CENSORED"
method: "getTokenResponse"
params:
clientId: "CENSORED.apps.googleusercontent.com"
forceRefresh: true
loginHint: "CENSORED"
request: {redirect_uri: "https://covid-co2-tracker.herokuapp.com/home", response_type: "token id_token", scope: "openid profile email", client_id: "CENSORED.apps.googleusercontent.com", login_hint: "CENSORED", …}
sessionSelector: {crossSubDomains: true, domain: "https://covid-co2-tracker.herokuapp.com"}
skipCache: false
userInteracted: true
__proto__: Object
rpcToken: "CENSORED"

and a.params:

clientId: "CENSORED.apps.googleusercontent.com"
forceRefresh: true
loginHint: "CENSORED"
request:
client_id: "CENSORED.apps.googleusercontent.com"
login_hint: "CENSORED"
redirect_uri: "https://covid-co2-tracker.herokuapp.com/home"
response_type: "token id_token"
scope: "openid profile email"
ss_domain: "https://covid-co2-tracker.herokuapp.com"
__proto__: Object
sessionSelector:
crossSubDomains: true
domain: "https://covid-co2-tracker.herokuapp.com"
__proto__: Object
skipCache: false

Now, the cookie on the device in question LACKS any of those three static strings. So this makes sense why clearing cookies works for some people. I have no idea why it gets that way.

ariccio avatar Jun 09 '21 22:06 ariccio

Hi @brunobmello25, I found your issue because I had the same problem and after some more searching I found this which fixed the problem for me (I had to disable PrivacyBadger browser addon): #278 (comment)

Hopefully, it will help you as well. 😎

YOU HAVE SAVED MY LIFE LOL!!!!!

rclarkem-fubotv avatar Jul 05 '21 02:07 rclarkem-fubotv

@ariccio thanks for the tip.

My problem was basically the same, when tried to sign in, it was returned "USER_LOGGED_OUT". Neither onSuccess() or onFailure() was being called. Just cleaning the browser cookies solved the problem.

bruno-silva5 avatar Jan 23 '22 19:01 bruno-silva5

Yeah, google really skimped on their error detection code here :)

ariccio avatar Jan 24 '22 19:01 ariccio

I started getting USER_LOGGED_OUT from API out of the blue on all sites and on all browsers across different devices on my network. I came to the conclusion that it must have something to do with IP since that's the only thing common and tried with a VPN enabled and it worked. Restarted my router to change my IP and it got fixed.

zeus2198 avatar May 04 '22 05:05 zeus2198

Hi @brunobmello25, I found your issue because I had the same problem and after some more searching I found this which fixed the problem for me (I had to disable PrivacyBadger browser addon): #278 (comment)

Hopefully, it will help you as well. 😎

you save my day...

gabrielew avatar Jun 17 '22 12:06 gabrielew

If you are running react with a backend like django you should consider changing the CROSS-ORIGIN-OPENER-POLICY, setting this to a value other than "same-origin" solved my issue

https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid#cross_origin_opener_policy

joetechster avatar Feb 13 '23 12:02 joetechster