How to Use PHP to Interface with Zoho Analytics API / OAuth 2.0

A PHP web app that I created using the Zoho Analytics (formerly Zoho Reports) API suddenly stopped working one day. Digging into the Zoho documentation, I discovered that they ended support for their “authtoken” authentication method (basically a password) and switched to OAuth 2.0. Hmmm…

After looking into it, upgrading my app to be compatible didn’t seem to be trivial. Before, the app just needed a simple password to access the API. Now, there were multiple tokens involved, all of which expired and had to be constantly renewed. It seemed daunting to implement.

In this article, I’ll document how I used PHP to talk to the Zoho Analytics OAuth 2.0 interface. My app grabs category and pricing information from tables in Zoho Analytics. In this case, I am the owner of the Zoho Account (i.e., the app doesn’t try to access the visitor’s Zoho account).


Beginners Read This

If you’re an OAuth noob (like I was), I would highly recommend watching these two excellent introductory videos by Java Brains:

If you don’t know the basic OAuth concepts described in these videos, you will be pretty lost after this point.

I definitely would not consider myself an OAuth expert, so comments, improvements, and corrections are welcome in the comments below!

The Zoho Analytics API

Zoho has some pretty good documentation on their Version 1.0 Analytics API, but some things were not so clear to me, so I’ll try to fill in the missing details here without re-hashing the stuff that they do explain well.

One sobering fact is that the Zoho Analytics access token is only valid for one hour! After that, your code needs to use the refresh token to get a new one! This seemed like a massive pain, but I figured out an easy way to do it which I’ll describe later on.

Zoho’s documentation mentions four steps to OAuth 2.0 authentication:

  • Step 1: Registering New Client
  • Step 2: Generating Code
  • Step 3: Generating Tokens
  • Step 4: Refreshing Access Tokens

1. Registering the New Client

To register your new client, you’ll need to visit Zoho’s API DeveloperConsole. You’ll be asked to choose a Client Type, which is where my first questions came up. What do they mean by “Client Type”, and which one should I pick?

Basically, this question is asking you which type of OAuth flow you’ll be using. In my case, I am the owner of the Zoho account (the resource). I don’t want to have a callback URL where Zoho asks the end-user for permission to access the resource, because I own the resource.

So, the client type I needed to pick is the “Self Client” option. That is also the one they recommend using if you’re going to use their client library (which I planned to do). It is a simple flow that does not require a callback URL during regular operation.

Zoho API Console showing Client Type selection (I already selected Self Client)
Zoho API Console showing Client Type selection (I already selected Self Client)

Upon successful registration, you’ll receive a Client ID and Client Secret.

Zoho API Console showing Client ID and Client Secret
Zoho API Console showing Client ID and Client Secret

2. Generating the Code

Next, choose “Self Client Method”, since that is the flow we are using, then enter the scope of your app. Since my app just needs to read Zoho Analytics data (not write), my scope is “ZohoAnalytics.data.read”.

Next, you’ll have to choose a Time Duration, which is the amount of time that your code is valid for. Enter the longest time possible (ten minutes) because you’ll need that time to set up an API call using that code. After that time, the code will expire and you’ll need to generate a new one. More on that later.

Finally, enter a description and click Create. You’ll get a popup with the generated Code. Copy that and move to the next step.


3. Generating Tokens

Generating the tokens is actually a pain compared to just setting an app password. You have to do a POST request to their accounts server using the Code you just generated, plus your Client ID, and Client Secret. Then you’ll get your Access Token and Refresh Token back in JSON format. And you have to do this within ten minutes or else you’ll need to start over!

To do this POST request, I used CocoaRestClient for Mac. Here’s how to set it up:

  1. Point it to Zoho’s accounts server, https://accounts.zoho.com/oauth/v2/token (in the U.S. See Zoho’s website for complete list of servers around the world.)
  2. Select POST method.
  3. In the Body section, create fields for code, client_id, client_secret, and grant_type, and enter the corresponding values.
  4. Click Request.

If this took more than ten minutes, you’ll have to start over. If it worked, you’ll get a JSON response with your refresh token and access token as shown below!

Using CocoaRestClient to get my refresh and access tokens
Using CocoaRestClient to get my refresh and access tokens

You can now use that acces_token to do a test POST to Zoho’s resource server (analyticsapi.zoho.com in the U.S.) to get your data! But that token is only valid for one hour!

4. Refreshing Access Tokens

You’re not done jumping through hoops since the access token you got only works for one hour. You’ll have to use your refresh token to request a new one. That means storing your access token somewhere and using it until it expires, at which point you need to request a new one.

But, why not just request a new access token every time? Well, that is actually what Zoho’s PHP client library does. The problem is that Zoho limits you to create no more than ten access tokens in a span of ten minutes. If you try to create more, you’re blocked for the rest of the ten minutes. I experienced this first hand in my testing.

