Oath Token issue

I’ve been researching the following issue for a couple of days and I have not been able to figure out why this is happening.

The issue: After about 30 min or so of being able to send user information through the infusionsoft api we suddenly have to reauthorize the application.
Access token, Refresh token and expires_in are being stored in a MSSQL database.

I am using a very recent version of the php SDK and running this on a Centos server running php 5.6.

   <?php
if(!session_id()) session_start();

ini_set("include_path", strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'
    ? "/wamp/common/inc"
    : "/var/www/common/inc:"
);

require_once "adminresponse.php";
require_once "dataadmin.php";
require_once "lib/infusionsoft/vendor/autoload.php";

use Infusionsoft\Infusionsoft;
use Infusionsoft\Token;
use Infusionsoft\TokenExpiredException;
use Infusionsoft\Http\HttpException;

define('API_URL', 'https://api.infusionsoft.com/crm/rest/v1');
// admediary app
define('CLIENT_ID', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
define('CLIENT_SECRET', 'XXXXXXXXXXXXX');
$clientId = (array_key_exists('client_id', $_GET) && !empty($_GET['client_id']))
    ? $_GET['client_id']
    : CLIENT_ID;

$clientSecret = (array_key_exists('client_secret', $_GET) && !empty($_GET['client_secret']))
    ? $_GET['client_secret']
    : CLIENT_SECRET;

$redirectUri = (array_key_exists('redirect_uri', $_GET) && !empty($_GET['redirect_uri']))
    ? $_GET['redirect_uri']
    : ( (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://{$_SERVER['HTTP_HOST']}/{$_SERVER['REQUEST_URI']}" );
    
   //need to strip out the code from the uri and then rebuild out the uri
   $parsed = parse_url($redirectUri);
   $query = $parsed['query'];

   parse_str($query, $params);
   unset($params['code']);
   unset($params['scope']);
   unset($params['state']);
   $redirectUri = http_build_query($params);
   $redirectUri =  (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://{$_SERVER['HTTP_HOST']}".substr($parsed['path'],1)."?".$redirectUri;
try
{
    $response_output = new AdminResponse();

    // Get configuration parameters for this campaign
    $campaignName = 'infusionsoft';

    if (empty($clientId)) {
        throw new Exception('Client ID value is empty. Enter value to the client_id query parameter or remove client_id parameter from query to use default value.');
    }

    if (empty($clientSecret)) {
        throw new Exception('Client Secret value is empty. Enter value to the client_secret query parameter or remove client_secret parameter from query to use default value.');
    }

    if (empty($redirectUri)) {
        throw new Exception('Client Secret value is empty. Enter value to the client_secret query parameter or remove client_secret parameter from query to use default value.');
    }

    $data = new DataAdmin();
    $db = $data->GetDBHandle();

    $config= getCampaignConfig($db, $campaignName);

    $token = new Token([
        'access_token' => $config['access_token'],
        'refresh_token' => $config['refresh_token'],
        'expires_in' => (isset($config['expires_in']))?$config['expires_in']:null
    ]);
    $infusionSoftClient = new Infusionsoft(array(
        'clientId'     => $clientId,
        'clientSecret' => $clientSecret,
        'redirectUri'  => $redirectUri,
    ));
    // If we are returning from InfusionSoft we need to exchange the code for an access token
    if (isset($_GET['code']) AND !$infusionSoftClient->getToken()) {
        $token = $infusionSoftClient->requestAccessToken($_GET['code']);
        
    } else {

        $infusionSoftClient->setToken($token);
    }
    
    $token = $infusionSoftClient->refreshAccessToken();
    // Save new access token and refresh token to the data DB
    $config['access_token'] = $token->getAccessToken();
    $config['refresh_token'] = $token->getRefreshToken();
    $config['expires_in'] = $token->getEndOfLife();

    saveCampaignConfig($db, $campaignName, $config);

    $action = array_key_exists('action', $_REQUEST) ? $_REQUEST['action'] : 'addContact';
    switch ( $action )
    {
        case 'addContact':

            $email          = isset($_GET['email']) ? $_GET['email'] : null;
            $fname          = isset($_GET['fname']) ? $_GET['fname'] : null;
            $lname          = isset($_GET['lname']) ? $_GET['lname'] : null;
            $recordId       = isset($_GET['record_id']) ? $_GET['record_id'] : null;
            $optInReason    = isset($_GET['opt_in_reason']) ? $_GET['opt_in_reason'] : "Customer opted-in through webform";

            if (empty($email)) {
                throw new Exception('Email value is empty. Enter value to the email query parameter.');
            }

            $email1 = new \stdClass;
            $email1->field = 'EMAIL1';
            $email1->email = $email;
            $contact = [
                'given_name' => $fname,
                'family_name' => $lname,
                'email_addresses' => [$email1],
            ];

            if (!empty($optInReason)) {
                $contact['opt_in_reason'] = $optInReason;
            }

            // Add custom 'Record ID' field [id=2]
            if (!empty($recordId)) {
                $contact['custom_fields'] = [
                    [
                        'id' => 2,
                        'content' => $recordId
                    ]
                ];
            }

            $isExist = false;

            $collection = $infusionSoftClient->contacts()->where(['email' => $email, 'limit' => 1])->get();

            if (!$collection->isEmpty() && $collection->count() > 0) {
                $items = $collection->toArray();

                if (!empty($items[0])) {
                    $isExist = true;

                    $contact['id'] = $items[0]->id;
                }
            }

            $cid = $infusionSoftClient->contacts()->mock($contact)->save();

            $response_output->response_code = 1;
            $response_output->response_msg = 'Contact was ' . ($isExist ? 'updated' : 'created');
            $response_output->response_data = $cid->toArray();

            break;

        default:
            throw new Exception('Unknown action: ' . $action);

            break;
    }

}
catch (HttpException $e)
{   
    if ( strpos($e->getMessage(), '{"error":"invalid_grant","error_description":"Invalid refresh token"}' ) !== false ) {

        echo '<a href="' . $infusionSoftClient->getAuthorizationUrl() . '">Click here to authorize</a>';
        exit();
    }

    $response_output->response_error = $e->getMessage();
}
catch (TokenExpiredException $e)
{
    // If the request fails due to an expired access token, we can refresh
    // the token and then do the request again.
    $token = $infusionSoftClient->refreshAccessToken();

    // Save the token to the data DB
    $config['access_token'] = $token->getAccessToken();
    $config['refresh_token'] = $token->getRefreshToken();
    $config['expires_in'] = $token->getEndOfLife();

    try
    {
        saveCampaignConfig($db, $campaignName, $config);
    } catch (Exception $e)
    {
        throw $e;
    }

    header("Location: $redirectUri");
    exit();
}
catch (Exception $e)
{
    $response_output->response_error = $e->getMessage();
}

print json_encode($response_output);



/**
 * param $db
 * param $campaignName
 * return array Config
 * throws Exception
 */
function getCampaignConfig($db, $campaignName)
{
    $query = "SELECT TOP 1 * FROM data..esp_campaign WHERE campaign_name = '{$campaignName}'";

    $result = odbc_exec($db, $query);
    if (!$result) {
        throw new Exception('Error from: ' . $query . odbc_errormsg());
    }

    if (odbc_fetch_row($result)) {
        $config = odbc_result($result, 'params_json');
    } else {
        throw new Exception("Configuration for the {$campaignName} campaign doesn't exist in the DB.");
    }

    odbc_free_result($result);

    $config = json_decode($config, true);

    return $config;
}

/**
 * param $db
 * param $campaignName
 * param array $config
 * throws Exception
 */
function saveCampaignConfig($db, $campaignName, $config)
{
    $config = json_encode($config);
    $query = "UPDATE data..esp_campaign SET params_json='{$config}' WHERE campaign_name='{$campaignName}'";

    $result = odbc_exec($db, $query);
    if (!$result) {
        throw new Exception('Error from: ' . $query . odbc_errormsg());
    }

    odbc_free_result($result);
}

So, here is what I was able to come up with I’m hoping that other people might have other suggestions.

<?php
if(!session_id()) session_start();

ini_set("include_path", strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'
    ? "/wamp/common/inc"
    : "/var/www/common/inc:"
);

require_once "adminresponse.php";
require_once "dataadmin.php";
require_once "lib/infusionsoft/vendor/autoload.php";

use Infusionsoft\Infusionsoft;
use Infusionsoft\Token;
use Infusionsoft\TokenExpiredException;
use Infusionsoft\Http\HttpException;

define('API_URL', 'https://api.infusionsoft.com/crm/rest/v1');
// admediary app
define('CLIENT_ID', 'xxxxxxxxxxxxxxxxxxxxxxxxx');
define('CLIENT_SECRET', 'xxxxxxxxxxxxxxxxxxxxxx');
$clientId = (array_key_exists('client_id', $_GET) && !empty($_GET['client_id']))
    ? $_GET['client_id']
    : CLIENT_ID;

$clientSecret = (array_key_exists('client_secret', $_GET) && !empty($_GET['client_secret']))
    ? $_GET['client_secret']
    : CLIENT_SECRET;

$redirectUri = (array_key_exists('redirect_uri', $_GET) && !empty($_GET['redirect_uri']))
    ? $_GET['redirect_uri']
    : ( (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://{$_SERVER['HTTP_HOST']}/{$_SERVER['REQUEST_URI']}" );
    
   //need to strip out the code from the uri and then rebuild out the uri
   $parsed = parse_url($redirectUri);
   $query = $parsed['query'];

   parse_str($query, $params);
   unset($params['code']);
   unset($params['scope']);
   unset($params['state']);
   $redirectUri = http_build_query($params);
   $redirectUri =  (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://{$_SERVER['HTTP_HOST']}".substr($parsed['path'],1)."?".$redirectUri;
try
{
    $response_output = new AdminResponse();

    // Get configuration parameters for this campaign
    $campaignName = 'infusionsoft';

    if (empty($clientId)) {
        throw new Exception('Client ID value is empty. Enter value to the client_id query parameter or remove client_id parameter from query to use default value.');
    }

    if (empty($clientSecret)) {
        throw new Exception('Client Secret value is empty. Enter value to the client_secret query parameter or remove client_secret parameter from query to use default value.');
    }

    if (empty($redirectUri)) {
        throw new Exception('Client Secret value is empty. Enter value to the client_secret query parameter or remove client_secret parameter from query to use default value.');
    }

    $data = new DataAdmin();
    $db = $data->GetDBHandle();

    $config= getCampaignConfig($db, $campaignName);
    
    $config['processing'] = (isset($config['processing']))?$config['processing']: 1;
    $tries = 0;
    while($config['processing'] === 1){
        if($tries >= 10){
            //$config['processing'] = 0;
            //var_dump('tried {$tries} times');
            break;
        }
        $tries ++;
        //var_dump("I'm sleeping tried {$tries} times");
            sleep(1);
            $config= getCampaignConfig($db, $campaignName);
    }

    $config['processing'] = 1;
    saveCampaignConfig($db,$campaignName,$config);

    $token = new Token([
        'access_token' => $config['access_token'],
        'refresh_token' => $config['refresh_token'],
        'expires_in' => (isset($config['expires_in']))?$config['expires_in']:null
    ]);
    $infusionSoftClient = new Infusionsoft(array(
        'clientId'     => $clientId,
        'clientSecret' => $clientSecret,
        'redirectUri'  => $redirectUri,
    ));
    // If we are returning from InfusionSoft we need to exchange the code for an access token
    if (isset($_GET['code']) AND !$infusionSoftClient->getToken()) {
        $token = $infusionSoftClient->requestAccessToken($_GET['code']);
        
    } else {

        $infusionSoftClient->setToken($token);
    }
    
    $token = $infusionSoftClient->refreshAccessToken();
    // Save new access token and refresh token to the data DB
    $config['access_token'] = $token->getAccessToken();
    $config['refresh_token'] = $token->getRefreshToken();
    $config['expires_in'] = $token->getEndOfLife();
    $config['processing'] = 0;
    saveCampaignConfig($db, $campaignName, $config);

    $action = array_key_exists('action', $_REQUEST) ? $_REQUEST['action'] : 'addContact';
    switch ( $action )
    {
        case 'addContact':

            $email          = isset($_GET['email']) ? $_GET['email'] : null;
            $fname          = isset($_GET['fname']) ? $_GET['fname'] : null;
            $lname          = isset($_GET['lname']) ? $_GET['lname'] : null;
            $recordId       = isset($_GET['record_id']) ? $_GET['record_id'] : null;
            $optInReason    = isset($_GET['opt_in_reason']) ? $_GET['opt_in_reason'] : "Customer opted-in through webform";

            if (empty($email)) {
                throw new Exception('Email value is empty. Enter value to the email query parameter.');
            }

            $email1 = new \stdClass;
            $email1->field = 'EMAIL1';
            $email1->email = $email;
            $contact = [
                'given_name' => $fname,
                'family_name' => $lname,
                'email_addresses' => [$email1],
            ];

            if (!empty($optInReason)) {
                $contact['opt_in_reason'] = $optInReason;
            }

            // Add custom 'Record ID' field [id=2]
            if (!empty($recordId)) {
                $contact['custom_fields'] = [
                    [
                        'id' => 2,
                        'content' => $recordId
                    ]
                ];
            }

            $isExist = false;

            $collection = $infusionSoftClient->contacts()->where(['email' => $email, 'limit' => 1])->get();

            if (!$collection->isEmpty() && $collection->count() > 0) {
                $items = $collection->toArray();

                if (!empty($items[0])) {
                    $isExist = true;

                    $contact['id'] = $items[0]->id;
                }
            }

            $cid = $infusionSoftClient->contacts()->mock($contact)->save();

            $response_output->response_code = 1;
            $response_output->response_msg = 'Contact was ' . ($isExist ? 'updated' : 'created');
            $response_output->response_data = $cid->toArray();

            break;

        default:
            throw new Exception('Unknown action: ' . $action);

            break;
    }

}

catch (TokenExpiredException $e)
{
    // If the request fails due to an expired access token, we can refresh
    // the token and then do the request again.
    $token = $infusionSoftClient->refreshAccessToken();

    // Save the token to the data DB
    $config['access_token'] = $token->getAccessToken();
    $config['refresh_token'] = $token->getRefreshToken();
    $config['expires_in'] = $token->getEndOfLife();

    try
    {
        saveCampaignConfig($db, $campaignName, $config);
    } catch (Exception $e)
    {
        throw $e;
    }

    header("Location: $redirectUri");
    exit();
}
catch (HttpException $e)
{   
    if ( strpos($e->getMessage(), '{"error":"invalid_grant","error_description":"Invalid refresh token"}' ) !== false ) {

        echo '<a href="' . $infusionSoftClient->getAuthorizationUrl() . '">Click here to authorize</a>';
        exit();
    }

    $response_output->response_error = $e->getMessage();
}
catch (Exception $e)
{
    $response_output->response_error = $e->getMessage();
}

print json_encode($response_output);



/**
 * param $db
 * param $campaignName
 * return array Config
 * throws Exception
 */
function getCampaignConfig($db, $campaignName)
{
    $query = "SELECT TOP 1 * FROM data..esp_campaign WHERE campaign_name = '{$campaignName}'";

    $result = odbc_exec($db, $query);
    if (!$result) {
        throw new Exception('Error from: ' . $query . odbc_errormsg());
    }

    if (odbc_fetch_row($result)) {
        $config = odbc_result($result, 'params_json');
    } else {
        throw new Exception("Configuration for the {$campaignName} campaign doesn't exist in the DB.");
    }

    odbc_free_result($result);

    $config = json_decode($config, true);
    //var_dump("config set to ".var_export($config,true));
    return $config;
}

/**
 * param $db
 * param $campaignName
 * param array $config
 * throws Exception
 */
function saveCampaignConfig($db, $campaignName, $config)
{
    $config = json_encode($config);
    $query = "UPDATE data..esp_campaign SET params_json='{$config}' WHERE campaign_name='{$campaignName}'";

    $result = odbc_exec($db, $query);
    if (!$result) {
        throw new Exception('Error from: ' . $query . odbc_errormsg());
    }

    odbc_free_result($result);
}

Could be something else of course but using the full following string:

as a !== is pretty restrictive and if it varies even by a character it’s going to trigger the need to re-authorize even if it’s not needed. Wouldn’t it be better to just use a keyword within rather than counting on the entire string being completely the same?

Curious question Paul, is there any particular reason you are running on PHP v5.6?

PHP v5.6 has reached its end of life. You would be better off jumping to PHP v7.3.
Unless you have old scripts running on the server which would cause problems if you upgraded.

On the OAuth issue, have you checked out John’s video? Maybe there is something you have made a mistake in.

1 Like