QuickBooks-V3-PHP-SDK
QuickBooks-V3-PHP-SDK copied to clipboard
Refresh OAuth 2 Access token with Refresh Token failed
I often get this error - Refresh OAuth 2 Access token with Refresh Token failed. Body: [{"error":"invalid_grant"}]
@mishabachkur This error typically occurs when you have an incorrect configuration for clientId, clientSecret etc. Can you please check you have configure the keys accurately?
The issue: confirmed
The reason:
With the same credentials:
$companyInfo = $dataService->getCompanyInfo();
return json_encode($companyInfo, true);
worked properly
But,
$OAuth2LoginHelper = $dataService->getOAuth2LoginHelper();
$accessTokenObj = $OAuth2LoginHelper->exchangeAuthorizationCodeForToken(
self::QUICKBOOKS_AUTHORIZATION_CODE,
self::QUICKBOOKS_REALM_ID
);
Provided with an issue:
Cron-job "socalgraph_quickbooks" threw exception QuickBooksOnline\API\Exception\ServiceException
[QuickBooksOnline\API\Exception\ServiceException]
Exchange Authorization Code for Access Token failed. Body: [{"error_description":"Token invalid","error":"invalid_grant"}].
@coresh That is very interesting find. Thanks for persisting on the issue. Would you be able to send me the raw request and response log for both the calls? I can bring it to my downstream team.
Executed code:
$dataService = DataService::Configure(array(
'auth_mode' => 'oauth2',
'ClientID' => self::QUICKBOOKS_CLIENT_ID,
'ClientSecret' => self::QUICKBOOKS_CLIENT_SECRET,
'RedirectURI' => self::QUICKBOOKS_REDIRECT_URI,
'scope' => "com.intuit.quickbooks.accounting",
'baseUrl' => "Production"
));
$OAuth2LoginHelper = $dataService->getOAuth2LoginHelper();
$accessTokenObj = $OAuth2LoginHelper->exchangeAuthorizationCodeForToken(
$this->authorizationCode, self::QUICKBOOKS_REALM_ID
);
$error = $dataService->getLastError();
if ($error) {
echo "The Status code is: " . $error->getHttpStatusCode() . "\n";
echo "The Helper message is: " . $error->getOAuthHelperError() . "\n";
echo "The Response message is: " . $error->getResponseBody() . "\n";
exit();
}
vendor/quickbooks/v3-php-sdk/src/Core/OAuth/OAuth2/OAuth2LoginHelper.php
public function exchangeAuthorizationCodeForToken($code, $realmID){
...
var_dump(http_build_query($parameters));
var_dump($intuitResponse->getBody());
var_dump($intuitResponse->getHeaders());
...
Result:
string(171)
grant_type=authorization_code
code=real_code
redirect_uri=https%3A%2F%2Fdeveloper.intuit.com%2Fv2%2FOAuth2Playground%2FRedirectUrl
string(61) "{"error_description":"Token invalid","error":"invalid_grant"}"
array(10) {
["date"]=>
string(29) "Mon, 13 Jul 2020 16:24:34 GMT"
["content-type"]=>
string(30) "application/json;charset=utf-8"
["content-length"]=>
string(2) "61"
["server"]=>
string(5) "nginx"
["strict-transport-security"]=>
string(16) "max-age=15552000"
["intuit_tid"]=>
string(35) "1-5f0c8ac2-94777b4a0a0f25100de4b6f2"
["x-spanid"]=>
string(38) "6f58449e-aa9e-4cf7-b640-8ec0744cc449:1"
["x-amzn-trace-id"]=>
string(81) "Self=1-5f0c8ac2-c63f2990c703c9bc452205fc;Root=1-5f0c8ac2-94777b4a0a0f25100de4b6f2"
["cache-control"]=>
string(18) "no-cache, no-store"
["pragma"]=>
string(8) "no-cache"
}
@coresh Ah I see what you are saying. These calls use different auth headers. The auth code call uses Basic XXXX where XXXX is a base 64 encoded value of client id and secret. The getUserInfo call is a Bearer token call, where you use the accessToken which you had last obtained. I would still suggest checking the value for clientId that is being passed in the request. Since the client information are secure, you could choose to create a support ticket and we can help you there.
Re : I would still suggest checking the value for clientId that is being passed in the request.
clientID -> it's Ok.
The reason:
call:
$dataService->getCompanyInfo()
returns properly output
Please check concerning to the Production environment.
Thank you
@coresh can you please elaborate what you mean by "Please check concerning to the Production environment."?
I am getting the same error. I have found that I get a new refresh token before 24 hours have elapsed since I received the old one. My application is configured to save the new token every time, but the new token does not work. If I log the old token, and use that to call the refresh token endpoint again it works fine, and I get the old token back. I do believe there might be an issue on your guy's end.
Checking my error logs I notice that I can't re-authenticate my server about 8 hours after doing so manually, leading me to believe that that's when you guys return a new refresh token to me.
My client ID, and secret are both correct, as if I go to my server and simply change the refresh token back to what it was before the automated process I can retrieve a new access token, but a new refresh token is not returned.
This is confusing so I'll go through step by step what I did, and how I think things went on the server:
- use the playground to retrieve an access and refresh token
- log both tokens at the time i receive them in my issue tracker
- save those on the server, and begin the re-authentication job (this runs every 15 minutes to retrieve a new access token)
- 8 hours later, on the server: quickbooks returns a new refresh token, earlier than expected, but it's saved anyway, as recommended by the documentation at point 3 here
- that new token does not work, leading to the
invalid_granterror from quickbooks - I notice the error (at the time of writing, roughly 22 hours after I initiated step 1), log into the server, with the old refresh token, which I logged earlier, and replace the new refresh token I got from quickbooks with the old one
- call the refresh_token endpoint, with the old token, which returns a new access_token as expected, and the old refresh token.
I think that around 8 hours after I initiated step 1, you guys return a new refresh token to me.
edit: I put the old token back on my server, and was able to proceed to receive a new access token using the old token (it hadn't expired as yet, still had two hours left on it). Two hours later, that refresh token expired, and I was not given a new one by quickbooks. I tried the new one I got earlier, and that doesn't work either.
@daraul Have you figure out how to solve this?