Zoho’s documentation describes the POST request that you need to do to request a new token, which is very similar to the original request you did above.

But at this point, I knew all of my access codes were working so I jumped right into code.


Zoho’s PHP Client

Zoho provides a list of API commands but doesn’t really explain how to set things up. The client I needed was the PHP ReportClient. There is one key sentence in the OAuth migration instructions, that talks about defining up your constants. So, your PHP app should include these lines:

include "ReportClient.php";
define('CLIENT_ID', "1000.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
define('CLIENT_SECRET', "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
define('REFRESH_TOKEN', "1000.XXXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXXXX");

That’s all you need to set up before you can start calling Zoho’s PHP client commands! The only command I needed was exportDataUsingSQL. I called it like this:

// SET TABLE AND URL
$database = 'Your Zoho Database Name';
$table = 'YourTableName';
$url = 'https://analyticsapi.zoho.com/api/owner@yourmail.com/'.$database.'/'.$table;

try 
{
  $zoho_api = new ReportClient( CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN );
  $zoho_result = json_decode( $zoho_api->exportDataUsingSQL(
  $url,
  "JSON",
  "SELECT distinct(Category) FROM `YourTableName` ORDER BY `Category`",
  NULL
  ) 
);
$result_array = $zoho_result->response->result->rows;
}

catch (IOException $e) {
	echo $e->getResponseContent();
}
catch (ServerException $e) {
	echo $e->toString();
}
catch (ParseException $e) {
	echo $e->getResponseContent();
}
catch (Exception $e) {
	echo $e->getMessage();
}

In this example, I was retrieving a list of categories. Note that you must use the account owner’s email in the URL, not your user email.

This worked great, except for one thing: Zoho’s client library code uses the refresh token every time, so after ten uses, it locks you out for the remainder of the ten minutes!

Modifying the PHP Client to Re-Use the Access Token

So, I had to modify the client to re-use the access token for up to an hour, and request a new one when it expired. At first, that sounded difficult but it was actually pretty easy.

The easiest way I found to do this was to request an access token and save it to a file. Then, when it is needed again, check the timestamp of the file. If it’s less than an hour old (with some margin added), then read it and use that token. If it’s more than an hour old, request a new token, use it, and save it back to that file.

So, in ReportClient.php, I added these methods for doing that:

/**
*@internal FOR SAVING OAUTH TOKEN FOR USE WITHIN THE HOUR
*/
function saveOauthTicket( $ticket )
{
  file_put_contents( "myticket.txt", $ticket, LOCK_EX );
}

/**
*@internal FOR RETRIEVING OAUTH TOKEN - BY BRIAN
*/
function readOauthTicket()
{
  $last_ticket_time = filemtime( "myticket.txt" );

  if ( !$last_ticket_time )
    return false;
  $now = time();

  // CHECK TO SEE IF THE LAST TICKET WAS GENERATED WITHIN THE HOUR, WITH SOME MARGIN
  if ( ( $now - $last_ticket_time ) < 3500 )
  {
    $last_ticket = file_get_contents( "myticket.txt" );
    if ( !$last_ticket )
      return false;
    if ( substr( $last_ticket, 0, 5 ) == "1000." )
      return $last_ticket;
    else
      return false;
  }
}

Now we have to call these functions. Go to the function, getOauthTicket, and add these lines to the beginning:

// IF THE SAVED TICKET IS LESS THAN AN HOUR OLD, GO AHEAD AND USE IT
$last_oauth_ticket = $this->readOauthTicket();
if ( $last_oauth_ticket )
  return $last_oauth_ticket;

That calls the function which checks the myticket.txt timestamp. If it’s less than an hour old, it returns the value and doesn’t bother executing the rest of the function which requests the new token.

Now we need to add the function call to save the access token. Search for these lines and add one in the middle:

if ($OAuth_status_code == 200 && array_key_exists('access_token',$OAuth_JSON_response))
{
  $this->saveOauthTicket( $OAuth_JSON_response['access_token'] ); // ADD THIS!
  return $OAuth_JSON_response['access_token'];
}

With these lines added, I was able to access the Zoho API as many times as I wanted!

Security

You might (rightly) point out that it is not good practice to save auth tokens unencrypted on the server.. or at all on the server. Feel free to modify this code to encrypt it and put it in a database.

In my case, the information being accessed is a massive price list. My app provides filtering and sorting options for this list. So basically, it was all public info anyway. Someone could hack into the account to get the list… or they could use the nice front-end user interface to get it without hacking it!

In my case, the scope is also limited to read-only so no one could mangle the database even if they got the token.

So yeah, not the best security here but I content it’s not needed in my case. It might be needed in your case so please modify the code accordingly!

Conclusion

I hope this was helpful to you. As I mentioned, I’m no OAuth expert, so I welcome your comments and corrections. – Brian


Please Leave a Question or Comment

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
Shares
%d bloggers like this: