summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBrent Shaffer <bshafs@gmail.com>2013-05-28 01:28:48 +0400
committerBrent Shaffer <bshafs@gmail.com>2013-05-28 01:28:48 +0400
commitf58f5adf9ff7f7dbb0e1938bce1e726d94837cc0 (patch)
tree4175cff0eeb992de6cf93a70367a94b0f595b426 /src
parentd5bb9c8bdbe8ef86c65d6479f81db859f707b865 (diff)
parente82dfa379bf021f2b3c00ce7f89a596ae4b0e5d4 (diff)
downloadoauth2-server-php-0.8.tar.xz
merges develop into trunk for v0.8 tagv0.8
Diffstat (limited to 'src')
-rw-r--r--src/OAuth2/ClientAssertionType/HttpBasic.php48
-rw-r--r--src/OAuth2/ClientAssertionTypeInterface.php9
-rw-r--r--src/OAuth2/CompatibilityInterface.php16
-rw-r--r--src/OAuth2/Controller/AuthorizeController.php76
-rw-r--r--src/OAuth2/Controller/AuthorizeControllerInterface.php10
-rw-r--r--src/OAuth2/Controller/ResourceController.php57
-rw-r--r--src/OAuth2/Controller/ResourceControllerInterface.php10
-rw-r--r--src/OAuth2/Controller/TokenController.php130
-rw-r--r--src/OAuth2/Controller/TokenControllerInterface.php13
-rw-r--r--src/OAuth2/Encryption/JWT.php14
-rw-r--r--src/OAuth2/GrantType/AuthorizationCode.php75
-rw-r--r--src/OAuth2/GrantType/ClientCredentials.php29
-rw-r--r--src/OAuth2/GrantType/JWTBearer.php288
-rw-r--r--src/OAuth2/GrantType/RefreshToken.php69
-rw-r--r--src/OAuth2/GrantType/UserCredentials.php56
-rw-r--r--src/OAuth2/GrantTypeInterface.php15
-rw-r--r--src/OAuth2/Request.php17
-rw-r--r--src/OAuth2/Response.php90
-rw-r--r--src/OAuth2/Response/AuthenticationError.php17
-rw-r--r--src/OAuth2/Response/Error.php48
-rw-r--r--src/OAuth2/Response/ProviderInterface.php6
-rw-r--r--src/OAuth2/Response/Redirect.php44
-rw-r--r--src/OAuth2/ResponseInterface.php20
-rw-r--r--src/OAuth2/ResponseType/AccessToken.php17
-rw-r--r--src/OAuth2/ResponseType/AccessTokenInterface.php19
-rw-r--r--src/OAuth2/ResponseType/AuthorizationCode.php21
-rw-r--r--src/OAuth2/ResponseType/AuthorizationCodeInterface.php20
-rw-r--r--src/OAuth2/ScopeInterface.php6
-rw-r--r--src/OAuth2/Server.php342
-rw-r--r--src/OAuth2/Storage/AccessTokenInterface.php2
-rw-r--r--src/OAuth2/Storage/AuthorizationCodeInterface.php2
-rw-r--r--src/OAuth2/Storage/ClientCredentialsInterface.php2
-rw-r--r--src/OAuth2/Storage/ClientInterface.php7
-rw-r--r--src/OAuth2/Storage/JWTBearerInterface.php3
-rw-r--r--src/OAuth2/Storage/Memory.php31
-rw-r--r--src/OAuth2/Storage/Mongo.php7
-rw-r--r--src/OAuth2/Storage/Pdo.php20
-rw-r--r--src/OAuth2/Storage/Redis.php195
-rw-r--r--src/OAuth2/Storage/RefreshTokenInterface.php3
-rw-r--r--src/OAuth2/Storage/ScopeInterface.php2
-rw-r--r--src/OAuth2/Storage/UserCredentialsInterface.php4
-rw-r--r--src/OAuth2/TokenType/Bearer.php20
-rw-r--r--src/OAuth2/TokenType/MAC.php2
-rw-r--r--src/OAuth2/TokenTypeInterface.php11
44 files changed, 1052 insertions, 841 deletions
diff --git a/src/OAuth2/ClientAssertionType/HttpBasic.php b/src/OAuth2/ClientAssertionType/HttpBasic.php
index aa1d130..db376b0 100644
--- a/src/OAuth2/ClientAssertionType/HttpBasic.php
+++ b/src/OAuth2/ClientAssertionType/HttpBasic.php
@@ -5,11 +5,11 @@
*
* @author Brent Shaffer <bshafs at gmail dot com>
*/
-class OAuth2_ClientAssertionType_HttpBasic implements OAuth2_ClientAssertionTypeInterface, OAuth2_Response_ProviderInterface
+class OAuth2_ClientAssertionType_HttpBasic implements OAuth2_ClientAssertionTypeInterface
{
- private $response;
private $storage;
private $config;
+ private $clientData;
public function __construct(OAuth2_Storage_ClientCredentialsInterface $storage, array $config = array())
{
@@ -19,34 +19,36 @@ class OAuth2_ClientAssertionType_HttpBasic implements OAuth2_ClientAssertionType
$this->storage = $storage;
}
- public function getClientDataFromRequest(OAuth2_RequestInterface $request)
+ public function validateRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
- if (!$clientData = $this->getClientCredentials($request)) {
- return null;
- }
-
- if (!isset($clientData['client_id']) || !isset($clientData['client_secret'])) {
- throw new LogicException('the clientData array must have "client_id" and "client_secret" values set.');
- }
+ if (!$clientData = $this->getClientCredentials($request, $response)) {
+ return false;
+ }
- return $clientData;
- }
+ if (!isset($clientData['client_id']) || !isset($clientData['client_secret'])) {
+ throw new LogicException('the clientData array must have "client_id" and "client_secret" values set.');
+ }
- public function validateClientData(array $clientData, $grantTypeIdentifier)
- {
if ($this->storage->checkClientCredentials($clientData['client_id'], $clientData['client_secret']) === false) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_client', 'The client credentials are invalid');
+ $response->setError(400, 'invalid_client', 'The client credentials are invalid');
return false;
}
- if (!$this->storage->checkRestrictedGrantType($clientData['client_id'], $grantTypeIdentifier)) {
- $this->response = new OAuth2_Response_Error(400, 'unauthorized_client', 'The grant type is unauthorized for this client_id');
+ if (!$this->storage->checkRestrictedGrantType($clientData['client_id'], $request->request('grant_type'))) {
+ $response->setError(400, 'unauthorized_client', 'The grant type is unauthorized for this client_id');
return false;
}
+ $this->clientData = $clientData;
+
return true;
}
+ public function getClientId()
+ {
+ return $this->clientData['client_id'];
+ }
+
/**
* Internal function used to get the client credentials from HTTP basic
* auth or POST data.
@@ -67,7 +69,7 @@ class OAuth2_ClientAssertionType_HttpBasic implements OAuth2_ClientAssertionType
*
* @ingroup oauth2_section_2
*/
- public function getClientCredentials(OAuth2_RequestInterface $request)
+ public function getClientCredentials(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response = null)
{
if (!is_null($request->headers('PHP_AUTH_USER')) && !is_null($request->headers('PHP_AUTH_PW'))) {
return array('client_id' => $request->headers('PHP_AUTH_USER'), 'client_secret' => $request->headers('PHP_AUTH_PW'));
@@ -80,12 +82,10 @@ class OAuth2_ClientAssertionType_HttpBasic implements OAuth2_ClientAssertionType
}
}
- $this->response = new OAuth2_Response_Error(400, 'invalid_client', 'Client credentials were not found in the headers or body');
- return null;
- }
+ if ($response) {
+ $response->setError(400, 'invalid_client', 'Client credentials were not found in the headers or body');
+ }
- public function getResponse()
- {
- return $this->response;
+ return null;
}
} \ No newline at end of file
diff --git a/src/OAuth2/ClientAssertionTypeInterface.php b/src/OAuth2/ClientAssertionTypeInterface.php
index 9d034c4..93f6005 100644
--- a/src/OAuth2/ClientAssertionTypeInterface.php
+++ b/src/OAuth2/ClientAssertionTypeInterface.php
@@ -1,8 +1,11 @@
<?php
-interface OAuth2_ClientAssertionTypeInterface
+/**
+ * Interface for all OAuth2 Client Assertion Types
+ *
+ * @see OAuth2_CompatibilityInterface
+ */
+interface OAuth2_ClientAssertionTypeInterface extends OAuth2_CompatibilityInterface
{
- public function getClientDataFromRequest(OAuth2_RequestInterface $request);
- public function validateClientData(array $clientData, $grantTypeIdentifier);
}
diff --git a/src/OAuth2/CompatibilityInterface.php b/src/OAuth2/CompatibilityInterface.php
new file mode 100644
index 0000000..6707573
--- /dev/null
+++ b/src/OAuth2/CompatibilityInterface.php
@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * OAuth2_CompatibilityInterface
+ * This interface exists because PHP 5.2 cannot combine matching
+ * functions from separate interfaces.
+ *
+ * Even though OAuth2_ClientAssertionTypeInterface and OAuth2_GrantTypeInterface
+ * have common methods, it doesn't actually make sense to extend a base interface.
+ * However, this is temporarily required for the library to run in PHP 5.2
+ *
+ */
+interface OAuth2_CompatibilityInterface
+{
+
+} \ No newline at end of file
diff --git a/src/OAuth2/Controller/AuthorizeController.php b/src/OAuth2/Controller/AuthorizeController.php
index 7075c3d..701e886 100644
--- a/src/OAuth2/Controller/AuthorizeController.php
+++ b/src/OAuth2/Controller/AuthorizeController.php
@@ -1,11 +1,10 @@
<?php
/**
- * @see OAuth2_Controller_AuthorizeControllerInterface
+ * @see OAuth2_Controller_AuthorizeControllerInterface
*/
class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_AuthorizeControllerInterface
{
- private $response;
private $clientStorage;
private $responseTypes;
private $config;
@@ -26,7 +25,7 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori
$this->scopeUtil = $scopeUtil;
}
- public function handleAuthorizeRequest(OAuth2_RequestInterface $request, $is_authorized, $user_id = null)
+ public function handleAuthorizeRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response, $is_authorized, $user_id = null)
{
if (!is_bool($is_authorized)) {
throw new InvalidArgumentException('Argument "is_authorized" must be a boolean. This method must know if the user has granted access to the client.');
@@ -34,45 +33,42 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori
// We repeat this, because we need to re-validate. In theory, this could be POSTed
// by a 3rd-party (because we are not internally enforcing NONCEs, etc)
- if (!$params = $this->validateAuthorizeRequest($request)) {
- return $this->response;
+ if (!$params = $this->validateAuthorizeRequest($request, $response)) {
+ return;
}
if ($is_authorized === false) {
- $this->response = new OAuth2_Response_Redirect($params['redirect_uri'], 302, 'access_denied', "The user denied access to your application", $params['state']);
- return $this->response;
+ $response->setRedirect(302, $params['redirect_uri'], $params['state'], 'access_denied', "The user denied access to your application");
+ return;
}
- if (!$authResult = $this->responseTypes[$params['response_type']]->getAuthorizeResponse($params, $user_id)) {
- // an error has occurred along the way, return error response
- return $this->response;
- }
+ $authResult = $this->responseTypes[$params['response_type']]->getAuthorizeResponse($params, $user_id);
- list($redirect_uri, $result) = $authResult;
- $uri = $this->buildUri($redirect_uri, $result);
+ list($redirect_uri, $uri_params) = $authResult;
+ $uri = $this->buildUri($redirect_uri, $uri_params);
// return redirect response
- return $this->response = new OAuth2_Response_Redirect($uri);
+ $response->setRedirect(302, $uri);
}
- public function validateAuthorizeRequest(OAuth2_RequestInterface $request)
+ public function validateAuthorizeRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
// Make sure a valid client id was supplied (we can not redirect because we were unable to verify the URI)
if (!$client_id = $request->query("client_id")) {
// We don't have a good URI to use
- $this->response = new OAuth2_Response_Error(400, 'invalid_client', "No client id supplied");
+ $response->setError(400, 'invalid_client', "No client id supplied");
return false;
}
// Get client details
if (!$clientData = $this->clientStorage->getClientDetails($client_id)) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_client', 'The client id supplied is invalid');
+ $response->setError(400, 'invalid_client', 'The client id supplied is invalid');
return false;
}
$clientData += array('redirect_uri' => null); // this should be set. We should create ClientData interface
if ($clientData === false) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_client', "Client id does not exist");
+ $response->setError(400, 'invalid_client', "Client id does not exist");
return false;
}
@@ -81,20 +77,20 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori
// @see http://tools.ietf.org/html/rfc6749#section-4.1.2.1
// @see http://tools.ietf.org/html/rfc6749#section-4.2.2.1
if (!($redirect_uri = $request->query('redirect_uri')) && !($redirect_uri = $clientData['redirect_uri'])) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_uri', 'No redirect URI was supplied or stored');
+ $response->setError(400, 'invalid_uri', 'No redirect URI was supplied or stored');
return false;
}
$parts = parse_url($redirect_uri);
if (isset($parts['fragment']) && $parts['fragment']) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_uri', 'The redirect URI must not contain a fragment');
+ $response->setError(400, 'invalid_uri', 'The redirect URI must not contain a fragment');
return false;
}
// Only need to validate if redirect_uri provided on input and clientData.
if ($clientData["redirect_uri"] && $redirect_uri && !$this->validateRedirectUri($redirect_uri, $clientData["redirect_uri"])) {
- $this->response = new OAuth2_Response_Error(400, 'redirect_uri_mismatch', 'The redirect URI provided is missing or does not match');
+ $response->setError(400, 'redirect_uri_mismatch', 'The redirect URI provided is missing or does not match');
return false;
}
@@ -108,54 +104,54 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori
// type and client_id are required
if (!$response_type || !in_array($response_type, array(self::RESPONSE_TYPE_AUTHORIZATION_CODE, self::RESPONSE_TYPE_ACCESS_TOKEN))) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'invalid_request', 'Invalid or missing response type', $state);
+ $response->setRedirect(302, $redirect_uri, $state, 'invalid_request', 'Invalid or missing response type', null);
return false;
}
if ($response_type == self::RESPONSE_TYPE_AUTHORIZATION_CODE) {
if (!isset($this->responseTypes['code'])) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'unsupported_response_type', 'authorization code grant type not supported', $state);
+ $response->setRedirect(302, $redirect_uri, $state, 'unsupported_response_type', 'authorization code grant type not supported', null);
return false;
}
if (!$this->clientStorage->checkRestrictedGrantType($client_id, 'authorization_code')) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'unauthorized_client', 'The grant type is unauthorized for this client_id', $state);
+ $response->setRedirect(302, $redirect_uri, $state, 'unauthorized_client', 'The grant type is unauthorized for this client_id', null);
return false;
}
if ($this->responseTypes['code']->enforceRedirect() && !$redirect_uri) {
- $this->response = new OAuth2_Response_Error(400, 'redirect_uri_mismatch', 'The redirect URI is mandatory and was not supplied.');
+ $response->setError(400, 'redirect_uri_mismatch', 'The redirect URI is mandatory and was not supplied.');
return false;
}
}
if ($response_type == self::RESPONSE_TYPE_ACCESS_TOKEN) {
if (!$this->config['allow_implicit']) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'unsupported_response_type', 'implicit grant type not supported', $state);
+ $response->setRedirect(302, $redirect_uri, $state, 'unsupported_response_type', 'implicit grant type not supported', null);
return false;
}
if (!$this->clientStorage->checkRestrictedGrantType($client_id, 'implicit')) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'unauthorized_client', 'The grant type is unauthorized for this client_id', $state);
+ $response->setRedirect(302, $redirect_uri, $state, 'unauthorized_client', 'The grant type is unauthorized for this client_id', null);
return false;
}
}
// Validate that the requested scope is supported
if (false === $scope) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'invalid_client', 'This application requires you specify a scope parameter', $state);
+ $response->setRedirect(302, $redirect_uri, $state, 'invalid_client', 'This application requires you specify a scope parameter', null);
return false;
}
if (!is_null($scope) && !$this->scopeUtil->scopeExists($scope, $client_id)) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'invalid_scope', 'An unsupported scope was requested', $state);
+ $response->setRedirect(302, $redirect_uri, $state, 'invalid_scope', 'An unsupported scope was requested', null);
return false;
}
// Validate state parameter exists (if configured to enforce this)
if ($this->config['enforce_state'] && !$state) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'invalid_request', 'The state parameter is required');
+ $response->setRedirect(302, $redirect_uri, null, 'invalid_request', 'The state parameter is required');
return false;
}
// Return retrieved client details together with input
- return array_merge(array('scope' => $scope, 'state' => null), $clientData, $request->getAllQueryParameters(), array('redirect_uri' => $redirect_uri));
+ return array_merge(array('scope' => $scope, 'state' => $state), $clientData, $request->getAllQueryParameters(), array('redirect_uri' => $redirect_uri));
}
/**
@@ -199,19 +195,27 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori
/**
* Internal method for validating redirect URI supplied
+ *
* @param string $inputUri
+ * The submitted URI to be validated
+ *
* @param string $storedUri
+ * The allowed URIs to validate against. Can be a space-delimited string of URIs to
+ * allow for multiple URIs
*/
private function validateRedirectUri($inputUri, $storedUri)
{
if (!$inputUri || !$storedUri) {
return false; // if either one is missing, assume INVALID
}
- return strcasecmp(substr($inputUri, 0, strlen($storedUri)), $storedUri) === 0;
- }
- public function getResponse()
- {
- return $this->response;
+ $stored_uris = explode(' ', $storedUri);
+
+ foreach ($stored_uris as $stored) {
+ if (strcasecmp(substr($inputUri, 0, strlen($stored)), $stored) === 0) {
+ return true;
+ }
+ }
+ return false;
}
}
diff --git a/src/OAuth2/Controller/AuthorizeControllerInterface.php b/src/OAuth2/Controller/AuthorizeControllerInterface.php
index 88c8793..7d9ae68 100644
--- a/src/OAuth2/Controller/AuthorizeControllerInterface.php
+++ b/src/OAuth2/Controller/AuthorizeControllerInterface.php
@@ -9,14 +9,16 @@
* ex:
* > $user_id = $this->somehowDetermineUserId();
* > $is_authorized = $this->somehowDetermineUserAuthorization();
- * > $response = $authorizeController->handleAuthorizeRequest(
+ * > $response = new OAuth2_Response();
+ * > $authorizeController->handleAuthorizeRequest(
* > OAuth2_Request::createFromGlobals(),
+ * > $response,
* > $is_authorized,
* > $user_id);
* > $response->send();
*
*/
-interface OAuth2_Controller_AuthorizeControllerInterface extends OAuth2_Response_ProviderInterface
+interface OAuth2_Controller_AuthorizeControllerInterface
{
/**
* List of possible authentication response types.
@@ -30,7 +32,7 @@ interface OAuth2_Controller_AuthorizeControllerInterface extends OAuth2_Response
const RESPONSE_TYPE_AUTHORIZATION_CODE = 'code';
const RESPONSE_TYPE_ACCESS_TOKEN = 'token';
- public function handleAuthorizeRequest(OAuth2_RequestInterface $request, $is_authorized, $user_id = null);
+ public function handleAuthorizeRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response, $is_authorized, $user_id = null);
- public function validateAuthorizeRequest(OAuth2_RequestInterface $request);
+ public function validateAuthorizeRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response);
}
diff --git a/src/OAuth2/Controller/ResourceController.php b/src/OAuth2/Controller/ResourceController.php
index 4dd827d..017b2cf 100644
--- a/src/OAuth2/Controller/ResourceController.php
+++ b/src/OAuth2/Controller/ResourceController.php
@@ -1,7 +1,7 @@
<?php
/**
- * @see OAuth2_Controller_ResourceControllerInterface
+ * @see OAuth2_Controller_ResourceControllerInterface
*/
class OAuth2_Controller_ResourceController implements OAuth2_Controller_ResourceControllerInterface
{
@@ -26,52 +26,43 @@ class OAuth2_Controller_ResourceController implements OAuth2_Controller_Resource
$this->scopeUtil = $scopeUtil;
}
- public function verifyResourceRequest(OAuth2_RequestInterface $request, $scope = null)
+ public function verifyResourceRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response, $scope = null)
{
- $token_data = $this->getAccessTokenData($request, $scope);
+ $token = $this->getAccessTokenData($request, $response, $scope);
- return (bool) $token_data;
+ // Check scope, if provided
+ // If token doesn't have a scope, it's null/empty, or it's insufficient, then throw an error
+ if ($scope && (!isset($token["scope"]) || !$token["scope"] || !$this->scopeUtil->checkScope($scope, $token["scope"]))) {
+ $response->setError(401, 'insufficient_scope', 'The request requires higher privileges than provided by the access token');
+ $response->addHttpHeaders(array('WWW-Authenticate' => sprintf('%s, realm="%s", scope="%s"', $this->tokenType->getTokenType(), $this->config['www_realm'], $scope)));
+ return false;
+ }
+
+ return (bool) $token;
}
- public function getAccessTokenData(OAuth2_RequestInterface $request, $scope = null)
+ public function getAccessTokenData(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
// Get the token parameter
- $token_param = $this->tokenType->getAccessTokenParameter($request);
+ $token_param = $this->tokenType->getAccessTokenParameter($request, $response);
if (is_null($token_param)) {
- $this->response = $this->tokenType->getResponse();
return null;
}
// Get the stored token data (from the implementing subclass)
- if (!$token = $this->tokenStorage->getAccessToken($token_param)) {
- $this->response = new OAuth2_Response_AuthenticationError(401, 'invalid_grant', 'The access token provided is invalid', $this->tokenType->getTokenType(), $this->config['www_realm'], $scope);
- return null;
- }
-
// Check we have a well formed token
- if (!isset($token["expires"]) || !isset($token["client_id"])) {
- $this->response = new OAuth2_Response_AuthenticationError(401, 'invalid_grant', 'Malformed token (missing "expires" or "client_id")', $this->tokenType->getTokenType(), $this->config['www_realm'], $scope);
- return null;
- }
-
// Check token expiration (expires is a mandatory paramter)
- if (isset($token["expires"]) && time() > $token["expires"]) {
- $this->response = new OAuth2_Response_AuthenticationError(401, 'invalid_grant', 'The access token provided has expired', $this->tokenType->getTokenType(), $this->config['www_realm'], $scope);
- return null;
- }
-
- // Check scope, if provided
- // If token doesn't have a scope, it's null/empty, or it's insufficient, then throw an error
- if ($scope && (!isset($token["scope"]) || !$token["scope"] || !$this->scopeUtil->checkScope($scope, $token["scope"]))) {
- $this->response = new OAuth2_Response_AuthenticationError(401, 'insufficient_scope', 'The request requires higher privileges than provided by the access token', $this->tokenType->getTokenType(), $this->config['www_realm'], $scope);
- return null;
+ if (!$token = $this->tokenStorage->getAccessToken($token_param)) {
+ $response->setError(401, 'invalid_grant', 'The access token provided is invalid', $this->tokenType->getTokenType(), $this->config['www_realm']);
+ } else if (!isset($token["expires"]) || !isset($token["client_id"])) {
+ $response->setError(401, 'invalid_grant', 'Malformed token (missing "expires" or "client_id")', $this->tokenType->getTokenType(), $this->config['www_realm']);
+ } else if (isset($token["expires"]) && time() > $token["expires"]) {
+ $response->setError(401, 'invalid_grant', 'The access token provided has expired');
+ } else {
+ return $token;
}
- return $token;
- }
-
- public function getResponse()
- {
- return $this->response;
+ $response->addHttpHeaders(array('WWW-Authenticate' => sprintf('%s, realm="%"', $this->tokenType->getTokenType(), $this->config['www_realm'])));
+ return null;
}
}
diff --git a/src/OAuth2/Controller/ResourceControllerInterface.php b/src/OAuth2/Controller/ResourceControllerInterface.php
index f98f697..df87338 100644
--- a/src/OAuth2/Controller/ResourceControllerInterface.php
+++ b/src/OAuth2/Controller/ResourceControllerInterface.php
@@ -6,16 +6,16 @@
* contains a valid token.
*
* ex:
- * > if (!$resourceController->verifyResourceRequest(OAuth2_Request::createFromGlobals())) {
- * > $resourceController->getResponse()->send(); // authorization failed
+ * > if (!$resourceController->verifyResourceRequest(OAuth2_Request::createFromGlobals(), $response = new OAuth2_Response())) {
+ * > $response->send(); // authorization failed
* > die();
* > }
* > return json_encode($resource); // valid token! Send the stuff!
*
*/
-interface OAuth2_Controller_ResourceControllerInterface extends OAuth2_Response_ProviderInterface
+interface OAuth2_Controller_ResourceControllerInterface
{
- public function verifyResourceRequest(OAuth2_RequestInterface $request, $scope = null);
+ public function verifyResourceRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response, $scope = null);
- public function getAccessTokenData(OAuth2_RequestInterface $request);
+ public function getAccessTokenData(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response);
}
diff --git a/src/OAuth2/Controller/TokenController.php b/src/OAuth2/Controller/TokenController.php
index f1be5da..1652531 100644
--- a/src/OAuth2/Controller/TokenController.php
+++ b/src/OAuth2/Controller/TokenController.php
@@ -1,7 +1,7 @@
<?php
/**
- * @see OAuth2_Controller_TokenControllerInterface
+ * @see OAuth2_Controller_TokenControllerInterface
*/
class OAuth2_Controller_TokenController implements OAuth2_Controller_TokenControllerInterface
{
@@ -11,14 +11,14 @@ class OAuth2_Controller_TokenController implements OAuth2_Controller_TokenContro
private $grantTypes;
private $scopeUtil;
- public function __construct($clientAssertionType = null, OAuth2_ResponseType_AccessTokenInterface $accessToken, array $grantTypes = array(), OAuth2_ScopeInterface $scopeUtil = null)
+ public function __construct(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $grantTypes = array(), OAuth2_ClientAssertionTypeInterface $clientAssertionType = null, OAuth2_ScopeInterface $scopeUtil = null)
{
- if ($clientAssertionType instanceof OAuth2_Storage_ClientCredentialsInterface) {
- // this is for backwards compatibility
- $clientAssertionType = new OAuth2_ClientAssertionType_HttpBasic($clientAssertionType);
- }
- if (!is_null($clientAssertionType) && !$clientAssertionType instanceof OAuth2_ClientAssertionTypeInterface) {
- throw new LogicException('$clientAssertionType must be an instance of OAuth2_Storage_ClientCredentialsInterface, OAuth2_ClientAssertionTypeInterface, or null');
+ if (is_null($clientAssertionType)) {
+ foreach ($grantTypes as $grantType) {
+ if (!$grantType instanceof OAuth2_ClientAssertionTypeInterface) {
+ throw new InvalidArgumentException('You must supply an instance of OAuth2_ClientAssertionTypeInterface or only use grant types which implement OAuth2_ClientAssertionTypeInterface');
+ }
+ }
}
$this->clientAssertionType = $clientAssertionType;
$this->accessToken = $accessToken;
@@ -32,14 +32,15 @@ class OAuth2_Controller_TokenController implements OAuth2_Controller_TokenContro
$this->scopeUtil = $scopeUtil;
}
- public function handleTokenRequest(OAuth2_RequestInterface $request)
+ public function handleTokenRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
- if ($token = $this->grantAccessToken($request)) {
+ if ($token = $this->grantAccessToken($request, $response)) {
// @see http://tools.ietf.org/html/rfc6749#section-5.1
// server MUST disable caching in headers when tokens are involved
- $this->response = new OAuth2_Response($token, 200, array('Cache-Control' => 'no-store', 'Pragma' => 'no-cache'));
+ $response->setStatusCode(200);
+ $response->addParameters($token);
+ $response->addHttpHeaders(array('Cache-Control' => 'no-store', 'Pragma' => 'no-cache'));
}
- return $this->response;
}
/**
@@ -61,67 +62,71 @@ class OAuth2_Controller_TokenController implements OAuth2_Controller_TokenContro
*
* @ingroup oauth2_section_4
*/
- public function grantAccessToken(OAuth2_RequestInterface $request)
+ public function grantAccessToken(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
if (strtolower($request->server('REQUEST_METHOD')) != 'post') {
- $this->response = new OAuth2_Response_Error(405, 'invalid_request', 'The request method must be POST when requesting an access token', 'http://tools.ietf.org/html/rfc6749#section-3.2');
- $this->response->setHttpHeader( 'Allow', 'POST' );
+ $response->setError(405, 'invalid_request', 'The request method must be POST when requesting an access token', '#section-3.2');
+ $response->addHttpHeaders(array('Allow' => 'POST'));
return null;
}
- // Determine grant type from request
- if (!$grantType = $request->request('grant_type')) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'The grant type was not specified in the request');
+ /* Determine grant type from request
+ * and validate the request for that grant type
+ */
+ if (!$grantTypeIdentifier = $request->request('grant_type')) {
+ $response->setError(400, 'invalid_request', 'The grant type was not specified in the request');
return null;
}
- if (!isset($this->grantTypes[$grantType])) {
+ if (!isset($this->grantTypes[$grantTypeIdentifier])) {
/* TODO: If this is an OAuth2 supported grant type that we have chosen not to implement, throw a 501 Not Implemented instead */
- $this->response = new OAuth2_Response_Error(400, 'unsupported_grant_type', sprintf('Grant type "%s" not supported', $grantType));
- return null;
- }
- $grantType = $this->grantTypes[$grantType];
-
- // Hack to see if clientAssertionType is part of the grant type
- // this should change, but right now changing it will break BC
- $clientAssertionType = $grantType instanceof OAuth2_ClientAssertionTypeInterface ? $grantType : $this->clientAssertionType;
-
- $clientData = $clientAssertionType->getClientDataFromRequest($request);
-
- if (!$clientData || !$clientAssertionType->validateClientData($clientData, $grantType->getQuerystringIdentifier())) {
- $this->response = $this->getObjectResponse($clientAssertionType, new OAuth2_Response_Error(400, 'invalid_request', 'Unable to verify client'));
- return null;
- }
-
- // validate the request for the token
- if (!$grantType->validateRequest($request)) {
- $this->response = $this->getObjectResponse($grantType, new OAuth2_Response_Error(400, 'invalid_request', sprintf('Invalid request for "%s" grant type', $grantType->getQuerystringIdentifier())));
- return null;
- }
-
- if (!$tokenData = $grantType->getTokenDataFromRequest($request)) {
- $this->response = $this->getObjectResponse($grantType, new OAuth2_Response_Error(400, 'invalid_grant', sprintf('Unable to retrieve token for "%s" grant type', $grantType->getQuerystringIdentifier())));
+ $response->setError(400, 'unsupported_grant_type', sprintf('Grant type "%s" not supported', $grantTypeIdentifier));
return null;
}
- if (!$grantType->validateTokenData($tokenData, $clientData)) {
- $this->response = $this->getObjectResponse($grantType, new OAuth2_Response_Error(400, 'invalid_grant', 'Token is no longer valid'));
+ $grantType = $this->grantTypes[$grantTypeIdentifier];
+ if (!$grantType->validateRequest($request, $response)) {
return null;
}
- if (!isset($tokenData["scope"])) {
- $tokenData["scope"] = $this->scopeUtil->getDefaultScope();
- }
-
- $scope = $this->scopeUtil->getScopeFromRequest($request);
- // Check scope, if provided
- if (!is_null($scope) && !$this->scopeUtil->checkScope($scope, $tokenData["scope"])) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_scope', 'An unsupported scope was requested.');
+ /* Retrieve the client information from the request
+ * ClientAssertionTypes allow for grant types which also assert the client data
+ * in which case ClientAssertion is handled in the validateRequest method
+ *
+ * @see OAuth2_GrantType_JWTBearer
+ * @see OAuth2_GrantType_ClientCredentials
+ */
+ if ($grantType instanceof OAuth2_ClientAssertionTypeInterface) {
+ $clientId = $grantType->getClientId();
+ } else {
+ if (!$this->clientAssertionType->validateRequest($request, $response)) {
+ return null;
+ }
+ $clientId = $this->clientAssertionType->getClientId();
+
+ // validate the Client ID (if applicable)
+ if (!is_null($storedClientId = $grantType->getClientId()) && $storedClientId != $clientId) {
+ $response->setError(400, 'invalid_grant', sprintf('%s doesn\'t exist or is invalid for the client', $grantTypeIdentifier));
+ return null;
+ }
+ }
+
+ /*
+ * Validate the scope of the token
+ * If the grant type returns a value for the scope,
+ * this value must be verified with the scope being requested
+ */
+ $availableScope = $grantType->getScope();
+ if (!$requestedScope = $this->scopeUtil->getScopeFromRequest($request)) {
+ $requestedScope = $availableScope ? $availableScope : $this->scopeUtil->getDefaultScope();
+ }
+
+ if (($requestedScope && !$this->scopeUtil->scopeExists($requestedScope, $clientId))
+ || ($availableScope && !$this->scopeUtil->checkScope($requestedScope, $availableScope))) {
+ $response->setError(400, 'invalid_scope', 'An unsupported scope was requested.');
return null;
}
- $tokenData['user_id'] = isset($tokenData['user_id']) ? $tokenData['user_id'] : null;
-
- return $grantType->createAccessToken($this->accessToken, $clientData, $tokenData);
+ return $grantType->createAccessToken($this->accessToken, $clientId, $grantType->getUserId(), $requestedScope);
}
/**
@@ -140,17 +145,4 @@ class OAuth2_Controller_TokenController implements OAuth2_Controller_TokenContro
$this->grantTypes[$identifier] = $grantType;
}
-
- public function getResponse()
- {
- return $this->response;
- }
-
- public function getObjectResponse($object, OAuth2_Response $defaultResponse = null)
- {
- if ($object instanceof OAuth2_Response_ProviderInterface && $response = $object->getResponse()) {
- return $response;
- }
- return $defaultResponse;
- }
}
diff --git a/src/OAuth2/Controller/TokenControllerInterface.php b/src/OAuth2/Controller/TokenControllerInterface.php
index 3af5ac5..4fe9624 100644
--- a/src/OAuth2/Controller/TokenControllerInterface.php
+++ b/src/OAuth2/Controller/TokenControllerInterface.php
@@ -6,11 +6,11 @@
* It also validates the client's credentials
*
* ex:
- * > $response = $tokenController->handleTokenRequest(OAuth2_Request::createFromGlobals());
+ * > $tokenController->handleTokenRequest(OAuth2_Request::createFromGlobals(), $response = new OAuth2_Response());
* > $response->send();
*
*/
-interface OAuth2_Controller_TokenControllerInterface extends OAuth2_Response_ProviderInterface
+interface OAuth2_Controller_TokenControllerInterface
{
/**
* handleTokenRequest
@@ -18,8 +18,11 @@ interface OAuth2_Controller_TokenControllerInterface extends OAuth2_Response_Pro
* @param $request
* OAuth2_RequestInterface - The current http request
*
- **/
- public function handleTokenRequest(OAuth2_RequestInterface $request);
+ * @param $response
+ * OAuth2_ResponseInterface - An instance of OAuth2_ResponseInterface to contain the response data
+ *
+ */
+ public function handleTokenRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response);
- public function grantAccessToken(OAuth2_RequestInterface $request);
+ public function grantAccessToken(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response);
}
diff --git a/src/OAuth2/Encryption/JWT.php b/src/OAuth2/Encryption/JWT.php
index 2398808..16d3102 100644
--- a/src/OAuth2/Encryption/JWT.php
+++ b/src/OAuth2/Encryption/JWT.php
@@ -1,9 +1,9 @@
<?php
+
/**
* @link https://github.com/F21/jwt
* @author F21
*/
-
class OAuth2_Encryption_JWT
{
public function encode($payload, $key, $algo = 'HS256')
@@ -33,23 +33,22 @@ class OAuth2_Encryption_JWT
list($headb64, $payloadb64, $cryptob64) = $tks;
- if (null === ($header = json_decode($this->urlsafeB64Decode($headb64)))){
+ if (null === ($header = json_decode($this->urlsafeB64Decode($headb64), true))) {
return false;
}
- if (null === $payload = json_decode($this->urlsafeB64Decode($payloadb64))){
+ if (null === $payload = json_decode($this->urlsafeB64Decode($payloadb64), true)) {
return false;
}
$sig = $this->urlsafeB64Decode($cryptob64);
if ($verify) {
-
- if (empty($header->alg)) {
+ if (!isset($header['alg'])) {
return false;
}
- if (!$this->verifySignature($sig, "$headb64.$payloadb64", $key, $header->alg)) {
+ if (!$this->verifySignature($sig, "$headb64.$payloadb64", $key, $header['alg'])) {
return false;
}
}
@@ -66,7 +65,7 @@ class OAuth2_Encryption_JWT
return $this->sign($input, $key, $algo) === $signature;
case 'RS256':
- return @openssl_verify($input, $signature, $key, 'sha256') === 1;
+ return openssl_verify($input, $signature, $key, 'sha256') === 1;
case 'RS384':
return @openssl_verify($input, $signature, $key, 'sha384') === 1;
@@ -82,7 +81,6 @@ class OAuth2_Encryption_JWT
private function sign($input, $key, $algo = 'HS256')
{
switch($algo){
-
case 'HS256':
return hash_hmac('sha256', $input, $key, true);
diff --git a/src/OAuth2/GrantType/AuthorizationCode.php b/src/OAuth2/GrantType/AuthorizationCode.php
index 6067c5a..55b8fba 100644
--- a/src/OAuth2/GrantType/AuthorizationCode.php
+++ b/src/OAuth2/GrantType/AuthorizationCode.php
@@ -1,13 +1,13 @@
<?php
/**
-*
-*/
-class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface, OAuth2_Response_ProviderInterface
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
+class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface
{
private $storage;
- private $response;
- private $code;
+ private $authCode;
public function __construct(OAuth2_Storage_AuthorizationCodeInterface $storage)
{
@@ -19,65 +19,68 @@ class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface, O
return 'authorization_code';
}
- public function validateRequest($request)
+ public function validateRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
if (!$request->request('code')) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameter: "code" is required');
+ $response->setError(400, 'invalid_request', 'Missing parameter: "code" is required');
return false;
}
- return true;
- }
-
- public function getTokenDataFromRequest($request)
- {
- $this->code = $request->request('code');
- if (!$tokenData = $this->storage->getAuthorizationCode($this->code)) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Authorization code doesn't exist or is invalid for the client");
- return null;
+ $code = $request->request('code');
+ if (!$authCode = $this->storage->getAuthorizationCode($code)) {
+ $response->setError(400, 'invalid_grant', 'Authorization code doesn\'t exist or is invalid for the client');
+ return false;
}
/*
* 4.1.3 - ensure that the "redirect_uri" parameter is present if the "redirect_uri" parameter was included in the initial authorization request
* @uri - http://tools.ietf.org/html/rfc6749#section-4.1.3
*/
- if (isset($tokenData['redirect_uri']) && $tokenData['redirect_uri']) {
- if (!$request->request('redirect_uri') || urldecode($request->request('redirect_uri')) != $tokenData['redirect_uri']) {
- $this->response = new OAuth2_Response_Error(400, 'redirect_uri_mismatch', "The redirect URI is missing or do not match", "#section-4.1.3");
+ if (isset($authCode['redirect_uri']) && $authCode['redirect_uri']) {
+ if (!$request->request('redirect_uri') || urldecode($request->request('redirect_uri')) != $authCode['redirect_uri']) {
+ $response->setError(400, 'redirect_uri_mismatch', "The redirect URI is missing or do not match", "#section-4.1.3");
return false;
}
}
- return $tokenData;
- }
+ if (!isset($authCode['expires'])) {
+ throw new Exception('Storage must return authcode with a value for "expires"');
+ }
- public function validateTokenData($tokenData, array $clientData)
- {
- // Check the code exists
- if ($tokenData === null || $clientData['client_id'] != $tokenData['client_id']) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Authorization code doesn't exist or is invalid for the client");
+ if ($authCode["expires"] < time()) {
+ $response->setError(400, 'invalid_grant', "The authorization code has expired");
return false;
}
- if ($tokenData["expires"] < time()) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "The authorization code has expired");
- return false;
+ if (!isset($authCode['code'])) {
+ $authCode['code'] = $code; // used to expire the code after the access token is granted
}
- // Scope is validated in the client class
+ $this->authCode = $authCode;
+
return true;
}
- public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData)
+ public function getClientId()
{
- $this->storage->expireAuthorizationCode($this->code);
- $token = $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope']);
+ return $this->authCode['client_id'];
+ }
- return $token;
+ public function getScope()
+ {
+ return isset($this->authCode['scope']) ? $this->authCode['scope'] : null;
}
- public function getResponse()
+ public function getUserId()
{
- return $this->response;
+ return $this->authCode['user_id'];
+ }
+
+ public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
+ {
+ $token = $accessToken->createAccessToken($client_id, $user_id, $scope);
+ $this->storage->expireAuthorizationCode($this->authCode['code']);
+
+ return $token;
}
}
diff --git a/src/OAuth2/GrantType/ClientCredentials.php b/src/OAuth2/GrantType/ClientCredentials.php
index d518414..ab37409 100644
--- a/src/OAuth2/GrantType/ClientCredentials.php
+++ b/src/OAuth2/GrantType/ClientCredentials.php
@@ -1,40 +1,33 @@
<?php
-class OAuth2_GrantType_ClientCredentials implements OAuth2_GrantTypeInterface
+/**
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
+class OAuth2_GrantType_ClientCredentials extends OAuth2_ClientAssertionType_HttpBasic implements OAuth2_GrantTypeInterface
{
public function getQuerystringIdentifier()
{
return 'client_credentials';
}
- public function validateRequest($request)
+ public function getScope()
{
- // This has been done in the server class
- return true;
+ return null;
}
- public function getTokenDataFromRequest($request)
+ public function getUserId()
{
- // the only piece to pull is the "scope" parameter
- $scope = $request->request('scope');
- return array(
- "scope" => $scope
- );
+ return null;
}
- public function validateTokenData($tokenData, array $clientData)
- {
- // Scope is validated in the client class
- return true;
- }
-
- public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData)
+ public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
{
/*
* Client Credentials Grant does NOT include a refresh token
* @see http://tools.ietf.org/html/rfc6749#section-4.4.3
*/
$includeRefreshToken = false;
- return $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope'], $includeRefreshToken);
+ return $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken);
}
}
diff --git a/src/OAuth2/GrantType/JWTBearer.php b/src/OAuth2/GrantType/JWTBearer.php
index 705856a..6ba2521 100644
--- a/src/OAuth2/GrantType/JWTBearer.php
+++ b/src/OAuth2/GrantType/JWTBearer.php
@@ -1,17 +1,19 @@
<?php
/**
-* The JWT bearer authorization grant implements JWT (JSON Web Tokens) as a grant type per the IETF draft.
-* @see http://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-04#section-4
-*/
-class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Response_ProviderInterface, OAuth2_ClientAssertionTypeInterface
+ * The JWT bearer authorization grant implements JWT (JSON Web Tokens) as a grant type per the IETF draft.
+ *
+ * @see http://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-04#section-4
+ *
+ * @author F21
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
+class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_ClientAssertionTypeInterface
{
private $storage;
- private $response;
- private $audience = null;
- private $jwt = null;
- private $undecodedJWT = null;
+ private $audience;
private $jwtUtil;
+ private $jwt;
/**
* Creates an instance of the JWT bearer grant type.
@@ -23,7 +25,7 @@ class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Re
* @param OAuth2_Encryption_JWT OPTIONAL $jwtUtil
* The class used to decode, encode and verify JWTs.
*/
- public function __construct(OAuth2_Storage_JWTBearerInterface $storage, $audience, OAuth2_Encryption_JWT $jwtUtil = null)
+ public function __construct(OAuth2_Storage_JWTBearerInterface $storage, $audience, OAuth2_Encryption_JWT $jwtUtil = null)
{
$this->storage = $storage;
$this->audience = $audience;
@@ -37,7 +39,10 @@ class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Re
/**
* Returns the grant_type get parameter to identify the grant type request as JWT bearer authorization grant.
- * @return The string identifier for grant_type.
+ *
+ * @return
+ * The string identifier for grant_type.
+ *
* @see OAuth2_GrantTypeInterface::getQuerystringIdentifier()
*/
public function getQuerystringIdentifier()
@@ -46,240 +51,137 @@ class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Re
}
/**
- * Validates the request by making share all GET parameters exists.
- * @return TRUE if the request is valid, otherwise FALSE.
- * @see OAuth2_GrantTypeInterface::validateRequest()
+ * Validates the data from the decoded JWT.
+ *
+ * @return
+ * TRUE if the JWT request is valid and can be decoded. Otherwise, FALSE is returned.
+ *
+ * @see OAuth2_GrantTypeInterface::getTokenData()
*/
- public function validateRequest($request)
+ public function validateRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
if (!$request->request("assertion")) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameters: "assertion" required');
-
- return false;
+ $response->setError(400, 'invalid_request', 'Missing parameters: "assertion" required');
+ return null;
}
- return true;
- }
-
- /**
- * Gets the data from the decoded JWT.
- * @return Array containing the token data if the JWT can be decoded. Otherwise, NULL is returned.
- * @see OAuth2_GrantTypeInterface::getTokenDataFromRequest()
- */
- public function getTokenDataFromRequest($request)
- {
-
if (!$request->request("assertion")) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameters: "assertion" required');
-
+ $response->setError(400, 'invalid_request', 'Missing parameters: "assertion" required');
return null;
}
- //Store the undecoded JWT for later use
- $this->undecodedJWT = $request->request('assertion');
+ // Store the undecoded JWT for later use
+ $undecodedJWT = $request->request('assertion');
- //Decode the JWT
+ // Decode the JWT
$jwt = $this->jwtUtil->decode($request->request('assertion'), null, false);
if (!$jwt) {
-
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', "JWT is malformed");
+ $response->setError(400, 'invalid_request', "JWT is malformed");
return null;
}
- $this->jwt = $jwt;
-
- $tokenData = array();
-
- $tokenData['scope'] = $this->getJWTParameter('scope');
- $tokenData['iss'] = $this->getJWTParameter('iss');
- $tokenData['sub'] = $this->getJWTParameter('sub');
- $tokenData['aud'] = $this->getJWTParameter('aud');
- $tokenData['exp'] = $this->getJWTParameter('exp');
- $tokenData['nbf'] = $this->getJWTParameter('nbf');
- $tokenData['iat'] = $this->getJWTParameter('iat');
- $tokenData['jti'] = $this->getJWTParameter('jti');
- $tokenData['typ'] = $this->getJWTParameter('typ');
-
- //Other token data in the claim
- foreach ($this->jwt as $key => $value) {
- if (!array_key_exists($key, $tokenData)) {
- $tokenData[$key] = $value;
- }
- }
-
- return $tokenData;
- }
-
-
- /**
- * Helper function to make it easier to return a JWT parameter.
- * @param string $parameter
- * The JWT parameter to get.
- * @param mixed $default
- * The value to return if the JWT parameter does not exist.
- * @return mixed
- * The JWT parameter.
- */
- private function getJWTParameter($parameter, $default = null)
- {
- return isset($this->jwt->$parameter) ? $this->jwt->$parameter : null;
- }
-
- /**
- * Return the data used to verify the request. For JWT bearer authorization grants, the 'iss' is synonymous to the 'client_id'.
- * The subject is 'sub' and the 'client_secret' is the key to decode the JWT.
- * @return array An array of the client data containing the client_id.
- * @see OAuth2_ClientAssertionTypeInterface::getClientDataFromRequest()
- */
- public function getClientDataFromRequest(OAuth2_RequestInterface $request)
- {
- $tokenData = $this->getTokenDataFromRequest($request);
-
- if (!$tokenData) {
+ // ensure these properties contain a value
+ // @todo: throw malformed error for missing properties
+ $jwt = array_merge(array(
+ 'scope' => null,
+ 'iss' => null,
+ 'sub' => null,
+ 'aud' => null,
+ 'exp' => null,
+ 'nbf' => null,
+ 'iat' => null,
+ 'jti' => null,
+ 'typ' => null,
+ ), $jwt);
+
+ if (!isset($jwt['iss'])) {
+ $response->setError(400, 'invalid_grant', "Invalid issuer (iss) provided");
return null;
}
- if (!isset($tokenData['iss'])) {
-
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Invalid issuer (iss) provided");
-
+ if (!isset($jwt['sub'])) {
+ $response->setError(400, 'invalid_grant', "Invalid subject (sub) provided");
return null;
}
- //Get the iss's public key (http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06#section-4.1.1)
- $key = $this->storage->getClientKey($tokenData['iss'], $tokenData['sub']);
-
- if (!$key) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Invalid issuer (iss) or subject (sub) provided");
-
+ if (!isset($jwt['exp'])) {
+ $response->setError(400, 'invalid_grant', "Expiration (exp) time must be present");
return null;
}
- return array('client_id' => $tokenData['iss'], 'subject' => $tokenData['sub'], 'client_secret' => $key);
- }
-
- /**
- * Validates the client data by checking if the client_id exists in storage. It also checks to see if the client_id is associated with a key to decode the JWT.
- * @see OAuth2_ClientAssertionTypeInterface::validateClientData()
- */
- public function validateClientData(array $clientData, $grantTypeIdentifier)
- {
-
- //Check that all array keys exist
- $diff = array_diff(array_keys($clientData), array('client_id', 'subject', 'client_secret'));
-
- if (!empty($diff)) {
-
- throw new DomainException('The clientData array is missing one or more of the following: client_id, subject, client_secret');
-
- return false;
- }
-
- foreach ($clientData as $key => $value) {
-
- if (in_array($key, array('client_id', 'client_secret'))) {
-
- if (!isset($value)) {
- throw new LogicException('client_id and client_secret in the clientData array may not be null');
-
- return false;
- }
- }
- }
-
- return true;
- }
-
- /**
- * Validates the token data using the rules in the IETF draft.
- * @see http://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-04#section-3
- * @see OAuth2_GrantTypeInterface::validateTokenData()
- */
- public function validateTokenData($tokenData, array $clientData)
- {
- // Note: Scope is validated in the client class
-
- //Check the expiry time
- $expiration = $tokenData['exp'];
-
- if (ctype_digit($expiration)) {
-
- if ($expiration <= time()) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "JWT has expired");
-
- return false;
+ // Check expiration
+ if (ctype_digit($jwt['exp'])) {
+ if ($jwt['exp'] <= time()) {
+ $response->setError(400, 'invalid_grant', "JWT has expired");
+ return null;
}
-
- } elseif (!$expiration) {
-
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Expiration (exp) time must be present");
-
- return false;
-
} else {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Expiration (exp) time must be a unix time stamp");
-
- return false;
+ $response->setError(400, 'invalid_grant', "Expiration (exp) time must be a unix time stamp");
+ return null;
}
-
- //Check the not before time
- if ($notBefore = $tokenData['nbf']) {
-
+ // Check the not before time
+ if ($notBefore = $jwt['nbf']) {
if (ctype_digit($notBefore)) {
-
if ($notBefore > time()) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "JWT cannot be used before the Not Before (nbf) time");
-
- return false;
+ $response->setError(400, 'invalid_grant', "JWT cannot be used before the Not Before (nbf) time");
+ return null;
}
-
} else {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Not Before (nbf) time must be a unix time stamp");
-
- return false;
+ $response->setError(400, 'invalid_grant', "Not Before (nbf) time must be a unix time stamp");
+ return null;
}
}
- //Check the audience if required to match
- $aud = $tokenData['aud'];
- if (!isset($aud) || ($aud != $this->audience)) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Invalid audience (aud)");
-
- return false;
+ // Check the audience if required to match
+ if (!isset($jwt['aud']) || ($jwt['aud'] != $this->audience)) {
+ $response->setError(400, 'invalid_grant', "Invalid audience (aud)");
+ return null;
}
- //Verify the JWT
- $jwt = $this->jwtUtil->decode($this->undecodedJWT, $clientData['client_secret'], true);
-
- if (!$jwt) {
+ // Get the iss's public key
+ // @see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06#section-4.1.1
+ if (!$key = $this->storage->getClientKey($jwt['iss'], $jwt['sub'])) {
+ $response->setError(400, 'invalid_grant', "Invalid issuer (iss) or subject (sub) provided");
+ return null;
+ }
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "JWT failed signature verification");
+ // Verify the JWT
+ if (!$this->jwtUtil->decode($undecodedJWT, $key, true)) {
+ $response->setError(400, 'invalid_grant', "JWT failed signature verification");
return null;
}
+ $this->jwt = $jwt;
+
return true;
}
- /**
- * Creates an access token that is NOT associated with a refresh token.
- * If a subject (sub) the name of the user/account we are accessing data on behalf of.
- * @see OAuth2_GrantTypeInterface::createAccessToken()
- */
- public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData)
+ public function getClientId()
{
- $includeRefreshToken = false;
+ return $this->jwt['iss'];
+ }
+
+ public function getUserId()
+ {
+ return $this->jwt['sub'];
+ }
- return $accessToken->createAccessToken($clientData['client_id'], $tokenData['sub'], $tokenData['scope'], $includeRefreshToken);
+ public function getScope()
+ {
+ return $this->jwt['scope'];
}
/**
- * Returns the response of this grant type.
- * @see OAuth2_Response_ProviderInterface::getResponse()
+ * Creates an access token that is NOT associated with a refresh token.
+ * If a subject (sub) the name of the user/account we are accessing data on behalf of.
+ *
+ * @see OAuth2_GrantTypeInterface::createAccessToken()
*/
- public function getResponse()
+ public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
{
- return $this->response;
+ $includeRefreshToken = false;
+ return $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken);
}
}
diff --git a/src/OAuth2/GrantType/RefreshToken.php b/src/OAuth2/GrantType/RefreshToken.php
index 2f5336a..f98ed84 100644
--- a/src/OAuth2/GrantType/RefreshToken.php
+++ b/src/OAuth2/GrantType/RefreshToken.php
@@ -1,14 +1,14 @@
<?php
/**
-*
-*/
-class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface, OAuth2_Response_ProviderInterface
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
+class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface
{
private $storage;
- private $response;
private $config;
- private $oldRefreshToken;
+ private $refreshToken;
public function __construct(OAuth2_Storage_RefreshTokenInterface $storage, $config = array())
{
@@ -23,45 +23,45 @@ class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface, OAuth2
return 'refresh_token';
}
- public function validateRequest($request)
+ public function validateRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
if (!$request->request("refresh_token")) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameter: "refresh_token" is required');
- return false;
+ $response->setError(400, 'invalid_request', 'Missing parameter: "refresh_token" is required');
+ return null;
}
- return true;
- }
+ if (!$refreshToken = $this->storage->getRefreshToken($request->request("refresh_token"))) {
+ $response->setError(400, 'invalid_grant', 'Invalid refresh token');
+ return null;
+ }
- public function getTokenDataFromRequest($request)
- {
- if (!$stored = $this->storage->getRefreshToken($request->request("refresh_token"))) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Invalid refresh token');
- return false;
+ if ($refreshToken["expires"] < time()) {
+ $response->setError(400, 'invalid_grant', 'Refresh token has expired');
+ return null;
}
- return $stored;
+ // store the refresh token locally so we can delete it when a new refresh token is generated
+ $this->refreshToken = $refreshToken;
+
+ return true;
}
- public function validateTokenData($tokenData, array $clientData)
+ public function getClientId()
{
- if ($tokenData === null || $clientData['client_id'] != $tokenData["client_id"]) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Invalid refresh token');
- return false;
- }
-
- if ($tokenData["expires"] < time()) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Refresh token has expired');
- return false;
- }
+ return $this->refreshToken['client_id'];
+ }
- // store the refresh token locally so we can delete it when a new refresh token is generated
- $this->oldRefreshToken = $tokenData["refresh_token"];
+ public function getUserId()
+ {
+ return $this->refreshToken['user_id'];
+ }
- return true;
+ public function getScope()
+ {
+ return $this->refreshToken['scope'];
}
- public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData)
+ public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
{
/*
* It is optional to force a new refresh token when a refresh token is used.
@@ -69,17 +69,12 @@ class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface, OAuth2
* @see http://tools.ietf.org/html/rfc6749#section-6
*/
$issueNewRefreshToken = $this->config['always_issue_new_refresh_token'];
- $token = $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope'], $issueNewRefreshToken);
+ $token = $accessToken->createAccessToken($client_id, $user_id, $scope, $issueNewRefreshToken);
if ($issueNewRefreshToken) {
- $this->storage->unsetRefreshToken($this->oldRefreshToken);
+ $this->storage->unsetRefreshToken($this->refreshToken['refresh_token']);
}
return $token;
}
-
- public function getResponse()
- {
- return $this->response;
- }
}
diff --git a/src/OAuth2/GrantType/UserCredentials.php b/src/OAuth2/GrantType/UserCredentials.php
index 0d4638e..c41e8db 100644
--- a/src/OAuth2/GrantType/UserCredentials.php
+++ b/src/OAuth2/GrantType/UserCredentials.php
@@ -1,12 +1,13 @@
<?php
/**
-*
-*/
-class OAuth2_GrantType_UserCredentials implements OAuth2_GrantTypeInterface, OAuth2_Response_ProviderInterface
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
+class OAuth2_GrantType_UserCredentials implements OAuth2_GrantTypeInterface
{
private $storage;
- private $response;
+ private $userInfo;
public function __construct(OAuth2_Storage_UserCredentialsInterface $storage)
{
@@ -18,47 +19,48 @@ class OAuth2_GrantType_UserCredentials implements OAuth2_GrantTypeInterface, OAu
return 'password';
}
- public function validateRequest($request)
+ public function validateRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
if (!$request->request("password") || !$request->request("username")) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameters: "username" and "password" required');
- return false;
+ $response->setError(400, 'invalid_request', 'Missing parameters: "username" and "password" required');
+ return null;
}
- return true;
- }
-
- public function getTokenDataFromRequest($request)
- {
if (!$this->storage->checkUserCredentials($request->request("username"), $request->request("password"))) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Invalid username and password combination');
- return false;
+ $response->setError(400, 'invalid_grant', 'Invalid username and password combination');
+ return null;
}
- $tokenData = $this->storage->getUserDetails($request->request("username"));
+ $userInfo = $this->storage->getUserDetails($request->request("username"));
- // tokenData can be an empty array
- if (false === $tokenData || is_null($tokenData)) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Unable to retrieve user information');
- return false;
+ // userInfo can be an empty array
+ if (false === $userInfo || is_null($userInfo)) {
+ $response->setError(400, 'invalid_grant', 'Unable to retrieve user information');
+ return null;
}
- return $tokenData;
+ $this->userInfo = $userInfo;
+
+ return true;
}
- public function validateTokenData($tokenData, array $clientData)
+ public function getClientId()
{
- // Scope is validated in the client class
- return true;
+ return null;
+ }
+
+ public function getUserId()
+ {
+ return $this->userInfo['user_id'];
}
- public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData)
+ public function getScope()
{
- return $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope']);
+ return isset($this->userInfo['scope']) ? $this->userInfo['scope'] : null;
}
- public function getResponse()
+ public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
{
- return $this->response;
+ return $accessToken->createAccessToken($client_id, $user_id, $scope);
}
}
diff --git a/src/OAuth2/GrantTypeInterface.php b/src/OAuth2/GrantTypeInterface.php
index c029a1d..9bc2327 100644
--- a/src/OAuth2/GrantTypeInterface.php
+++ b/src/OAuth2/GrantTypeInterface.php
@@ -1,10 +1,13 @@
<?php
-interface OAuth2_GrantTypeInterface
+/**
+ * Interface for all OAuth2 Grant Types
+ *
+ * @see OAuth2_CompatibilityInterface
+ */
+interface OAuth2_GrantTypeInterface extends OAuth2_CompatibilityInterface
{
- public function getQuerystringIdentifier();
- public function validateRequest($request);
- public function getTokenDataFromRequest($request);
- public function validateTokenData($tokenData, array $clientData);
- public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData);
+ public function getUserId();
+ public function getScope();
+ public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, $client_id, $user_id, $scope);
}
diff --git a/src/OAuth2/Request.php b/src/OAuth2/Request.php
index 8f2ca3e..063966d 100644
--- a/src/OAuth2/Request.php
+++ b/src/OAuth2/Request.php
@@ -1,10 +1,10 @@
<?php
/**
-* OAuth2_Request
-* This class is taken from the Symfony2 Framework and is part of the Symfony package.
-* See Symfony\Component\HttpFoundation\Request (https://github.com/symfony/symfony)
-*/
+ * OAuth2_Request
+ * This class is taken from the Symfony2 Framework and is part of the Symfony package.
+ * See Symfony\Component\HttpFoundation\Request (https://github.com/symfony/symfony)
+ */
class OAuth2_Request implements OAuth2_RequestInterface
{
public $attributes;
@@ -29,9 +29,9 @@ class OAuth2_Request implements OAuth2_RequestInterface
*
* @api
*/
- public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
+ public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null, array $headers = null)
{
- $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
+ $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content, $headers);
}
/**
@@ -49,7 +49,7 @@ class OAuth2_Request implements OAuth2_RequestInterface
*
* @api
*/
- public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
+ public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null, array $headers = null)
{
$this->request = $request;
$this->query = $query;
@@ -57,7 +57,8 @@ class OAuth2_Request implements OAuth2_RequestInterface
$this->cookies = $cookies;
$this->files = $files;
$this->server = $server;
- $this->headers = $this->getHeadersFromServer($this->server);
+ $this->content = $content;
+ $this->headers = is_null($headers) ? $this->getHeadersFromServer($this->server) : $headers;
}
public function query($name, $default = null)
diff --git a/src/OAuth2/Response.php b/src/OAuth2/Response.php
index bc88f2e..4ef801a 100644
--- a/src/OAuth2/Response.php
+++ b/src/OAuth2/Response.php
@@ -1,9 +1,15 @@
<?php
/**
-*
-*/
-class OAuth2_Response
+ * Class to handle OAuth2 Responses in a graceful way. Use this interface
+ * to output the proper OAuth2 responses.
+ *
+ * @see OAuth2_ResponseInterface
+ *
+ * This class borrows heavily from the Symfony2 Framework and is part of the symfony package
+ * @see Symfony\Component\HttpFoundation\Request (https://github.com/symfony/symfony)
+ */
+class OAuth2_Response implements OAuth2_ResponseInterface
{
public $version;
protected $statusCode = 200;
@@ -119,10 +125,16 @@ class OAuth2_Response
return $this->parameters;
}
- public function setParameters($parameters)
+ public function setParameters(array $parameters)
{
$this->parameters = $parameters;
}
+
+ public function addParameters(array $parameters)
+ {
+ $this->parameters = array_merge($this->parameters, $parameters);
+ }
+
public function getParameter($name, $default = null)
{
return isset($this->parameters[$name]) ? $this->parameters[$name] : $default;
@@ -133,7 +145,7 @@ class OAuth2_Response
$this->parameters[$name] = $value;
}
- public function setHttpHeaders($httpHeaders)
+ public function setHttpHeaders(array $httpHeaders)
{
$this->httpHeaders = $httpHeaders;
}
@@ -143,6 +155,11 @@ class OAuth2_Response
$this->httpHeaders[$name] = $value;
}
+ public function addHttpHeaders(array $httpHeaders)
+ {
+ $this->httpHeaders = array_merge($this->httpHeaders, $httpHeaders);
+ }
+
public function getHttpHeaders()
{
return $this->httpHeaders;
@@ -193,7 +210,67 @@ class OAuth2_Response
echo $this->getResponseBody($format);
}
-// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+ public function setError($statusCode, $error, $errorDescription = null, $errorUri = null)
+ {
+ $parameters = array(
+ 'error' => $error,
+ 'error_description' => $errorDescription,
+ );
+
+ if (!is_null($errorUri)) {
+ if (strlen($errorUri) > 0 && $errorUri[0] == '#') {
+ // we are referencing an oauth bookmark (for brevity)
+ $errorUri = 'http://tools.ietf.org/html/rfc6749' . $errorUri;
+ }
+ $parameters['error_uri'] = $errorUri;
+ }
+
+ $httpHeaders = array(
+ 'Cache-Control' => 'no-store'
+ );
+
+ $this->setStatusCode($statusCode);
+ $this->addParameters($parameters);
+ $this->addHttpHeaders($httpHeaders);
+
+ if (!$this->isClientError() && !$this->isServerError()) {
+ throw new InvalidArgumentException(sprintf('The HTTP status code is not an error ("%s" given).', $statusCode));
+ }
+ }
+
+ public function setRedirect($statusCode = 302, $url, $state = null, $error = null, $errorDescription = null, $errorUri = null)
+ {
+ if (empty($url)) {
+ throw new InvalidArgumentException('Cannot redirect to an empty URL.');
+ }
+
+ $parameters = array();
+
+ if (!is_null($state)) {
+ $parameters['state'] = $state;
+ }
+
+ if (!is_null($error)) {
+ $this->setError(400, $error, $errorDescription, $errorUri);
+ }
+ $this->setStatusCode($statusCode);
+ $this->addParameters($parameters);
+
+ if (count($this->parameters) > 0) {
+ // add parameters to URL redirection
+ $parts = parse_url($url);
+ $sep = isset($parts['query']) && count($parts['query']) > 0 ? '&' : '?';
+ $url .= $sep . http_build_query($this->parameters);
+ }
+
+ $this->addHttpHeaders(array('Location' => $url));
+
+ if (!$this->isRedirection()) {
+ throw new InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $statusCode));
+ }
+ }
+
+ // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
/**
* @return Boolean
*
@@ -262,6 +339,7 @@ class OAuth2_Response
if (count($headers) == 0) {
return '';
}
+
$max = max(array_map('strlen', array_keys($headers))) + 1;
$content = '';
ksort($headers);
diff --git a/src/OAuth2/Response/AuthenticationError.php b/src/OAuth2/Response/AuthenticationError.php
deleted file mode 100644
index dadd4bf..0000000
--- a/src/OAuth2/Response/AuthenticationError.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-/**
-*
-*/
-class OAuth2_Response_AuthenticationError extends OAuth2_Response_Error
-{
- public function __construct($statusCode, $error, $errorDescription, $tokenType, $realm, $scope = null)
- {
- parent::__construct($statusCode, $error, $errorDescription);
- $authHeader = sprintf('%s realm="%s"', $tokenType, $realm);
- if ($scope) {
- $authHeader = sprintf('%s, scope="%s"', $authHeader, $scope);
- }
- $this->setHttpHeader('WWW-Authenticate', $authHeader);
- }
-}
diff --git a/src/OAuth2/Response/Error.php b/src/OAuth2/Response/Error.php
deleted file mode 100644
index 305abd4..0000000
--- a/src/OAuth2/Response/Error.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-/**
-*
-*/
-class OAuth2_Response_Error extends OAuth2_Response
-{
- public function __construct($statusCode, $error, $errorDescription, $errorUri = null)
- {
- $parameters = array(
- 'error' => $error,
- 'error_description' => $errorDescription,
- );
-
- if (!is_null($errorUri)) {
- if (strlen($errorUri) > 0 && $errorUri[0] == '#') {
- // we are referencing an oauth bookmark (for brevity)
- $errorUri = 'http://tools.ietf.org/html/rfc6749' . $errorUri;
- }
- $parameters['error_uri'] = $errorUri;
- }
-
- $httpHeaders = array(
- 'Cache-Control' => 'no-store'
- );
-
- parent::__construct($parameters, $statusCode, $httpHeaders);
-
- if (!$this->isClientError() && !$this->isServerError()) {
- throw new InvalidArgumentException(sprintf('The HTTP status code is not an error ("%s" given).', $statusCode));
- }
- }
-
- public function getError()
- {
- return $this->parameters['error'];
- }
-
- public function getErrorDescription()
- {
- return $this->parameters['error_description'];
- }
-
- public function getErrorUri()
- {
- return $this->parameters['error_uri'];
- }
-}
diff --git a/src/OAuth2/Response/ProviderInterface.php b/src/OAuth2/Response/ProviderInterface.php
deleted file mode 100644
index 9b147c0..0000000
--- a/src/OAuth2/Response/ProviderInterface.php
+++ /dev/null
@@ -1,6 +0,0 @@
-<?php
-
-interface OAuth2_Response_ProviderInterface
-{
- public function getResponse();
-}
diff --git a/src/OAuth2/Response/Redirect.php b/src/OAuth2/Response/Redirect.php
deleted file mode 100644
index 86ec291..0000000
--- a/src/OAuth2/Response/Redirect.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
-*
-*/
-class OAuth2_Response_Redirect extends OAuth2_Response
-{
- public function __construct($url, $statusCode = 302, $error = null, $errorDescription = null, $state = null)
- {
- if (empty($url)) {
- throw new InvalidArgumentException('Cannot redirect to an empty URL.');
- }
-
- $query = array();
-
- if (!is_null($error)) {
- $query['error'] = $error;
- }
-
- if (!is_null($errorDescription)) {
- $query['error_description'] = $errorDescription;
- }
-
- if (!is_null($state)) {
- $query['state'] = $state;
- }
-
- if (count($query) > 0) {
- $parts = parse_url($url);
- $sep = isset($parts['query']) && count($parts['query']) > 0 ? '&' : '?';
- $url = $url . $sep . http_build_query($query);
- }
-
- $httpHeaders = array(
- 'Location' => $url,
- );
-
- parent::__construct(array(), $statusCode, $httpHeaders);
-
- if (!$this->isRedirection()) {
- throw new InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $statusCode));
- }
- }
-}
diff --git a/src/OAuth2/ResponseInterface.php b/src/OAuth2/ResponseInterface.php
new file mode 100644
index 0000000..4aa32cd
--- /dev/null
+++ b/src/OAuth2/ResponseInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * Interface which represents an object response. Meant to handle and display the proper OAuth2 Responses
+ * for errors and successes
+ *
+ * @see OAuth2_Response
+ */
+interface OAuth2_ResponseInterface
+{
+ public function addParameters(array $parameters);
+
+ public function addHttpHeaders(array $httpHeaders);
+
+ public function setStatusCode($statusCode);
+
+ public function setError($statusCode, $name, $description = null, $uri = null);
+
+ public function setRedirect($statusCode = 302, $url, $state = null, $error = null, $errorDescription = null, $errorUri = null);
+} \ No newline at end of file
diff --git a/src/OAuth2/ResponseType/AccessToken.php b/src/OAuth2/ResponseType/AccessToken.php
index 30beaf6..b92ead9 100644
--- a/src/OAuth2/ResponseType/AccessToken.php
+++ b/src/OAuth2/ResponseType/AccessToken.php
@@ -1,8 +1,9 @@
<?php
/**
-*
-*/
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
class OAuth2_ResponseType_AccessToken implements OAuth2_ResponseType_AccessTokenInterface
{
private $tokenStorage;
@@ -20,7 +21,6 @@ class OAuth2_ResponseType_AccessToken implements OAuth2_ResponseType_AccessToken
), $config);
}
- // same params as above
public function getAuthorizeResponse($params, $user_id = null)
{
// build the URL to redirect to
@@ -44,17 +44,16 @@ class OAuth2_ResponseType_AccessToken implements OAuth2_ResponseType_AccessToken
}
/**
- * Handle the creation of access token, also issue refresh token if support.
- *
- * This belongs in a separate factory, but to keep it simple, I'm just
- * keeping it here.
+ * Handle the creation of access token, also issue refresh token if supported / desirable.
*
* @param $client_id
* Client identifier related to the access token.
+ * @param $user_id
+ * User ID associated with the access token
* @param $scope
* (optional) Scopes to be stored in space-separated string.
- * @param bool $excludeRefreshToken
- * If true, the refresh_token will be omitted from the response
+ * @param bool $includeRefreshToken
+ * If true, a new refresh_token will be added to the response
*
* @see http://tools.ietf.org/html/rfc6749#section-5
* @ingroup oauth2_section_5
diff --git a/src/OAuth2/ResponseType/AccessTokenInterface.php b/src/OAuth2/ResponseType/AccessTokenInterface.php
index d362893..19180b9 100644
--- a/src/OAuth2/ResponseType/AccessTokenInterface.php
+++ b/src/OAuth2/ResponseType/AccessTokenInterface.php
@@ -1,6 +1,25 @@
<?php
+/**
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
interface OAuth2_ResponseType_AccessTokenInterface extends OAuth2_ResponseTypeInterface
{
+ /**
+ * Handle the creation of access token, also issue refresh token if supported / desirable.
+ *
+ * @param $client_id
+ * Client identifier related to the access token.
+ * @param $user_id
+ * User ID associated with the access token
+ * @param $scope
+ * (optional) Scopes to be stored in space-separated string.
+ * @param bool $includeRefreshToken
+ * If true, a new refresh_token will be added to the response
+ *
+ * @see http://tools.ietf.org/html/rfc6749#section-5
+ * @ingroup oauth2_section_5
+ */
public function createAccessToken($client_id, $user_id, $scope = null, $includeRefreshToken = true);
}
diff --git a/src/OAuth2/ResponseType/AuthorizationCode.php b/src/OAuth2/ResponseType/AuthorizationCode.php
index d8a2626..84f2eeb 100644
--- a/src/OAuth2/ResponseType/AuthorizationCode.php
+++ b/src/OAuth2/ResponseType/AuthorizationCode.php
@@ -1,8 +1,9 @@
<?php
/**
-*
-*/
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
class OAuth2_ResponseType_AuthorizationCode implements OAuth2_ResponseType_AuthorizationCodeInterface
{
private $storage;
@@ -17,7 +18,7 @@ class OAuth2_ResponseType_AuthorizationCode implements OAuth2_ResponseType_Autho
), $config);
}
- // same params as above
+
public function getAuthorizeResponse($params, $user_id = null)
{
// build the URL to redirect to
@@ -35,19 +36,19 @@ class OAuth2_ResponseType_AuthorizationCode implements OAuth2_ResponseType_Autho
}
/**
- * Handle the creation of auth code.
- *
- * This belongs in a separate factory, but to keep it simple, I'm just
- * keeping it here.
+ * Handle the creation of the authorization code.
*
* @param $client_id
- * Client identifier related to the access token.
+ * Client identifier related to the authorization code
+ * @param $user_id
+ * User ID associated with the authorization code
* @param $redirect_uri
* An absolute URI to which the authorization server will redirect the
* user-agent to when the end-user authorization step is completed.
* @param $scope
* (optional) Scopes to be stored in space-separated string.
*
+ * @see http://tools.ietf.org/html/rfc6749#section-4
* @ingroup oauth2_section_4
*/
public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null)
@@ -57,6 +58,10 @@ class OAuth2_ResponseType_AuthorizationCode implements OAuth2_ResponseType_Autho
return $code;
}
+ /**
+ * @return
+ * TRUE if the grant type requires a redirect_uri, FALSE if not
+ */
public function enforceRedirect()
{
return $this->config['enforce_redirect'];
diff --git a/src/OAuth2/ResponseType/AuthorizationCodeInterface.php b/src/OAuth2/ResponseType/AuthorizationCodeInterface.php
index f139ae3..6a791b3 100644
--- a/src/OAuth2/ResponseType/AuthorizationCodeInterface.php
+++ b/src/OAuth2/ResponseType/AuthorizationCodeInterface.php
@@ -1,5 +1,9 @@
<?php
+/**
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
interface OAuth2_ResponseType_AuthorizationCodeInterface extends OAuth2_ResponseTypeInterface
{
/**
@@ -8,5 +12,21 @@ interface OAuth2_ResponseType_AuthorizationCodeInterface extends OAuth2_Response
*/
public function enforceRedirect();
+ /**
+ * Handle the creation of the authorization code.
+ *
+ * @param $client_id
+ * Client identifier related to the authorization code
+ * @param $user_id
+ * User ID associated with the authorization code
+ * @param $redirect_uri
+ * An absolute URI to which the authorization server will redirect the
+ * user-agent to when the end-user authorization step is completed.
+ * @param $scope
+ * (optional) Scopes to be stored in space-separated string.
+ *
+ * @see http://tools.ietf.org/html/rfc6749#section-4
+ * @ingroup oauth2_section_4
+ */
public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null);
}
diff --git a/src/OAuth2/ScopeInterface.php b/src/OAuth2/ScopeInterface.php
index 63fb906..bab9ac6 100644
--- a/src/OAuth2/ScopeInterface.php
+++ b/src/OAuth2/ScopeInterface.php
@@ -1,8 +1,10 @@
<?php
/**
-* Class to handle scope implementation logic
-*/
+ * Class to handle scope implementation logic
+ *
+ * @see OAuth2_Storage_ScopeInterface
+ */
interface OAuth2_ScopeInterface extends OAuth2_Storage_ScopeInterface
{
/**
diff --git a/src/OAuth2/Server.php b/src/OAuth2/Server.php
index be115b7..c730607 100644
--- a/src/OAuth2/Server.php
+++ b/src/OAuth2/Server.php
@@ -1,8 +1,9 @@
<?php
/**
-* Service class for OAuth
-* This class serves only to wrap the other Controller classes
+* Server class for OAuth2
+* This class serves as a convience class which wraps the other Controller classes
+*
* @see OAuth2_Controller_ResourceController
* @see OAuth2_Controller_AuthorizeController
* @see OAuth2_Controller_TokenController
@@ -16,15 +17,15 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
protected $storages;
// servers
- protected $resourceController;
protected $authorizeController;
protected $tokenController;
+ protected $resourceController;
// config classes
protected $responseTypes;
protected $grantTypes;
- protected $accessTokenResponseType;
protected $scopeUtil;
+ protected $clientAssertionType;
protected $storageMap = array(
'access_token' => 'OAuth2_Storage_AccessTokenInterface',
@@ -34,6 +35,7 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
'refresh_token' => 'OAuth2_Storage_RefreshTokenInterface',
'user_credentials' => 'OAuth2_Storage_UserCredentialsInterface',
'jwt_bearer' => 'OAuth2_Storage_JWTBearerInterface',
+ 'scope' => 'OAuth2_Storage_ScopeInterface',
);
protected $responseTypeMap = array(
'token' => 'OAuth2_ResponseType_AccessTokenInterface',
@@ -55,8 +57,14 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
* Response types to use. array keys should be "code" and and "token" for
* Access Token and Authorization Code response types
*
- * @param OAuth2_ResponseType_AccessTokenInterface $accessTokenResponseType
- * Response type to use for access token
+ * @param OAuth2_TokenTypeInterface $tokenType
+ * The token type object to use. Valid token types are "bearer" and "mac"
+ *
+ * @param OAuth2_ScopeInterface $scopeUtil
+ * The scope utility class to use to validate scope
+ *
+ * @param OAuth2_ClientAssertionTypeInterface $clientAssertionType
+ * The method in which to verify the client identity. Default is HttpBasic
*
* @return
* TRUE if everything in required scope is contained in available scope,
@@ -66,7 +74,7 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
*
* @ingroup oauth2_section_7
*/
- public function __construct($storage = array(), array $config = array(), array $grantTypes = array(), array $responseTypes = array(), OAuth2_ResponseType_AccessTokenInterface $accessTokenResponseType = null, OAuth2_ScopeInterface $scopeUtil = null)
+ public function __construct($storage = array(), array $config = array(), array $grantTypes = array(), array $responseTypes = array(), OAuth2_TokenTypeInterface $tokenType = null, OAuth2_ScopeInterface $scopeUtil = null, OAuth2_ClientAssertionTypeInterface $clientAssertionType = null)
{
$storage = is_array($storage) ? $storage : array($storage);
$this->storages = array();
@@ -76,7 +84,6 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
// merge all config values. These get passed to our controller objects
$this->config = array_merge(array(
- 'token_type' => 'bearer',
'access_lifetime' => 3600,
'www_realm' => 'Service',
'token_param_name' => 'access_token',
@@ -85,51 +92,21 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
'allow_implicit' => false,
), $config);
- foreach ($responseTypes as $key => $responseType) {
- $this->addResponseType($responseType, $key);
- }
foreach ($grantTypes as $key => $grantType) {
$this->addGrantType($grantType, $key);
}
- $this->accessTokenResponseType = $accessTokenResponseType;
- $this->scopeUtil = $scopeUtil;
- }
-
- public function getResourceController()
- {
- if (is_null($this->resourceController)) {
- if (is_null($this->config['token_type'])) {
- $this->config['token_type'] = 'bearer';
- }
- $tokenType = null;
- if ($this->config['token_type'] == 'bearer') {
- $config = array_intersect_key($this->config, array_flip(explode(' ', 'token_param_name token_bearer_header_name')));
- $tokenType = new OAuth2_TokenType_Bearer($config);
- } elseif ($this->config['token_type'] == 'mac') {
- $tokenType = new OAuth2_TokenType_MAC();
- } else {
- throw new LogicException('unrecognized token type: '.$this->config['token_type']);
- }
- if (!isset($this->storages['access_token'])) {
- throw new LogicException("You must supply a storage object implementing OAuth2_Storage_AccessTokenInterface to use the access server");
- }
- $config = array_intersect_key($this->config, array('www_realm' => ''));
- $this->resourceController = new OAuth2_Controller_ResourceController($tokenType, $this->storages['access_token'], $config, $this->scopeUtil);
+ foreach ($responseTypes as $key => $responseType) {
+ $this->addResponseType($responseType, $key);
}
- return $this->resourceController;
+ $this->tokenType = $tokenType;
+ $this->scopeUtil = $scopeUtil;
+ $this->clientAssertionType = $clientAssertionType;
}
public function getAuthorizeController()
{
if (is_null($this->authorizeController)) {
- if (!isset($this->storages['client'])) {
- throw new LogicException("You must supply a storage object implementing OAuth2_Storage_ClientInterface to use the authorize server");
- }
- if (0 == count($this->responseTypes)) {
- $this->responseTypes = $this->getDefaultResponseTypes();
- }
- $config = array_intersect_key($this->config, array_flip(explode(' ', 'allow_implicit enforce_state')));
- $this->authorizeController = new OAuth2_Controller_AuthorizeController($this->storages['client'], $this->responseTypes, $config, $this->scopeUtil);
+ $this->authorizeController = $this->createDefaultAuthorizeController();
}
return $this->authorizeController;
}
@@ -137,81 +114,41 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
public function getTokenController()
{
if (is_null($this->tokenController)) {
- if (!isset($this->storages['client_credentials'])) {
- throw new LogicException("You must supply a storage object implementing OAuth2_Storage_ClientCredentialsInterface to use the grant server");
- }
- if (is_null($this->accessTokenResponseType)) {
- if (isset($this->responseTypes['access_token'])) {
- $this->accessTokenResponseType = $this->responseTypes['access_token'];
- } else {
- if (!isset($this->storages['access_token'])) {
- throw new LogicException("You must supply a storage object implementing OAuth2_Storage_AccessTokenInterface to use the grant server");
- }
- $refreshStorage = null;
- if (isset($this->storages['refresh_token'])) {
- $refreshStorage = $this->storages['refresh_token'];
- }
- $config = array_intersect_key($this->config, array_flip(explode(' ', 'token_type access_lifetime refresh_token_lifetime')));
- $this->accessTokenResponseType = new OAuth2_ResponseType_AccessToken($this->storages['access_token'], $refreshStorage, $config);
- }
- }
- if (0 == count($this->grantTypes)) {
- $this->grantTypes = $this->getDefaultGrantTypes();
- }
- $this->tokenController = new OAuth2_Controller_TokenController($this->storages['client_credentials'], $this->accessTokenResponseType, $this->grantTypes, $this->scopeUtil);
+ $this->tokenController = $this->createDefaultTokenController();
}
return $this->tokenController;
}
- protected function getDefaultResponseTypes()
+ public function getResourceController()
{
- $responseTypes = array();
- if (isset($this->storages['access_token'])) {
- $refreshStorage = null;
- if (isset($this->storages['refresh_token'])) {
- $refreshStorage = $this->storages['refresh_token'];
- }
- $config = array_intersect_key($this->config, array_flip(explode(' ', 'token_type access_lifetime refresh_token_lifetime')));
- $responseTypes['token'] = new OAuth2_ResponseType_AccessToken($this->storages['access_token'], $refreshStorage, $config);
- }
-
- if (isset($this->storages['authorization_code'])) {
- $config = array_intersect_key($this->config, array_flip(explode(' ', 'enforce_redirect auth_code_lifetime')));
- $responseTypes['code'] = new OAuth2_ResponseType_AuthorizationCode($this->storages['authorization_code'], $config);
- }
-
- if (count($responseTypes) == 0) {
- throw new LogicException("You must supply an array of response_types in the constructor or implement a OAuth2_Storage_AccessTokenInterface or OAuth2_Storage_AuthorizationCodeInterface storage object");
+ if (is_null($this->resourceController)) {
+ $this->resourceController = $this->createDefaultResourceController();
}
-
- return $responseTypes;
+ return $this->resourceController;
}
- protected function getDefaultGrantTypes()
+ /**
+ * every getter deserves a setter
+ */
+ public function setAuthorizeController(OAuth2_Controller_AuthorizeControllerInterface $authorizeController)
{
- $grantTypes = array();
-
- if (isset($this->storages['user_credentials'])) {
- $grantTypes['password'] = new OAuth2_GrantType_UserCredentials($this->storages['user_credentials']);
- }
-
- if (isset($this->storages['client_credentials'])) {
- $grantTypes['client_credentials'] = new OAuth2_GrantType_ClientCredentials($this->storages['client_credentials']);
- }
-
- if (isset($this->storages['refresh_token'])) {
- $grantTypes['refresh_token'] = new OAuth2_GrantType_RefreshToken($this->storages['refresh_token']);
- }
-
- if (isset($this->storages['authorization_code'])) {
- $grantTypes['authorization_code'] = new OAuth2_GrantType_AuthorizationCode($this->storages['authorization_code']);
- }
+ $this->authorizeController = $authorizeController;
+ }
- if (count($grantTypes) == 0) {
- throw new LogicException("Unable to build default grant types - You must supply an array of grant_types in the constructor");
- }
+ /**
+ * every getter deserves a setter
+ */
+ public function setTokenController(OAuth2_Controller_TokenControllerInterface $tokenController)
+ {
+ $this->tokenController = $tokenController;
+ }
- return $grantTypes;
+ /**
+ * every getter deserves a setter
+ */
+ public function setResourceController(OAuth2_Controller_ResourceControllerInterface $resourceController)
+ {
+ $this->resourceController = $resourceController;
}
/**
@@ -234,24 +171,17 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
*
* @ingroup oauth2_section_4
*/
- public function handleTokenRequest(OAuth2_RequestInterface $request)
- {
- $value = $this->getTokenController()->handleTokenRequest($request);
- $this->response = $this->tokenController->getResponse();
- return $value;
- }
-
- public function grantAccessToken(OAuth2_RequestInterface $request)
+ public function handleTokenRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
- $value = $this->getTokenController()->grantAccessToken($request);
- $this->response = $this->tokenController->getResponse();
- return $value;
+ $this->response = is_null($response) ? new OAuth2_Response() : $response;
+ $this->getTokenController()->handleTokenRequest($request, $this->response);
+ return $this->response;
}
- public function getClientCredentials(OAuth2_RequestInterface $request)
+ public function grantAccessToken(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
- $value = $this->getTokenController()->getClientCredentials($request);
- $this->response = $this->tokenController->getResponse();
+ $this->response = is_null($response) ? new OAuth2_Response() : $response;
+ $value = $this->getTokenController()->grantAccessToken($request, $this->response);
return $value;
}
@@ -285,11 +215,11 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
*
* @ingroup oauth2_section_4
*/
- public function handleAuthorizeRequest(OAuth2_RequestInterface $request, $is_authorized, $user_id = null)
+ public function handleAuthorizeRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response, $is_authorized, $user_id = null)
{
- $value = $this->getAuthorizeController()->handleAuthorizeRequest($request, $is_authorized, $user_id);
- $this->response = $this->authorizeController->getResponse();
- return $value;
+ $this->response = is_null($response) ? new OAuth2_Response() : $response;
+ $this->getAuthorizeController()->handleAuthorizeRequest($request, $this->response, $is_authorized, $user_id);
+ return $this->response;
}
/**
@@ -311,24 +241,24 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
*
* @ingroup oauth2_section_3
*/
- public function validateAuthorizeRequest(OAuth2_RequestInterface $request)
+ public function validateAuthorizeRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
- $value = $this->getAuthorizeController()->validateAuthorizeRequest($request);
- $this->response = $this->authorizeController->getResponse();
+ $this->response = is_null($response) ? new OAuth2_Response() : $response;
+ $value = $this->getAuthorizeController()->validateAuthorizeRequest($request, $this->response);
return $value;
}
- public function verifyResourceRequest(OAuth2_RequestInterface $request, $scope = null)
+ public function verifyResourceRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response, $scope = null)
{
- $value = $this->getResourceController()->verifyResourceRequest($request, $scope);
- $this->response = $this->resourceController->getResponse();
+ $this->response = is_null($response) ? new OAuth2_Response() : $response;
+ $value = $this->getResourceController()->verifyResourceRequest($request, $this->response, $scope);
return $value;
}
- public function getAccessTokenData(OAuth2_RequestInterface $request, $scope = null)
+ public function getAccessTokenData(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
- $value = $this->getResourceController()->getAccessTokenData($request, $scope);
- $this->response = $this->resourceController->getResponse();
+ $this->response = is_null($response) ? new OAuth2_Response() : $response;
+ $value = $this->getResourceController()->getAccessTokenData($request, $this->response);
return $value;
}
@@ -346,6 +276,16 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
}
}
+ /**
+ * Set a storage object for the server
+ *
+ * @param $storage
+ * An object implementing one of the Storage interfaces
+ *
+ * @param $key
+ * If null, the storage is set to the key of each storage interface it implements
+ * @see storageMap
+ */
public function addStorage($storage, $key = null)
{
// if explicitly set to a valid key, do not "magically" set below
@@ -358,9 +298,6 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
throw new InvalidArgumentException(sprintf('unknown storage key "%s", must be one of [%s]', $key, implode(', ', array_keys($this->storageMap))));
} else {
$set = false;
- // set a storage object to each key for the interface it represents
- // this means if an object represents more than one storage type, it will be referenced by multiple storage keys
- // ex: OAuth2_Storage_Pdo will be set for all the $storageMap keys
foreach ($this->storageMap as $type => $interface) {
if ($storage instanceof $interface) {
$this->storages[$type] = $storage;
@@ -398,11 +335,140 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
}
}
+ public function getScopeUtil()
+ {
+ if (!$this->scopeUtil) {
+ $storage = isset($this->storages['scope']) ? $this->storages['scope'] : null;
+ $this->scopeUtil = new OAuth2_Scope($storage);
+ }
+ return $this->scopeUtil;
+ }
+
+ /**
+ * every getter deserves a setter
+ */
public function setScopeUtil($scopeUtil)
{
$this->scopeUtil = $scopeUtil;
}
+ protected function createDefaultAuthorizeController()
+ {
+ if (!isset($this->storages['client'])) {
+ throw new LogicException("You must supply a storage object implementing OAuth2_Storage_ClientInterface to use the authorize server");
+ }
+ if (0 == count($this->responseTypes)) {
+ $this->responseTypes = $this->getDefaultResponseTypes();
+ }
+ $config = array_intersect_key($this->config, array_flip(explode(' ', 'allow_implicit enforce_state')));
+ return new OAuth2_Controller_AuthorizeController($this->storages['client'], $this->responseTypes, $config, $this->getScopeUtil());
+ }
+
+ protected function createDefaultTokenController()
+ {
+ if (0 == count($this->grantTypes)) {
+ $this->grantTypes = $this->getDefaultGrantTypes();
+ }
+
+ if (is_null($this->clientAssertionType)) {
+ // see if HttpBasic assertion type is requred. If so, then create it from storage classes.
+ foreach ($this->grantTypes as $grantType) {
+ if (!$grantType instanceof OAuth2_ClientAssertionTypeInterface) {
+ if (!isset($this->storages['client_credentials'])) {
+ throw new LogicException("You must supply a storage object implementing OAuth2_Storage_ClientCredentialsInterface to use the token server");
+ }
+ $this->clientAssertionType = new OAuth2_ClientAssertionType_HttpBasic($this->storages['client_credentials']);
+ break;
+ }
+ }
+ }
+
+ return new OAuth2_Controller_TokenController($this->getAccessTokenResponseType(), $this->grantTypes, $this->clientAssertionType, $this->getScopeUtil());
+ }
+
+ protected function createDefaultResourceController()
+ {
+ if (!isset($this->storages['access_token'])) {
+ throw new LogicException("You must supply a storage object implementing OAuth2_Storage_AccessTokenInterface to use the resource server");
+ }
+ if (!$this->tokenType) {
+ $this->tokenType = $this->getDefaultTokenType();
+ }
+ $config = array_intersect_key($this->config, array('www_realm' => ''));
+ return new OAuth2_Controller_ResourceController($this->tokenType, $this->storages['access_token'], $config, $this->getScopeUtil());
+ }
+
+ protected function getDefaultTokenType()
+ {
+ $config = array_intersect_key($this->config, array_flip(explode(' ', 'token_param_name token_bearer_header_name')));
+ return new OAuth2_TokenType_Bearer($config);
+ }
+
+ protected function getDefaultResponseTypes()
+ {
+ $responseTypes = array();
+
+ if (isset($this->storages['access_token'])) {
+ $responseTypes['token'] = $this->getAccessTokenResponseType();
+ }
+
+ if (isset($this->storages['authorization_code'])) {
+ $config = array_intersect_key($this->config, array_flip(explode(' ', 'enforce_redirect auth_code_lifetime')));
+ $responseTypes['code'] = new OAuth2_ResponseType_AuthorizationCode($this->storages['authorization_code'], $config);
+ }
+
+ if (count($responseTypes) == 0) {
+ throw new LogicException("You must supply an array of response_types in the constructor or implement a OAuth2_Storage_AccessTokenInterface or OAuth2_Storage_AuthorizationCodeInterface storage object");
+ }
+
+ return $responseTypes;
+ }
+
+ protected function getDefaultGrantTypes()
+ {
+ $grantTypes = array();
+
+ if (isset($this->storages['user_credentials'])) {
+ $grantTypes['password'] = new OAuth2_GrantType_UserCredentials($this->storages['user_credentials']);
+ }
+
+ if (isset($this->storages['client_credentials'])) {
+ $grantTypes['client_credentials'] = new OAuth2_GrantType_ClientCredentials($this->storages['client_credentials']);
+ }
+
+ if (isset($this->storages['refresh_token'])) {
+ $grantTypes['refresh_token'] = new OAuth2_GrantType_RefreshToken($this->storages['refresh_token']);
+ }
+
+ if (isset($this->storages['authorization_code'])) {
+ $grantTypes['authorization_code'] = new OAuth2_GrantType_AuthorizationCode($this->storages['authorization_code']);
+ }
+
+ if (count($grantTypes) == 0) {
+ throw new LogicException("Unable to build default grant types - You must supply an array of grant_types in the constructor");
+ }
+
+ return $grantTypes;
+ }
+
+ protected function getAccessTokenResponseType()
+ {
+ if (isset($this->responseTypes['access_token'])) {
+ return $this->responseTypes['access_token'];
+ }
+ if (!isset($this->storages['access_token'])) {
+ throw new LogicException("You must supply a response type implementing OAuth2_ResponseType_AccessTokenInterface, or a storage object implementing OAuth2_Storage_AccessTokenInterface to use the token server");
+ }
+ $refreshStorage = null;
+ if (isset($this->storages['refresh_token'])) {
+ $refreshStorage = $this->storages['refresh_token'];
+ }
+ $config = array_intersect_key($this->config, array_flip(explode(' ', 'access_lifetime refresh_token_lifetime')));
+ $config['token_type'] = $this->tokenType ? $this->tokenType->getTokenType() : $this->getDefaultTokenType()->getTokenType();
+
+ return new OAuth2_ResponseType_AccessToken($this->storages['access_token'], $refreshStorage, $config);
+ }
+
public function getResponse()
{
return $this->response;
diff --git a/src/OAuth2/Storage/AccessTokenInterface.php b/src/OAuth2/Storage/AccessTokenInterface.php
index 4bbad84..6454ab1 100644
--- a/src/OAuth2/Storage/AccessTokenInterface.php
+++ b/src/OAuth2/Storage/AccessTokenInterface.php
@@ -4,7 +4,7 @@
* Implement this interface to specify where the OAuth2 Server
* should get/save access tokens
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_AccessTokenInterface
{
diff --git a/src/OAuth2/Storage/AuthorizationCodeInterface.php b/src/OAuth2/Storage/AuthorizationCodeInterface.php
index 71fa5a8..16a055d 100644
--- a/src/OAuth2/Storage/AuthorizationCodeInterface.php
+++ b/src/OAuth2/Storage/AuthorizationCodeInterface.php
@@ -5,7 +5,7 @@
* should get/save authorization codes for the "Authorization Code"
* grant type
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_AuthorizationCodeInterface
{
diff --git a/src/OAuth2/Storage/ClientCredentialsInterface.php b/src/OAuth2/Storage/ClientCredentialsInterface.php
index 45af123..d66da74 100644
--- a/src/OAuth2/Storage/ClientCredentialsInterface.php
+++ b/src/OAuth2/Storage/ClientCredentialsInterface.php
@@ -4,7 +4,7 @@
* Implement this interface to specify how the OAuth2 Server
* should verify client credentials
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_ClientCredentialsInterface extends OAuth2_Storage_ClientInterface
{
diff --git a/src/OAuth2/Storage/ClientInterface.php b/src/OAuth2/Storage/ClientInterface.php
index 9ca80fe..0f3def5 100644
--- a/src/OAuth2/Storage/ClientInterface.php
+++ b/src/OAuth2/Storage/ClientInterface.php
@@ -4,7 +4,7 @@
* Implement this interface to specify where the OAuth2 Server
* should retrieve client information
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_ClientInterface
{
@@ -18,8 +18,9 @@ interface OAuth2_Storage_ClientInterface
* Client identifier to be check with.
*
* @return array
- * Client details. Only mandatory item is the "registered redirect URI", and MUST
- * return FALSE if the given client does not exist or is invalid.
+ * Client details. The only mandatory key in the array is "redirect_uri".
+ * This function MUST return FALSE if the given client does not exist or is
+ * invalid. "redirect_uri" can be space-delimited to allow for multiple valid uris.
*
* @ingroup oauth2_section_4
*/
diff --git a/src/OAuth2/Storage/JWTBearerInterface.php b/src/OAuth2/Storage/JWTBearerInterface.php
index 65001d1..d1cc86f 100644
--- a/src/OAuth2/Storage/JWTBearerInterface.php
+++ b/src/OAuth2/Storage/JWTBearerInterface.php
@@ -8,11 +8,10 @@
* be the same storage as retrieving clientData
*
* @author F21
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_JWTBearerInterface
{
-
/**
* Get the public key associated with a client_id
*
diff --git a/src/OAuth2/Storage/Memory.php b/src/OAuth2/Storage/Memory.php
index 32bf485..97acd12 100644
--- a/src/OAuth2/Storage/Memory.php
+++ b/src/OAuth2/Storage/Memory.php
@@ -6,7 +6,7 @@
* NOTE: This class should never be used in production, and is
* a stub class for example use only
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface,
OAuth2_Storage_UserCredentialsInterface, OAuth2_Storage_AccessTokenInterface,
@@ -20,6 +20,7 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface
private $accessTokens;
private $jwt;
private $supportedScopes;
+ private $clientSupportedScopes;
private $defaultScope;
public function __construct($params = array())
@@ -32,6 +33,7 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface
'access_tokens' => array(),
'jwt' => array(),
'default_scope' => null,
+ 'client_supported_scopes' => array(),
'supported_scopes' => array(),
), $params);
@@ -42,6 +44,7 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface
$this->accessTokens = $params['access_tokens'];
$this->jwt = $params['jwt'];
$this->supportedScopes = $params['supported_scopes'];
+ $this->clientSupportedScopes = $params['client_supported_scopes'];
$this->defaultScope = $params['default_scope'];
}
@@ -76,14 +79,16 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface
return isset($this->userCredentials[$username]) && $this->userCredentials[$username] === $password;
}
- public function setUserCredentials($user_credentials)
- {
- return isset($this->userCredentials[$username]) ? $this->userCredentials[$username] : null;
- }
-
public function getUserDetails($username)
{
- return $this->getUser($username);
+ if (!isset($this->userCredentials[$username])) {
+ return null;
+ }
+
+ return array(
+ 'user_id' => $username,
+ 'password' => $this->userCredentials[$username],
+ );
}
/* ClientCredentialsInterface */
@@ -151,7 +156,14 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface
public function scopeExists($scope, $client_id = null)
{
$scope = explode(' ', trim($scope));
- return (count(array_diff($scope, $this->supportedScopes)) == 0);
+
+ if (!is_null($client_id) && array_key_exists($client_id, $this->clientSupportedScopes)) {
+ $allowedScopes = array_merge($this->supportedScopes, $this->clientSupportedScopes[$client_id]);
+ } else {
+ $allowedScopes = $this->supportedScopes;
+ }
+
+ return (count(array_diff($scope, $allowedScopes)) == 0);
}
public function getDefaultScope()
@@ -163,11 +175,8 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface
public function getClientKey($client_id, $subject)
{
if (isset($this->jwt[$client_id])) {
-
$jwt = $this->jwt[$client_id];
-
if ($jwt) {
-
if ($jwt["subject"] == $subject) {
return $jwt["key"];
}
diff --git a/src/OAuth2/Storage/Mongo.php b/src/OAuth2/Storage/Mongo.php
index 6fd1182..2a2cdfc 100644
--- a/src/OAuth2/Storage/Mongo.php
+++ b/src/OAuth2/Storage/Mongo.php
@@ -169,9 +169,10 @@ class OAuth2_Storage_Mongo implements OAuth2_Storage_AuthorizationCodeInterface,
public function getUserDetails($username)
{
- $user = $this->getUser($username);
- // User details must have a 'user_id' field, to store in token data for example:
- $user['user_id'] = $user['username'];
+ if ($user = $this->getUser($username)) {
+ $user['user_id'] = $user['username'];
+ }
+
return $user;
}
diff --git a/src/OAuth2/Storage/Pdo.php b/src/OAuth2/Storage/Pdo.php
index ca8bcb5..d45d8e4 100644
--- a/src/OAuth2/Storage/Pdo.php
+++ b/src/OAuth2/Storage/Pdo.php
@@ -3,11 +3,14 @@
/**
* Simple PDO storage for all storage types
*
- * NOTE: This class is meant to get users started
- * quickly. If your application requires further
+ * NOTE: This class is meant to get users started
+ * quickly. If your application requires further
* customization, extend this class or create your own.
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * NOTE: Passwords are stored in plaintext, which is never
+ * a good idea. Be sure to override this for your application
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface,
OAuth2_Storage_AccessTokenInterface, OAuth2_Storage_ClientCredentialsInterface,
@@ -47,7 +50,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface,
), $config);
}
- /* ClientCredentialsInterface */
+ /* OAuth2_Storage_ClientCredentialsInterface */
public function checkClientCredentials($client_id, $client_secret = null)
{
$stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['client_table']));
@@ -77,7 +80,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface,
return true;
}
- /* AccessTokenInterface */
+ /* OAuth2_Storage_AccessTokenInterface */
public function getAccessToken($access_token)
{
$stmt = $this->db->prepare(sprintf('SELECT * from %s where access_token = :access_token', $this->config['access_token_table']));
@@ -105,7 +108,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface,
return $stmt->execute(compact('access_token', 'client_id', 'user_id', 'expires', 'scope'));
}
- /* AuthorizationCodeInterface */
+ /* OAuth2_Storage_AuthorizationCodeInterface */
public function getAuthorizationCode($code)
{
$stmt = $this->db->prepare(sprintf('SELECT * from %s where authorization_code = :code', $this->config['code_table']));
@@ -140,7 +143,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface,
return $stmt->execute(compact('code'));
}
- /* UserCredentialsInterface */
+ /* OAuth2_Storage_UserCredentialsInterface */
public function checkUserCredentials($username, $password)
{
if ($user = $this->getUser($username)) {
@@ -154,7 +157,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface,
return $this->getUser($username);
}
- /* RefreshTokenInterface */
+ /* OAuth2_Storage_RefreshTokenInterface */
public function getRefreshToken($refresh_token)
{
$stmt = $this->db->prepare(sprintf('SELECT * FROM %s WHERE refresh_token = :refresh_token', $this->config['refresh_token_table']));
@@ -209,6 +212,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface,
return $stmt->execute(compact('username', 'password', 'firstName', 'lastName'));
}
+ /* OAuth2_Storage_JWTBearerInterface */
public function getClientKey($client_id, $subject)
{
$stmt = $this->db->prepare($sql = sprintf('SELECT public_key from %s where client_id=:client_id AND subject=:subject', $this->config['jwt_table']));
diff --git a/src/OAuth2/Storage/Redis.php b/src/OAuth2/Storage/Redis.php
new file mode 100644
index 0000000..bce438e
--- /dev/null
+++ b/src/OAuth2/Storage/Redis.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * redis storage for all storage types
+ *
+ * Register client:
+ * <code>
+ * $storage = new OAuth2_Storage_Redis($redis);
+ * $storage->registerClient($client_id, $client_secret, $redirect_uri);
+ * </code>
+ */
+class OAuth2_Storage_Redis implements OAuth2_Storage_AuthorizationCodeInterface,
+ OAuth2_Storage_AccessTokenInterface,
+ OAuth2_Storage_ClientCredentialsInterface,
+ OAuth2_Storage_UserCredentialsInterface,
+ OAuth2_Storage_RefreshTokenInterface,
+ OAuth2_Storage_JWTBearerInterface
+{
+ private $redis;
+ private $config;
+ private $cache;
+
+ /**
+ * Redis Storage!
+ *
+ * @param \Predis\Client $redis
+ * @param array $config
+ */
+ public function __construct($redis, $config=array())
+ {
+ $this->redis = $redis;
+ $this->config = array_merge(array(
+ 'client_key' => 'oauth_clients:',
+ 'access_token_key' => 'oauth_access_tokens:',
+ 'refresh_token_key' => 'oauth_refresh_tokens:',
+ 'code_key' => 'oauth_authorization_codes:',
+ 'user_key' => 'oauth_users:',
+ 'jwt_key' => 'oauth_jwt:',
+ ), $config);
+ }
+
+ protected function getValue($key)
+ {
+ if ( isset($this->cache[$key]) ) {
+ return $this->cache[$key];
+ }
+ $value = $this->redis->get($key);
+ if ( isset($value) ) {
+ return json_decode($value, true);
+ } else {
+ return false;
+ }
+ }
+
+ protected function setValue($key, $value, $expire=0)
+ {
+ $this->cache[$key] = $value;
+ $str = json_encode($value);
+ if ( $expire > 0 ) {
+ $seconds = $expire - time();
+ return $this->redis->setex($key, $seconds, $str);
+ } else {
+ return $this->redis->set($key, $str);
+ }
+ }
+
+ protected function expireValue($key)
+ {
+ unset($this->cache[$key]);
+ return $this->redis->expire($key);
+ }
+
+ /* AuthorizationCodeInterface */
+ public function getAuthorizationCode($code)
+ {
+ return $this->getValue($this->config['code_key'] . $code);
+ }
+
+ public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null)
+ {
+ return $this->setValue(
+ $this->config['code_key'] . $authorization_code,
+ compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope'),
+ $expires
+ );
+ }
+
+ public function expireAuthorizationCode($code)
+ {
+ $key = $this->config['code_key'] . $code;
+ unset($this->cache[$key]);
+ return $this->expireValue($key);
+ }
+
+ /* UserCredentialsInterface */
+ public function checkUserCredentials($username, $password)
+ {
+ $user = $this->getUserDetails($username);
+ return $user && $user['password'] === $password;
+ }
+
+ public function getUserDetails($username)
+ {
+ return $this->getUser($username);
+ }
+
+ public function getUser($username)
+ {
+ return $this->getValue($this->config['user_key'] . $username);
+ }
+
+ public function setUser($username, $password, $first_name = null, $last_name = null)
+ {
+ return $this->setValue(
+ $this->config['user_key'] . $username,
+ compact('username', 'password', 'first_name', 'last_name')
+ );
+ }
+
+ /* ClientCredentialsInterface */
+ public function checkClientCredentials($client_id, $client_secret = null)
+ {
+ $client = $this->getClientDetails($client_id);
+ return isset($client['client_secret'])
+ && $client['client_secret'] == $client_secret;
+ }
+
+ public function getClientDetails($client_id)
+ {
+ return $this->getValue($this->config['client_key'] . $client_id);
+ }
+
+ public function checkRestrictedGrantType($client_id, $grant_type)
+ {
+ $details = $this->getClientDetails($client_id);
+ if (isset($details['grant_types'])) {
+ return in_array($grant_type, (array) $details['grant_types']);
+ }
+
+ // if grant_types are not defined, then none are restricted
+ return true;
+ }
+
+ public function registerClient($client_id, $client_secret, $redirect_uri)
+ {
+ return $this->setValue(
+ $this->config['client_key'] . $client_id,
+ compact('client_id', 'client_secret', 'redirect_uri')
+ );
+ }
+
+ /* RefreshTokenInterface */
+ public function getRefreshToken($refresh_token)
+ {
+ return $this->getValue($this->config['refresh_token_key'] . $refresh_token);
+ }
+
+ public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null)
+ {
+ return $this->setValue(
+ $this->config['refresh_token_key'] . $refresh_token,
+ compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope'),
+ $expires
+ );
+ }
+
+ public function unsetRefreshToken($refresh_token)
+ {
+ return $this->expireValue($this->config['refresh_token_key'] . $refresh_token);
+ }
+
+ /* AccessTokenInterface */
+ public function getAccessToken($access_token)
+ {
+ return $this->getValue($this->config['access_token_key'].$access_token);
+ }
+
+ public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null)
+ {
+ return $this->setValue(
+ $this->config['access_token_key'].$access_token,
+ compact('access_token', 'client_id', 'user_id', 'expires', 'scope'),
+ $expires
+ );
+ }
+
+ /*JWTBearerInterface */
+ public function getClientKey($client_id, $subject)
+ {
+ $jwt = $this->getValue($this->config['jwt_key'] . $client_id);
+ if ( isset($jwt['subject']) && $jwt['subject'] == $subject ) {
+ return $jwt['key'];
+ }
+ return null;
+ }
+}
diff --git a/src/OAuth2/Storage/RefreshTokenInterface.php b/src/OAuth2/Storage/RefreshTokenInterface.php
index eaff39e..b148ecb 100644
--- a/src/OAuth2/Storage/RefreshTokenInterface.php
+++ b/src/OAuth2/Storage/RefreshTokenInterface.php
@@ -5,11 +5,10 @@
* should get/save refresh tokens for the "Refresh Token"
* grant type
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_RefreshTokenInterface
{
-
/**
* Grant refresh access tokens.
*
diff --git a/src/OAuth2/Storage/ScopeInterface.php b/src/OAuth2/Storage/ScopeInterface.php
index 720b74d..5b2caaa 100644
--- a/src/OAuth2/Storage/ScopeInterface.php
+++ b/src/OAuth2/Storage/ScopeInterface.php
@@ -5,7 +5,7 @@
* should retrieve data involving the relevent scopes associated
* with this implementation.
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_ScopeInterface
{
diff --git a/src/OAuth2/Storage/UserCredentialsInterface.php b/src/OAuth2/Storage/UserCredentialsInterface.php
index 5ae184a..ea414d3 100644
--- a/src/OAuth2/Storage/UserCredentialsInterface.php
+++ b/src/OAuth2/Storage/UserCredentialsInterface.php
@@ -5,11 +5,10 @@
* should retrieve user credentials for the
* "Resource Owner Password Credentials" grant type
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_UserCredentialsInterface
{
-
/**
* Grant access tokens for basic user credentials.
*
@@ -47,7 +46,6 @@ interface OAuth2_Storage_UserCredentialsInterface
* @return
* ARRAY the associated "scope" or "user_id" values if applicable, or an empty array
* if this does not apply
- *
*/
public function getUserDetails($username);
}
diff --git a/src/OAuth2/TokenType/Bearer.php b/src/OAuth2/TokenType/Bearer.php
index ad5e344..838286b 100644
--- a/src/OAuth2/TokenType/Bearer.php
+++ b/src/OAuth2/TokenType/Bearer.php
@@ -3,9 +3,8 @@
/**
*
*/
-class OAuth2_TokenType_Bearer implements OAuth2_TokenTypeInterface, OAuth2_Response_ProviderInterface
+class OAuth2_TokenType_Bearer implements OAuth2_TokenTypeInterface
{
- private $response;
private $config;
public function __construct(array $config = array())
@@ -43,25 +42,25 @@ class OAuth2_TokenType_Bearer implements OAuth2_TokenTypeInterface, OAuth2_Respo
* @see http://code.google.com/p/android/issues/detail?id=6684
*
*/
- public function getAccessTokenParameter(OAuth2_RequestInterface $request)
+ public function getAccessTokenParameter(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
$headers = $request->headers('AUTHORIZATION');
// Check that exactly one method was used
$methodsUsed = !empty($headers) + !is_null($request->query($this->config['token_param_name'])) + !is_null($request->request($this->config['token_param_name']));
if ($methodsUsed > 1) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Only one method may be used to authenticate at a time (Auth header, GET or POST)');
+ $response->setError(400, 'invalid_request', 'Only one method may be used to authenticate at a time (Auth header, GET or POST)');
return null;
}
if ($methodsUsed == 0) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'The access token was not found');
+ $response->setError(400, 'invalid_request', 'The access token was not found');
return null;
}
// HEADER: Get the access token from the header
if (!empty($headers)) {
if (!preg_match('/' . $this->config['token_bearer_header_name'] . '\s(\S+)/', $headers, $matches)) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Malformed auth header');
+ $response->setError(400, 'invalid_request', 'Malformed auth header');
return null;
}
return $matches[1];
@@ -70,14 +69,14 @@ class OAuth2_TokenType_Bearer implements OAuth2_TokenTypeInterface, OAuth2_Respo
if ($request->request($this->config['token_param_name'])) {
// POST: Get the token from POST data
if (strtolower($request->server('REQUEST_METHOD')) != 'post') {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'When putting the token in the body, the method must be POST');
+ $response->setError(400, 'invalid_request', 'When putting the token in the body, the method must be POST');
return null;
}
if ($request->server('CONTENT_TYPE') !== null && $request->server('CONTENT_TYPE') != 'application/x-www-form-urlencoded') {
// IETF specifies content-type. NB: Not all webservers populate this _SERVER variable
// @see http://tools.ietf.org/html/rfc6750#section-2.2
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'The content type for POST requests must be "application/x-www-form-urlencoded"');
+ $response->setError(400, 'invalid_request', 'The content type for POST requests must be "application/x-www-form-urlencoded"');
return null;
}
@@ -87,9 +86,4 @@ class OAuth2_TokenType_Bearer implements OAuth2_TokenTypeInterface, OAuth2_Respo
// GET method
return $request->query($this->config['token_param_name']);
}
-
- public function getResponse()
- {
- return $this->response;
- }
}
diff --git a/src/OAuth2/TokenType/MAC.php b/src/OAuth2/TokenType/MAC.php
index c6c8fe3..60427e0 100644
--- a/src/OAuth2/TokenType/MAC.php
+++ b/src/OAuth2/TokenType/MAC.php
@@ -10,7 +10,7 @@ class OAuth2_TokenType_MAC implements OAuth2_TokenTypeInterface
return 'mac';
}
- public function getAccessTokenParameter(OAuth2_RequestInterface $request)
+ public function getAccessTokenParameter(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
throw new LogicException("Not supported");
diff --git a/src/OAuth2/TokenTypeInterface.php b/src/OAuth2/TokenTypeInterface.php
index 8960e9e..fe7cfdb 100644
--- a/src/OAuth2/TokenTypeInterface.php
+++ b/src/OAuth2/TokenTypeInterface.php
@@ -2,6 +2,15 @@
interface OAuth2_TokenTypeInterface
{
+ /**
+ * Token type identification string
+ *
+ * ex: "bearer" or "mac"
+ */
public function getTokenType();
- public function getAccessTokenParameter(OAuth2_RequestInterface $request);
+
+ /**
+ * Retrieves the token string from the request object
+ */
+ public function getAccessTokenParameter(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response);
}