2

I recently moved from the deprecated gapi.auth2 to the new Google Identity Services, using the javascript client library, and noticed a big difference: if someone signs in, and then reloads the page, the session is lost, and has to sign in again, every time the page is loaded. This was not the case with the deprecated library. The problem can be easily reproduced with the Calendar API example.

Is there any configuration option to keep the session persistent? Or do I need to store the access tokens somehow? I could not find anything relevant in the official docs.

UPDATE:

The migration guide states the following:

Previously, Google Sign-In helped you to manage user signed-in status using:

  • Callback handlers for Monitoring the user's session state.
  • Listeners for events and changes to signed-in status for a user's Google Account.

You are responsible for managing sign-in state and user sessions to your web app.

However there's absolutely no information on what needs to be done.

UPDATE 2

To be more specific, the actual issue is not making the session persistent. Managing the sign in state and user session is something I can solve.

The real problem is the access token used to call the Google APIs. As mentioned in the comments, the access tokens are 1) short lived 2) are not stored anywhere, so even if not expired, they do not persist between page reloads.

Google provides the requestAccessToken method for this, however even if I specify prompt: '', it opens the sign-in popup. If I also specify the hint option with the signed in user's email address, than the popup opens, displays a loading animation briefly, and closes without user interaction. I could live with this, however this only works if triggered by a user interaction, otherwise the browser blocks the popup window, meaning that I cannot renew the token without user interaction, e.g. on page load. Any tips to solve this?

istvan.halmen
  • 3,320
  • 1
  • 23
  • 29
  • For security reasons, access tokens often have a very short lifetime. When an access token expires, a refresh token can be used to get a new access token without entering login credentials again. Refresh tokens have a long lifetime. If they are valid and not expired, clients can obtain new access tokens and will help to keep the session persistent. For more details check these links, [link1](https://developers.google.com/identity/protocols/oauth2#5.-refresh-the-access-token,-if-necessary) and [link2](https://blog.logrocket.com/persistent-login-in-react-using-refresh-token-rotation/) – Roopa M Jul 06 '22 at 11:11
  • @RoopaM, thanks! I updated the question, see the "Update 2" section – istvan.halmen Jul 07 '22 at 09:40
  • 1
    Can you share an example where this reproduces? Also, from [5. Refresh the access token, if necessary.](https://developers.google.com/identity/protocols/oauth2#5.-refresh-the-access-token,-if-necessary.) it is said: `Save refresh tokens in secure long-term storage and continue to use them as long as they remain valid` – Kessy Jul 07 '22 at 13:42

1 Answers1

1

I faced all the same issues you described in your question.

In order to help:

  1. Google 3P Authorization JavaScript Library: in this link we can check all the methods the new library has (it does not refresh token, etc..)
  2. This doc says the library won't control the cookies to keep the state anymore.

Solution

As Sam described: "you can somehow save access token and use it to speed-up things after page reload."

Given the the Google's exampe, we should call initTokenClient in order to configure the Google Auth and the requestAccessToken to popup the auth:

tokenClient = google.accounts.oauth2.initTokenClient({
          client_id: 'YOUR_CLIENT_ID',
          scope: 'https://www.googleapis.com/auth/calendar.readonly',
          prompt: 'consent',
          callback: tokenCallback
      });
tokenClient.requestAccessToken({prompt: ''})

In your tokenCallback you can save the credentials you get somehow, e.g.:

const tokenCallback(credentials) => {
    // save here the credentials using localStorage or cookies or whatever you want to.
}

Finally, when you restart/reload your application and you initialize the gapi.server again, you only need to get the credentials again and set token to gapi, like:

gapi.load('client', function() {
    gapi.client.init({}).then(function() {
      let credentials = // get your credentials from where you saved it
      credentials = JSON.parse(credentials); // parse it if you got it as string
      gapi.client.setToken(credentials);

      ... continue you app ...
    }).catch(function(err) {
      // do catch...
    });
});

Doing it, your application will work after the reload. I know it could not be the best solution, but seeing what you have and the library offers, I think that's you can do.

p.s.: the token expires after 1 hour and there is no refresh token (using the implicit flow) so, you will have to ask the user to sign-in again.

Vítor Resende
  • 230
  • 3
  • 16