46

I'm using Google Sign-In. A user comes to my site and logs in with gapi.auth2.getAuthInstance().signIn(), or they are already logged in and when the page loads (or reloads) we fetch the status. At this point I have an identity token good for an hour that I can validate on the server.

When a user leaves the browser sitting (say, overnight), this token expires. gapi.auth2.getAuthInstance().isSignedIn.get() returns true, but the token does not validate.

How can I log in a user and keep them logged in while their session is active (ie, browser hasn't been closed)? Or refresh the token? Anything more graceful than reloading the page...

Edit: The refresh token is not a correct answer; I don't want offline access (and don't want to ask for the permission). Google obviously thinks the user is still signed into my application; the user can reload the page and get a new token without providing credentials again. Surely there is some mechanism more graceful than a hidden iframe to get an updated token?

stickfigure
  • 13,458
  • 5
  • 34
  • 50
  • 2
    Did you manage to find a correct answer to this? I have the same issue and am at a loss on how to solve it. I initially went for Refresh Tokens as well, as suggested in the answer, but I agree that this is not an actual solution. – csvan Dec 22 '15 at 01:28
  • No answer yet, which is dumbfounding. When I get some free time I'm going to try to spin up a second login session in a hidden iframe and reap a new token from there. In the mean time my users are forced to reload their page once an hour - yuck. – stickfigure Dec 22 '15 at 06:15
  • 1
    The Google API does attempt to update its access token 5 minutes before it expires, so most active sessions shouldn't be a problem. The real issue is what happens with [`setTimeout()` when the computer sleeps](http://stackoverflow.com/a/6347336/1462337). On some (all?) platforms the token update will be further delayed by the time the computer was sleeping, which is likely to be past time of the current token expiration. The workaround suggested by @JacekKopecký - testing for expiration and using `reloadAuthResponse()` - works well. The only drawback is this is an undocumented method. – rhashimoto Aug 05 '16 at 15:49
  • For my Android app I was going to use the ID token, and if my server end points find it has expired, then let the Android client know, so that it can do another silent sign in to obtain a fresh ID token, which I can pass back to the server, which will then work nicely. Not sure if this is the right approach, but just looking into it now. Likewise, I do not require "offline" access. –  May 14 '18 at 17:09

5 Answers5

24

If the token is expired, you can call gapi.auth2.getAuthInstance().currentUser.get().reloadAuthResponse(). It returns a Promise.

