blob: 8454309753cd87d759e95f98327f5bf672ce8da2 [file] [log] [blame]
<?php
/**
* Copyright 2010-2014 baas-platform.com, Pty Ltd. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Apache\Usergrid\Guzzle\Plugin\Oauth2;
use Apache\Usergrid\Guzzle\Plugin\Oauth2\GrantType\GrantTypeInterface;
use Guzzle\Common\Event;
use Guzzle\Http\Exception\BadResponseException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* OAuth2 plugin
*
* @package Apache/Usergrid
* @version 1.0.0
* @author Jason Kristian <jasonkristian@gmail.com>
* @license Apache License, Version 2.0
* @copyright (c) 2008-2014, Baas Platform Pty. Ltd
*
* @link http://tools.ietf.org/html/rfc6749
*/
class Oauth2Plugin implements EventSubscriberInterface
{
/** @var GrantTypeInterface The grant type implementation used to acquire access tokens */
protected $grantType;
/** @var GrantTypeInterface The grant type implementation used to refresh access tokens */
protected $refreshTokenGrantType;
/** @var array An array with the "access_token" and "expires" keys */
protected $accessToken;
/** @var string The refresh token string. * */
protected $refreshToken;
/**
* Create a new Oauth2 plugin
* @param GrantTypeInterface $grantType
* @param GrantTypeInterface $refreshTokenGrantType
*/
public function __construct(GrantTypeInterface $grantType = null, GrantTypeInterface $refreshTokenGrantType = null)
{
$this->grantType = $grantType;
$this->refreshTokenGrantType = $refreshTokenGrantType;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
'request.before_send' => 'onRequestBeforeSend',
'request.error' => 'onRequestError',
);
}
/**
* Request before-send event handler.
*
* Adds the Authorization header if an access token was found.
*
* @param Event $event Event received
*/
public function onRequestBeforeSend(Event $event)
{
$accessToken = $this->getAccessToken();
if ($accessToken) {
$event['request']->setHeader('Authorization', 'Bearer ' . $accessToken['access_token']);
}
}
/**
* Get the access token.
*
* Handles token expiration for tokens with an "expires" timestamp.
* In case no valid token was found, tries to acquire a new one.
*
* @return array|null
*/
public function getAccessToken()
{
if (isset($this->accessToken['expires']) && $this->accessToken['expires'] < time()) {
// The access token has expired.
$this->accessToken = null;
}
if (!$this->accessToken) {
// Try to acquire a new access token from the server.
$this->acquireAccessToken();
}
return $this->accessToken;
}
/**
* Set the access token.
*
* @param array|string $accessToken The access token
*/
public function setAccessToken(array $accessToken)
{
$this->accessToken = $accessToken;
}
/**
* Acquire a new access token from the server.
*
* @return array|null
*/
protected function acquireAccessToken()
{
if ($this->refreshTokenGrantType && $this->refreshToken) {
try {
// Get an access token using the stored refresh token.
$tokenData = $this->refreshTokenGrantType->getTokenData($this->refreshToken);
} catch (BadResponseException $e) {
// The refresh token has probably expired.
$this->refreshToken = null;
}
}
if ($this->grantType && !isset($tokenData)) {
// Get a new access token.
$tokenData = $this->grantType->getTokenData();
}
$this->accessToken = null;
if (isset($tokenData)) {
// Process the returned data. Both expired_in and refresh_token
// are optional parameters.
$this->accessToken = array(
'access_token' => $tokenData['access_token'],
);
if (isset($tokenData['expires_in'])) {
$this->accessToken['expires'] = time() + $tokenData['expires_in'];
}
if (isset($tokenData['refresh_token'])) {
$this->refreshToken = $tokenData['refresh_token'];
}
}
return $this->accessToken;
}
/**
* Request error event handler.
*
* Handles unauthorized errors by acquiring a new access token and
* retrying the request.
*
* @param Event $event Event received
*/
public function onRequestError(Event $event)
{
if ($event['response']->getStatusCode() == 401) {
if ($event['request']->getHeader('X-Guzzle-Retry')) {
// We already retried once, give up.
return;
}
// Acquire a new access token, and retry the request.
$newAccessToken = $this->acquireAccessToken();
if ($newAccessToken) {
$newRequest = clone $event['request'];
$newRequest->setHeader('Authorization', 'Bearer ' . $newAccessToken['access_token']);
$newRequest->setHeader('X-Guzzle-Retry', '1');
$event['response'] = $newRequest->send();
$event->stopPropagation();
}
}
}
/**
* Get the refresh token.
*
* @return string
*/
public function getRefreshToken()
{
return $this->refreshToken;
}
/**
* Set the refresh token.
*
* @param string $refreshToken The refresh token
*/
public function setRefreshToken($refreshToken)
{
$this->refreshToken = $refreshToken;
}
/**
* @return GrantTypeInterface
*/
public function getGrantType()
{
return $this->grantType;
}
}