How to sign out a user after page refresh?

All we need is an easy explanation of the problem, so here it is.

I’m following Google’s guide to sign out a user.

Considering that gapi.auth2 will be undefined after refreshing the page, I’m doing:

if (gapi.auth2) {
    var auth2 = gapi.auth2.getAuthInstance();
    auth2.signOut();
} else {
    gapi.load('auth2', function () {
        gapi.auth2.init({
            client_id: 'myAppID',
            cookiepolicy: 'single_host_origin'
        }).signOut();
    });
}

But I get uncaught exception: This method can only be invoked after the token manager is started in the else block.

I also have tried to store the auth instance in local storage but doing that led to some cyclic object value errors while stringifying it.

One posible solution is to do a

document.location.href = "https://www.google.com/accounts/Logout?continue=https://appengine.google.com/_ah/logout?continue=myUrl";

but, instead of logging the user out only of my application, that would affect all Google’s services in which he is logged, besides doing an unwanted redirection.

Is there a different approach?

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

there is a easier way, you just have to call .then after calling gapi.auth2.init

gapi.load('auth2', function () {
   var auth2 = gapi.auth2.init({
       client_id: 'myAppID',
       cookiepolicy: 'single_host_origin'
   });
   auth2.then(function(){
        // this get called right after token manager is started
        auth2.signOut();
   });
});

Method 2

Instead of retrieving the singleton for the GoogleAuth library and setting up the client in my sign-in page controller, I had to initialize it in the index.html file:

<script src="https://apis.google.com/js/api:client.js?onload=start" async defer></script>
<script>
    function start() {
      gapi.load('auth2', function() {
        auth2 = gapi.auth2.init({
            client_id: 'myAppID',
            cookiepolicy: 'single_host_origin'
        });
      });
    }
</script>

That solved the logout problem. However, if the sign-in page was refreshed, its controller logic would be executed before the gapi.auth2 was defined and it wouldn’t be posible to successfully attach the click handler to the sign-in button.

In order to avoid that – though not being an elegant solution -, I used $interval to wait until gapi.auth2 was initialized:

waitForAuth2Initialization = $interval(function () {
    console.log("Checking...");
    if (!gapi.auth2)
        return;
    console.log("Ok, gapi.auth2 is not undefined anymore");
    var auth2 = gapi.auth2.getAuthInstance();
    // Attach signin
    auth2.attachClickHandler...

    $interval.cancel(waitForAuth2Initialization);
}, 50);

EDIT: another possible solution is to use a promise callback for the controller logic to wait until the promise is resolved i.e. until Google API is fully loaded and gapi.auth2 is ready to use.
It is posible to achieve that by doing:

<script src="https://apis.google.com/js/api:client.js?onload=start" async defer></script>
<script>
    gapiPromise = (function () {
        var deferred = $.Deferred();
        window.start = function () {
            deferred.resolve(gapi);
        };
        return deferred.promise();
    }());
    auth2Promise = gapiPromise.then(function () {
        var deferred = $.Deferred();
        gapi.load('auth2', function () {
            auth2 = gapi.auth2.init({
                client_id: 'myAppID',
                cookiepolicy: 'single_host_origin'
            }).then(function () { 
                deferred.resolve(gapi.auth2); 
            });
        });
        return deferred.promise();
    });
</script>

And then in the controller:

auth2Promise.then(function () {
    console.log("Ok, gapi.auth2 is not undefined anymore");
    var auth2 = gapi.auth2.getAuthInstance();
    // Attach signin
    auth2.attachClickHandler...
});

But, the downside of this approach is that it is slower (taking twice as much time for the click handler to be attached) than the first one using $interval.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply