diff options
| author | Brent Shaffer <bshafs@gmail.com> | 2012-12-20 20:09:08 +0400 |
|---|---|---|
| committer | Brent Shaffer <bshafs@gmail.com> | 2012-12-20 20:09:08 +0400 |
| commit | 8cb2360d9d76db082f6db9fbaccf05d1e2dcbb5b (patch) | |
| tree | 5fbe976d7e3a50539a81fc96533579aaefec3677 /src | |
| parent | 5e4d75e3b155c449ebe1d4a734568780d3e57d69 (diff) | |
| parent | 81ceb467b9b6745574a9ece4b3b2bbebf1e7c9b9 (diff) | |
| download | oauth2-server-php-0.2.tar.xz | |
Merge branch 'develop'v0.2
Diffstat (limited to 'src')
24 files changed, 238 insertions, 128 deletions
diff --git a/src/OAuth2/Controller/AccessController.php b/src/OAuth2/Controller/AccessController.php index 8c2449a..883c64a 100644 --- a/src/OAuth2/Controller/AccessController.php +++ b/src/OAuth2/Controller/AccessController.php @@ -25,7 +25,7 @@ class OAuth2_Controller_AccessController implements OAuth2_Controller_AccessCont $this->util = $util; } - public function verifyAccessRequest(OAuth2_Request $request) + public function verifyAccessRequest(OAuth2_RequestInterface $request) { $token = $this->tokenType->getAccessTokenParameter($request); $this->response = $this->tokenType->getResponse(); diff --git a/src/OAuth2/Controller/AccessControllerInterface.php b/src/OAuth2/Controller/AccessControllerInterface.php index ff63b13..a7b77c8 100644 --- a/src/OAuth2/Controller/AccessControllerInterface.php +++ b/src/OAuth2/Controller/AccessControllerInterface.php @@ -2,7 +2,7 @@ interface OAuth2_Controller_AccessControllerInterface extends OAuth2_Response_ProviderInterface { - public function verifyAccessRequest(OAuth2_Request $request); + public function verifyAccessRequest(OAuth2_RequestInterface $request); public function getAccessTokenData($token_param, $scope = null); }
\ No newline at end of file diff --git a/src/OAuth2/Controller/AuthorizeController.php b/src/OAuth2/Controller/AuthorizeController.php index c7603ff..09ff56c 100644 --- a/src/OAuth2/Controller/AuthorizeController.php +++ b/src/OAuth2/Controller/AuthorizeController.php @@ -27,7 +27,7 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori $this->util = $util; } - public function handleAuthorizeRequest(OAuth2_Request $request, $is_authorized, $user_id = null) + public function handleAuthorizeRequest(OAuth2_RequestInterface $request, $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.'); @@ -56,7 +56,7 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori return new OAuth2_Response_Redirect($uri); } - public function validateAuthorizeRequest(OAuth2_Request $request) + public function validateAuthorizeRequest(OAuth2_RequestInterface $request) { // 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")) { @@ -139,7 +139,7 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori } // Return retrieved client details together with input - return ($request->query + $clientData + array('state' => null)); + return ((array)$request->getAllQueryParameters() + $clientData + array('state' => null)); } public function getResponse() diff --git a/src/OAuth2/Controller/AuthorizeControllerInterface.php b/src/OAuth2/Controller/AuthorizeControllerInterface.php index 463ebda..4110297 100644 --- a/src/OAuth2/Controller/AuthorizeControllerInterface.php +++ b/src/OAuth2/Controller/AuthorizeControllerInterface.php @@ -14,7 +14,7 @@ interface OAuth2_Controller_AuthorizeControllerInterface extends OAuth2_Response const RESPONSE_TYPE_AUTHORIZATION_CODE = 'code'; const RESPONSE_TYPE_ACCESS_TOKEN = 'token'; - public function handleAuthorizeRequest(OAuth2_Request $request, $is_authorized, $user_id = null); + public function handleAuthorizeRequest(OAuth2_RequestInterface $request, $is_authorized, $user_id = null); - public function validateAuthorizeRequest(OAuth2_Request $request); + public function validateAuthorizeRequest(OAuth2_RequestInterface $request); }
\ No newline at end of file diff --git a/src/OAuth2/Controller/GrantController.php b/src/OAuth2/Controller/GrantController.php index d22ddd8..0e46802 100644 --- a/src/OAuth2/Controller/GrantController.php +++ b/src/OAuth2/Controller/GrantController.php @@ -15,15 +15,16 @@ class OAuth2_Controller_GrantController implements OAuth2_Controller_GrantContro foreach ($grantTypes as $grantType) { $this->addGrantType($grantType); } + if (is_null($util)) { $util = new OAuth2_Util(); } $this->util = $util; } - public function handleGrantRequest(OAuth2_Request $request, $grantType = null) + public function handleGrantRequest(OAuth2_RequestInterface $request) { - if ($token = $this->grantAccessToken($request, $grantType)) { + if ($token = $this->grantAccessToken($request)) { $this->response = new OAuth2_Response($token); } return $this->response; @@ -32,9 +33,9 @@ class OAuth2_Controller_GrantController implements OAuth2_Controller_GrantContro /** * Grant or deny a requested access token. * This would be called from the "/token" endpoint as defined in the spec. - * Obviously, you can call your endpoint whatever you want. + * You can call your endpoint whatever you want. * - * @param $request - OAuth2_Request + * @param $request - OAuth2_RequestInterface * Request object to grant access token * @param $grantType - mixed * OAuth2_GrantTypeInterface instance or one of the grant types configured in the constructor @@ -48,31 +49,27 @@ class OAuth2_Controller_GrantController implements OAuth2_Controller_GrantContro * * @ingroup oauth2_section_4 */ - public function grantAccessToken(OAuth2_Request $request, $grantType = null) + public function grantAccessToken(OAuth2_RequestInterface $request) { - if (!$grantType instanceof OAuth2_GrantTypeInterface) { - if (is_null($grantType)) { - if (!$grantType = $request->parameter('grant_type')) { - $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'The grant type was not specified in the request'); - return null; - } - } else if (!is_string($grantType)) { - throw new InvalidArgumentException('parameter $grantType must be an instance of OAuth2_GrantTypeInterface, a string representing a configured grant type, or null to pull grant type from request'); - } - if (!isset($this->grantTypes[$grantType])) { - /* 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]; + // Determine grant type from request + if (!($grantType = $request->query('grant_type')) && !($grantType = $request->request('grant_type'))) { + $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'The grant type was not specified in the request'); + return null; } + if (!isset($this->grantTypes[$grantType])) { + /* 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]; + // get and validate client authorization from the request 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. Use getClientCredentials()'); + throw new LogicException('the clientData array must have "client_id" and "client_secret" values set.'); } if ($this->clientStorage->checkClientCredentials($clientData['client_id'], $clientData['client_secret']) === false) { @@ -80,13 +77,12 @@ class OAuth2_Controller_GrantController implements OAuth2_Controller_GrantContro return null; } - if (!$this->clientStorage->checkRestrictedGrantType($clientData['client_id'], $grantType)) { + if (!$this->clientStorage->checkRestrictedGrantType($clientData['client_id'], $grantType->getQuerystringIdentifier())) { $this->response = new OAuth2_Response_Error(400, 'unauthorized_client', 'The grant type is unauthorized for this client_id'); return null; } - /* TODO: Find a better way to handle grantTypes and their responses */ - + // validate the request for the token if (!$grantType->validateRequest($request)) { if ($grantType instanceof OAuth2_Response_ProviderInterface && $response = $grantType->getResponse()) { $this->response = $response; @@ -122,18 +118,14 @@ class OAuth2_Controller_GrantController implements OAuth2_Controller_GrantContro } // Check scope, if provided - if (isset($request->query['scope']) && (!is_array($tokenData) || !isset($tokenData["scope"]) || !$this->util->checkScope($request->query['scope'], $tokenData["scope"]))) { + if (null != $request->query('scope') && (!is_array($tokenData) || !isset($tokenData["scope"]) || !$this->util->checkScope($request->query('scope'), $tokenData["scope"]))) { $this->response = new OAuth2_Response_Error(400, 'invalid_scope', 'An unsupported scope was requested.'); return null; } - $user_id = isset($tokenData['user_id']) ? $tokenData['user_id'] : null; - $token = $this->accessToken->createAccessToken($clientData['client_id'], $user_id, $tokenData['scope']); - - // TODO: Remove this - $grantType->finishGrantRequest($token); + $tokenData['user_id'] = isset($tokenData['user_id']) ? $tokenData['user_id'] : null; - return $token; + return $grantType->createAccessToken($this->accessToken, $clientData, $tokenData); } /** @@ -156,7 +148,7 @@ class OAuth2_Controller_GrantController implements OAuth2_Controller_GrantContro * * @ingroup oauth2_section_2 */ - public function getClientCredentials(OAuth2_Request $request) + public function getClientCredentials(OAuth2_RequestInterface $request) { 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')); @@ -186,7 +178,7 @@ class OAuth2_Controller_GrantController implements OAuth2_Controller_GrantContro public function addGrantType(OAuth2_GrantTypeInterface $grantType, $identifier = null) { if (is_null($identifier) || is_numeric($identifier)) { - $identifier = $grantType->getIdentifier(); + $identifier = $grantType->getQuerystringIdentifier(); } $this->grantTypes[$identifier] = $grantType; diff --git a/src/OAuth2/Controller/GrantControllerInterface.php b/src/OAuth2/Controller/GrantControllerInterface.php index 77fd66f..2bdcfd0 100644 --- a/src/OAuth2/Controller/GrantControllerInterface.php +++ b/src/OAuth2/Controller/GrantControllerInterface.php @@ -2,9 +2,16 @@ interface OAuth2_Controller_GrantControllerInterface extends OAuth2_Response_ProviderInterface { - public function handleGrantRequest(OAuth2_Request $request, $grantType = null); + /** + * handleGrantRequest + * + * @param $request + * OAuth2_RequestInterface - The current http request + * + **/ + public function handleGrantRequest(OAuth2_RequestInterface $request); - public function grantAccessToken(OAuth2_Request $request, $grantType = null); + public function grantAccessToken(OAuth2_RequestInterface $request); - public function getClientCredentials(OAuth2_Request $request); + public function getClientCredentials(OAuth2_RequestInterface $request); }
\ No newline at end of file diff --git a/src/OAuth2/GrantType/AuthorizationCode.php b/src/OAuth2/GrantType/AuthorizationCode.php index 5ae1bd1..2a40b2f 100644 --- a/src/OAuth2/GrantType/AuthorizationCode.php +++ b/src/OAuth2/GrantType/AuthorizationCode.php @@ -13,14 +13,14 @@ class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface, O $this->storage = $storage; } - public function getIdentifier() + public function getQuerystringIdentifier() { return 'authorization_code'; } public function validateRequest($request) { - if (!isset($request->query['code']) || !$request->query['code']) { + if (!$request->query('code')) { $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameter: "code" is required'); return false; } @@ -30,7 +30,7 @@ class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface, O public function getTokenDataFromRequest($request) { - if (!$tokenData = $this->storage->getAuthorizationCode($request->query['code'])) { + if (!$tokenData = $this->storage->getAuthorizationCode($request->query('code'))) { $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Authorization code doesn't exist or is invalid for the client"); return null; } @@ -49,7 +49,7 @@ class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface, O return $tokenData; } - public function validateTokenData(array $tokenData, array $clientData) + public function validateTokenData($tokenData, array $clientData) { // Check the code exists if ($tokenData === null || $clientData['client_id'] != $tokenData['client_id']) { @@ -66,8 +66,10 @@ class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface, O return true; } - public function finishGrantRequest($token) - {} + public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData) + { + return $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope']); + } public function getResponse() { diff --git a/src/OAuth2/GrantType/ClientCredentials.php b/src/OAuth2/GrantType/ClientCredentials.php index 4d47c6b..2dae255 100644 --- a/src/OAuth2/GrantType/ClientCredentials.php +++ b/src/OAuth2/GrantType/ClientCredentials.php @@ -2,7 +2,7 @@ class OAuth2_GrantType_ClientCredentials implements OAuth2_GrantTypeInterface { - public function getIdentifier() + public function getQuerystringIdentifier() { return 'client_credentials'; } @@ -22,12 +22,19 @@ class OAuth2_GrantType_ClientCredentials implements OAuth2_GrantTypeInterface ); } - public function validateTokenData(array $tokenData, array $clientData) + public function validateTokenData($tokenData, array $clientData) { // Scope is validated in the client class return true; } - public function finishGrantRequest($token) - {} + public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData) + { + /* + * Client Credentials Grant does NOT include a refresh token + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.4.3 + */ + $includeRefreshToken = false; + return $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope'], $includeRefreshToken); + } }
\ No newline at end of file diff --git a/src/OAuth2/GrantType/RefreshToken.php b/src/OAuth2/GrantType/RefreshToken.php index 36057fd..5bc9732 100644 --- a/src/OAuth2/GrantType/RefreshToken.php +++ b/src/OAuth2/GrantType/RefreshToken.php @@ -7,21 +7,25 @@ class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface, OAuth2 { private $storage; private $response; + private $config; private $oldRefreshToken; - public function __construct(OAuth2_Storage_RefreshTokenInterface $storage) + public function __construct(OAuth2_Storage_RefreshTokenInterface $storage, $config = array()) { + $this->config = array_merge(array( + 'always_issue_new_refresh_token' => false + ), $config); $this->storage = $storage; } - public function getIdentifier() + public function getQuerystringIdentifier() { return 'refresh_token'; } public function validateRequest($request) { - if (!isset($request->query["refresh_token"]) || !$request->query['refresh_token']) { + if (!$request->query("refresh_token")) { $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameter: "refresh_token" is required'); return false; } @@ -31,7 +35,7 @@ class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface, OAuth2 public function getTokenDataFromRequest($request) { - if (!$stored = $this->storage->getRefreshToken($request->query["refresh_token"])) { + if (!$stored = $this->storage->getRefreshToken($request->query("refresh_token"))) { $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Invalid refresh token'); return false; } @@ -39,7 +43,7 @@ class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface, OAuth2 return $stored; } - public function validateTokenData(array $tokenData, array $clientData) + public function validateTokenData($tokenData, array $clientData) { if ($tokenData === null || $clientData['client_id'] != $tokenData["client_id"]) { $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Invalid refresh token'); @@ -57,9 +61,21 @@ class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface, OAuth2 return true; } - public function finishGrantRequest($token) + public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData) { - $this->storage->unsetRefreshToken($this->oldRefreshToken); + /* + * It is optional to force a new refresh token when a refresh token is used. + * However, if a new refresh token is issued, the old one MUST be expired + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-6 + */ + $issueNewRefreshToken = $this->config['always_issue_new_refresh_token']; + $token = $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope'], $issueNewRefreshToken); + + if ($issueNewRefreshToken) { + $this->storage->unsetRefreshToken($this->oldRefreshToken); + } + + return $token; } public function getResponse() diff --git a/src/OAuth2/GrantType/UserCredentials.php b/src/OAuth2/GrantType/UserCredentials.php index 5a5dabe..51d05aa 100644 --- a/src/OAuth2/GrantType/UserCredentials.php +++ b/src/OAuth2/GrantType/UserCredentials.php @@ -13,14 +13,14 @@ class OAuth2_GrantType_UserCredentials implements OAuth2_GrantTypeInterface, OAu $this->storage = $storage; } - public function getIdentifier() + public function getQuerystringIdentifier() { return 'password'; } public function validateRequest($request) { - if (!isset($request->query["username"]) || !isset($request->query["password"]) || !$request->query["username"] || !$request->query["password"]) { + if (!$request->query("password") || !$request->query("username")) { $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameters: "username" and "password" required'); return false; } @@ -30,22 +30,32 @@ class OAuth2_GrantType_UserCredentials implements OAuth2_GrantTypeInterface, OAu public function getTokenDataFromRequest($request) { - if (!$tokenData = $this->storage->checkUserCredentials($request->query["username"], $request->query["password"])) { + if (!$this->storage->checkUserCredentials($request->query("username"), $request->query("password"))) { $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Invalid username and password combination'); return false; } + $tokenData = $this->storage->getUserDetails($request->query("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; + } + return $tokenData; } - public function validateTokenData(array $tokenData, array $clientData) + public function validateTokenData($tokenData, array $clientData) { // Scope is validated in the client class return true; } - public function finishGrantRequest($token) - {} + public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData) + { + return $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope']); + } public function getResponse() { diff --git a/src/OAuth2/GrantTypeInterface.php b/src/OAuth2/GrantTypeInterface.php index 25146de..0573f9d 100644 --- a/src/OAuth2/GrantTypeInterface.php +++ b/src/OAuth2/GrantTypeInterface.php @@ -2,9 +2,9 @@ interface OAuth2_GrantTypeInterface { - public function getIdentifier(); + public function getQuerystringIdentifier(); public function validateRequest($request); public function getTokenDataFromRequest($request); - public function validateTokenData(array $tokenData, array $clientData); - public function finishGrantRequest($token); + public function validateTokenData($tokenData, array $clientData); + public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData); }
\ No newline at end of file diff --git a/src/OAuth2/Request.php b/src/OAuth2/Request.php index 3f745c9..3ddc3ff 100644 --- a/src/OAuth2/Request.php +++ b/src/OAuth2/Request.php @@ -14,6 +14,7 @@ class OAuth2_Request implements OAuth2_RequestInterface public $files; public $cookies; public $headers; + public $content; /** * Constructor. @@ -79,9 +80,9 @@ class OAuth2_Request implements OAuth2_RequestInterface return isset($this->headers[$name]) ? $this->headers[$name] : $default; } - public function parameter($name, $default = null) + public function getAllQueryParameters() { - return $this->query($name) ? $this->query($name) : $this->request($name, $default); + return $this->query; } /** diff --git a/src/OAuth2/RequestInterface.php b/src/OAuth2/RequestInterface.php index c23eb55..0195605 100644 --- a/src/OAuth2/RequestInterface.php +++ b/src/OAuth2/RequestInterface.php @@ -2,4 +2,13 @@ interface OAuth2_RequestInterface { + public function query($name, $default = null); + + public function request($name, $default = null); + + public function server($name, $default = null); + + public function headers($name, $default = null); + + public function getAllQueryParameters(); }
\ No newline at end of file diff --git a/src/OAuth2/Response/AuthenticationError.php b/src/OAuth2/Response/AuthenticationError.php index 7c2566a..c3c5a62 100644 --- a/src/OAuth2/Response/AuthenticationError.php +++ b/src/OAuth2/Response/AuthenticationError.php @@ -8,7 +8,7 @@ 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('WWW-Authenticate: %s realm=%s', $tokenType, $realm); + $authHeader = sprintf('%s realm=%s', $tokenType, $realm); if ($scope) { $authHeader = sprintf('%s, scope=%s', $authHeader, $scope); } diff --git a/src/OAuth2/ResponseType/AccessToken.php b/src/OAuth2/ResponseType/AccessToken.php index 43efc95..37b5e54 100644 --- a/src/OAuth2/ResponseType/AccessToken.php +++ b/src/OAuth2/ResponseType/AccessToken.php @@ -17,7 +17,7 @@ class OAuth2_ResponseType_AccessToken implements OAuth2_ResponseType_AccessToken 'token_type' => 'bearer', 'access_lifetime' => 3600, 'refresh_token_lifetime' => 1209600, - )); + ), $config); } // same params as above @@ -28,8 +28,13 @@ class OAuth2_ResponseType_AccessToken implements OAuth2_ResponseType_AccessToken $params += array('scope' => null, 'state' => null); - // should this call from a grant type? - $result["fragment"] = $this->createAccessToken($params['client_id'], $user_id, $params['scope']); + /* + * a refresh token MUST NOT be included in the fragment + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.2.2 + */ + $includeRefreshToken = false; + $result["fragment"] = $this->createAccessToken($params['client_id'], $user_id, $params['scope'], $includeRefreshToken); if (isset($params['state'])) { $result["fragment"]["state"] = $params['state']; @@ -48,11 +53,13 @@ class OAuth2_ResponseType_AccessToken implements OAuth2_ResponseType_AccessToken * Client identifier related to 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 * * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5 * @ingroup oauth2_section_5 */ - public function createAccessToken($client_id, $user_id, $scope = null) + public function createAccessToken($client_id, $user_id, $scope = null, $includeRefreshToken = true) { $token = array( "access_token" => $this->generateAccessToken(), @@ -63,9 +70,13 @@ class OAuth2_ResponseType_AccessToken implements OAuth2_ResponseType_AccessToken $this->tokenStorage->setAccessToken($token["access_token"], $client_id, $user_id, $this->config['access_lifetime'] ? time() + $this->config['access_lifetime'] : null, $scope); - // Issue a refresh token also, if we support them - /* TODO: How do we handle this? */ - if ($this->refreshStorage) { + /* + * Issue a refresh token also, if we support them + * + * Refresh Tokens are considered supported if an instance of OAuth2_Storage_RefreshTokenInterface + * is supplied in the constructor + */ + if ($includeRefreshToken && $this->refreshStorage) { $token["refresh_token"] = $this->generateRefreshToken(); $this->refreshStorage->setRefreshToken($token['refresh_token'], $client_id, $user_id, time() + $this->config['refresh_token_lifetime'], $scope); } diff --git a/src/OAuth2/ResponseType/AccessTokenInterface.php b/src/OAuth2/ResponseType/AccessTokenInterface.php index 5247f6f..457c3b8 100644 --- a/src/OAuth2/ResponseType/AccessTokenInterface.php +++ b/src/OAuth2/ResponseType/AccessTokenInterface.php @@ -2,5 +2,5 @@ interface OAuth2_ResponseType_AccessTokenInterface extends OAuth2_ResponseTypeInterface { - public function createAccessToken($client_id, $user_id, $scope = null); + public function createAccessToken($client_id, $user_id, $scope = null, $includeRefreshToken = true); }
\ No newline at end of file diff --git a/src/OAuth2/Server.php b/src/OAuth2/Server.php index bfad2ab..c67057d 100644 --- a/src/OAuth2/Server.php +++ b/src/OAuth2/Server.php @@ -2,7 +2,10 @@ /** * Service class for OAuth -* Inspired by oauth2-php (https://github.com/quizlet/oauth2-php) +* This class serves only to wrap the other Controller classes +* @see OAuth2_Controller_AccessController +* @see OAuth2_Controller_AuthorizeController +* @see OAuth2_Controller_GrantController */ class OAuth2_Server implements OAuth2_Controller_AccessControllerInterface, OAuth2_Controller_AuthorizeControllerInterface, OAuth2_Controller_GrantControllerInterface @@ -27,14 +30,18 @@ class OAuth2_Server implements OAuth2_Controller_AccessControllerInterface, * array - array of Objects to implement storage * OAuth2_Storage object implementing all required storage types (ClientCredentialsInterface and AccessTokenInterface as a minimum) * + * @param array $config + * specify a different token lifetime, token header name, etc + * * @param array $grantTypes * An array of OAuth2_GrantTypeInterface to use for granting access tokens * - * @param array $config - * specify a different token lifetime, token header name, etc + * @param array $responseTypes + * Response types to use. array keys should be "code" and and "token" for + * Access Token and Authorization Code response types * - * @param array $response - * Send in an instance of OAuth2_ResponseInterface to use a different response object + * @param OAuth2_ResponseType_AccessTokenInterface $accessTokenResponseType + * Response type to use for access token * * @return * TRUE if everything in required scope is contained in available scope, @@ -44,7 +51,7 @@ class OAuth2_Server implements OAuth2_Controller_AccessControllerInterface, * * @ingroup oauth2_section_7 */ - public function __construct($storage = array(), array $config = array(), array $grantTypes = array(), array $responseTypes = array(), OAuth2_ResponseType_AccessToken $accessTokenResponseType = null) + public function __construct($storage = array(), array $config = array(), array $grantTypes = array(), array $responseTypes = array(), OAuth2_ResponseType_AccessTokenInterface $accessTokenResponseType = null) { $validStorage = array( 'access_token' => 'OAuth2_Storage_AccessTokenInterface', @@ -147,7 +154,7 @@ class OAuth2_Server implements OAuth2_Controller_AccessControllerInterface, if (isset($this->storages['refresh_token'])) { $refreshStorage = $this->storages['refresh_token']; } - $config = array_intersect_key($this->config, array_flip(explode(' ', 'token_type access_lifetime'))); + $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); } } @@ -167,7 +174,7 @@ class OAuth2_Server implements OAuth2_Controller_AccessControllerInterface, if (isset($this->storages['refresh_token'])) { $refreshStorage = $this->storages['refresh_token']; } - $config = array_intersect_key($this->config, array_flip(explode(' ', 'token_type access_lifetime'))); + $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); } @@ -217,8 +224,9 @@ class OAuth2_Server implements OAuth2_Controller_AccessControllerInterface, * * @param $request - OAuth2_Request * Request object to grant access token - * @param $grantType - mixed - * OAuth2_GrantTypeInterface instance or one of the grant types configured in the constructor + * + * @return + * OAuth_Response * * @throws InvalidArgumentException * @throws LogicException @@ -229,21 +237,21 @@ class OAuth2_Server implements OAuth2_Controller_AccessControllerInterface, * * @ingroup oauth2_section_4 */ - public function handleGrantRequest(OAuth2_Request $request, $grantType = null) + public function handleGrantRequest(OAuth2_RequestInterface $request) { - $value = $this->getGrantController()->handleGrantRequest($request, $grantType); + $value = $this->getGrantController()->handleGrantRequest($request); $this->response = $this->grantController->getResponse(); return $value; } - public function grantAccessToken(OAuth2_Request $request, $grantType = null) + public function grantAccessToken(OAuth2_RequestInterface $request) { - $value = $this->getGrantController()->grantAccessToken($request, $grantType); + $value = $this->getGrantController()->grantAccessToken($request); $this->response = $this->grantController->getResponse(); return $value; } - public function getClientCredentials(OAuth2_Request $request) + public function getClientCredentials(OAuth2_RequestInterface $request) { $value = $this->getGrantController()->getClientCredentials($request); $this->response = $this->grantController->getResponse(); @@ -269,8 +277,10 @@ class OAuth2_Server implements OAuth2_Controller_AccessControllerInterface, * list of space-delimited strings. * - state: (optional) An opaque value used by the client to maintain * state between the request and callback. + * * @param $is_authorized * TRUE or FALSE depending on whether the user authorized the access. + * * @param $user_id * Identifier of user who authorized the client * @@ -278,7 +288,7 @@ class OAuth2_Server implements OAuth2_Controller_AccessControllerInterface, * * @ingroup oauth2_section_4 */ - public function handleAuthorizeRequest(OAuth2_Request $request, $is_authorized, $user_id = null) + public function handleAuthorizeRequest(OAuth2_RequestInterface $request, $is_authorized, $user_id = null) { $value = $this->getAuthorizeController()->handleAuthorizeRequest($request, $is_authorized, $user_id); $this->response = $this->authorizeController->getResponse(); @@ -304,14 +314,14 @@ class OAuth2_Server implements OAuth2_Controller_AccessControllerInterface, * * @ingroup oauth2_section_3 */ - public function validateAuthorizeRequest(OAuth2_Request $request) + public function validateAuthorizeRequest(OAuth2_RequestInterface $request) { $value = $this->getAuthorizeController()->validateAuthorizeRequest($request); $this->response = $this->authorizeController->getResponse(); return $value; } - public function verifyAccessRequest(OAuth2_Request $request) + public function verifyAccessRequest(OAuth2_RequestInterface $request) { $value = $this->getAccessController()->verifyAccessRequest($request); $this->response = $this->accessController->getResponse(); diff --git a/src/OAuth2/Storage/ClientCredentialsInterface.php b/src/OAuth2/Storage/ClientCredentialsInterface.php index 0110c9f..374d628 100644 --- a/src/OAuth2/Storage/ClientCredentialsInterface.php +++ b/src/OAuth2/Storage/ClientCredentialsInterface.php @@ -35,8 +35,7 @@ interface OAuth2_Storage_ClientCredentialsInterface extends OAuth2_Storage_Clien * @param $client_id * Client identifier to be check with. * @param $grant_type - * Grant type to be check with, would be one of the values contained in - * OAuth2::GRANT_TYPE_REGEXP. + * Grant type to be check with * * @return * TRUE if the grant type is supported by this client identifier, and diff --git a/src/OAuth2/Storage/Memory.php b/src/OAuth2/Storage/Memory.php index c986076..ec5d97a 100644 --- a/src/OAuth2/Storage/Memory.php +++ b/src/OAuth2/Storage/Memory.php @@ -58,7 +58,12 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface public function setUserCredentials($user_credentials) { - $this->userCredentials = $user_credentials; + return isset($this->userCredentials[$username]) ? $this->userCredentials[$username] : null; + } + + public function getUserDetails($username) + { + return $this->getUser($username); } /* ClientCredentialsInterface */ diff --git a/src/OAuth2/Storage/Pdo.php b/src/OAuth2/Storage/Pdo.php index e806bed..de659e2 100644 --- a/src/OAuth2/Storage/Pdo.php +++ b/src/OAuth2/Storage/Pdo.php @@ -5,7 +5,7 @@ */ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, OAuth2_Storage_AccessTokenInterface, OAuth2_Storage_ClientCredentialsInterface, - OAuth2_Storage_UserCredentialsInterface + OAuth2_Storage_UserCredentialsInterface, OAuth2_Storage_RefreshTokenInterface { private $db; private $config; @@ -32,17 +32,18 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->config = array_merge(array( - 'client_table_name' => 'oauth_clients', - 'token_table_name' => 'oauth_access_tokens', - 'code_table_name' => 'oauth_authorization_codes', - 'user_table_name' => 'oauth_users', + 'client_table' => 'oauth_clients', + 'access_token_table' => 'oauth_access_tokens', + 'refresh_token_table' => 'oauth_refresh_tokens', + 'code_table' => 'oauth_authorization_codes', + 'user_table' => 'oauth_users', ), $config); } /* ClientCredentialsInterface */ public function checkClientCredentials($client_id, $client_secret = null) { - $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = "%s"', $this->config['client_table_name'], $client_id)); + $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = "%s"', $this->config['client_table'], $client_id)); $stmt->execute(); $result = $stmt->fetch(); @@ -52,7 +53,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, public function getClientDetails($client_id) { - $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = "%s"', $this->config['client_table_name'], $client_id)); + $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = "%s"', $this->config['client_table'], $client_id)); $stmt->execute(); return $stmt->fetch(); @@ -72,7 +73,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, /* AccessTokenInterface */ public function getAccessToken($access_token) { - $stmt = $this->db->prepare(sprintf('SELECT * from %s where access_token = "%s"', $this->config['token_table_name'], $access_token)); + $stmt = $this->db->prepare(sprintf('SELECT * from %s where access_token = "%s"', $this->config['access_token_table'], $access_token)); $token = $stmt->execute(); if ($token = $stmt->fetch()) { @@ -90,9 +91,9 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, // if it exists, update it. if ($this->getAccessToken($access_token)) { - $stmt = $this->db->prepare(sprintf('UPDATE %s SET client_id=:client_id, expires=:expires, user_id=:user_id, scope=:scope where access_token=:access_token', $this->config['token_table_name'])); + $stmt = $this->db->prepare(sprintf('UPDATE %s SET client_id=:client_id, expires=:expires, user_id=:user_id, scope=:scope where access_token=:access_token', $this->config['access_token_table'])); } else { - $stmt = $this->db->prepare(sprintf('INSERT INTO %s (access_token, client_id, expires, user_id, scope) VALUES (:access_token, :client_id, :expires, :user_id, :scope)', $this->config['token_table_name'])); + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (access_token, client_id, expires, user_id, scope) VALUES (:access_token, :client_id, :expires, :user_id, :scope)', $this->config['access_token_table'])); } return $stmt->execute(compact('access_token', 'client_id', 'user_id', 'expires', 'scope')); } @@ -100,7 +101,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, /* AuthorizationCodeInterface */ public function getAuthorizationCode($code) { - $stmt = $this->db->prepare(sprintf('SELECT * from %s where authorization_code = "%s"', $this->config['code_table_name'], $code)); + $stmt = $this->db->prepare(sprintf('SELECT * from %s where authorization_code = "%s"', $this->config['code_table'], $code)); $stmt->execute(); if ($code = $stmt->fetch()) { @@ -118,9 +119,9 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, // if it exists, update it. if ($this->getAuthorizationCode($code)) { - $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_id=:client_id, user_id=:user_id, redirect_uri=:redirect_uri, expires=:expires, scope=:scope where authorization_code=:code', $this->config['code_table_name'])); + $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_id=:client_id, user_id=:user_id, redirect_uri=:redirect_uri, expires=:expires, scope=:scope where authorization_code=:code', $this->config['code_table'])); } else { - $stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope) VALUES (:code, :client_id, :user_id, :redirect_uri, :expires, :scope)', $this->config['code_table_name'])); + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope) VALUES (:code, :client_id, :user_id, :redirect_uri, :expires, :scope)', $this->config['code_table'])); } return $stmt->execute(compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope')); } @@ -134,6 +135,42 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, return false; } + public function getUserDetails($username) + { + return $this->getUser($username); + } + + /* RefreshTokenInterface */ + public function getRefreshToken($refresh_token) + { + $stmt = $this->db->prepare(sprintf('SELECT * FROM %s WHERE refresh_token = "%s"', $this->config['refresh_token_table'], $refresh_token)); + + $token = $stmt->execute(); + if ($token = $stmt->fetch()) { + // convert expires to epoch time + $token['expires'] = strtotime($token['expires']); + } + + return $token; + } + + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = NULL) + { + // convert expires to datestring + $expires = date('Y-m-d H:i:s', $expires); + + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (refresh_token, client_id, user_id, expires, scope) VALUES (:refresh_token, :client_id, :user_id, :expires, :scope)', $this->config['refresh_token_table'])); + + return $stmt->execute(compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope')); + } + + public function unsetRefreshToken($refresh_token) + { + $stmt = $this->db->prepare(sprintf('DELETE FROM %s WHERE refresh_token = "%s"', $this->config['refresh_token_table'], $refresh_token)); + + return $stmt->execute(); + } + // plaintext passwords are bad! Override this for your application protected function checkPassword($user, $password) { @@ -142,7 +179,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, public function getUser($username) { - $stmt = $this->db->prepare($sql = sprintf('SELECT * from %s where username=:username', $this->config['user_table_name'])); + $stmt = $this->db->prepare($sql = sprintf('SELECT * from %s where username=:username', $this->config['user_table'])); $stmt->execute(array('username' => $username)); return $stmt->fetch(); } @@ -151,10 +188,10 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, { // if it exists, update it. if ($this->getUser($username)) { - $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET username=:username, password=:password, first_name=:firstName, last_name=:lastName where username=:username', $this->config['user_table_name'])); + $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET username=:username, password=:password, first_name=:firstName, last_name=:lastName where username=:username', $this->config['user_table'])); } else { - $stmt = $this->db->prepare(sprintf('INSERT INTO %s (username, password, first_name, last_name) VALUES (:username, :password, :firstName, :lastName)', $this->config['user_table_name'])); + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (username, password, first_name, last_name) VALUES (:username, :password, :firstName, :lastName)', $this->config['user_table'])); } return $stmt->execute(compact('username', 'password', 'firstName', 'lastName')); } -}
\ No newline at end of file +}
diff --git a/src/OAuth2/Storage/UserCredentialsInterface.php b/src/OAuth2/Storage/UserCredentialsInterface.php index 8ecf866..9fff562 100644 --- a/src/OAuth2/Storage/UserCredentialsInterface.php +++ b/src/OAuth2/Storage/UserCredentialsInterface.php @@ -3,13 +3,13 @@ /** * Storage engines that support the "Resource Owner Password Credentials" * grant type should implement this interface - * + * * @author Dave Rochwerger <catch.dave@gmail.com> * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.3 */ interface OAuth2_Storage_UserCredentialsInterface { - + /** * Grant access tokens for basic user credentials. * @@ -42,4 +42,12 @@ interface OAuth2_Storage_UserCredentialsInterface * @ingroup oauth2_section_4 */ public function checkUserCredentials($username, $password); + + /** + * @return + * ARRAY the associated "scope" or "user_id" values if applicable, or an empty array + * if this does not apply + * + */ + public function getUserDetails($username); }
\ No newline at end of file diff --git a/src/OAuth2/TokenType/Bearer.php b/src/OAuth2/TokenType/Bearer.php index bcccd08..08492a4 100644 --- a/src/OAuth2/TokenType/Bearer.php +++ b/src/OAuth2/TokenType/Bearer.php @@ -42,14 +42,10 @@ class OAuth2_TokenType_Bearer implements OAuth2_TokenTypeInterface, OAuth2_Respo * Old Android version bug (at least with version 2.2) * @see http://code.google.com/p/android/issues/detail?id=6684 * - * We don't want to test this functionality as it relies on superglobals and headers: - * @codeCoverageIgnoreStart */ - public function getAccessTokenParameter(OAuth2_Request $request) + public function getAccessTokenParameter(OAuth2_RequestInterface $request) { - if (isset($request->server['AUTHORIZATION'])) { - $headers = $request->server['AUTHORIZATION']; - } + $headers = $request->server('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'])); diff --git a/src/OAuth2/TokenType/MAC.php b/src/OAuth2/TokenType/MAC.php index c809a3b..6d47932 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_Request $request) + public function getAccessTokenParameter(OAuth2_RequestInterface $request) { throw new LogicException("Not supported"); diff --git a/src/OAuth2/TokenTypeInterface.php b/src/OAuth2/TokenTypeInterface.php index ab8ac9f..27ca292 100644 --- a/src/OAuth2/TokenTypeInterface.php +++ b/src/OAuth2/TokenTypeInterface.php @@ -3,5 +3,5 @@ interface OAuth2_TokenTypeInterface { public function getTokenType(); - public function getAccessTokenParameter(OAuth2_Request $request); + public function getAccessTokenParameter(OAuth2_RequestInterface $request); }
\ No newline at end of file |
