REST API Connection

Hi @Richard_Sanderson, when you refresh an access token you are given a new access and refresh token. The new access token will be good for 24 hours and the refresh token for 180 days. If you store the new refresh token each time you refresh the access token and use it for the next refresh you will be good.

Hi @Richard_Sanderson,

It’s not a case of “people needing oauth”. OAuth is the method of authentication and it’s not based on someone needing it, no different that a user id and password has anything to do with someone needing it. It’s required, regardless of the use of the api. So if you’re creating an application/integration that is meant for only your company’s use, the api getting used to do that would still use oauth. That’s your authentication method.

For a short time longer, you can still use the legacy key method which provides direct SSO (which you can have with oauth by keeping the token refreshed every 24 hour), but the caveat is that it’s not going to be continued in it’s use and at some point that will result in the requirement of oauth anyway so it just makes sense to use what will be around for longer.

I’ve tried to setup the API connection again. I have added a callback URL on my server but when I go to the URL generated by getAuthorizationUrl() it allows me to authorise the infusionsoft app and then just sends me to the account section.

How is this thing supposed to work? Normally oAuth will send the user to the callback URL then the token is stored in the application. Why is IS just sending me to the account section? There’s a way to click onto generate acces and refresh tokens but the people that use the system i’m creating shouldn’t need to do this then add it to the database manually. Why can’t it just send the user back to the callback url?

Is there really not just a simple way to connect to an account using a client ID and secret?

Due to the fact that Infusionsoft ties all apps to one login, there is the extra step of choosing which app is to be authorized. The point you’re getting to then, is to tell oauth what app the authentication is to be for. I shot a video that might explain some of this a bit better.

Hi my oauth flow does not work like in your video. It takes me to the allow/deny page and I get this…

The application ___ by ___ would like the ability to interact with one of your Infusionsoft applications.

Allow ___ access?

There’s only 1 infusionsoft account on this profile so there is no dropdown. I click Allow then it just takes me to the “Your Accounts” section of the Infusionsoft dashboard.

I setup a test application with a callback URL but it just sends me to the accounts dashboard after authorisation.

I want to re-iterate something… This is just an internal PHP code that will send some data to Infusionsoft. No clients or anybody other than us will be using this. This 1 account will be hooked up to the PHP code. Why do we need to setup oAuth for this?

Good morning, @Richard_Sanderson!

I think there are two questions there, “Why use not support simpler mechanisms?” and “Why does Infusionsoft only support OAuth2 going forward?”

In the first case, we did previously support an API Key form of authentication. We found that it had many failings, including inability to segment throttles based on multiple accessing applications (since they were all using the same key) and problems with deauthorization (since every authorized application was using the same key, the only way to remove one was to change the key and update all of them). Additionally, should the API key ever be compromised, either through man-in-the-middle attacks or application owner data breeches, there was not a way to identify bad actors by traffic patterns of a single token owner, and they would maintain access in perpetuity without any indication to the authorizer.

That leads to the second point: Infusionsoft has chosen to only support OAuth2 as our authentication method going forward because it does mitigate the above issues, and so that we have the ability to provide additional protection to the finances and livelihoods of small business owners around the world. It is indeed a more complicated flow than a simple API key, but the exchange is more than worth the additional effort involved, and there are any number of client libraries for various languages that simplify the process.

What is the url you are using to authorize? Something like this…

https://signin.infusionsoft.com/app/oauth/authorize?client_id=7w9scm6bgpkmk49pp9vycb6u&response_type=code&redirect_uri=http://localhost

Your example worked when I clicked authorize.

Here’s my authorize url

https://signin.infusionsoft.com/app/oauth/authorize?client_id=wesz46wbzq8przjpevwnc296&redirect_uri=%2Finfusionsoft%2Fcallback&response_type=code&scope=full

You can’t use a relative path for the redirect_uri

Ok I thought it could have been relative. It’s now sending me back to the callback url with the code query var appended.

I’ve just written this laravel code to test…

if (request()->query('code') && !$infusionsoft->getToken()) {
    $token = serialize($infusionsoft->requestAccessToken(request()->query('code')));
    Cache::store('file')->set('token', $token);
}

Is this a step in the right direction?

Hi brad I just quickly wrote a wrapper class to manage my tokens for the infusionsoft SDK

Is this a good way to manage tokens and re-generating a token? It’s very basic for now and I can run requestAccessToken on the class easy enough but i’m not sure on a good way to refresh an access token as its a bit hard to test when my access token isn’t expired

