node-quickbooks icon indicating copy to clipboard operation
node-quickbooks copied to clipboard

refreshAccessToken doesn't check {"error":"invalid_grant"} response and sets both tokens to undefined

Open darkman82 opened this issue 4 years ago • 4 comments

Sometimes, when refreshing the access token, QB responds with this body {"error":"invalid_grant"} which is accepted as a successful response and leads to both tokens being set to undefined.

A this point the qbo object becomes useless and you need a new one.

e : null r : {"error":"invalid_grant"}

index.js#L146

    request.post(postBody, (function (e, r, data) {
        if (r && r.body) {
            var refreshResponse = JSON.parse(r.body);
            this.refreshToken = refreshResponse.refresh_token;
            this.token = refreshResponse.access_token;
            if (callback) callback(e, refreshResponse);
        } else {
            if (callback) callback(e, r, data);
        }
    }).bind(this));

darkman82 avatar Feb 17 '21 09:02 darkman82

  const oauth = getOAuthClient();
  oauth.setToken(token);
  const authResponse = await oauth.refresh();
  const newToken: QuickBooksToken = authResponse.token;

  // https://help.developer.intuit.com/s/article/Validity-of-Refresh-Token
  if (newToken.refresh_token !== token.refresh_token) {
    // save new token
  }

I use this code but quite often I'm also hitting the invalid_grant issue. Then my users need to go through the oauth process again.

I feel like I'm doing what's described here.

Has anyone figured out a good solution?

lukas1994 avatar Feb 17 '21 22:02 lukas1994

To be clear: I don't use oauth, I used it once in the QB Playground just to get the refreshToken, which is supposed to last 100 days, then I keep it updated when it changes across the refresh processes.

The invalid_grant error does NOT invalidate the current refreshToken (if you updated it from the previous successful request), this means that it can be used again to get a new access token (=> you don't need to OAuth again).

The problem is that, bug or not, invalid_grant error, and any others, are not handled at all, leading to both tokens being set to undefined.

The solution is: check if the "error" field is present in the body response and, in case, avoid updating the token variables with undefined. Ignore it and retry after a while.

PS: the reason why I get invalid_grant error is related to the asynchronous logic behind my invoicing system which leads to pseudo-concurrent refresh requests. I would probably need to convert some array.forEach (async) to a for-of loop (synchronous).

darkman82 avatar Feb 17 '21 23:02 darkman82

@darkman82 Have you figured out how to solve this?

aniespica avatar May 17 '22 14:05 aniespica

As I said, I got the error due to concurrent API calls, so I moved to synchronous logic and it worked.

darkman82 avatar May 17 '22 15:05 darkman82