Google APIs : Handling Access Token Expiration

The immense popularity of Google services such as Youtube, Analytics, Spreadsheets etc has led to more and more applications integrating Google APIs. All Google APIs have a common thing — they make use of an access token.

An application first gets the access token of the user through the Login API. After that it can perform its intended operations such as uploading a video on Youtube or creating an event on Google Calendar or getting Google Analytics data of the user's website or something else.

But access tokens have an expiry time of an hour or so. After that if any API call is made with that access token, the API call will just fail. Most of the applications will show an error message "Error : Access Token Failed" and prompt the user to re-login. This method does not sound good especially when Google provides methods to get a new access token without requiring the user to re-login.

However if your application is just implementing Google login and is not using any other API services afterwards, then there is no need to handle the expired access token.

This tutorial assumes that you have set up Google Login API in your application and then using other Google APIs such as Youtube, Calendar, Analytics etc.

If are looking to integrate Google login in your application, check out Google Login API with PHP Curl. It contains a demo, and sample codes are provided for download.

Handling access token expiration will require some simple changes to be made in your existing code.

Step 1 : Request for Offline Access

Google provides offline access to its APIs. This means that your application can make API calls on behalf of the user even when he is not online.

Your application must request for offline access permission from the user at the time of login. This can be done by just adding a parameter access_type=offline to the Google API login url.

<?php $login_url = 'https://accounts.google.com/o/oauth2/v2/auth?scope=' . urlencode('https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/plus.me') . '&redirect_uri=' . urlencode(CLIENT_REDIRECT_URL) . '&response_type=code&client_id=' . CLIENT_ID . '&access_type=offline'; ?>

The above url contains the scope of login and getting user's profile information with email. But typically your scope parameter will contain other scopes that your application requires (Youtube, Calendar) etc.

Step 2 : Saving the Refresh Token and Access Token Expiration Timestamp

After the user logs in, you get an OAuth2 authorization code from Google. You use this authorization code to make another API call to get the access token. The response of this API call will include :

  • access_token
  • refresh_token
  • expires_in

expires_in gives the time (in seconds) in which the access token will expire. Typically it will set to be 3600 seconds, which mean the access token will expire in an hour's time.

The point of the refresh token is to refresh the access token. Ideally you should save the refresh token in your user database. It is because Google passes a refresh token only at the time when the user authorizes your application via a consent screen. After that when the user tries to login again, he will not see the consent screen. No consent means no refresh token passed by Google.

To get a new refresh token you need to show the consent screen again to the user. Re-prompting the user for consent can done by adding a prompt parameter to the login url.

<?php $login_url = 'https://accounts.google.com/o/oauth2/v2/auth?scope=' . urlencode('https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/plus.me') . '&redirect_uri=' . urlencode(CLIENT_REDIRECT_URL) . '&response_type=code&client_id=' . CLIENT_ID . '&access_type=offline&prompt=consent'; ?>

Save the access token expiry time as a session variable by adding the current timestamp and the value of expires_in. Your redirect url script will look something like :

<?php session_start(); // Google passes a parameter 'code' in the Redirect Url if(isset($_GET['code'])) { try { $gapi = new GoogleLoginApi(); // Get the access token $data = $gapi->GetAccessToken(CLIENT_ID, CLIENT_REDIRECT_URL, CLIENT_SECRET, $_GET['code']); // Access Token $access_token = $data['access_token']; // Refresh Token if(array_key_exists('refresh_token', $data)) $refresh_token = $data['refresh_token']; // Save the access token expiry timestamp $_SESSION['access_token_expiry'] = time() + $data['expires_in']; $_SESSION['access_token'] = $data['access_token']; } catch(Exception $e) { echo $e->getMessage(); exit(); } } ?>

Step 3 : If the Access Token Expires Get a New Access Token

Before you call any Google API that makes use of an access token, check whether the access token expiry timestamp has passed. If yes, request for a new access token using the refresh token.

if(time() > $_SESSION['access_token_expiry']) { // Get a new access token using the refresh token $data = $gapi->GetRefreshedAccessToken(CLIENT_ID, $_SESSION['refresh_token'], CLIENT_SECRET); // Again save the expiry time of the new token $_SESSION['access_token_expiry'] = time() + $data['expires_in']; // The new access token $_SESSION['access_token'] = $data['access_token']; } // now proceed with calling other Google APIs with the new access token // upload video using Youtube API // create event using Calendar API // create spreadsheet using Spreadsheets API

Please note that you have to re-save the expiry timestamp of the new access token as well.

The API call that refreshes the access token :

public function GetRefreshedAccessToken($client_id, $refresh_token, $client_secret) { $url_token = 'https://www.googleapis.com/oauth2/v4/token'; $curlPost = 'client_id=' . $client_id . '&client_secret=' . $client_secret . '&refresh_token='. $refresh_token . '&grant_type=refresh_token'; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url_token); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost); $data = json_decode(curl_exec($ch), true); //print_r($data); $http_code = curl_getinfo($ch,CURLINFO_HTTP_CODE); if($http_code != 200) throw new Exception('Error : Failed to refresh access token'); return $data; }


Useful Links :
Using OAuth 2.0 for Web Server Applications : Offline access

Login with Google using PHP
Google Refresh Tokens : Answers to Some Common Questions