Banjo Batman
Banjo Batman

Reputation: 199

NodeJs Google API oauth2client refresh_token used too late googleapi

I'm using the npm package googleapis:^100.0.0 and I'm getting this error:

Error: Token used too late, 1653569732.911 > 1653511671: {"iss":"https://accounts.google.com","azp":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com","aud":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com","sub":"113117610373384376838","email":"[email protected]","email_verified":true,"at_hash":"xxxxxxxxxxxxxxxxxxxxxxxx","iat":1653507771,"exp":1653511371}

My workflow is this:

  1. I get access_token, refresh_token, and id_token from google oath upon first app authorization.

    const scopes = ["https://www.googleapis.com/auth/userinfo.email", "openid"];

     oauth2client.setCredentials({ refresh_token: tokens.refresh_token }); 
      const url = oauth2client.generateAuthUrl({
       // 'online' (default) or 'offline' (gets refresh_token)
       access_type: "offline",
       // If you only need one scope you can pass it as a string
       scope: scopes,
     });
    
  2. I store those in my database.

  3. I set the oath credentials to the refresh_token

    oauth2client.setCredentials({ refresh_token: tokens.refresh_token });

  4. I get an unexpired token from Google: const accessToken = await oauth2client.getAccessToken();

  5. I set the credentials for the oauth2client to the new unexpired credentials.

    oauth2client.setCredentials(accessToken);

  6. I verify the IdToken.

    const loginTicket = await oauth2client .verifyIdToken({ idToken: tokens.id_token, }) .catch((ex) => { logE(ex); result.error = "Token likely used too late."; });

Google's documentation says

After a user grants offline access to the requested scopes, you can continue to use the API client to access Google APIs on the user's behalf when the user is offline. The client object will refresh the access token as needed.

I can log into google and verify the id_token for a few hours. However, several hours later I'm getting a token used too late error.

Am I missing something here that would allow me to re-validate the user without sending the user back to the google oauth screen?

Upvotes: 1

Views: 1342

Answers (1)

Banjo Batman
Banjo Batman

Reputation: 199

It took way longer than it should, but I found what the problem was. I'm posting it here in the hopes that if someone else finds this same issue I can save them the trouble of tracking it down. The issue for me was the return value of

const accessToken = await oauth2client.getAccessToken();

In Google's documentation, they show this as an example of how you use this token:

oauth2client.setCredentials(accessToken);

The problem is that this only works if the original token used to obtain the unexpired token was not expired. Once it expires, the return value of getAccessToken() changes shape completely.

The fix is fairly easy. You first have to check if the accessToken has a member named res. If it does, then you set the credentials of your oauth2client to accessToken.res.data instead of using accessToken itself.

 if (accessToken.res) {
      oauth2client.setCredentials(accessToken.res.data);

Upvotes: 3

Related Questions