Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refresh token automatically on HTTP 401 or 403 response? #261

Closed
bradvogel opened this issue Aug 22, 2014 · 25 comments
Closed

Refresh token automatically on HTTP 401 or 403 response? #261

bradvogel opened this issue Aug 22, 2014 · 25 comments
Assignees
Labels
🚨 This issue needs some love. triage me I really want to be triaged.

Comments

@bradvogel
Copy link

I'm using the API (v1.0.10) in the following way:

    var oauth2Client = new OAuth2(getGoogleCredentials().clientId, getGoogleCredentials().secret);
    oauth2Client.setCredentials({
      access_token: user.services.google.accessToken,
      refresh_token: user.services.google.refreshToken
    });
    calendar.events.list({
        calendarId: 'primary',
        auth: oauth2Client
      }, ...);

When my token is expired I get an HTTP 401 back from the server. However, I'd expect the library to automatically refresh my token and make the request again. It doesn't.

I figured out that a workaround is to add the expiry_date when calling setCredentials, e.g.

oauth2Client.setCredentials({
      access_token: user.services.google.accessToken,
      refresh_token: user.services.google.refreshToken,
      expiry_date: user.services.google.expiresAt
    });

But this isn't mentioned in the docs. I'd expect expiry_date to be optional and the API to auto refresh the token.

@bradvogel bradvogel changed the title Refresh token automatically on HTTP 401 or 403 respose? Refresh token automatically on HTTP 401 or 403 response? Aug 22, 2014
@ryanseys
Copy link
Contributor

Nope it won't refresh. We did this because when you make a request that requires a media body we can't reliably re-add the media body due to the possibility that the media body is a stream or that the entire body is being piped into the request. Yes, it means you need to add expiry date if one hasn't been added or refresh the token yourself if you know it is expired.

@bradvogel
Copy link
Author

Oh ok, makes sense. Can you add expiry_date to the main docs then?

@ryanseys
Copy link
Contributor

It's not meant to be set by the user really. Not sure if documenting it is suggested because it may change in the future without warning.

@bradvogel
Copy link
Author

Ok. I still think it's worth mentioning something about the library not taking care of refreshing the token, and how to do it in an example.

@ryanseys
Copy link
Contributor

This is a good point. I filed #262 for this.

@ChrisAlvares
Copy link

Hi Ryan,

I was looking through the source, and it seems that if you do not supply an access token in the setCredentials, it will automatically renew the access token before making the request. Can you confirm this behavior? I sometimes receive an "invalid_grant" after about 30-60 days from authenticating even though I supplied the offline parameter in the oAuth dance.

Would you recommend doing something like this to prevent getting the invalid_grant parameter?

setAuthTokens = function(accessToken, refreshToken) {
    if (refreshToken) {
        accessToken = null;
    }
    this.client.setCredentials({
        access_token: accessToken,
        refresh_token: refreshToken
    });
}

@joewoodhouse
Copy link

The source code for oauth2client.js does basically say that on a 401 or 403 it will refresh the token and retry?

/**
 * Provides a request implementation with OAuth 2.0 flow.
 * If credentials have a refresh_token, in cases of HTTP
 * 401 and 403 responses, it automatically asks for a new
 * access token and replays the unsuccessful request.
 * @param {object} opts Request options.
 * @param {function} callback callback.
 * @return {Request} Request object
 */
OAuth2Client.prototype.request = function(opts, callback) {

Perhaps the comment should also be removed to avoid confusion?

@ryanseys
Copy link
Contributor

Oh ya this is wrong. I'll fix it.

On Wednesday, September 10, 2014, joewoodhouse [email protected]
wrote:

The source code for oauth2client.js does basically say that on a 401 or
403 it will refresh the token and retry?

/**

  • Provides a request implementation with OAuth 2.0 flow.
  • If credentials have a refresh_token, in cases of HTTP
  • 401 and 403 responses, it automatically asks for a new
  • access token and replays the unsuccessful request.
  • @param {object} opts Request options.
  • @param {function} callback callback.
  • @return {Request} Request object
    */
    OAuth2Client.prototype.request = function(opts, callback) {

Perhaps the comment should also be removed to avoid confusion?


Reply to this email directly or view it on GitHub
#261 (comment)
.

@vladmiller
Copy link

So is there any event I can listen and refresh token?

@zoellner
Copy link

I ran into a similar issue. What works for me is to set expiry_date to true when setting the credentials and I don't have a date availble. This will force a token refresh, you can then store the client's expiry_date after using it.

    oauth2Client.setCredentials({
      access_token: user.google.accessToken,
      refresh_token: user.google.refreshToken,
      expiry_date: true
    });

@irisSchaffer
Copy link

As this issue is not closed yet, I thought I'd ask for an update on the matter. As discussed before, the token is not automatically refreshed, when sending both the refresh_token and the access_token and as @zoellner suggested, adding expiry_date: true solves this problem. However, in the documentation (README.md) it says:

You can start using OAuth2 to authorize and authenticate your requests to Google APIs with the retrieved tokens. If you provide a refresh_token and the access_token has expired, the access_token will be automatically refreshed and the request is replayed.

So you might want to update that.

@ghost
Copy link

ghost commented Mar 8, 2016

Spent the last hour debugging my code because of the misleading Readme.md :( You should fix this.

@njcaruso
Copy link

Please update the readme.md. As was pointed out by the last two posters, the readme is wrong. The access_token is NOT automatically refreshed. Thanks above for the tip on expiry_date: true.

@lirbank
Copy link

lirbank commented Jul 18, 2016

Actually, setting expiry_date: true is forcing a refresh of the access token on every request, which probably is not what you want.

A better work around is to store the expiry_date the API is returning and including it in the following requests. That will make the auto-refresh work, while only refreshing the access token when needed (instead of on every request).

Eg, something like:

const oauth2Client = new OAuth2(clientId, secret, redirectUrl);
let tokens = db.getTokens();
let {access_token, refresh_token, expiry_date} = tokens;
oauth2Client.setCredentials({access_token, refresh_token, expiry_date});

let response = await gmail.users.messages.list({ userId: 'me', auth: oauth2Client });

// If a refresh occured, store the new access_token and expiry_date
if (oauth2Client.credentials.id_token) {
  console.log('Received new tokens');
  db.storeTokens(oauth2Client.credentials);
}

@pishguy
Copy link

pishguy commented Aug 29, 2016

@lirbank refresh token work on sockets? such as socket.io

@Maulik0923
Copy link

Maulik0923 commented Oct 17, 2016

Hello,
How can i get access_toke and refresh_token ?

Can anyone help me??

My Code is :

var http = new XMLHttpRequest();
            var url = "https://accounts.google.com/o/oauth2/revoke/?token=" + token;
            //var params = "token=" + token;
            http.open("GET", url, true);

            //Send the proper header information along with the request
            http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

            http.onreadystatechange = function () {//Call a function when the state changes.
                if (http.readyState == 4 && http.status == 200) {
                    alert(http.responseText);
                }
            }
            http.withCredentials = true;
            http.send(null);

Error getting : - 404.No 'Access-Control-Allow-Origin' header is present on the requested resource.

@Maulik0923
Copy link

Can i call the calendar event(insert,update,delete) by authentication token without authorization process?

@jmdobry
Copy link
Contributor

jmdobry commented Oct 19, 2016

I clarified in the README.

See #261 (comment) for an example.

@jmdobry jmdobry closed this as completed Oct 19, 2016
@tnguyen14
Copy link

@jmdobry where does the initial expiry_date value come from?

@ghost
Copy link

ghost commented Nov 14, 2016

After logging into my web app PassportJs Google Oauth2

      oAuth2.setCredentials({
          access_tokens: user[0].access_token,
          refresh_tokens: user[0].refresh_token,
          expiry_date: true
        });
    plus.people.get({
      userId: 'me',
      auth: oAuth2
    }, function (err, response) {

      console.log(err);
      res.send(response);

    });

I am always receiving an error [Error: No access or refresh token is set.]

Can anyone please help me why the tokens are not set

@jmdobry
Copy link
Contributor

jmdobry commented Dec 28, 2016

@bdrazen
Copy link

bdrazen commented Jun 10, 2018

@lirbank #261 (comment)

In contrast, the readme details the following approach:

oauth2client.on('tokens', (tokens) => {
  if (tokens.refresh_token) {
    // store the refresh_token in my database!
    console.log(tokens.refresh_token);
  }
  console.log(tokens.access_token);
});

Are both approaches fine, or is one recommended? Does tokens here include expiry_date?

@JustinBeckwith
Copy link
Contributor

Greetings! The event based approach is new, and was added after this post was originally answered :) . Both ways are fine honestly. The nice thing about using the event is that you get notified not only when the refresh_token changes, but also when the access_token is refreshed. The tokens returned here should return the expiry_date.

@bdrazen
Copy link

bdrazen commented Jun 10, 2018

Cheers for the snappy response! 🍻

@pitops
Copy link

pitops commented Jun 12, 2018

@JustinBeckwith is there a better example with using the event based approach? I mean in a fuller context. i.e do i listen to the event before making a request to youtube api for example or something?

@yoshi-automation yoshi-automation added 🚨 This issue needs some love. triage me I really want to be triaged. labels Apr 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🚨 This issue needs some love. triage me I really want to be triaged.
Projects
None yet
Development

No branches or pull requests