node-quickbooks
node-quickbooks copied to clipboard
refreshAccessToken doesn't check {"error":"invalid_grant"} response and sets both tokens to undefined
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));
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?
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 Have you figured out how to solve this?
As I said, I got the error due to concurrent API calls, so I moved to synchronous logic and it worked.