Closed sanatani9 closed 7 years ago
Did you manage to fix this issue? I am also having the same problem, fresh access token throws TokenExpiredException and refreshaccesstoken gives me identical error to yours.
@infusionsoftbamboo also experiencing this problem please rectify immediately
We will have the API team look into this shortly and report and update back here.
@archish1989 When it fails, have you tried retrying the refresh token call with the same info? Also I suggest you store a serialized version of the token in the database instead of breaking it apart so it maintains the entire token object which should prevent any weird saving issues of the refresh token.
Any word here? My Client is FREAKING OUT!
We have been unable to duplicate this issue thus far. One thought is that if multiple threads are being used they could be inadvertently invalidating the refresh token. Can you confirm that there is only one thread being used for refreshing?
I'm having this exact issue. I can see the Infusionsoft SDK is attempting this API call:
->request('POST', 'https://api.infusionsoft.com/token', array('body' => 'grant_type=refresh_token&refresh_token={refreshToken}', 'headers' => array('Authorization' => 'Basic {code}', 'Content-Type' => 'application/x-www-form-urlencoded')))
And I guaranty that {refreshToken}
is exactly the same string I just got 10 minutes ago via oAuth process. Yet Infusionsoft's API responds:
{"error":"invalid_grant","error_description":"Invalid refresh token"}
Is there something else I could be missing?
@pfeiferchristopher If you retry refreshing the token (using the exact same data) do you get the same error repeatedly?
@bgpartridge yes, I can re-oauth and get new tokens but I can not successfully refresh what I'm given.
We have reached out to our 3rd party vendor who provides the auth token service with this thread to have them look into it as well. I will update this thread as we have new information.
After a few conversations with Mashery (who provides the OAuth token endpoint for our API) here is the update:
Mashery thoroughly checked their monitoring system for this endpoint and there are no errors with the system being reported (they confirmed that their monitors check the exact use case we describe in our API documentation). They also spot checked a few heavy usage non Infusionsoft clients and found nothing reported correlating to this issue. We're left to conclude that on Mashery's end the token endpoint is functioning properly.
We confirmed with Mashery that there are two cases in which a refresh token becomes invalid:
Mashery reiterated something mentioned earlier in this thread. If you are using multiple threads and one of them refreshes the token, the other threads will then get an invalid token error unless you have explicitly solved for this case.
One suggestion we have is to maintain a log/text file/csv etc. Update it as soon as the refresh token is used with the data time stamp. This should catch if tokens are being used multiple times.
We can duplicate the invalid token using the cases listed above because they as designed. Neither Infusionsoft nor Mashery has been able to reproduce this issue otherwise. If you verify that everything in mentioned is being accounted for, and you still see the error please provide as much detail as you can through a support ticket and we will continue to investigate.
Hi I am having the same issue. May i know the TTL for refresh token. I have one endOfLife value in my token. i suspect that endOfLife value is for access token. Can you please let me know what is TTL for refresh token. below is the exception i am getting
Infusionsoft\Http\HttpException: exception 'GuzzleHttp\Exception\ClientException' with message 'Client error: POST https://api.infusionsoft.com/token
resulted in a 400 Bad Request
response: {"error":"invalid_grant","error_description":"Invalid refresh token"} ' in D:\WorkDir\WebSite\vendor\guzzlehttp\guzzle\src\Exception\RequestException.php:111 Stack trace: #0 D:\WorkDir\WebSite\vendor\guzzlehttp\guzzle\src\Middleware.php(65): GuzzleHttp\Exception\RequestException::create(Object(GuzzleHttp\Psr7\Request), Object(GuzzleHttp\Psr7\Response)) #1 D:\WorkDir\WebSite\vendor\guzzlehttp\promises\src\Promise.php(203): GuzzleHttp\Middleware::GuzzleHttp{closure}
Thanks.
I'm really sorry that you are still having troubles with this. We definitely want to get you over this hurdle. There is no known issue with refreshing tokens on either the Infusionsoft end or with Mashery (our API Proxy provider). This appears to be an issue in implementation and so the two best options at this point are to:
For your info, auth tokens are available for 24 hours, our refresh tokens have no expiration (infinite TTL). They are however invalidated when a new auth token is issued, so each time you get a new auth token you need to make sure you're storing the newly issues refresh token as well to use that the next time you want to refresh.
hi @bgpartridge I also have the same issue. Can you suggest me on this see below. My idea is to authorize the app only one time and get refreshed token every time.
hi @micfai , @bgpartridge Can you provide the code for refresh access token with db connectivity.
The problem is, when you authorize the app very first time and save the token in session and after that you refresh the accesstoken. The server grants you a new refreshtoken but you are not saving the most recent token in DB or session. Everytime you refresh the token, you must also pull the token and update your database with new refreshed token.
What I have done is, I created two files, one to deserealize the token and create object for my application to use and another script which runs every hour and refreshes the token and saves the token after it is refreshed.
I keep getting the error Invalid Client..!!!
Post an example of how to save these tokens, because I'm with the same error as above, so it's easier to understand. Ma an example of saving in DB not just the session.
Thanks.
Here is my example, this is the cron script which runs midnight every night to refresh the token and saves it in DB
global $infusionsoft;
//get a clientid and secret from infusionsoft developer website, not main website
$infusionsoft = new \Infusionsoft\Infusionsoft(array(
'clientId' => 'XXXXXXXXXXXXXXXXXXXXXXXX',
'clientSecret' => 'XXXXXXXX',
'redirectUri' => 'https://www.example.com/setupinfusion.php',
));
$tokenfromdb = pulltoken();
//when you serealize the infusionsoft object it strips the backslash at the time of saving it to DB so
//you must put back the backslash after you retrieve the serealized object
$tokenfromdb = str_replace('InfusionsoftToken','Infusionsoft\Token',$tokenfromdb);
$infusionsoft->setToken(unserialize($tokenfromdb));
if (isset($_GET['code']) and !$infusionsoft->getToken()) {
$infusionsoft->requestAccessToken($_GET['code']);
}
if (!$infusionsoft->getToken()) {
echo '<a href="' . $infusionsoft->getAuthorizationUrl() . '">Click here to authorize</a>';
}
$infusionsoft->refreshAccessToken();
//$infusionsoft->contacts()->add($contactData);
if ($infusionsoft->getToken()) {
// Save the serialized token to the current session or DB for subsequent requests
$toknvar = serialize($infusionsoft->getToken());
savetoken($toknvar);
}
And this is the script which I include in every script where I need infusionsoft object
global $infusionsoft;
//get a clientid and secret from infusionsoft developer website, not main website
$infusionsoft = new \Infusionsoft\Infusionsoft(array(
'clientId' => 'XXXXXXXXXXXXXXXXXXXXXXXX',
'clientSecret' => 'XXXXXXXX',
'redirectUri' => 'https://www.example.com/setupinfusion.php',
));
//pulltoken is the function which retrieves serialized string in DB
$tokenfromdb = pulltoken();
//when you serealize the infusionsoft object it strips the backslash at the time of saving it to DB so
//you must put back the backslash after you retrieve the serealized object
$tokenfromdb = str_replace('InfusionsoftToken','Infusionsoft\Token',$tokenfromdb);
$infusionsoft->setToken(unserialize($tokenfromdb));
Thank YOu Soo much for your kind support but my work already have been done
On Tue, Jun 13, 2017 at 8:23 AM, Muhammad Ismail notifications@github.com wrote:
Here is my example, this is the cron script which runs midnight every night to refresh the token and saves it in DB
global $infusionsoft;
//get a clientid and secret from infusionsoft developer website, not main website $infusionsoft = new \Infusionsoft\Infusionsoft(array( 'clientId' => 'XXXXXXXXXXXXXXXXXXXXXXXX', 'clientSecret' => 'XXXXXXXX', 'redirectUri' => 'https://www.example.com/setupinfusion.php', ));
$tokenfromdb = pulltoken(); //when you serealize the infusionsoft object it strips the backslash at the time of saving it to DB so //you must put back the backslash after you retrieve the serealized object $tokenfromdb = str_replace('InfusionsoftToken','Infusionsoft\Token',$tokenfromdb); $infusionsoft->setToken(unserialize($tokenfromdb)); if (isset($_GET['code']) and !$infusionsoft->getToken()) { $infusionsoft->requestAccessToken($_GET['code']); } if (!$infusionsoft->getToken()) { echo 'Click here to authorize'; } $infusionsoft->refreshAccessToken(); //$infusionsoft->contacts()->add($contactData); if ($infusionsoft->getToken()) { // Save the serialized token to the current session or DB for subsequent requests $toknvar = serialize($infusionsoft->getToken()); savetoken($toknvar);
}
And this is the script which I include in every script where I need infusionsoft object
global $infusionsoft;
//get a clientid and secret from infusionsoft developer website, not main website $infusionsoft = new \Infusionsoft\Infusionsoft(array( 'clientId' => 'XXXXXXXXXXXXXXXXXXXXXXXX', 'clientSecret' => 'XXXXXXXX', 'redirectUri' => 'https://www.example.com/setupinfusion.php', )); //pulltoken is the function which retrieves serialized string in DB $tokenfromdb = pulltoken(); //when you serealize the infusionsoft object it strips the backslash at the time of saving it to DB so //you must put back the backslash after you retrieve the serealized object $tokenfromdb = str_replace('InfusionsoftToken','Infusionsoft\Token',$tokenfromdb); $infusionsoft->setToken(unserialize($tokenfromdb));
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/infusionsoft/infusionsoft-php/issues/99#issuecomment-308152964, or mute the thread https://github.com/notifications/unsubscribe-auth/ARemHo8Qyoaz_EQRAqbyJr8E4tluLDdAks5sDqlngaJpZM4LijWC .
After pulling my hair out all morning working through these issues I figured I'd drop in my full path here to help anyone else that might be struggling. Sorry for the unpolished code below. Also my project's in Laravel but the idea should remain the same.
So initially I ran into the invalid token issue because I assumed there was an automated method of authentication since I only wanted to connect to just my account, I was also authenticating using credentials from my Infusionsoft account and not the credentials from keys.developer.infusionsoft.
I Setup an admin only page that opens the oauth2 authorization url (Infusionsoft::getAuthorizationUrl()). When the server is setup an admin would click this button as a first time integration
On the controller for my redirect url, I just save the data that I get back from the request. It turns out saving the code and scope is not necessary.
public function store(Request $request){
if(isset($request->code)){
Setting::valueSet('infusionsoft.auth.code',$request->code);
Setting::valueSet('infusionsoft.auth.scope',$request->scope);
$token = \Infusionsoft::requestAccessToken($request->code);
Setting::valueSet('infusionsoft.auth.accessToken',$token->accessToken);
Setting::valueSet('infusionsoft.auth.refreshToken',$token->refreshToken);
Setting::valueSet('infusionsoft.auth.endOfLife',$token->endOfLife);
}else{
dd($request->all());
}
return redirect('/manage/integrate/infusionsoft'); //an admin page that just lists the settings
}
So at this point, your token should work, although when it expires in 24 hours, your administrator would need to re-click that authorization button. So I setup a command (InfusionsoftTokenRefresh) to run twice a day to refresh the token (And re-save the new info). Below is my handle method for that command:
public function handle()
{
$token = new \Infusionsoft\Token([
'access_token' => Setting::valueGet('infusionsoft.auth.accessToken'),
'refresh_token' => Setting::valueGet('infusionsoft.auth.refreshToken'),
'expires_in' => Setting::valueGet('infusionsoft.auth.endOfLife'),
]);
\Infusionsoft::setToken($token);
\Infusionsoft::refreshAccessToken();
$token = \Infusionsoft::getToken();
Setting::valueSet('infusionsoft.auth.accessToken',$token->accessToken);
Setting::valueSet('infusionsoft.auth.refreshToken',$token->refreshToken);
Setting::valueSet('infusionsoft.auth.endOfLife',$token->endOfLife);
}
So then finally I was able to run a query like this:
$token = new \Infusionsoft\Token([
'access_token' => Setting::valueGet('infusionsoft.auth.accessToken'),
'refresh_token' => Setting::valueGet('infusionsoft.auth.refreshToken'),
'expires_in' => Setting::valueGet('infusionsoft.auth.endOfLife'),
]);
\Infusionsoft::setToken($token);
$data = \Infusionsoft::contacts()->findByEmail('tester@tester.com',['Id']);
Also had this window up all morning working on this and didn't see the update from @ismailiiui haha
I can not use authentication methods, so my refresh token code returns always empty. Anything that can help me. please..
im getting same error with iSDK.php {"error":"invalid_grant","error_description":"Invalid refresh token"}
@bgpartridge You mentioned - " If you are using multiple threads and one of them refreshes the token, the other threads will then get an invalid token error unless you have explicitly solved for this case."
We are running multiple cron jobs to sync contacts, post notes, receive webhook payloads. At any time we detect a token is expired, we catch that, generate a new token and save the newly received paramteres - access token, refresh token and expires in our database. So, that any other script can use the updated information.
Still, at times we start receiving {"error":"invalid_grant","error_description":"Invalid refresh token"} error out of nowhere.
Is there is something wrong with our implementation or it's something which now has been marked as a known issue. I can see that multiple issues are already open mentioning the same problem and it has been almost an year and the issue still plagues us devs.
@khurana3192 are you using legacy iSDK.php or new PHP integration method?
@pagedesigner Hey I am using the PHP SDK (not the ISDK one)
ok are you using only access token here?
$infusionsoft->setToken(unserialize($tokenfromdb));
if yes use the full returning token string from db, i also got same error until i used that instead of using only access token in it
I started working on an Oauth integration today and I've located the root cause of this.
{"error":"invalid_grant","error_description":"Invalid refresh token"}
The problem is actually in the Infusionsoft\Token constructor because it transforms the original JSON properties to something different. So, when you persist the serialized token to your database (as I did in step 3 above), it doesn't match the properties anymore when you pass it into the constructor for a new Token object (as I did in step 6).
You start with the properties:
You end up (when passing the deserialized data you persisted (because that's what the Infusionsoft client object returns to to you and we developers trust is going to be accurate) with:
So, the only way to regenerate the correct token information in step 6, is to deserialize your data and then pass explicitly named array properties into the constructor for Infusionsoft\Token.
The resolution should be Infusionsoft correcting their library so it doesn't transform anything or, at the very least, gives developers a way to get the original response payload. If you want me to give you line numbers, let me know.
I save the serialized version of the Infusionsoft\Token object to the database
ok, perfect
I deserialize the token information and pass it to a Token object constructor.
Why would you pass it to the token object constructor, you already have a token object? Just pass your unserialized object to the infusionsoft class.
I think the point of the Token class is if you are starting separated information and wanting to form a Token, but since you already have a token, what is the point of the extra overhead?
This is what I do
public function getToken($appDomainName)
{
$data = $this->readFile();
if (isset($data[$appDomainName])) {
$token = unserialize($data[$appDomainName]);
} else {
$token = new Token();
}
return $token;
}
// Try to get a token from storage
$token = $storage->getToken($APP_NAME);
// If a token is available in storage, we tell the SDK to use that token for subsequent requests.
if (!empty($token->getAccessToken())) {
$infusionsoft->setToken($token);
}
//Try to refresh if necessary
if ($infusionsoft->getToken()->endOfLife - time() < 7200) {
$tokenData = $infusionsoft->refreshAccessToken();
$token = $infusionsoft->getToken();
$storage->saveToken($APP_NAME, $token);
}
@brumiser1550 That's similar to how I ended up doing it. My issue arose from working with an array and expecting the property names in the documentation to remain constant (according to https://developer.infusionsoft.com/docs/xml-rpc/#authentication-request-an-access-token). I would also expect the data we work with to be consistent. Documentation says you get the access_token, refresh_token, and number of seconds until the access_token expires as a JSON array, but what you actually get back is the Token object, with different property names and the actual expiration date. Those are very different from what the documentation is indicating developers will work with and it causes undue headache by not documenting the response information accurately. The Token object is supposed to make it easier on the developer, but with inconsistent documentation, it just convolutes the entire thing.
I ultimately had to modify my application a bit to handle multiple OAuth workflows (multiple integrations). That's fine, but documentation needs to be updated and it would be very helpful to be able to authenticate against the developer account (I had to sign up for free trial of Infusionsoft just so I could test the workflow - something their documentation asks you not to do). I saw some references to being able to manually acquire an access and refresh token from your developer login, but the references don't match up with the actual interface.
Sloved. I had a problem with multiple threads. That is, at the same time two threads tried to update the token. @bgpartridge thanks!
Hey,
I just signed the petition "Office of the High Commissioner for Human Rights: URGENT PETITION ON THE UN KASHMIR REPORT" and wanted to see if you could help by adding your name.
Our goal is to reach 200,000 signatures and we need more support. You can read more and sign the petition here:
Thanks! saad
I faced same issue. Turned out when document says parameter it is NOT url params, these parameters needs to be passed as "request body form params". Passing it in request form body solved it.
Hello, Go to /src/Infusionsoft/Infusionsoft.php file and edit line no 249 and add scope to "full,refresh_token" and then again authorize then it will work.
thanks, gatetouch team
this is my code,
`
for some time my code work fine, and then suddenly i am getting error like,
Fatal error: Uncaught exception 'Infusionsoft\Http\HttpException' with message 'exception 'GuzzleHttp\Exception\ClientException' with message 'Client error:
POST https://api.infusionsoft.com/tokenresulted in a
400 Bad Requestresponse: {"error":"invalid_grant","error_description":"Invalid refresh token"} ' in E:\www\nginx\html\unifinedmainsite\vendor\guzzlehttp\guzzle\src\Exception\RequestException.php:107
so why is this happening ? any suggestions? i see this issues #16 #61 but nothing help me