Google OAuth 2.0 refresh token for web application with public access
Each access_token expires after a few seconds and need to be refreshed by refresh_token, "Offline access" is what you are looking for. Here you can follow the documentation:
https://developers.google.com/accounts/docs/OAuth2WebServer#offline
To obtain a refresh_token you have to run this code only one time:
require_once 'Google/Client.php';
$client = new Google_Client();
$client->setClientId('{MY_CLIENT_ID}.apps.googleusercontent.com');
$client->setClientSecret('{MY_KEY}');
$client->setRedirectUri('{MY_REDIRECT_URI}');
//next two line added to obtain refresh_token
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->setScopes(array('https://www.googleapis.com/auth/gmail.readonly'));
if (isset($_GET['code'])) {
$credentials = $client->authenticate($_GET['code']);
/*TODO: Store $credentials somewhere secure */
} else {
$authUrl = $client->createAuthUrl();
print "<a class='login' href='$authUrl'>Connect Me!</a>";
}
$credentials
includes an access token and a refresh token. You have to store this to obtain new access tokens at any time. So when you wanted to make a call to api:
require_once 'Google/Client.php';
require_once 'Google/Service/Gmail.php';
/*TODO: get stored $credentials */
$client = new Google_Client();
$client->setClientId('{MY_CLIENT_ID}.apps.googleusercontent.com');
$client->setRedirectUri('{MY_REDIRECT_URI}');
$client->setClientSecret('{MY_KEY}');
$client->setScopes(array('https://www.googleapis.com/auth/gmail.readonly'));
$client->setAccessToken($credentials);
$service = new Google_Service_Gmail($client);
Comments
-
Moose almost 2 years
I'm getting the following error:
The OAuth 2.0 access token has expired, and a refresh token is not available. Refresh tokens are not returned for responses that were auto-approved
I have a web application which only my server will be accessing, the initial auth works fine, but after an hour, the aforementioned error message pops up. My script looks like this:
require_once 'Google/Client.php'; require_once 'Google/Service/Analytics.php'; session_start(); $client = new Google_Client(); $client->setApplicationName("Client_Library_Examples"); $client->setDeveloperKey("{MY_API_KEY}"); $client->setClientId('{MY_CLIENT_ID}.apps.googleusercontent.com'); $client->setClientSecret('{MY_KEY}'); $client->setRedirectUri('{MY_REDIRECT_URI}'); $client->setScopes(array('https://www.googleapis.com/auth/gmail.readonly')); if (isset($_GET['code'])) { $client->authenticate($_GET['code']); $_SESSION['token'] = $client->getAccessToken(); $redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF']; header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL)); } if (!$client->getAccessToken() && !isset($_SESSION['token'])) { $authUrl = $client->createAuthUrl(); print "<a class='login' href='$authUrl'>Connect Me!</a>"; }
How can I set up a refresh token for this?
Edit 1: I've tried doing this with a Service account as well. I followed the documentation available on GitHub:
https://github.com/google/google-api-php-client/blob/master/examples/service-account.php
My script looks like this:
session_start(); include_once "templates/base.php"; require_once 'Google/Client.php'; require_once 'Google/Service/Gmail.php'; $client_id = '{MY_CLIENT_ID}.apps.googleusercontent.com'; $service_account_name = '{MY_EMAIL_ADDRESS}@developer.gserviceaccount.com '; $key_file_location = 'Google/{MY_KEY_FILE}.p12'; echo pageHeader("Service Account Access"); if ($client_id == '' || !strlen($service_account_name) || !strlen($key_file_location)) { echo missingServiceAccountDetailsWarning(); } $client = new Google_Client(); $client->setApplicationName("Client_Library_Examples"); $service = new Google_Service_Gmail($client); if (isset($_SESSION['service_token'])) { $client->setAccessToken($_SESSION['service_token']); } $key = file_get_contents($key_file_location); $cred = new Google_Auth_AssertionCredentials( $service_account_name, array('https://www.googleapis.com/auth/gmail.readonly'), $key ); $client->setAssertionCredentials($cred); if($client->getAuth()->isAccessTokenExpired()) { $client->getAuth()->refreshTokenWithAssertion($cred); }
This returns the following message:
Error refreshing the OAuth2 token, message: '{ "error" : "invalid_grant" }''
After tracking this code to it's source, which can be found in
Google/Auth/OAuth2.php
The method in question,
refreshTokenRequest
:private function refreshTokenRequest($params) { $http = new Google_Http_Request( self::OAUTH2_TOKEN_URI, 'POST', array(), $params ); $http->disableGzip(); $request = $this->client->getIo()->makeRequest($http); $code = $request->getResponseHttpCode(); $body = $request->getResponseBody(); if (200 == $code) { $token = json_decode($body, true); if ($token == null) { throw new Google_Auth_Exception("Could not json decode the access token"); } if (! isset($token['access_token']) || ! isset($token['expires_in'])) { throw new Google_Auth_Exception("Invalid token format"); } if (isset($token['id_token'])) { $this->token['id_token'] = $token['id_token']; } $this->token['access_token'] = $token['access_token']; $this->token['expires_in'] = $token['expires_in']; $this->token['created'] = time(); } else { throw new Google_Auth_Exception("Error refreshing the OAuth2 token, message: '$body'", $code); } }
Which means that the
$code
variable is NULL. I found this post on SO:Error refreshing the OAuth2 token { “error” : “invalid_grant” }
And can see that there is still no prefered solution. This is driving me nuts. There is very little to no documentation available on this and if anybody has a solution, I'm sure I'm not the only one looking for it.