'Quickbooks PHP API: Refresh OAuth 2 Token Failed
I'm trying to access Quickbooks API using the PHP SDK but getting the following error:
Refresh OAuth 2 Access token with Refresh Token failed. Body: [{"error":"invalid_grant"}].
My Tokens seem to work for 24 hours but after that I receive the error above. Each time I call the API, I am saving my updated tokens to my database:
//Client ID & Secret
$qbClientId = $this->scopeConfig->getValue('quickbooks/api/qb_client_id', $storeScope);
$qbClientSecret = $this->scopeConfig->getValue('quickbooks/api/qb_client_secret', $storeScope);
//Retrieve currently saved Refresh_Token from DB
$qbRefreshToken = $this->scopeConfig->getValue('quickbooks/api/qb_refresh_token', $storeScope);
$OAuth2LoginHelper = new OAuth2LoginHelper($qbClientId, $qbClientSecret);
$accessTokenObj = $OAuth2LoginHelper->refreshAccessTokenWithRefreshToken($qbRefreshToken);
$error = $OAuth2LoginHelper->getLastError();
if($error) {
throw new \Exception($error);
} else {
// The refresh token and access token expiration
$refreshTokenValue = $accessTokenObj->getRefreshToken();
$refreshTokenExpiry = $accessTokenObj->getRefreshTokenExpiresAt();
// Save new Refresh Token & Expiry to DB
$this->configInterface->saveConfig('quickbooks/api/qb_refresh_token', $this->encryptor->encrypt($refreshTokenValue), 'default', 0);
$this->configInterface->saveConfig('quickbooks/api/qb_refresh_token_expiry', $refreshTokenExpiry, 'default', 0);
// The access token and access token expiration
$accessTokenValue = $accessTokenObj->getAccessToken();
$accessTokenExpiry = $accessTokenObj->getAccessTokenExpiresAt();
// Save new Access Token & Expiry to DB
$this->configInterface->saveConfig('quickbooks/api/qb_access_token', $this->encryptor->encrypt($accessTokenValue), 'default', 0);
$this->configInterface->saveConfig('quickbooks/api/qb_access_token_expiry', $accessTokenExpiry, 'default', 0);
return DataService::Configure(array(
'auth_mode' => 'oauth2',
'ClientID' => $qbClientId,
'ClientSecret' => $qbClientSecret,
'accessTokenKey' => $accessTokenValue,
'refreshTokenKey' => $refreshTokenValue,
'QBORealmID' => 'MyRealmID',
'baseUrl' => 'Development'
));
}
So as you can see, on each API call, I'm using the refreshAccessTokenWithRefreshToken($qbRefreshToken) method to get new Refresh and Access Tokens and saving those to my DB for next use, however I still receive invalid_grant errors after 24hours.
Any ideas?
Solution 1:[1]
I struggled with the same problem. In my case, I had updated the tokens in the database, but not everywhere in memory (specifically in the $dataService object). So continuing to use the $dataService object to make API calls was using the old tokens. D'oh!
Although I think it would still work, you do not need to refresh the tokens with every API call (as David pointed out). My solution was to make the API call and if it fails, then I refresh the tokens and make the API call again. Here is a simplified version of my code:
$user = ...; // get from database
$dataService = getDataServiceObject();
$response = $dataService->Add(...); // hit the QBO API
$error = $dataService->getLastError();
if ($error) {
refreshTokens();
$response = $dataService->Add(...); // try the API call again
}
// ... "Add" complete, onto the next thing
////////////////////////////////
function getDataServiceObject() {
global $user;
return DataService::Configure(array(
'auth_mode' => 'oauth2',
'ClientID' => '...',
'ClientSecret' => '...',
'accessTokenKey' => $user->getQbAccessToken(),
'refreshTokenKey' => $user->getQbRefreshToken(),
'QBORealmID' => $user->getQbRealmId(),
'baseUrl' => '...',
));
}
function refreshTokens() {
global $dataService;
global $user;
$OAuth2LoginHelper = $dataService->getOAuth2LoginHelper();
$obj = $OAuth2LoginHelper->refreshAccessTokenWithRefreshToken($user->getQbRefreshToken());
$newAccessToken = $obj->getAccessToken();
$newRefreshToken = $obj->getRefreshToken();
// update $user and store in database
$user->setQbAccessToken($newAccessToken);
$user->setQbRefreshToken($newRefreshToken);
$user->save();
// update $dataService object
$dataService = getDataServiceObject();
}
Solution 2:[2]
https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/faq
Why does my refresh token expire after 24 hours?
Stale refresh tokens expire after 24 hours. Each time you refresh the access_token a new refresh_token is returned with a lifetime of 100 days. The previous refresh_token is now stale and expires after 24 hours. When refreshing the access_token, always use the latest refresh_token returned to you.
Are you sure that the latest refresh token is used?
Solution 3:[3]
So as you can see, on each API call, I'm using the refreshAccessTokenWithRefreshToken($qbRefreshToken) method to get new Refresh and Access Tokens and saving those to my DB for next use...
Why are you requesting a new access token and refresh token every time you make a request? Why are not checking if the old access token is stil valid?
As you stated above, you are even storing the expiration time of the access token. So you should know if it is still valid or not.
So when you are making the API request and you access token has expired, you will get a error message. In return, you can now request a new one.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Alan P. |
| Solution 2 | Community |
| Solution 3 | davidev |