Jacek Kopecký
  • 256
  • 2
  • 4
  • This works for me. I opened a [RFE to add this to the supported API](https://github.com/google/google-api-javascript-client/issues/234). – rhashimoto Aug 05 '16 at 16:07
  • 1
    Hi @Jacek, if I use `gapi.auth2.getAuthInstance().currentUser.get().reloadAuthResponse().then(fnsuccess(){},fnerr(){})` , it throws error `Uncaught TypeError: Cannot read property 'postMessage' of null`. Can you help posing some code? – Ravikumar Sharma Sep 02 '16 at 08:06
  • 4
    `reloadAuthResponse()` is now part of the documented API. – rhashimoto Feb 18 '17 at 22:21
  • if the user refreshes the page, you lose it and can't call this method anymore. Any way to solve this? – Ulises CT Oct 11 '19 at 12:08
  • @UlisesCT `gapi.auth2.init`...? – ghchoi Aug 07 '20 at 01:42
7

I've raised an issue with Google over this because it's simply ridiculous they haven't documented this properly.

My comment here advises how I've accomplished refresh using the above.

Andrew
  • 1,406
  • 1
  • 15
  • 23
3

FWIW, we've managed to (mostly) make it work via a listener approach. It appears that 'userChanged' callback is invoked ~5 minutes before the access token expires. That's enough for us to extract and update the access token without refreshing the page.

What does not quite work though is when computer comes back from sleep. This can be solved relatively easy by reloading the page on wake up.

Community
  • 1
  • 1
maxim
  • 46
  • 3
  • I used this reload on wakeup approach and it worked well. However, I used a web worker as in the answer further down that same page: https://stackoverflow.com/a/25100973/1161948 – ThisClark Jan 04 '18 at 04:21
2

You can accomplish this with listeners.

var auth2 = gapi.auth2.getAuthInstance();

// Listen for changes to current user.
// (called shortly before expiration)
auth2.currentUser.listen(function(user){

    // use new user in your OpenID Connect flow

});

This will allow you to keep current credentials, as long as the browser remains active.

If the computer is put to sleep additional work must done to get current credentials.

if (auth2.isSignedIn.get() == true) {
    auth2.signIn();
}
Waylon Flinn
  • 19,969
  • 15
  • 70
  • 72
  • `auth2.signin()` was blocked by a popup blocker, but I *was* able to do some useful things with your technique, like fetch the value from `user.getAuthResponse().id_token` and store it in a variable I use for authorization headers. Thanks for sharing this. – ThisClark Jan 04 '18 at 04:15
0

You can use Refresh Token to get offline access. As per the official reference

Access tokens have limited lifetimes. If your application needs access to a Google API beyond the lifetime of a single access token, it can obtain a refresh token. A refresh token allows your application to obtain new access tokens.

Basically you will get the refresh token the first time you ask for authentication. You need to save that token securely for future use. The access token (you mentioned as identity token) expires after an hour. After that you have to use the refresh token each time you want to get a new usable access token.

Depending on the client library you are using the syntax will differ. following is a sample for php client library.

// get access token from refresh token if access token expire

if($client->getAuth()->isAccessTokenExpired()) {
    $client->refreshToken($securelyPreservedRefreshToken);
    $newToken = $client->getAccessToken();
}

check this for details steps.

arifin4web
  • 696
  • 8
  • 12
  • This answer is missing something essential. The user can reload the page and get a new token without re-entering their credentials. Indeed, I can almost certainly use this behavior with a hidden iframe to obtain a new token. So Google thinks the user is still "logged in" to my site in a way that is meaningful and does not require me to ask for offline access. I don't want offline access and don't want to ask for this permission. I just want their browser session to continue the same way a Facebook login session does. – stickfigure Aug 29 '15 at 15:12
  • As far my knowledge go the browser session for authentication will continue. as you mention you get from the auth2 `gapi.auth2.getAuthInstance().isSignedIn.get()`. But you can't do extended api requests without `access token`. Therefore you need the `refresh token` to obtain `access token` afterwards. – arifin4web Aug 30 '15 at 04:13
  • 1
    Doesn't this seem strange? Their API says "you're signed in" and yet the token it hands back - for the explicit purpose of allowing my server to validate the authenticity of a call - is invalid? I'm clearly either signed in or I'm not - the Google APIs understand this, and reloading the page clearly establishes this fact one way or another each time. This feels like a bug, except it's so obvious it's hard to see how it could be allowed to exist. – stickfigure Aug 30 '15 at 06:42
  • Well the the Token it hands over is not `invalid` but `short-lived`. So `isSignedIn` will evaluate if the current authenticated user is signed in to your app and with `access token ` and `refresh token` you will make other api calls. Now from security point of view the idea of refresh tokens is that if an access token is compromised, because it is short-lived, the attacker has a limited window. Refresh tokens, if compromised, are useless because the attacker requires the client id and secret in addition to the refresh token. – arifin4web Aug 30 '15 at 10:01
  • 1
    Refresh tokens exist to enable offline access, and require that I request an additional permission (`access_type=offline`). That isn't necessary in this case; Google knows the user is still signed in, and any reload of the page will refresh the access token. I can almost certainly do this in an iframe and build the feature myself. So where's the API to do it? I appreciate that you are trying to help, but refresh tokens are a red herring - they are not relevant here. – stickfigure Aug 30 '15 at 16:26