class IS
{

    public $token;
    public $client;

    public function __construct() {

      $this->client = new Infusionsoft([
        'clientId' => env('INFUSIONSOFT_CLIENT_ID'),
        'clientSecret' => env('INFUSIONSOFT_CLIENT_SECRET'),
        'redirectUri' => route('infusionsoft.callback')
      ]);

      if (!Cache::store('file')->has('infusionsoft_access_token'))
        return false;

      $this->token = unserialize(Cache::store('file')->get('infusionsoft_access_token'));
      $this->client->setToken($this->token);

      if ($this->client->isTokenExpired()) 
        $this->refreshAccessToken();

      return false;

    }

    public function __call($method, $args) {
      if (!method_exists($this->client, $method)) 
        return false;
        
      return call_user_func_array([
        $this->client,
        $method
      ], $args);
    }

    private function refreshAccessToken() {
      $token = $this->client->refreshAccessToken([
        'grant_type' => 'refresh_token',
        'refresh_token' => $this->token->refreshToken
      ]);

      $this->token = serialize($token);
      
      Cache::store('file')->put('infusionsoft_access_token', $this->token, 43800);
    }


}

It’s been like a week and nobody has replied.

Can I get some support on this?

I was hoping someone with some php experience was going to jump in. Maybe @John_Borelli?

Hey, @Richard_Sanderson,

I don’t traditionally favor caching or sessions for token management. Not that you can’t do it but it just introduces more variables that can go wrong. The most consistent uptime I’ve seen and used has been to use a DB table and run a refresh cycle every hour on a CRON job. When the service runs, it checks the stored time to live and then, if four hours or less remain, attempt a refresh. This gives at least three opportunities to succeed should an issue be present that prevents it from passing the first time. Then, on success, update the table with the new set of access/refresh tokens, the date/time and the time to live because when the refresh succeeds, the previous set becomes invalidated. You would then only need an endpoint (or create an object) to manage retrieval of the access token from the database when needed. My video discusses this approach and covers some of the pit falls to look out for:

@John_Borelli

I have moved to storing the token in my DB but when I write a task to refresh the token it works fine. But if I try again to refresh the token then it will error. Here’s my error code:

Client error: POST https://api.infusionsoft.com/token resulted in a 400 Bad Request response: {"error":"invalid_grant","error_description":"Invalid refresh token"}

Here’s my code to refresh the token:

$token = $client->refreshAccessToken([
  'grant_type' => 'refresh_token',
  'refresh_token' => $token->refreshToken
]);
Config::set('infusionsoft_access_token', serialize($token));

This refresh code worth the first time around but once I retry it after the first refresh it will just error every time. Am I doing something wrong?

@Richard_Sanderson,

I would only expect to see that if you didn’t replace the DB access/refresh tokens with the new ones you get back because once you request the new set the ones in the DB are invalid and must be replaced by the new.

@John_Borelli I think I may have figured it out… I’ve setup a cronjob now to refresh the token which seems to be working.

I am using the PHP SDK and I am not too familiar with the API just yet. I am creating a test contact using the following code:

$res = $is->contacts()->create([
  'duplicate_option' => 'Email',
  'email_addresses' => [
    'email' => 'testtesttest@gmail.com',
    'field' => 'EMAIL1'
  ],
  'custom_fields' => [
    'serial' => 'ABCABCABC',
    'machine' => 'TEST'
  ],
  'family_name' => 'Test',
  'given_name' => 'Tester',
  'middle_name' => 'Testing',
  'notes' => 'TEST',
  'source_type' => 'TEST'
], true);

Would this be a good way to createOrUpdate a contact? When I just tried this I got a “Input could not be converted to a valid request” response

That’s good but I’d probably also create a text log with the return values from the refresh request so if an error happens on the api server it will be reported back as a text string and then you will have a copy of the message.

Yea this was the error:

400 Bad Request response: {"message":"Input could not be converted to a valid request"}

Any ideas how I can get it to work?

Also does the response for creating a contact return the contact ID? I cannot see it in the documentation for the create contact response. This is so I can apply a tag to it after

The good news is that the authentication is working because you wouldn’t have gotten that message if it were not.

So that message almost always refers to a malformed JSON payload. I would recopy the sample json from the interactive iodocs, hard code some test values and then get that to work. Then you can insert variable data where appropriate.