Tag Archives: Google

Google API Offline Access Using OAuth 2.0 Refresh Token

Offline access for Google APIs is achieved through a refresh token that is issued when a user first visits the app and grants offline access. Essentially, you have to ask for offline access, Google warns them that you are asking for it, and then they have to grant it. The return contains the golden ticket refresh token.

The first time a given user’s browser is sent to this URL, they see a consent page. If they grant access, then the response includes an authorization code which may be redeemed for an access token and a refresh token.

Once a refresh token is obtained, it can be used to access Google APIs that the user has access to without re-authorizing.

See Google’s documentation on OAuth 2 for web apps for more information.

Asking for Offline Access

Parameters for Login URL

Client id and client secret are set by Google when the app is registered for api access in the Google APIs Console.

The redirect uri is a location on the server that the user is sent to after authenticating. This uri is registered in the Google APIs Console during app registration.

These values can be included as a separate file so the values can easily be swapped out on a per app basis.

$client_id = "1111111111111.apps.googleusercontent.com"; //your client id
$client_secret = "xXasdfd212312_dfdf dfxxdf"; //your client secret
$redirect_uri = "http://YOUR-SITE.com/YOUR-PATH/";
$scope = "https://GOOGLE-SCOPE-TO-ACCESS"; //google scope to access
$state = "profile"; //optional - could be whatever value you want
$access_type = "offline"; //optional - allows for retrieval of refresh_token for offline access

User Login URL

The login URL will prompt the user for permission to access their Google content via the app and a “code” request variable will be returned in the URL. See Forming the URL for more detailed information.

$loginUrl = sprintf("https://accounts.google.com/o/oauth2/auth?scope=%s&state=%s&redirect_uri=%s&response_type=code&client_id=%s&access_type=%s", $scope, $state, $redirect_uri, $client_id, $access_type);

<a href="<?php echo $loginUrl ?>">Login with Google account using OAuth 2.0</a>

Returned URL example (http://YOUR-SITE.com/YOUR-PATH/ is your redirect uri):


http://YOUR-SITE.com/YOUR-PATH/?state=profile&code=1/fFBGRNJru1FQd44AzqT3Zg

Get Access Token and Refresh Token

They said “Yes!” Initial consent received for offline access and a “code” was returned as a request parameter:

//Initial grant for access approved by user returns 'code' URL param
if(isset($_REQUEST['code'])){
    $accessToken = get_oauth2_token($_REQUEST['code'],"online");
}

The get_oauth2_token does double duty. It will return an access token for either online or offline access grants.

For the initial grant, if a refresh token is returned, the function puts the refresh token into ‘global $refreshToken’

Why not store the refresh token here instead of putting it in a var? Because there is no reference for the refresh token at this point other than the scope of the grant request. You don’t know who it belongs to. The initial access token returned with the refresh token can be used to query the API and get data to associate with the refresh token for the database.

If there is a known value (like it belongs to you) to associate with the refresh token that can be used to retrieve it later on, put a call to the database save function in here.

//returns session token for calls to API using oauth 2.0
//set global refreshToken var if refresh token is returned
function get_oauth2_token($grantCode,$grantType) {
	global $client_id;
	global $client_secret;
	global $redirect_uri;

	$oauth2token_url = "https://accounts.google.com/o/oauth2/token";
	$clienttoken_post = array(
	"client_id" => $client_id,
	"client_secret" => $client_secret);

	if ($grantType === "online"){
		$clienttoken_post["code"] = $grantCode;
		$clienttoken_post["redirect_uri"] = $redirect_uri;
		$clienttoken_post["grant_type"] = "authorization_code";
	}

	if ($grantType === "offline"){
		$clienttoken_post["refresh_token"] = $grantCode;
		$clienttoken_post["grant_type"] = "refresh_token";
	}

	$curl = curl_init($oauth2token_url);

	curl_setopt($curl, CURLOPT_POST, true);
	curl_setopt($curl, CURLOPT_POSTFIELDS, $clienttoken_post);
	curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

	$json_response = curl_exec($curl);
	curl_close($curl);

	$authObj = json_decode($json_response);

	//if offline access requested and granted, get refresh token
	if (isset($authObj->refresh_token)){
		global $refreshToken;
		$refreshToken = $authObj->refresh_token;
	}

	$accessToken = $authObj->access_token;
	return $accessToken;
}

Storing the Refresh Token

The refresh token will need to be stored for retrieval in the future. This example puts it in a MySQL database.

If a refresh token has been granted, store it in a database. Otherwise, retrieve a refresh token from the database. And, use the refresh token to set an access token for API calls.

//get name on account to use as reference in database for retrieving the refresh token
//can be done with data from primary API that is being queried

$googleUserInfoAPI = "https://www.googleapis.com/oauth2/v1/userinfo";

if (isset($accessToken)){
    //got access token, get data
    $accountObj = call_api($accessToken, $googleUserInfoAPI);
    $account_name =  $accountObj->name;
}

//refresh token handling - save to db if returned with access token
//or retrieve from db if needed for app
if(isset($refreshToken)){
    $accessToken = dbRefreshToken($account_name,$demo_scope,$refreshToken);
} else {
    $accessTokenFromRefresh = dbRefreshToken('ACCT_NAME_HERE',$demo_scope);
}

‘ACCT_NAME_HERE’ is a string that is used to identify for retrieval the refresh token you want to use.

The dbRefreshToken function for saving or retrieving from database and returning an access token:

function dbRefreshToken($name,$scope,$refreshToken = ""){
    global $serverpath;
    $path = $serverpath."/config/token_config.php";
    include_once($path);
    $path = $serverpath."/config/db.php";
    include_once($path);

    if ($conn){
	if (strlen($refreshToken)){
	//if refreshToken in param list, save to db
	    $query = "INSERT INTO tokens (name, scope, token) VALUES (:name, :scope, :refreshToken)";
	    $result = $conn->prepare($query);
	    $result->bindValue(':name', $name, PDO::PARAM_STR);
	    $result->bindValue(':scope', $scope, PDO::PARAM_STR);
	    $result->bindValue(':refreshToken', $refreshToken, PDO::PARAM_STR);
	    $result->execute();
	    $token = $refreshToken;
       } else {
        //else retrieve refresh token from db
	    $query = "SELECT token from tokens where name = :name and scope = :scope";
	    $result = $conn->prepare($query);
	    $result->bindValue(':name',$name, PDO::PARAM_STR);
	    $result->bindValue(':scope', $scope, PDO::PARAM_STR);
	    $result->execute();
	    $row = $result->fetch(PDO::FETCH_ASSOC);
	    $token = $row["token"];
        }

    mysql_close($conn);

    $accessTokenfromRefresh = get_oauth2_token($token,"offline");
    return $accessTokenfromRefresh;
   }
}

Note that config.php contains your database configuration parameter (username, password, database name) and db.php contains the actual connection string. Put them in a folder and restrict web browsing to that folder.

The table is called ‘tokens’ and the columns are name, scope, and token.

Demo

Download code

Google API Requests with OAuth 2.0 Access Token

In the Authenticating with OAuth 2.0 for Google API Access with PHP post, an access token was retrieved for calls to Google APIs for data. In this post, the user’s name will be retrieved from their account.

To do that, the access token needs to be sent as a header param or query param with the call to the userinfo Google API. See Calling a Google API for more information. The header param method is shown below.

A PHP object containing the account info will be returned by a call to “call_api” function with the access token and API URL passed in:

$accountObj = call_api($_SESSION['accessToken'],"https://www.googleapis.com/oauth2/v1/userinfo");

“call_api” calls the api and gets the data:

function call_api($accessToken,$url){
	$curl = curl_init($url);

	curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
	$curlheader[0] = "Authorization: Bearer " . $accessToken;
	curl_setopt($curl, CURLOPT_HTTPHEADER, $curlheader);

	$json_response = curl_exec($curl);
	curl_close($curl);

	$responseObj = json_decode($json_response);

	return $responseObj;
}

From the account object, the name can be accessed;

$your_name =  $accountObj->name;

echo "<p class='successMessage'>The name on your Google account is: " . $your_name . "</p>";

Demo

Download code

Authenticating with OAuth 2.0 for Google API Access with PHP

Google is leading developers to OAuth 2.0 for access to its API’s:

Given the security implications of getting the implementation correct, we strongly encourage developers to use OAuth 2.0 libraries when interacting with Google’s OAuth 2.0 endpoints (see Client libraries for more information). Over time, more features will be added to these libraries.

Steps

  1. Register the app with Google
  2. Create login URL and retrieve “code”
  3. Exchange “code” for access token
  4. Send access token with API requests (details in this post)

Google details the instructions in Using OAuth 2.0 to Access Google APIs.

Steps for registering your app can be found in APIs Client Library for PHP.

Google scope values for its API’s can be found in the OAuth 2.0 Playground.

Create Login URL and Retrieve “Code”

Parameters for Login URL

Client id and client secret are set by Google when the app is registered for api access in the Google APIs Console.

The redirect uri is a location on the server that the user is sent to after authenticating. This uri is registered in the Google APIs Console during app registration.

These values can be included as a separate file so the values can easily be swapped out on a per app basis.

$client_id = "1111111111111.apps.googleusercontent.com"; //your client id
$client_secret = "XxxXXxxXXxXXxXxXXXxXXXX"; //your client secret
$redirect_uri = "http://YOUR-SITE.com/YOUR-PATH/";
$scope = "https://GOOGLE-SCOPE-TO-ACCESS"; //google scope to access
$state = "profile"; //optional
$access_type = "offline"; //optional - allows for retrieval of refresh_token for offline access

User Login URL

The login URL will prompt the user for permission to access their Google content via the app and a “code” request variable will be returned in the URL. See Forming the URL for more detailed information.

$loginUrl = sprintf("https://accounts.google.com/o/oauth2/auth?scope=%s&state=%s&redirect_uri=%s&response_type=code&client_id=%s&access_type=%s", $scope, $state, $redirect_uri, $client_id, $access_type);

<a href="<?php echo $loginUrl ?>">Login with Google account using OAuth 2.0</a>

Returned URL example (http://YOUR-SITE.com/YOUR-PATH/ is your redirect uri):


http://YOUR-SITE.com/YOUR-PATH/?state=profile&code=1/fFBGRNJru1FQd44AzqT3Zg

Exchange “Code” for Access Token

If access type was set to “offline” in the login URL, a refresh token will be sent with the access token so the data can be accessed without prompting the user again.

//Oauth 2.0: exchange token for session token so multiple calls can be made to api
if(isset($_REQUEST['code'])){
	$_SESSION['accessToken'] = get_oauth2_token($_REQUEST['code']);
}

//returns session token for calls to API using oauth 2.0
function get_oauth2_token($code) {
	global $client_id;
	global $client_secret;
	global $redirect_uri;

	$oauth2token_url = "https://accounts.google.com/o/oauth2/token";
	$clienttoken_post = array(
	"code" => $code,
	"client_id" => $client_id,
	"client_secret" => $client_secret,
	"redirect_uri" => $redirect_uri,
	"grant_type" => "authorization_code"
	);

	$curl = curl_init($oauth2token_url);

	curl_setopt($curl, CURLOPT_POST, true);
	curl_setopt($curl, CURLOPT_POSTFIELDS, $clienttoken_post);
	curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

	$json_response = curl_exec($curl);
	curl_close($curl);

	$authObj = json_decode($json_response);

	if (isset($authObj->refresh_token)){
        //refresh token only granted on first authorization for offline access
        //save to db for future use (db saving not included in example)
		global $refreshToken;
		$refreshToken = $authObj->refresh_token;
	}

	$accessToken = $authObj->access_token;
	return $accessToken;
}

You now have an access token to present to the Google API of your choice (specified in the scope) for data.

Demo

Download code

Google Analytics Core Reporting API (Data Export) with Google Chart Visualizations

Updated 12/20/11 and 01/04/12 to reflect Google’s API changes

You can punch into the Google Analytics Core Reporting (Data Export) API, pull out some stats, and stuff them into some nice graphical charts using Google Chart Visualizations. This demo is done in PHP.
Google Chart Visualizations

Authenticate the User

The user can authenticate via the ClientLogin or using the AuthSub login which is actually more secure. For the ClientLogin, a typical username/password form is used. For the AuthSub login a link is used to send the user to Google to log in. Both are shown below. Normally you would use one or the other.

* Use ClientLogin ONLY for installed apps and not web apps

<form name="loginForm" action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
		    <label for="email">Gmail:</label>
		    <input id="email" type="text" name="email" />
		    <label for="password">Password:</label>
		    <input type="password" name="password" id="password"/>
		    <br /><br />
		    <button type="submit" id="submitLogin">Submit</button>
		</form>
        <p><a class="button" href="https://www.google.com/accounts/AuthSubRequest?next=http://your-return-url&scope=https://www.googleapis.com/auth/analytics.readonly&secure=0&session=1">Or,authenticate using AuthSub through Google.</a></p>

And, of course, the two authentication methods use different http calls to return a token that can be used to access the API.

//ClientLogin: try to log in and get session token for multiple API calls
if(isset($_POST['email']) && isset($_POST['password'])){
	$_SESSION['sessionToken'] = googleLogin($_POST['email'],$_POST['password']);
}
//AuthSub: exchange token for session token so multiple calls can be made to api
if(isset($_REQUEST['token'])){
	$_SESSION['authSub'] = true;
	$_SESSION['sessionToken'] = get_session_token($_REQUEST['token']);
}

//returns sessionToken for multiple calls to API
function googleLogin($email,$passwd){

    $clientlogin_url = "https://www.google.com/accounts/ClientLogin";
     $clientlogin_post = array(
    "accountType" => "GOOGLE",
    "Email" => $email,
    "Passwd" => $passwd,
    "service" => "analytics",
    "source" => "my-analytics"
	);

	$curl = curl_init($clientlogin_url);

	curl_setopt($curl, CURLOPT_POST, true);
	curl_setopt($curl, CURLOPT_POSTFIELDS, $clientlogin_post);
	curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

	$response = curl_exec($curl);

	preg_match("/Auth=([a-z0-9_\-]+)/i", $response, $matches);
	$sessionToken = $matches[1];

	if (strlen($sessionToken) == 0){
		$sessionToken = "Authentication Failed.";
	}

 	return $sessionToken;
}

//AuthSub returns session token for multiple calls to API
	function get_session_token($onetimetoken) {
		$output = call_api($onetimetoken, "https://www.google.com/accounts/AuthSubSessionToken");

		if (preg_match("/Token=(.*)/", $output, $matches))
		{
			$sessionToken = $matches[1];
		} else {
			echo "Error authenticating with Google.";
			exit;
		}

		return $sessionToken;
	}

Data Requests

Once authenticated to a Google Analytics account and a multi-use session token is acquired, the data requests can be made. The first one will request the profiles (websites) associated with the account. If there is more than one, a dropdown select is populated allowing for the selection of the profile from which to pull data.

The key is added to the request per this post by Google.

$accountxml = call_api($_SESSION['sessionToken'],"https://www.googleapis.com/analytics/v2.4/management/accounts/~all/webproperties/~all/profiles?key=Axxxxxxxxxxxxxxxxxx4");
// Get an array with the available accounts
$profiles = parse_account_list($accountxml);

The call_api function is going to return the XML data from Google based on the request URL sent in. In this case, it is getting the profile data and the parse_account_list function is rolling through that XML and putting the profile data in an array.

//gets the data
function call_api($sessionToken,$url){
	$curl = curl_init($url);

	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
	if (isset($_SESSION['authSub'])){
		$curlheader[0] = sprintf("Authorization: AuthSub token=\"%s\"/n", $sessionToken);
	} else {
		$curlheader[0] = "Authorization: GoogleLogin auth=" . $sessionToken;
	}
	curl_setopt($curl, CURLOPT_HTTPHEADER, $curlheader);

	$response = curl_exec($curl);
	curl_close($curl);

	return $response;
}

//returns accounts list as array
function parse_account_list($xml){
	$doc = new DOMDocument();
	if(stripos($xml,"<") !== FALSE)
	{
		$doc->loadXML($xml);

		$entries = $doc->getElementsByTagName('entry');
		$i = 0;
		$profiles= array();
		foreach($entries as $entry)
		{
			$profiles[$i] = array();

			$properties = $entry->getElementsByTagName('property');
			foreach($properties as $property)
			{
				if (strcmp($property->getAttribute('name'), 'ga:accountId') == 0)
					$profiles[$i]["accountId"] = $property->getAttribute('value');

				if (strcmp($property->getAttribute('name'), 'ga:profileName') == 0)
					$profiles[$i]["title"] = $property->getAttribute('value');

				if (strcmp($property->getAttribute('name'), 'dxp:tableId') == 0)
					$profiles[$i]["tableId"] = $property->getAttribute('value');
			}

			$i++;
		}

		return $profiles;
	} else {
		$sessionToken = "Authentication Failed.";
	}
}

The dropdown of the profile array:

echo "<form name='siteSelect' id='siteSelect' method='post' action='" . $_SERVER['PHP_SELF'] . "'><p><label for='tableId'>Select Site:</label><select name='tableId' id='tableId'>";
		foreach($profiles as $profile)
		{
			if($profile["tableId"] == $table_Id)
				$selected = "selected='selected'";
				echo "<option value='" . $profile["tableId"] . "|" . $profile["title"] . "'" . $selected  . ">" . $profile["title"] . "</option>";
				$selected = " ";
		}
		echo "</select></p>";

// set $tableId
if(isset($_POST['tableId'])){
    $tableId = substr($_POST['tableId'],0,strpos($_POST['tableId'],"|"));
}

The parse_data function below is going to roll through the data returned from Google Analytics and spit out an array that can be used to create the Google Visualization graphs.

//returns data as array
function parse_data($xml){
		$doc = new DOMDocument();
		$doc->loadXML($xml);

		$entries = $doc->getElementsByTagName('entry');
		$i = 0;
		$results = array();
		foreach($entries as $entry)
		{
			$dimensions = $entry->getElementsByTagName('dimension');
			foreach($dimensions as $dimension)
			{
				$results[$i][ltrim($dimension->getAttribute("name"),"ga:")] =  $dimension->getAttribute('value');
			}

			$metrics = $entry->getElementsByTagName('metric');
			foreach($metrics as $metric)
			{
				$results[$i][ltrim($metric->getAttribute('name'),"ga:")] =  $metric->getAttribute('value');
			}

			$i++;
		}
		return $results;
}

Graph Generation

Request the data

// For each website, get referrals
$requrl = sprintf("https://www.googleapis.com/analytics/v2.4/data?ids=%s&dimensions=ga:source&metrics=ga:visits&filters=ga:medium==referral&start-date=%s&end-date=%s&sort=-ga:visits&max-results=10",$tableId,$start_date,$end_date);

$referralsxml = call_api($_SESSION['sessionToken'],$requrl);
$referrers = parse_data($referralsxml);

$requrlvisitsgraph = sprintf("https://www.googleapis.com/analytics/v2.4/data?ids=%s&dimensions=ga:date&metrics=ga:visits&start-date=%s&end-date=%s",$tableId,$start_date,$end_date);
$visitsgraphxml = call_api($_SESSION['sessionToken'],$requrlvisitsgraph);
$visitsgraph = parse_data($visitsgraphxml);

Google Visualizations requires the inclusion of a javascript file in the head tag and empty div’s that will be the target for the graphs:

<script type="text/javascript" src="//www.google.com/jsapi"></script>

The target div’s should be placed on the page where you want them to appear.

<div id='barchart_div'></div>
<div id='piechart_div'></div>

Finally, the data can be added to the chart generation javascript:

<script>
function drawPieChart() {
	var data = new google.visualization.DataTable();
    data.addColumn('string', 'Referrer');
    data.addColumn('number', 'Visits');
    data.addRows(<?php echo sizeof($referrers) ?>);
    <?php
    $row = 0;
    foreach($referrers as $referrer)
		{
	?>
	data.setValue(<?php echo $row ?>,0,'<?php echo $referrer["source"] ?>');
	data.setValue(<?php echo $row ?>,1,<?php echo $referrer["visits"] ?>);
	<?php
	$row++;
	}
	?>

	var chart = new google.visualization.PieChart(document.getElementById('piechart_div'));
    chart.draw(data, {width: 600, height: 440, is3D: true, title: 'Referrer/Visits'});
}

function drawBarChart() {
	var data = new google.visualization.DataTable();
    data.addColumn('string', 'Day');
    data.addColumn('number', 'Visits');
    data.addRows(<?php echo sizeof($visitsgraph) ?>);
	<?php
    $row = 0;
    foreach($visitsgraph as $visits)
		{
	?>
		data.setValue(<?php echo $row ?>,0,'<?php if ($visits_graph_type === "month"){echo date("M", mktime(0, 0, 0, $visits["month"]))." ".$visits["year"];}else{echo substr($visits['date'],6,2)."-".date('M', mktime(0, 0, 0, substr($visits['date'],4,2)))."-".substr($visits['date'],0,4);} ?>');
		data.setValue(<?php echo $row ?>,1,<?php echo $visits["visits"] ?>);
		<?php
		$row++;
		}
		?>
    var chart = new google.visualization.ColumnChart(document.getElementById('barchart_div'));
    chart.draw(data, {'width': 700, 'height': 400, 'is3D': true, 'title': 'Visits'});
}

google.load("visualization", "1.0", {packages:["corechart"]});
google.setOnLoadCallback(drawPieChart);
google.setOnLoadCallback(drawBarChart);

</script>

Recommended

Demo

Download code

Using Google Docs to Create a Survey

After creating the pop-up survey in jQuery, further exploration on creating online polls has turned up Google Docs quick and dirty form creation.

Google posted a 2-minute video on Google Docs form creation that pretty much sums it up. This has actually been around for a couple of years now but, even though it’s a little late to the party, here is a quick illustration of my jQuery poll done with Google Docs:

Select “Form” from the “Create New” menu.

Fill in the name of the poll or form, short description, and the first question.

Add a question by clicking the “Add Item” button and choose from the menu items. Under “More Actions” you can create the confirmation message and allow the submitters to see the results.

You can also get the embed link for putting the poll right on a web page.

And, you can email the form and have users submit answers right from their email by hitting the “Email this form” button and adding the email addresses.

Here it is in all it’s embedded goodness. Try it out: