Skip to content

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

Description

@bradvogel

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.

Activity

  1. changed the title [-]Refresh token automatically on HTTP 401 or 403 respose?[/-] [+]Refresh token automatically on HTTP 401 or 403 response?[/+] on Aug 22, 2014
  2. ryanseys commented on Aug 22, 2014

    @ryanseys
    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.

  3. bradvogel commented on Aug 22, 2014

    @bradvogel
    Author

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

  4. ryanseys commented on Aug 22, 2014

    @ryanseys
    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.

  5. bradvogel commented on Aug 22, 2014

    @bradvogel
    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.

  6. ryanseys commented on Aug 22, 2014

    @ryanseys
    Contributor

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

  7. ChrisAlvares commented on Sep 4, 2014

    @ChrisAlvares

    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
        });
    }
  8. joewoodhouse commented on Sep 10, 2014

    @joewoodhouse

    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?

  9. ryanseys commented on Sep 10, 2014

    @ryanseys
    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)
    .

  10. vladmiller commented on Mar 30, 2015

    @vladmiller

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

  11. zoellner commented on Apr 14, 2015

    @zoellner

    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
        });
    
  12. irisSchaffer commented on Jun 16, 2015

    @irisSchaffer

    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.

  13. njcaruso commented on Apr 13, 2016

    @njcaruso

    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.

  14. lirbank commented on Jul 18, 2016

    @lirbank

    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);
    }
  15. pishguy commented on Aug 29, 2016

    @pishguy

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

  16. Maulik0923 commented on Oct 17, 2016

    @Maulik0923

    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.

  17. Maulik0923 commented on Oct 17, 2016

    @Maulik0923

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

  18. jmdobry commented on Oct 19, 2016

    @jmdobry
    Contributor

    I clarified in the README.

    See #261 (comment) for an example.

  19. tnguyen14 commented on Nov 1, 2016

    @tnguyen14

    @jmdobry where does the initial expiry_date value come from?

  20. jmdobry commented on Dec 28, 2016

    @jmdobry
    Contributor
  21. bdrazen commented on Jun 10, 2018

    @bdrazen

    @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?

  22. JustinBeckwith commented on Jun 10, 2018

    @JustinBeckwith
    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.

  23. bdrazen commented on Jun 10, 2018

    @bdrazen

    Cheers for the snappy response! 🍻

  24. pitops commented on Jun 12, 2018

    @pitops

    @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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

🚨This issue needs some love.triage meI really want to be triaged.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions