Skip to content

Instantly share code, notes, and snippets.

@monkbroc
Created May 28, 2015 17:10
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save monkbroc/4251effc7912b9764ca1 to your computer and use it in GitHub Desktop.
Save monkbroc/4251effc7912b9764ca1 to your computer and use it in GitHub Desktop.
Zend 1 Gdata OAuthClient
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Gdata
* @subpackage Gdata
* @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id: HttpClient.php 20096 2010-01-06 02:05:09Z bkarwin $
*/
/**
* Zend_Http_Client
*/
require_once 'Zend/Http/Client.php';
/**
* Gdata Http Client object.
*
* Class to extend the generic Zend Http Client with the ability to perform
* secure AuthSub requests
*
* @category Zend
* @package Zend_Gdata
* @subpackage Gdata
* @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Gdata_HttpClient extends Zend_Http_Client
{
/**
* OpenSSL private key resource id
* This key is used for AuthSub authentication. If this value is set,
* it is assuemd that secure AuthSub is desired.
*
* @var resource
*/
private $_authSubPrivateKeyId = null;
/**
* Token for AuthSub authentication.
* If this token is set, AuthSub authentication is used.
*
* @var string
*/
private $_authSubToken = null;
/**
* Token for ClientLogin authentication.
* If only this token is set, ClientLogin authentication is used.
*
* @var string
*/
private $_clientLoginToken = null;
/**
* Token for OAuth
*/
private $_bearerToken = null;
/**
* Token for ClientLogin authentication.
* If this token is set, and the AuthSub key is not set,
* ClientLogin authentication is used
*
* @var string
*/
private $_clientLoginKey = null;
/**
* True if this request is being made with data supplied by
* a stream object instead of a raw encoded string.
*
* @var bool
*/
protected $_streamingRequest = null;
/**
* Sets the PEM formatted private key, as read from a file.
*
* This method reads the file and then calls setAuthSubPrivateKey()
* with the file contents.
*
* @param string $file The location of the file containing the PEM key
* @param string $passphrase The optional private key passphrase
* @param bool $useIncludePath Whether to search the include_path
* for the file
* @return void
*/
public function setAuthSubPrivateKeyFile($file, $passphrase = null,
$useIncludePath = false) {
$fp = @fopen($file, "r", $useIncludePath);
if (!$fp) {
require_once 'Zend/Gdata/App/InvalidArgumentException.php';
throw new Zend_Gdata_App_InvalidArgumentException('Failed to open private key file for AuthSub.');
}
$key = '';
while (!feof($fp)) {
$key .= fread($fp, 8192);
}
$this->setAuthSubPrivateKey($key, $passphrase);
fclose($fp);
}
/**
* Sets the PEM formatted private key to be used for secure AuthSub auth.
*
* In order to call this method, openssl must be enabled in your PHP
* installation. Otherwise, a Zend_Gdata_App_InvalidArgumentException
* will be thrown.
*
* @param string $key The private key
* @param string $passphrase The optional private key passphrase
* @throws Zend_Gdata_App_InvalidArgumentException
* @return Zend_Gdata_HttpClient Provides a fluent interface
*/
public function setAuthSubPrivateKey($key, $passphrase = null) {
if ($key != null && !function_exists('openssl_pkey_get_private')) {
require_once 'Zend/Gdata/App/InvalidArgumentException.php';
throw new Zend_Gdata_App_InvalidArgumentException(
'You cannot enable secure AuthSub if the openssl module ' .
'is not enabled in your PHP installation.');
}
$this->_authSubPrivateKeyId = openssl_pkey_get_private(
$key, $passphrase);
return $this;
}
/**
* Gets the openssl private key id
*
* @return string The private key
*/
public function getAuthSubPrivateKeyId() {
return $this->_authSubPrivateKeyId;
}
/**
* Gets the AuthSub token used for authentication
*
* @return string The token
*/
public function getAuthSubToken() {
return $this->_authSubToken;
}
/**
* Sets the AuthSub token used for authentication
*
* @param string $token The token
* @return Zend_Gdata_HttpClient Provides a fluent interface
*/
public function setAuthSubToken($token) {
$this->_authSubToken = $token;
return $this;
}
/**
* Gets the ClientLogin token used for authentication
*
* @return string The token
*/
public function getClientLoginToken() {
return $this->_clientLoginToken;
}
/**
* Sets the ClientLogin token used for authentication
*
* @param string $token The token
* @return Zend_Gdata_HttpClient Provides a fluent interface
*/
public function setClientLoginToken($token) {
$this->_clientLoginToken = $token;
return $this;
}
/**
* Gets the Bearer token used for authentication
*
* @return string The token
*/
public function getBearerToken() {
return $this->_bearerToken;
}
/**
* Sets the Bearer token used for authentication
*
* @param string $token The token
* @return Zend_Gdata_HttpClient Provides a fluent interface
*/
public function setBearerToken($token) {
$this->_bearerToken = $token;
return $this;
}
/**
* Filters the HTTP requests being sent to add the Authorization header.
*
* If both AuthSub and ClientLogin tokens are set,
* AuthSub takes precedence. If an AuthSub key is set, then
* secure AuthSub authentication is used, and the request is signed.
* Requests must be signed only with the private key corresponding to the
* public key registered with Google. If an AuthSub key is set, but
* openssl support is not enabled in the PHP installation, an exception is
* thrown.
*
* @param string $method The HTTP method
* @param string $url The URL
* @param array $headers An associate array of headers to be
* sent with the request or null
* @param string $body The body of the request or null
* @param string $contentType The MIME content type of the body or null
* @throws Zend_Gdata_App_Exception if there was a signing failure
* @return array The processed values in an associative array,
* using the same names as the params
*/
public function filterHttpRequest($method, $url, $headers = array(), $body = null, $contentType = null) {
if ($this->getAuthSubToken() != null) {
// AuthSub authentication
if ($this->getAuthSubPrivateKeyId() != null) {
// secure AuthSub
$time = time();
$nonce = mt_rand(0, 999999999);
$dataToSign = $method . ' ' . $url . ' ' . $time . ' ' . $nonce;
// compute signature
$pKeyId = $this->getAuthSubPrivateKeyId();
$signSuccess = openssl_sign($dataToSign, $signature, $pKeyId,
OPENSSL_ALGO_SHA1);
if (!$signSuccess) {
require_once 'Zend/Gdata/App/Exception.php';
throw new Zend_Gdata_App_Exception(
'openssl_signing failure - returned false');
}
// encode signature
$encodedSignature = base64_encode($signature);
// final header
$headers['authorization'] = 'AuthSub token="' . $this->getAuthSubToken() . '" ' .
'data="' . $dataToSign . '" ' .
'sig="' . $encodedSignature . '" ' .
'sigalg="rsa-sha1"';
} else {
// AuthSub without secure tokens
$headers['authorization'] = 'AuthSub token="' . $this->getAuthSubToken() . '"';
}
} elseif ($this->getClientLoginToken() != null) {
$headers['authorization'] = 'GoogleLogin auth=' . $this->getClientLoginToken();
} elseif ($this->getBearerToken() != null) {
$headers['authorization'] = 'Bearer ' . $this->getBearerToken();
}
return array('method' => $method, 'url' => $url, 'body' => $body, 'headers' => $headers, 'contentType' => $contentType);
}
/**
* Method for filtering the HTTP response, though no filtering is
* currently done.
*
* @param Zend_Http_Response $response The response object to filter
* @return Zend_Http_Response The filterd response object
*/
public function filterHttpResponse($response) {
return $response;
}
/**
* Return the current connection adapter
*
* @return Zend_Http_Client_Adapter_Interface|string $adapter
*/
public function getAdapter()
{
return $this->adapter;
}
/**
* Load the connection adapter
*
* @param Zend_Http_Client_Adapter_Interface $adapter
* @return void
*/
public function setAdapter($adapter)
{
if ($adapter == null) {
$this->adapter = $adapter;
} else {
parent::setAdapter($adapter);
}
}
/**
* Set the streamingRequest variable which controls whether we are
* sending the raw (already encoded) POST data from a stream source.
*
* @param boolean $value The value to set.
* @return void
*/
public function setStreamingRequest($value)
{
$this->_streamingRequest = $value;
}
/**
* Check whether the client is set to perform streaming requests.
*
* @return boolean True if yes, false otherwise.
*/
public function getStreamingRequest()
{
if ($this->_streamingRequest()) {
return true;
} else {
return false;
}
}
/**
* Prepare the request body (for POST and PUT requests)
*
* @return string
* @throws Zend_Http_Client_Exception
*/
protected function _prepareBody()
{
if($this->_streamingRequest) {
$this->setHeaders(self::CONTENT_LENGTH,
$this->raw_post_data->getTotalSize());
return $this->raw_post_data;
}
else {
return parent::_prepareBody();
}
}
/**
* Clear all custom parameters we set.
*
* @return Zend_Http_Client
*/
public function resetParameters($clearAll = false)
{
$this->_streamingRequest = false;
return parent::resetParameters($clearAll);
}
/**
* Set the raw (already encoded) POST data from a stream source.
*
* This is used to support POSTing from open file handles without
* caching the entire body into memory. It is a wrapper around
* Zend_Http_Client::setRawData().
*
* @param string $data The request data
* @param string $enctype The encoding type
* @return Zend_Http_Client
*/
public function setRawDataStream($data, $enctype = null)
{
$this->_streamingRequest = true;
return $this->setRawData($data, $enctype);
}
}
<?php
/**
* Zend_Gdata_HttpClient
*/
require_once 'Zend/Gdata/HttpClient.php';
/**
* Zend_Version
*/
require_once 'Zend/Version.php';
/**
* Class to facilitate Google's "Account Authentication
* for Installed Applications" also known as "ClientLogin".
* @see http://code.google.com/apis/accounts/AuthForInstalledApps.html
*
* @category Zend
* @package Zend_Gdata
* @subpackage Gdata
* @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Gdata_OAuthClient
{
const TOKEN_CREDENTIAL_URI = 'https://www.googleapis.com/oauth2/v3/token';
/**
* The default 'source' parameter to send to Google
*
*/
const DEFAULT_SOURCE = 'Zend-ZendFramework';
/**
* Set Google authentication credentials.
* Must be done before trying to do any Google Data operations that
* require authentication.
* For example, viewing private data, or posting or deleting entries.
*
* @param string $email
* @param string $password
* @param string $service
* @param Zend_Gdata_HttpClient $client
* @param string $source
* @param string $loginToken The token identifier as provided by the server.
* @param string $loginCaptcha The user's response to the CAPTCHA challenge.
* @param string $accountType An optional string to identify whether the
* account to be authenticated is a google or a hosted account. Defaults to
* 'HOSTED_OR_GOOGLE'. See: http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#Request
* @throws Zend_Gdata_App_AuthException
* @throws Zend_Gdata_App_HttpException
* @throws Zend_Gdata_App_CaptchaRequiredException
* @return Zend_Gdata_HttpClient
*/
public static function getHttpClient($clientId, $clientSecret, $refreshToken,
$client = null,
$source = self::DEFAULT_SOURCE,
$tokenCredentialUri = self::TOKEN_CREDENTIAL_URI)
{
if (! ($clientId && $clientSecret && $refreshToken)) {
require_once 'Zend/Gdata/App/AuthException.php';
throw new Zend_Gdata_App_AuthException(
'Missing Google credentials before trying to ' .
'authenticate');
}
if ($client == null) {
$client = new Zend_Gdata_HttpClient();
}
if (!$client instanceof Zend_Http_Client) {
require_once 'Zend/Gdata/App/HttpException.php';
throw new Zend_Gdata_App_HttpException(
'Client is not an instance of Zend_Http_Client.');
}
// Build the HTTP client for authentication
$client->setUri($tokenCredentialUri);
$useragent = $source . ' Zend_Framework_Gdata/' . Zend_Version::VERSION;
$client->setConfig(array(
'maxredirects' => 0,
'strictredirects' => true,
'useragent' => $useragent
)
);
$client->setParameterPost('grant_type', 'refresh_token');
$client->setParameterPost('refresh_token', (string) $refreshToken);
$client->setParameterPost('client_id', (string) $clientId);
$client->setParameterPost('client_secret', (string) $clientSecret);
// Send the authentication request
// For some reason Google's server causes an SSL error. We use the
// output buffer to supress an error from being shown. Ugly - but works!
ob_start();
try {
$response = $client->request('POST');
} catch (Zend_Http_Client_Exception $e) {
require_once 'Zend/Gdata/App/HttpException.php';
throw new Zend_Gdata_App_HttpException($e->getMessage(), $e);
}
ob_end_clean();
// Parse Google's response
$goog_resp = json_decode($response->getBody());
if ($response->getStatus() == 200) {
$client->setBearerToken($goog_resp->{'access_token'});
$useragent = $source . ' Zend_Framework_Gdata/' . Zend_Version::VERSION;
$client->setConfig(array(
'strictredirects' => true,
'useragent' => $useragent
)
);
return $client;
} else {
require_once 'Zend/Gdata/App/AuthException.php';
throw new Zend_Gdata_App_AuthException('Authentication with Google failed. Reason: ' .
$goog_resp->{'error_description'});
}
}
}
@monkbroc
Copy link
Author

Keep using your old Zend PHP application now that Google shutdown ClientLogin.

The only problem is getting a refresh token for your own account. Here are the steps adapted from Martin Fowler's article.

  1. Create project in Google Developer Console
  2. Create new OAuth client id for installed application. Note the client ID and client secret for later.
  3. Craft URL to get token
    https://accounts.google.com/o/oauth2/auth?scope=http://picasaweb.google.com/data/&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=YOUR_CLIENT_ID.apps.googleusercontent.com
  4. Open browser logged in to your Google account and paste URL above
  5. Note the authorization code
  6. Convert the authorization code to a refresh token
    On a machine with ruby, gem install google-api-client
    Run irb
    require 'google/api_client'
    require 'google/api_client/auth/installed_app'
    client = Google::APIClient.new(:application_name => "My App", application_version: "1.0.0")
    client.authorization = Signet::OAuth2::Client.new(token_credential_uri: 'https://www.googleapis.com/oauth2/v3/token', code: 'YOUR_AUTHORIZATION_TOKEN', client_id: 'YOUR_CLIENT_ID', client_secret: 'YOUR_CLIENT_SECRET', redirect_uri: 'urn:ietf:wg:oauth:2.0:oob', grant_type: 'authorization_code')
    client.authorization.fetch_access_token!
  7. Note the refresh token
  8. Feed the client id, client secret and refresh token to a modified Zend Gdata client that will convert
    the refresh token to an access token.
  9. Profit!

@Janverhu
Copy link

Thanks a lot for this! You saved my day!!! I owe you a couple of beers!!!

@xiopt
Copy link

xiopt commented Aug 12, 2015

Where exactly do I need to put these 2 files and what changes should I do when I initialise the client?

Copy link

ghost commented Oct 25, 2015

Excellent! Thank you! :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment