diff --git a/.gitignore b/.gitignore index 3c44ccf9..e9100837 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ /docs/build/ /build/logs/ /build/coverage/ -test \ No newline at end of file +test +/docs/ +/testing/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index fe21d0e2..5034e99f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,5 +4,5 @@ php: - 5.3 - 5.4 -before_script: composer install +before_script: composer install --dev script: phpunit -c build/phpunit.xml \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..4cceea32 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## 1.0.0 (released 2012-02-15) + +* First release \ No newline at end of file diff --git a/README.md b/README.md index ff47bfce..d65bcaf3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PHP OAuth Framework -The goal of this project is to develop a standards compliant [OAuth 2](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-v2/) authentication server, resource server and client library with support for a major OAuth 2 providers. +The goal of this project is to develop a standards compliant [OAuth 2](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-v2/) authentication server and resource server. ## Package Installation @@ -9,33 +9,30 @@ The framework is provided as a Composer package which can be installed by adding ```javascript { "require": { - "lncd\Oauth2": "*" + "lncd\OAuth2": "*" } } ``` -## Package Integration +--- -Check out the [wiki](https://github.com/lncd/OAuth2/wiki) +The library features 100% unit test code coverage. To run the tests yourself run `phpunit -c build/phpunit.xml`. ## Current Features ### Authentication Server -The authentication server is a flexible class that supports the following grants: +The authentication server is a flexible class and following core specification grants are implemented: -* authentication code -* refresh token -* client credentials -* password (user credentials) +* authentication code ([section 4.1](http://tools.ietf.org/html/rfc6749#section-4.1)) +* refresh token ([section 6](http://tools.ietf.org/html/rfc6749#section-6)) +* client credentials ([section 2.3.1](http://tools.ietf.org/html/rfc6749#section-2.3.1)) +* password (user credentials) ([section 4.3](http://tools.ietf.org/html/rfc6749#section-4.3)) ### Resource Server The resource server allows you to secure your API endpoints by checking for a valid OAuth access token in the request and ensuring the token has the correct permission to access resources. - - - ## Future Goals ### Authentication Server @@ -43,10 +40,13 @@ The resource server allows you to secure your API endpoints by checking for a va * Support for [JSON web tokens](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-json-web-token/). * Support for [SAML assertions](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-saml2-bearer/). -### Client support - -* Merge in https://github.com/philsturgeon/codeigniter-oauth2 - --- -This code will be developed as part of the [Linkey](http://linkey.blogs.lincoln.ac.uk) project which has been funded by [JISC](http://jisc.ac.uk) under the Access and Identity Management programme. \ No newline at end of file +This code will be developed as part of the [Linkey](http://linkey.blogs.lincoln.ac.uk) project which has been funded by [JISC](http://jisc.ac.uk) under the Access and Identity Management programme. + +This code was principally developed by [Alex Bilbie](http://alexbilbie.com/) ([Twitter](https://twitter.com/alexbilbie)|[Github](https://github.com/alexbilbie)). + +Valuable contribtions have been made by the following: + +* [Dan Horrigan](http://dandoescode.com) ([Twitter](https://twitter.com/dandoescode)|[Github](https://github.com/dandoescode)) +* [Nick Jackson](http://nickjackson.me) ([Twitter](https://twitter.com/jacksonj04)|[Github](https://github.com/jacksonj04)) \ No newline at end of file diff --git a/build/phpunit.xml b/build/phpunit.xml index 792b1799..5b44b45c 100644 --- a/build/phpunit.xml +++ b/build/phpunit.xml @@ -2,10 +2,13 @@ - ../tests/authentication + ../tests/authentication - ../tests/resource + ../tests/resource + + + ../tests/util @@ -13,7 +16,10 @@ PEAR_INSTALL_DIR PHP_LIBDIR ../vendor/composer + ../vendor/mockery + ../vendor/phpunit ../tests + ../testing diff --git a/sql/database.sql b/sql/mysql.sql similarity index 66% rename from sql/database.sql rename to sql/mysql.sql index b260d9f0..9d30a76d 100644 --- a/sql/database.sql +++ b/sql/mysql.sql @@ -1,5 +1,13 @@ --- Create syntax for TABLE 'clients' -CREATE TABLE `clients` ( +CREATE TABLE `oauth_client_endpoints` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `client_id` varchar(40) NOT NULL DEFAULT '', + `redirect_uri` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `client_id` (`client_id`), + CONSTRAINT `oauth_client_endpoints_ibfk_1` FOREIGN KEY (`client_id`) REFERENCES `oauth_clients` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `oauth_clients` ( `id` varchar(40) NOT NULL DEFAULT '', `secret` varchar(40) NOT NULL DEFAULT '', `name` varchar(255) NOT NULL DEFAULT '', @@ -7,26 +15,26 @@ CREATE TABLE `clients` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; --- Create syntax for TABLE 'client_endpoints' -CREATE TABLE `client_endpoints` ( +CREATE TABLE `oauth_session_scopes` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `client_id` varchar(40) NOT NULL DEFAULT '', - `redirect_uri` varchar(255) DEFAULT NULL, + `session_id` int(11) unsigned NOT NULL, + `scope_id` int(11) unsigned NOT NULL, PRIMARY KEY (`id`), - KEY `client_id` (`client_id`), - CONSTRAINT `client_endpoints_ibfk_1` FOREIGN KEY (`client_id`) REFERENCES `clients` (`id`) ON DELETE CASCADE ON UPDATE CASCADE + KEY `session_id` (`session_id`), + KEY `scope_id` (`scope_id`), + CONSTRAINT `oauth_session_scopes_ibfk_5` FOREIGN KEY (`scope_id`) REFERENCES `oauth_scopes` (`id`) ON DELETE CASCADE, + CONSTRAINT `oauth_session_scopes_ibfk_4` FOREIGN KEY (`session_id`) REFERENCES `oauth_sessions` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; --- Create syntax for TABLE 'oauth_sessions' CREATE TABLE `oauth_sessions` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `client_id` varchar(40) NOT NULL DEFAULT '', `redirect_uri` varchar(250) DEFAULT '', `owner_type` enum('user','client') NOT NULL DEFAULT 'user', - `owner_id` varchar(255) DEFAULT NULL, + `owner_id` varchar(255) DEFAULT '', `auth_code` varchar(40) DEFAULT '', `access_token` varchar(40) DEFAULT '', - `refresh_token` varchar(40) NOT NULL, + `refresh_token` varchar(40) DEFAULT '', `access_token_expires` int(10) DEFAULT NULL, `stage` enum('requested','granted') NOT NULL DEFAULT 'requested', `first_requested` int(10) unsigned NOT NULL, @@ -35,26 +43,11 @@ CREATE TABLE `oauth_sessions` ( KEY `client_id` (`client_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; --- Create syntax for TABLE 'scopes' -CREATE TABLE `scopes` ( +CREATE TABLE `oauth_scopes` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `scope` varchar(255) NOT NULL DEFAULT '', `name` varchar(255) NOT NULL DEFAULT '', `description` varchar(255) DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY `scope` (`scope`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- Create syntax for TABLE 'oauth_session_scopes' -CREATE TABLE `oauth_session_scopes` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `session_id` int(11) unsigned NOT NULL, - `access_token` varchar(40) NOT NULL DEFAULT '', - `scope` varchar(255) NOT NULL DEFAULT '', - PRIMARY KEY (`id`), - KEY `session_id` (`session_id`), - KEY `access_token` (`access_token`), - KEY `scope` (`scope`), - CONSTRAINT `oauth_session_scopes_ibfk_3` FOREIGN KEY (`scope`) REFERENCES `scopes` (`scope`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `oauth_session_scopes_ibfk_4` FOREIGN KEY (`session_id`) REFERENCES `oauth_sessions` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/src/OAuth2/AuthServer.php b/src/OAuth2/AuthServer.php new file mode 100644 index 00000000..82f464fa --- /dev/null +++ b/src/OAuth2/AuthServer.php @@ -0,0 +1,408 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2; + +use OAuth2\Util\Request; +use OAuth2\Util\SecureKey; +use OAuth2\Storage\SessionInterface; +use OAuth2\Storage\ClientInterface; +use OAuth2\Storage\ScopeInterface; +use OAuth2\Grant\GrantTypeInterface; + +/** + * OAuth 2.0 authentication server class + */ +class AuthServer +{ + /** + * The delimeter between scopes specified in the scope query string parameter + * + * The OAuth 2 specification states it should be a space but that is stupid + * and everyone excepted Google use a comma instead. + * + * @var string + */ + protected $scopeDelimeter = ','; + + /** + * The TTL (time to live) of an access token in seconds (default: 3600) + * @var integer + */ + static protected $expiresIn = 3600; + + /** + * The registered grant response types + * @var array + */ + protected $responseTypes = array(); + + /** + * The client, scope and session storage classes + * @var array + */ + static protected $storages = array(); + + /** + * The registered grant types + * @var array + */ + static protected $grantTypes = array(); + + /** + * The request object + * @var Util\RequestInterface + */ + static protected $request = null; + + /** + * Exception error codes + * @var array + */ + protected static $exceptionCodes = array( + 0 => 'invalid_request', + 1 => 'unauthorized_client', + 2 => 'access_denied', + 3 => 'unsupported_response_type', + 4 => 'invalid_scope', + 5 => 'server_error', + 6 => 'temporarily_unavailable', + 7 => 'unsupported_grant_type', + 8 => 'invalid_client', + 9 => 'invalid_grant' + ); + + /** + * Exception error messages + * @var array + */ + static protected $exceptionMessages = array( + 'invalid_request' => 'The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the "%s" parameter.', + 'unauthorized_client' => 'The client is not authorized to request an access token using this method.', + 'access_denied' => 'The resource owner or authorization server denied the request.', + 'unsupported_response_type' => 'The authorization server does not support obtaining an access token using this method.', + 'invalid_scope' => 'The requested scope is invalid, unknown, or malformed. Check the "%s" scope.', + 'server_error' => 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.', + 'temporarily_unavailable' => 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.', + 'unsupported_grant_type' => 'The authorization grant type "%s" is not supported by the authorization server', + 'invalid_client' => 'Client authentication failed', + 'invalid_grant' => 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. Check the "%s" parameter.', + 'invalid_credentials' => 'The user credentials were incorrect.', + 'invalid_refresh' => 'The refresh token is invalid.', + ); + + /** + * Get an exception message + * + * @param string $error The error message key + * @return string The error message + */ + public static function getExceptionMessage($error = '') + { + return self::$exceptionMessages[$error]; + } + + /** + * Get an exception code + * + * @param integer $code The exception code + * @return string The exception code type + */ + public static function getExceptionType($code = 0) + { + return self::$exceptionCodes[$code]; + } + + /** + * Create a new OAuth2 authentication server + * + * @param ClientInterface $client A class which inherits from Storage/ClientInterface + * @param SessionInterface $session A class which inherits from Storage/SessionInterface + * @param ScopeInterface $scope A class which inherits from Storage/ScopeInterface + */ + public function __construct(ClientInterface $client, SessionInterface $session, ScopeInterface $scope) + { + self::$storages = array( + 'client' => $client, + 'session' => $session, + 'scope' => $scope + ); + } + + /** + * Enable support for a grant + * @param GrantTypeInterface $grantType A grant class which conforms to Interface/GrantTypeInterface + * @param null|string $identifier An identifier for the grant (autodetected if not passed) + */ + public function addGrantType(GrantTypeInterface $grantType, $identifier = null) + { + if (is_null($identifier)) { + $identifier = $grantType->getIdentifier(); + } + self::$grantTypes[$identifier] = $grantType; + + if ( ! is_null($grantType->getResponseType())) { + $this->responseTypes[] = $grantType->getResponseType(); + } + } + + /** + * Check if a grant type has been enabled + * @param string $identifier The grant type identifier + * @return boolean Returns "true" if enabled, "false" if not + */ + public static function hasGrantType($identifier) + { + return (array_key_exists($identifier, self::$grantTypes)); + } + + /** + * Get the scope delimeter + * + * @return string The scope delimiter (default: ",") + */ + public function getScopeDelimeter() + { + return $this->scopeDelimeter; + } + + /** + * Set the scope delimiter + * + * @param string $scopeDelimeter + */ + public function setScopeDelimeter($scopeDelimeter) + { + $this->scopeDelimeter = $scopeDelimeter; + } + + /** + * Get the TTL for an access token + * @return int The TTL + */ + public static function getExpiresIn() + { + return self::$expiresIn; + } + + /** + * Set the TTL for an access token + * @param int $expiresIn The new TTL + */ + public function setExpiresIn($expiresIn) + { + self::$expiresIn = $expiresIn; + } + + /** + * Sets the Request Object + * + * @param Util\RequestInterface The Request Object + */ + public function setRequest(Util\RequestInterface $request) + { + self::$request = $request; + } + + /** + * Gets the Request object. It will create one from the globals if one is not set. + * + * @return Util\RequestInterface + */ + public static function getRequest() + { + if (self::$request === null) { + // @codeCoverageIgnoreStart + self::$request = Request::buildFromGlobals(); + + } + // @codeCoverageIgnoreEnd + + return self::$request; + } + + /** + * Return a storage class + * @param string $obj The class required + * @return Storage\ClientInterface|Storage\ScopeInterface|Storage\SessionInterface + */ + public static function getStorage($obj) + { + return self::$storages[$obj]; + } + + /** + * Check authorise parameters + * + * @param array $inputParams Optional array of parsed $_GET keys + * @throws \OAuth2\Exception\ClientException + * @return array Authorise request parameters + */ + public function checkAuthoriseParams($inputParams = array()) + { + $authParams = array(); + + // Client ID + $authParams['client_id'] = (isset($inputParams['client_id'])) ? + $inputParams['client_id'] : + self::getRequest()->get('client_id'); + + if (is_null($authParams['client_id'])) { + throw new Exception\ClientException(sprintf(self::$exceptionMessages['invalid_request'], 'client_id'), 0); + } + + // Redirect URI + $authParams['redirect_uri'] = (isset($inputParams['redirect_uri'])) ? + $inputParams['redirect_uri'] : + self::getRequest()->get('redirect_uri'); + + if (is_null($authParams['redirect_uri'])) { + throw new Exception\ClientException(sprintf(self::$exceptionMessages['invalid_request'], 'redirect_uri'), 0); + } + + // Validate client ID and redirect URI + $clientDetails = self::getStorage('client')->getClient($authParams['client_id'], null, $authParams['redirect_uri']); + + if ($clientDetails === false) { + throw new Exception\ClientException(self::$exceptionMessages['invalid_client'], 8); + } + + $authParams['client_details'] = $clientDetails; + + // Response type + $authParams['response_type'] = (isset($inputParams['response_type'])) ? + $inputParams['response_type'] : + self::getRequest()->get('response_type'); + + if (is_null($authParams['response_type'])) { + throw new Exception\ClientException(sprintf(self::$exceptionMessages['invalid_request'], 'response_type'), 0); + } + + // Ensure response type is one that is recognised + if ( ! in_array($authParams['response_type'], $this->responseTypes)) { + throw new Exception\ClientException(self::$exceptionMessages['unsupported_response_type'], 3); + } + + // Get and validate scopes + $scopes = (isset($inputParams['scope'])) ? + $inputParams['scope'] : + self::getRequest()->get('scope', ''); + + $scopes = explode($this->scopeDelimeter, $scopes); + + for ($i = 0; $i < count($scopes); $i++) { + $scopes[$i] = trim($scopes[$i]); + if ($scopes[$i] === '') unset($scopes[$i]); // Remove any junk scopes + } + + if (count($scopes) === 0) { + throw new Exception\ClientException(sprintf(self::$exceptionMessages['invalid_request'], 'scope'), 0); + } + + $authParams['scopes'] = array(); + + foreach ($scopes as $scope) { + $scopeDetails = self::getStorage('scope')->getScope($scope); + + if ($scopeDetails === false) { + throw new Exception\ClientException(sprintf(self::$exceptionMessages['invalid_scope'], $scope), 4); + } + + $authParams['scopes'][] = $scopeDetails; + } + + return $authParams; + } + + /** + * Parse a new authorise request + * + * @param string $type The session owner's type + * @param string $typeId The session owner's ID + * @param array $authParams The authorise request $_GET parameters + * @return string An authorisation code + */ + public function newAuthoriseRequest($type, $typeId, $authParams = array()) + { + // Generate an auth code + $authCode = SecureKey::make(); + + // Remove any old sessions the user might have + self::getStorage('session')->deleteSession($authParams['client_id'], $type, $typeId); + + // Create a new session + $sessionId = self::getStorage('session')->createSession($authParams['client_id'], $authParams['redirect_uri'], $type, $typeId, $authCode); + + // Associate scopes with the new session + foreach ($authParams['scopes'] as $scope) + { + self::getStorage('session')->associateScope($sessionId, $scope['id']); + } + + return $authCode; + } + + /** + * Issue an access token + * + * @param array $inputParams Optional array of parsed $_POST keys + * @return array Authorise request parameters + */ + public function issueAccessToken($inputParams = array()) + { + $grantType = (isset($inputParams['grant_type'])) ? + $inputParams['grant_type'] : + self::getRequest()->post('grant_type'); + + if (is_null($grantType)) { + throw new Exception\ClientException(sprintf(self::$exceptionMessages['invalid_request'], 'grant_type'), 0); + } + + // Ensure grant type is one that is recognised and is enabled + if ( ! in_array($grantType, array_keys(self::$grantTypes))) { + throw new Exception\ClientException(sprintf(self::$exceptionMessages['unsupported_grant_type'], $grantType), 7); + } + + // Complete the flow + return $this->getGrantType($grantType)->completeFlow($inputParams); + } + + /** + * Return a grant type class + * @param string $grantType The grant type identifer + * @return class + */ + protected function getGrantType($grantType) + { + return self::$grantTypes[$grantType]; + } + + /** + * Get a parameter from passed input parameters or the Request class + * @param string|array $param Requried parameter + * @param string $method Get/put/post/delete + * @param array $inputParams Passed input parameters + * @return mixed 'Null' if parameter is missing + */ + public static function getParam($param = '', $method = 'get', $inputParams = array()) + { + if (is_string($param)) { + return (isset($inputParams[$param])) ? $inputParams['client_id'] : self::getRequest()->{$method}($param); + } else { + $response = array(); + foreach ($param as $p) { + $response[$p] = self::getParam($p, $method, $inputParams); + } + return $response; + } + } + +} diff --git a/src/OAuth2/Exception/ClientException.php b/src/OAuth2/Exception/ClientException.php new file mode 100644 index 00000000..cfd8090a --- /dev/null +++ b/src/OAuth2/Exception/ClientException.php @@ -0,0 +1,20 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2\Exception; + +/** + * ClientException Exception + */ +class ClientException extends OAuth2Exception +{ + +} \ No newline at end of file diff --git a/src/OAuth2/Exception/InvalidAccessTokenException.php b/src/OAuth2/Exception/InvalidAccessTokenException.php new file mode 100644 index 00000000..fa598bd6 --- /dev/null +++ b/src/OAuth2/Exception/InvalidAccessTokenException.php @@ -0,0 +1,20 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2\Exception; + +/** + * InvalidAccessToken Exception + */ +class InvalidAccessTokenException extends OAuth2Exception +{ + +} diff --git a/src/OAuth2/Exception/InvalidGrantTypeException.php b/src/OAuth2/Exception/InvalidGrantTypeException.php new file mode 100644 index 00000000..4cf5c99f --- /dev/null +++ b/src/OAuth2/Exception/InvalidGrantTypeException.php @@ -0,0 +1,20 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2\Exception; + +/** + * InvalidGrantTypeException Exception + */ +class InvalidGrantTypeException extends OAuth2Exception +{ + +} diff --git a/src/OAuth2/Exception/OAuth2Exception.php b/src/OAuth2/Exception/OAuth2Exception.php new file mode 100644 index 00000000..ad294199 --- /dev/null +++ b/src/OAuth2/Exception/OAuth2Exception.php @@ -0,0 +1,20 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2\Exception; + +/** + * Exception class + */ +class OAuth2Exception extends \Exception +{ + +} diff --git a/src/OAuth2/Grant/AuthCode.php b/src/OAuth2/Grant/AuthCode.php new file mode 100644 index 00000000..38c0316c --- /dev/null +++ b/src/OAuth2/Grant/AuthCode.php @@ -0,0 +1,132 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2\Grant; + +use OAuth2\Request; +use OAuth2\AuthServer; +use OAuth2\Exception; +use OAuth2\Util\SecureKey; +use OAuth2\Storage\SessionInterface; +use OAuth2\Storage\ClientInterface; +use OAuth2\Storage\ScopeInterface; + +/** + * Auth code grant class + */ +class AuthCode implements GrantTypeInterface { + + /** + * Grant identifier + * @var string + */ + protected $identifier = 'authorization_code'; + + /** + * Response type + * @var string + */ + protected $responseType = 'code'; + + /** + * Return the identifier + * @return string + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * Return the response type + * @return string + */ + public function getResponseType() + { + return $this->responseType; + } + + /** + * Complete the auth code grant + * @param null|array $inputParams + * @return array + */ + public function completeFlow($inputParams = null) + { + // Get the required params + $authParams = AuthServer::getParam(array('client_id', 'client_secret', 'redirect_uri', 'code'), 'post', $inputParams); + + if (is_null($authParams['client_id'])) { + throw new Exception\ClientException(sprintf(AuthServer::getExceptionMessage('invalid_request'), 'client_id'), 0); + } + + if (is_null($authParams['client_secret'])) { + throw new Exception\ClientException(sprintf(AuthServer::getExceptionMessage('invalid_request'), 'client_secret'), 0); + } + + if (is_null($authParams['redirect_uri'])) { + throw new Exception\ClientException(sprintf(AuthServer::getExceptionMessage('invalid_request'), 'redirect_uri'), 0); + } + + // Validate client ID and redirect URI + $clientDetails = AuthServer::getStorage('client')->getClient($authParams['client_id'], $authParams['client_secret'], $authParams['redirect_uri']); + + if ($clientDetails === false) { + throw new Exception\ClientException(AuthServer::getExceptionMessage('invalid_client'), 8); + } + + $authParams['client_details'] = $clientDetails; + + // Validate the authorization code + if (is_null($authParams['code'])) { + throw new Exception\ClientException(sprintf(AuthServer::getExceptionMessage('invalid_request'), 'code'), 0); + } + + // Verify the authorization code matches the client_id and the request_uri + $session = AuthServer::getStorage('session')->validateAuthCode($authParams['client_id'], $authParams['redirect_uri'], $authParams['code']); + + if ( ! $session) { + throw new Exception\ClientException(sprintf(AuthServer::getExceptionMessage('invalid_grant'), 'code'), 9); + } + + // A session ID was returned so update it with an access token, + // remove the authorisation code, change the stage to 'granted' + + $accessToken = SecureKey::make(); + $refreshToken = (AuthServer::hasGrantType('refresh_token')) ? SecureKey::make() : null; + + $accessTokenExpires = time() + AuthServer::getExpiresIn(); + $accessTokenExpiresIn = AuthServer::getExpiresIn(); + + AuthServer::getStorage('session')->updateSession( + $session['id'], + null, + $accessToken, + $refreshToken, + $accessTokenExpires, + 'granted' + ); + + $response = array( + 'access_token' => $accessToken, + 'token_type' => 'bearer', + 'expires' => $accessTokenExpires, + 'expires_in' => $accessTokenExpiresIn + ); + + if (AuthServer::hasGrantType('refresh_token')) { + $response['refresh_token'] = $refreshToken; + } + + return $response; + } + +} \ No newline at end of file diff --git a/src/OAuth2/Grant/ClientCredentials.php b/src/OAuth2/Grant/ClientCredentials.php new file mode 100644 index 00000000..3bb590bb --- /dev/null +++ b/src/OAuth2/Grant/ClientCredentials.php @@ -0,0 +1,121 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2\Grant; + +use OAuth2\Request; +use OAuth2\AuthServer; +use OAuth2\Exception; +use OAuth2\Util\SecureKey; +use OAuth2\Storage\SessionInterface; +use OAuth2\Storage\ClientInterface; +use OAuth2\Storage\ScopeInterface; + +/** + * Client credentials grant class + */ +class ClientCredentials implements GrantTypeInterface { + + /** + * Grant identifier + * @var string + */ + protected $identifier = 'client_credentials'; + + /** + * Response type + * @var string + */ + protected $responseType = null; + + /** + * Return the identifier + * @return string + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * Return the response type + * @return string + */ + public function getResponseType() + { + return $this->responseType; + } + + /** + * Complete the client credentials grant + * @param null|array $inputParams + * @return array + */ + public function completeFlow($inputParams = null) + { + // Get the required params + $authParams = AuthServer::getParam(array('client_id', 'client_secret'), 'post', $inputParams); + + if (is_null($authParams['client_id'])) { + throw new Exception\ClientException(sprintf(AuthServer::getExceptionMessage('invalid_request'), 'client_id'), 0); + } + + if (is_null($authParams['client_secret'])) { + throw new Exception\ClientException(sprintf(AuthServer::getExceptionMessage('invalid_request'), 'client_secret'), 0); + } + + // Validate client ID and client secret + $clientDetails = AuthServer::getStorage('client')->getClient($authParams['client_id'], $authParams['client_secret']); + + if ($clientDetails === false) { + throw new Exception\ClientException(AuthServer::getExceptionMessage('invalid_client'), 8); + } + + $authParams['client_details'] = $clientDetails; + + // Generate an access token + $accessToken = SecureKey::make(); + $refreshToken = (AuthServer::hasGrantType('refresh_token')) ? SecureKey::make() : null; + + $accessTokenExpires = time() + AuthServer::getExpiresIn(); + $accessTokenExpiresIn = AuthServer::getExpiresIn(); + + // Delete any existing sessions just to be sure + AuthServer::getStorage('session')->deleteSession($authParams['client_id'], 'client', $authParams['client_id']); + + // Create a new session + AuthServer::getStorage('session')->createSession( + $authParams['client_id'], + null, + 'client', + $authParams['client_id'], + null, + $accessToken, + $refreshToken, + $accessTokenExpires, + 'granted' + ); + + $response = array( + 'access_token' => $accessToken, + 'token_type' => 'bearer', + 'expires' => $accessTokenExpires, + 'expires_in' => $accessTokenExpiresIn + ); + + if (AuthServer::hasGrantType('refresh_token')) { + $response['refresh_token'] = $refreshToken; + } + + return $response; + } + +} \ No newline at end of file diff --git a/src/OAuth2/Grant/GrantTypeInterface.php b/src/OAuth2/Grant/GrantTypeInterface.php new file mode 100644 index 00000000..b05c959e --- /dev/null +++ b/src/OAuth2/Grant/GrantTypeInterface.php @@ -0,0 +1,54 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2\Grant; + +use OAuth2\Request; +use OAuth2\AuthServer; +use OAuth2\Exception; +use OAuth2\Util\SecureKey; +use OAuth2\Storage\SessionInterface; +use OAuth2\Storage\ClientInterface; +use OAuth2\Storage\ScopeInterface; + +interface GrantTypeInterface +{ + /** + * Returns the grant identifier (used to validate grant_type in OAuth2\AuthServer\issueAccessToken()) + * @return string + */ + public function getIdentifier(); + + /** + * Returns the response type (used to validate response_type in OAuth2\AuthServer\checkAuthoriseParams()) + * @return null|string + */ + public function getResponseType(); + + /** + * Complete the grant flow + * + * Example response: + * + * array( + * 'access_token' => (string), // The access token + * 'refresh_token' => (string), // The refresh token (only set if the refresh token grant is enabled) + * 'token_type' => 'bearer', // Almost always "bearer" (exceptions: JWT, SAML) + * 'expires' => (int), // The timestamp of when the access token will expire + * 'expires_in' => (int) // The number of seconds before the access token will expire + * ) + * + * + * @param null|array $inputParams Null unless the input parameters have been manually set + * @return array An array of parameters to be passed back to the client + */ + public function completeFlow($inputParams = null); +} diff --git a/src/OAuth2/Grant/Password.php b/src/OAuth2/Grant/Password.php new file mode 100644 index 00000000..1cf61c74 --- /dev/null +++ b/src/OAuth2/Grant/Password.php @@ -0,0 +1,164 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2\Grant; + +use OAuth2\Request; +use OAuth2\AuthServer; +use OAuth2\Exception; +use OAuth2\Util\SecureKey; +use OAuth2\Storage\SessionInterface; +use OAuth2\Storage\ClientInterface; +use OAuth2\Storage\ScopeInterface; + +/** + * Password grant class + */ +class Password implements GrantTypeInterface { + + /** + * Grant identifier + * @var string + */ + protected $identifier = 'password'; + + /** + * Response type + * @var string + */ + protected $responseType = null; + + /** + * Callback to authenticate a user's name and password + * @var function + */ + protected $callback = null; + + /** + * Return the identifier + * @return string + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * Return the response type + * @return string + */ + public function getResponseType() + { + return $this->responseType; + } + + /** + * Set the callback to verify a user's username and password + * @param function $callback The callback function + */ + public function setVerifyCredentialsCallback($callback) + { + $this->callback = $callback; + } + + /** + * Return the callback function + * @return function + */ + protected function getVerifyCredentialsCallback() + { + if (is_null($this->callback) || ! is_callable($this->callback)) { + throw new Exception\InvalidGrantTypeException('Null or non-callable callback set'); + } + + return $this->callback; + } + + /** + * Complete the password grant + * @param null|array $inputParams + * @return array + */ + public function completeFlow($inputParams = null) + { + // Get the required params + $authParams = AuthServer::getParam(array('client_id', 'client_secret', 'username', 'password'), 'post', $inputParams); + + if (is_null($authParams['client_id'])) { + throw new Exception\ClientException(sprintf(AuthServer::getExceptionMessage('invalid_request'), 'client_id'), 0); + } + + if (is_null($authParams['client_secret'])) { + throw new Exception\ClientException(sprintf(AuthServer::getExceptionMessage('invalid_request'), 'client_secret'), 0); + } + + // Validate client ID and redirect URI + $clientDetails = AuthServer::getStorage('client')->getClient($authParams['client_id'], $authParams['client_secret']); + + if ($clientDetails === false) { + throw new Exception\ClientException(AuthServer::getExceptionMessage('invalid_client'), 8); + } + + $authParams['client_details'] = $clientDetails; + + if (is_null($authParams['username'])) { + throw new Exception\ClientException(sprintf(AuthServer::getExceptionMessage('invalid_request'), 'username'), 0); + } + + if (is_null($authParams['password'])) { + throw new Exception\ClientException(sprintf(AuthServer::getExceptionMessage('invalid_request'), 'password'), 0); + } + + // Check if user's username and password are correct + $userId = call_user_func($this->getVerifyCredentialsCallback(), $authParams['username'], $authParams['password']); + + if ($userId === false) { + throw new Exception\ClientException(AuthServer::getExceptionMessage('invalid_credentials'), 0); + } + + // Generate an access token + $accessToken = SecureKey::make(); + $refreshToken = (AuthServer::hasGrantType('refresh_token')) ? SecureKey::make() : null; + + $accessTokenExpires = time() + AuthServer::getExpiresIn(); + $accessTokenExpiresIn = AuthServer::getExpiresIn(); + + // Delete any existing sessions just to be sure + AuthServer::getStorage('session')->deleteSession($authParams['client_id'], 'user', $userId); + + // Create a new session + AuthServer::getStorage('session')->createSession( + $authParams['client_id'], + null, + 'user', + $userId, + null, + $accessToken, + $refreshToken, + $accessTokenExpires, + 'granted' + ); + + $response = array( + 'access_token' => $accessToken, + 'token_type' => 'bearer', + 'expires' => $accessTokenExpires, + 'expires_in' => $accessTokenExpiresIn + ); + + if (AuthServer::hasGrantType('refresh_token')) { + $response['refresh_token'] = $refreshToken; + } + + return $response; + } + +} \ No newline at end of file diff --git a/src/OAuth2/Grant/RefreshToken.php b/src/OAuth2/Grant/RefreshToken.php new file mode 100644 index 00000000..bd7839ca --- /dev/null +++ b/src/OAuth2/Grant/RefreshToken.php @@ -0,0 +1,116 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2\Grant; + +use OAuth2\Request; +use OAuth2\AuthServer; +use OAuth2\Exception; +use OAuth2\Util\SecureKey; +use OAuth2\Storage\SessionInterface; +use OAuth2\Storage\ClientInterface; +use OAuth2\Storage\ScopeInterface; + +/** + * Referesh token grant + */ +class RefreshToken implements GrantTypeInterface { + + /** + * Grant identifier + * @var string + */ + protected $identifier = 'refresh_token'; + + /** + * Response type + * @var string + */ + protected $responseType = null; + + /** + * Return the identifier + * @return string + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * Return the response type + * @return string + */ + public function getResponseType() + { + return $this->responseType; + } + + /** + * Complete the refresh token grant + * @param null|array $inputParams + * @return array + */ + public function completeFlow($inputParams = null) + { + // Get the required params + $authParams = AuthServer::getParam(array('client_id', 'client_secret', 'refresh_token'), 'post', $inputParams); + + if (is_null($authParams['client_id'])) { + throw new Exception\ClientException(sprintf(AuthServer::getExceptionMessage('invalid_request'), 'client_id'), 0); + } + + if (is_null($authParams['client_secret'])) { + throw new Exception\ClientException(sprintf(AuthServer::getExceptionMessage('invalid_request'), 'client_secret'), 0); + } + + // Validate client ID and client secret + $clientDetails = AuthServer::getStorage('client')->getClient($authParams['client_id'], $authParams['client_secret']); + + if ($clientDetails === false) { + throw new Exception\ClientException(AuthServer::getExceptionMessage('invalid_client'), 8); + } + + $authParams['client_details'] = $clientDetails; + + if (is_null($authParams['refresh_token'])) { + throw new Exception\ClientException(sprintf(AuthServer::getExceptionMessage('invalid_request'), 'refresh_token'), 0); + } + + // Validate refresh token + $sessionId = AuthServer::getStorage('client')->validateRefreshToken( + $authParams['refresh_token'], + $authParams['client_id'] + ); + + if ($sessionId === false) { + throw new Exception\ClientException(AuthServer::getExceptionMessage('invalid_refresh'), 0); + } + + // Generate new tokens + $accessToken = SecureKey::make(); + $refreshToken = (AuthServer::hasGrantType('refresh_token')) ? SecureKey::make() : null; + + $accessTokenExpires = time() + AuthServer::getExpiresIn(); + $accessTokenExpiresIn = AuthServer::getExpiresIn(); + + AuthServer::getStorage('session')->updateRefreshToken($sessionId, $accessToken, $refreshToken, $accessTokenExpires); + + return array( + 'access_token' => $accessToken, + 'refresh_token' => $refreshToken, + 'token_type' => 'bearer', + 'expires' => $accessTokenExpires, + 'expires_in' => $accessTokenExpiresIn + ); + } + +} \ No newline at end of file diff --git a/src/OAuth2/ResourceServer.php b/src/OAuth2/ResourceServer.php new file mode 100644 index 00000000..e7cc7d2f --- /dev/null +++ b/src/OAuth2/ResourceServer.php @@ -0,0 +1,232 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2; + +use OutOfBoundsException; +use OAuth2\Storage\SessionInterface; +use OAuth2\Storage\SessionScopeInterface; +use OAuth2\Util\RequestInterface; +use OAuth2\Util\Request; + +/** + * OAuth 2.0 Resource Server + */ +class ResourceServer +{ + /** + * The access token + * @var string + */ + protected $accessToken = null; + + /** + * The session ID + * @var string + */ + protected $sessionId = null; + + /** + * The type of the owner of the access token + * @var string + */ + protected $ownerType = null; + + /** + * The ID of the owner of the access token + * @var string + */ + protected $ownerId = null; + + /** + * The scopes associated with the access token + * @var array + */ + protected $sessionScopes = array(); + + /** + * The client, scope and session storage classes + * @var array + */ + protected $storages = array(); + + /** + * The request object + * @var Util\RequestInterface + */ + protected $request = null; + + /** + * The query string key which is used by clients to present the access token (default: access_token) + * @var string + */ + protected $tokenKey = 'access_token'; + + /** + * Sets up the Resource + * + * @param SessionInterface The Session Storage Object + */ + public function __construct(SessionInterface $session) + { + $this->storages['session'] = $session; + } + + /** + * Sets the Request Object + * + * @param RequestInterface The Request Object + */ + public function setRequest(RequestInterface $request) + { + $this->request = $request; + } + + /** + * Gets the Request object. It will create one from the globals if one is not set. + * + * @return Util\RequestInterface + */ + public function getRequest() + { + if ($this->request === null) { + // @codeCoverageIgnoreStart + $this->request = Request::buildFromGlobals(); + } + // @codeCoverageIgnoreEnd + + return $this->request; + } + + /** + * Returns the query string key for the access token. + * + * @return string + */ + public function getTokenKey() + { + return $this->tokenKey; + } + + /** + * Sets the query string key for the access token. + * + * @param $key The new query string key + */ + public function setTokenKey($key) + { + $this->tokenKey = $key; + } + + /** + * Gets the access token owner ID. + * + * @return string + */ + public function getOwnerId() + { + return $this->ownerId; + } + + /** + * Gets the owner type. + * + * @return string + */ + public function getOwnerType() + { + return $this->ownerType; + } + + /** + * Gets the access token. + * + * @return string + */ + public function getAccessToken() + { + return $this->accessToken; + } + + /** + * Checks if the access token is valid or not. + * + * @throws Exception\InvalidAccessTokenException Thrown if the presented access token is not valid + * @return bool + */ + public function isValid() + { + $access_token = $this->determineAccessToken(); + + $result = $this->storages['session']->validateAccessToken($access_token); + + if ( ! $result) { + throw new Exception\InvalidAccessTokenException('Access token is not valid'); + } + + $this->accessToken = $access_token; + $this->sessionId = $result['id']; + $this->ownerType = $result['owner_type']; + $this->ownerId = $result['owner_id']; + + $this->sessionScopes = $this->storages['session']->getScopes($this->sessionId); + + return true; + } + + /** + * Checks if the presented access token has the given scope(s). + * + * @param array|string An array of scopes or a single scope as a string + * @return bool Returns bool if all scopes are found, false if any fail + */ + public function hasScope($scopes) + { + if (is_string($scopes)) { + if (in_array($scopes, $this->sessionScopes)) { + return true; + } + return false; + } elseif (is_array($scopes)) { + foreach ($scopes as $scope) { + if ( ! in_array($scope, $this->sessionScopes)) { + return false; + } + } + return true; + } + + return false; + } + + /** + * Reads in the access token from the headers. + * + * @throws Exception\MissingAccessTokenException Thrown if there is no access token presented + * @return string + */ + protected function determineAccessToken() + { + if ($header = $this->getRequest()->header('Authorization')) { + $access_token = base64_decode(trim(str_replace('Bearer', '', $header))); + } else { + $method = $this->getRequest()->server('REQUEST_METHOD'); + $access_token = $this->getRequest()->{$method}($this->tokenKey); + } + + if (empty($access_token)) { + throw new Exception\InvalidAccessTokenException('Access token is missing'); + } + + return $access_token; + } + +} diff --git a/src/OAuth2/Storage/ClientInterface.php b/src/OAuth2/Storage/ClientInterface.php new file mode 100644 index 00000000..605c42a9 --- /dev/null +++ b/src/OAuth2/Storage/ClientInterface.php @@ -0,0 +1,56 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2\Storage; + +interface ClientInterface +{ + /** + * Validate a client + * + * Example SQL query: + * + * + * # Client ID + redirect URI + * SELECT clients.id FROM clients LEFT JOIN client_endpoints ON + * client_endpoints.client_id = clients.id WHERE clients.id = $clientId AND + * client_endpoints.redirect_uri = $redirectUri + * + * # Client ID + client secret + * SELECT clients.id FROM clients WHERE clients.id = $clientId AND + * clients.secret = $clientSecret + * + * # Client ID + client secret + redirect URI + * SELECT clients.id FROM clients LEFT JOIN client_endpoints ON + * client_endpoints.client_id = clients.id WHERE clients.id = $clientId AND + * clients.secret = $clientSecret AND client_endpoints.redirect_uri = + * $redirectUri + * + * + * Response: + * + * + * Array + * ( + * [client_id] => (string) The client ID + * [client secret] => (string) The client secret + * [redirect_uri] => (string) The redirect URI used in this request + * [name] => (string) The name of the client + * ) + * + * + * @param string $clientId The client's ID + * @param string $clientSecret The client's secret (default = "null") + * @param string $redirectUri The client's redirect URI (default = "null") + * @return bool|array Returns false if the validation fails, array on success + */ + public function getClient($clientId = null, $clientSecret = null, $redirectUri = null); +} \ No newline at end of file diff --git a/src/OAuth2/Storage/ScopeInterface.php b/src/OAuth2/Storage/ScopeInterface.php new file mode 100644 index 00000000..82c71c39 --- /dev/null +++ b/src/OAuth2/Storage/ScopeInterface.php @@ -0,0 +1,41 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2\Storage; + +interface ScopeInterface +{ + /** + * Return information about a scope + * + * Example SQL query: + * + * + * SELECT * FROM scopes WHERE scope = $scope + * + * + * Response: + * + * + * Array + * ( + * [id] => (int) The scope's ID + * [scope] => (string) The scope itself + * [name] => (string) The scope's name + * [description] => (string) The scope's description + * ) + * + * + * @param string $scope The scope + * @return bool|array If the scope doesn't exist return false + */ + public function getScope($scope); +} diff --git a/src/Oauth2/Authentication/Database.php b/src/OAuth2/Storage/SessionInterface.php similarity index 50% rename from src/Oauth2/Authentication/Database.php rename to src/OAuth2/Storage/SessionInterface.php index b9de679b..1c2632f6 100644 --- a/src/Oauth2/Authentication/Database.php +++ b/src/OAuth2/Storage/SessionInterface.php @@ -1,58 +1,22 @@ + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ -namespace Oauth2\Authentication; +namespace OAuth2\Storage; -interface Database +interface SessionInterface { - /** - * Validate a client - * - * Database query: - * - * - * # Client ID + redirect URI - * SELECT clients.id FROM clients LEFT JOIN client_endpoints ON - * client_endpoints.client_id = clients.id WHERE clients.id = $clientId AND - * client_endpoints.redirect_uri = $redirectUri - * - * # Client ID + client secret - * SELECT clients.id FROM clients WHERE clients.id = $clientId AND - * clients.secret = $clientSecret - * - * # Client ID + client secret + redirect URI - * SELECT clients.id FROM clients LEFT JOIN client_endpoints ON - * client_endpoints.client_id = clients.id WHERE clients.id = $clientId AND - * clients.secret = $clientSecret AND client_endpoints.redirect_uri = - * $redirectUri - * - * - * Response: - * - * - * Array - * ( - * [client_id] => (string) The client ID - * [client secret] => (string) The client secret - * [redirect_uri] => (string) The redirect URI used in this request - * [name] => (string) The name of the client - * ) - * - * - * @param string $clientId The client's ID - * @param string $clientSecret The client's secret (default = "null") - * @param string $redirectUri The client's redirect URI (default = "null") - * @return bool|array Returns false if the validation fails, array on success - */ - public function validateClient( - $clientId, - $clientSecret = null, - $redirectUri = null - ); - - /** + /** * Create a new OAuth session * - * Database query: + * Example SQL query: * * * INSERT INTO oauth_sessions (client_id, redirect_uri, owner_type, @@ -61,17 +25,18 @@ interface Database * $accessToken, $stage, UNIX_TIMESTAMP(NOW()), UNIX_TIMESTAMP(NOW())) * * - * @param string $clientId The client ID - * @param string $redirectUri The redirect URI - * @param string $type The session owner's type (default = "user") - * @param string $typeId The session owner's ID (default = "null") - * @param string $authCode The authorisation code (default = "null") - * @param string $accessToken The access token (default = "null") - * @param string $refreshToken The refresh token (default = "null") - * @param string $stage The stage of the session (default ="request") - * @return int The session ID + * @param string $clientId The client ID + * @param string $redirectUri The redirect URI + * @param string $type The session owner's type (default = "user") + * @param string $typeId The session owner's ID (default = "null") + * @param string $authCode The authorisation code (default = "null") + * @param string $accessToken The access token (default = "null") + * @param string $refreshToken The refresh token (default = "null") + * @param int $accessTokenExpire The expiry time of an access token as a unix timestamp + * @param string $stage The stage of the session (default ="request") + * @return int The session ID */ - public function newSession( + public function createSession( $clientId, $redirectUri, $type = 'user', @@ -86,7 +51,7 @@ interface Database /** * Update an OAuth session * - * Database query: + * Example SQL query: * * * UPDATE oauth_sessions SET auth_code = $authCode, access_token = @@ -94,11 +59,12 @@ interface Database * id = $sessionId * * - * @param string $sessionId The session ID - * @param string $authCode The authorisation code (default = "null") - * @param string $accessToken The access token (default = "null") - * @param string $refreshToken The refresh token (default = "null") - * @param string $stage The stage of the session (default ="request") + * @param string $sessionId The session ID + * @param string $authCode The authorisation code (default = "null") + * @param string $accessToken The access token (default = "null") + * @param string $refreshToken The refresh token (default = "null") + * @param int $accessTokenExpire The expiry time of an access token as a unix timestamp + * @param string $stage The stage of the session (default ="request") * @return void */ public function updateSession( @@ -129,31 +95,10 @@ interface Database $typeId ); - public function validateRefreshToken($refreshToken, $clientId); - - /** - * Update the refresh token - * - * Database query: - * - * - * UPDATE oauth_sessions SET access_token = $newAccessToken, refresh_token = - * $newRefreshToken, access_toke_expires = $accessTokenExpires, last_updated = UNIX_TIMESTAMP(NOW()) WHERE - * id = $sessionId - * - * - * @param string $sessionId The session ID - * @param string $newAccessToken The new access token for this session - * @param string $newRefreshToken The new refresh token for the session - * @param int $accessTokenExpires The UNIX timestamp of when the new token expires - * @return void - */ - public function updateRefreshToken($sessionId, $newAccessToken, $newRefreshToken, $accessTokenExpires); - /** * Validate that an authorisation code is valid * - * Database query: + * Example SQL query: * * * SELECT id FROM oauth_sessions WHERE client_id = $clientID AND @@ -192,31 +137,34 @@ interface Database ); /** - * Return the session ID for a given session owner and client combination + * Validate an access token * - * Database query: + * Example SQL query: * * - * SELECT id FROM oauth_sessions WHERE client_id = $clientId - * AND owner_type = $type AND owner_id = $typeId + * SELECT id, owner_id, owner_type FROM oauth_sessions WHERE access_token = $accessToken * * - * @param string $type The session owner's type - * @param string $typeId The session owner's ID - * @param string $clientId The client ID - * @return string|null Return the session ID as an integer if - * found otherwise returns false + * Response: + * + * + * Array + * ( + * [id] => (int) The session ID + * [owner_type] => (string) The owner type + * [owner_id] => (string) The owner ID + * ) + * + * + * @param [type] $accessToken [description] + * @return [type] [description] */ - public function hasSession( - $type, - $typeId, - $clientId - ); + public function validateAccessToken($accessToken); /** * Return the access token for a given session * - * Database query: + * Example SQL query: * * * SELECT access_token FROM oauth_sessions WHERE id = $sessionId @@ -229,106 +177,52 @@ interface Database public function getAccessToken($sessionId); /** - * Removes an authorisation code associated with a session - * - * Database query: - * - * - * UPDATE oauth_sessions SET auth_code = NULL WHERE id = $sessionId - * - * - * @param int $sessionId The OAuth session ID - * @return void + * Validate a refresh token + * @param string $refreshToken The refresh token + * @param string $clientId The client ID + * @return int The session ID */ - public function removeAuthCode($sessionId); + public function validateRefreshToken($refreshToken, $clientId); /** - * Sets a sessions access token + * Update the refresh token * - * Database query: + * Example SQL query: * * - * UPDATE oauth_sessions SET access_token = $accessToken WHERE id = - * $sessionId + * UPDATE oauth_sessions SET access_token = $newAccessToken, refresh_token = + * $newRefreshToken, access_toke_expires = $accessTokenExpires, last_updated = UNIX_TIMESTAMP(NOW()) WHERE + * id = $sessionId * * - * @param int $sessionId The OAuth session ID - * @param string $accessToken The access token + * @param string $sessionId The session ID + * @param string $newAccessToken The new access token for this session + * @param string $newRefreshToken The new refresh token for the session + * @param int $accessTokenExpires The UNIX timestamp of when the new token expires * @return void */ - public function setAccessToken( - $sessionId, - $accessToken - ); + public function updateRefreshToken($sessionId, $newAccessToken, $newRefreshToken, $accessTokenExpires); /** * Associates a session with a scope * - * Database query: + * Example SQL query: * * - * INSERT INTO oauth_session_scopes (session_id, scope) VALUE ($sessionId, - * $scope) + * INSERT INTO oauth_session_scopes (session_id, scope_id) VALUE ($sessionId, + * $scopeId) * * * @param int $sessionId The session ID - * @param string $scope The scope + * @param string $scopeId The scope ID * @return void */ - public function addSessionScope( - $sessionId, - $scope - ); - - /** - * Return information about a scope - * - * Database query: - * - * - * SELECT * FROM scopes WHERE scope = $scope - * - * - * Response: - * - * - * Array - * ( - * [id] => (int) The scope's ID - * [scope] => (string) The scope itself - * [name] => (string) The scope's name - * [description] => (string) The scope's description - * ) - * - * - * @param string $scope The scope - * @return array - */ - public function getScope($scope); - - /** - * Associate a session's scopes with an access token - * - * Database query: - * - * - * UPDATE oauth_session_scopes SET access_token = $accessToken WHERE - * session_id = $sessionId - * - * - * @param int $sessionId The session ID - * @param string $accessToken The access token - * @return void - */ - public function updateSessionScopeAccessToken( - $sessionId, - $accessToken - ); + public function associateScope($sessionId, $scopeId); /** * Return the scopes associated with an access token * - * Database query: + * Example SQL query: * * * SELECT scopes.scope, scopes.name, scopes.description FROM @@ -353,5 +247,5 @@ interface Database * @param string $accessToken The access token * @return array */ - public function accessTokenScopes($accessToken); + public function getScopes($accessToken); } diff --git a/src/OAuth2/Util/RedirectUri.php b/src/OAuth2/Util/RedirectUri.php new file mode 100644 index 00000000..c123b187 --- /dev/null +++ b/src/OAuth2/Util/RedirectUri.php @@ -0,0 +1,31 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2\Util; + +/** + * RedirectUri class + */ +class RedirectUri +{ + /** + * Generate a new redirect uri + * @param string $uri The base URI + * @param array $params The query string parameters + * @param string $queryDelimeter The query string delimeter (default: "?") + * @return string The updated URI + */ + public static function make($uri, $params = array(), $queryDelimeter = '?') + { + $uri .= (strstr($uri, $queryDelimeter) === false) ? $queryDelimeter : '&'; + return $uri.http_build_query($params); + } +} \ No newline at end of file diff --git a/src/OAuth2/Util/Request.php b/src/OAuth2/Util/Request.php new file mode 100644 index 00000000..ccb19dac --- /dev/null +++ b/src/OAuth2/Util/Request.php @@ -0,0 +1,100 @@ +get = $get; + $this->post = $post; + $this->cookies = $cookies; + $this->files = $files; + $this->server = $server; + + if (empty($headers)) { + $this->headers = $this->readHeaders(); + } + } + + public function get($index = null, $default = null) + { + return $this->getPropertyValue('get', $index, $default); + } + + public function post($index = null, $default = null) + { + return $this->getPropertyValue('post', $index, $default); + } + + public function file($index = null, $default = null) + { + return $this->getPropertyValue('files', $index, $default); + } + + public function cookie($index = null, $default = null) + { + return $this->getPropertyValue('cookies', $index, $default); + } + + public function server($index = null, $default = null) + { + return $this->getPropertyValue('server', $index, $default); + } + + public function header($index = null, $default = null) + { + return $this->getPropertyValue('headers', $index, $default); + } + + protected function readHeaders() + { + if (function_exists('getallheaders')) { + // @codeCoverageIgnoreStart + $headers = getallheaders(); + } else { + // @codeCoverageIgnoreEnd + $headers = array(); + foreach ($this->server() as $name => $value) { + if (substr($name, 0, 5) == 'HTTP_') { + $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5))))); + $headers[$name] = $value; + } + } + } + + return $headers; + } + + protected function getPropertyValue($property, $index = null, $default = null) + { + if ( ! isset($this->{$property})) { + throw new InvalidArgumentException("Property '$property' does not exist."); + } + if (is_null($index)) { + return $this->{$property}; + } + + if ( ! array_key_exists($index, $this->{$property})) { + return $default; + } + + return $this->{$property}[$index]; + } +} \ No newline at end of file diff --git a/src/OAuth2/Util/RequestInterface.php b/src/OAuth2/Util/RequestInterface.php new file mode 100644 index 00000000..4374c5d9 --- /dev/null +++ b/src/OAuth2/Util/RequestInterface.php @@ -0,0 +1,24 @@ + + * @copyright Copyright (c) 2013 University of Lincoln + * @license http://mit-license.org/ + * @link http://github.com/lncd/oauth2 + */ + +namespace OAuth2\Util; + +/** + * SecureKey class + */ +class SecureKey +{ + /** + * Generate a new unique code + * @param integer $len Length of the generated code + * @return string + */ + public static function make($len = 40) + { + // We generate twice as many bytes here because we want to ensure we have + // enough after we base64 encode it to get the length we need because we + // take out the "/", "+", and "=" characters. + $bytes = openssl_random_pseudo_bytes($len * 2, $strong); + + // We want to stop execution if the key fails because, well, that is bad. + if ($bytes === false || $strong === false) { + // @codeCoverageIgnoreStart + throw new \Exception('Error Generating Key'); + // @codeCoverageIgnoreEnd + } + + return substr(str_replace(array('/', '+', '='), '', base64_encode($bytes)), 0, $len); + } +} \ No newline at end of file diff --git a/src/Oauth2/Authentication/Server.php b/src/Oauth2/Authentication/Server.php deleted file mode 100644 index 82a2800a..00000000 --- a/src/Oauth2/Authentication/Server.php +++ /dev/null @@ -1,791 +0,0 @@ - ',', - 'access_token_ttl' => 3600 - ); - - /** - * Supported response types - * @var array - */ - private $_responseTypes = array( - 'code' - ); - - /** - * Supported grant types - * @var array - */ - private $_grantTypes = array( - 'authorization_code' => false, - 'client_credentials' => false, - 'password' => false, - 'refresh_token' => false, - ); - - private $_grantTypeCallbacks = array( - 'password' => null - ); - - /** - * Exception error codes - * @var array - */ - public $exceptionCodes = array( - 0 => 'invalid_request', - 1 => 'unauthorized_client', - 2 => 'access_denied', - 3 => 'unsupported_response_type', - 4 => 'invalid_scope', - 5 => 'server_error', - 6 => 'temporarily_unavailable', - 7 => 'unsupported_grant_type', - 8 => 'invalid_client', - 9 => 'invalid_grant' - ); - - /** - * Error codes. - * - * To provide i8ln errors just overwrite the keys - * - * @var array - */ - public $errors = array( - 'invalid_request' => 'The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the "%s" parameter.', - 'unauthorized_client' => 'The client is not authorized to request an access token using this method.', - 'access_denied' => 'The resource owner or authorization server denied the request.', - 'unsupported_response_type' => 'The authorization server does not support obtaining an access token using this method.', - 'invalid_scope' => 'The requested scope is invalid, unknown, or malformed. Check the "%s" scope.', - 'server_error' => 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.', - 'temporarily_unavailable' => 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.', - 'unsupported_grant_type' => 'The authorization grant type "%s" is not supported by the authorization server', - 'invalid_client' => 'Client authentication failed', - 'invalid_grant' => 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. Check the "%s" parameter.', - 'invalid_credentials' => 'The user credentials were incorrect.', - 'invalid_refresh' => 'The refresh token is invalid.', - ); - - /** - * Constructor - * - * @access public - * @param array $options Optional list of options to overwrite the defaults - * @return void - */ - public function __construct($options = null) - { - if ($options !== null) { - $this->_config = array_merge($this->_config, $options); - } - } - - /** - * Register a database abstrator class - * - * @access public - * @param object $db A class that implements OAuth2ServerDatabase - * @return void - */ - public function registerDbAbstractor($db) - { - $this->_db = $db; - } - - /** - * Enable a grant type - * - * @access public - * @return void - */ - public function enableGrantType($type, $callback = null) - { - if (isset($this->_grantTypes[$type])) { - $this->_grantTypes[$type] = true; - } - - if (in_array($type, array_keys($this->_grantTypeCallbacks))) { - if (is_null($callback) || ! is_callable($callback)) { - throw new ServerException('No registered callback function for grant type `'.$type.'`'); - } - - $this->_grantTypeCallbacks[$type] = $callback; - } - } - - /** - * Check client authorise parameters - * - * @access public - * @param array $authParams Optional array of parsed $_GET keys - * @return array Authorise request parameters - */ - public function checkClientAuthoriseParams($authParams = null) - { - $params = array(); - - // Client ID - if ( ! isset($authParams['client_id']) && ! isset($_GET['client_id'])) { - throw new ClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0); - } - - $params['client_id'] = (isset($authParams['client_id'])) ? - $authParams['client_id'] : - $_GET['client_id']; - - // Redirect URI - if ( ! isset($authParams['redirect_uri']) && ! isset($_GET['redirect_uri'])) { - throw new ClientException(sprintf($this->errors['invalid_request'], 'redirect_uri'), 0); - } - - $params['redirect_uri'] = (isset($authParams['redirect_uri'])) ? - $authParams['redirect_uri'] : - $_GET['redirect_uri']; - - // Validate client ID and redirect URI - $clientDetails = $this->_dbCall( - 'validateClient', - $params['client_id'], - null, - $params['redirect_uri'] - ); - - if ($clientDetails === false) { - throw new ClientException($this->errors['invalid_client'], 8); - } - - $params['client_details'] = $clientDetails; - - // Response type - if ( ! isset($authParams['response_type']) && ! isset($_GET['response_type'])) { - throw new ClientException(sprintf($this->errors['invalid_request'], 'response_type'), 0); - } - - $params['response_type'] = (isset($authParams['response_type'])) ? - $authParams['response_type'] : - $_GET['response_type']; - - // Ensure response type is one that is recognised - if ( ! in_array($params['response_type'], $this->_responseTypes)) { - throw new ClientException($this->errors['unsupported_response_type'], 3); - } - - // Get and validate scopes - if (isset($authParams['scope']) || isset($_GET['scope'])) { - - $scopes = (isset($_GET['scope'])) ? - $_GET['scope'] : - $authParams['scope']; - - $scopes = explode($this->_config['scope_delimeter'], $scopes); - - // Remove any junk scopes - for ($i = 0; $i < count($scopes); $i++) { - - $scopes[$i] = trim($scopes[$i]); - - if ($scopes[$i] === '') { - unset($scopes[$i]); - } - } - - if (count($scopes) === 0) { - throw new ClientException(sprintf($this->errors['invalid_request'], 'scope'), 0); - } - - $params['scopes'] = array(); - - foreach ($scopes as $scope) { - - $scopeDetails = $this->_dbCall( - 'getScope', - $scope - ); - - if ($scopeDetails === false) { - throw new ClientException(sprintf($this->errors['invalid_scope'], $scope), 4); - } - - $params['scopes'][] = $scopeDetails; - - } - } - - return $params; - } - - /** - * Parse a new authorise request - * - * @param string $type The session owner's type - * @param string $typeId The session owner's ID - * @param array $authoriseParams The authorise request $_GET parameters - * @return string An authorisation code - */ - public function newAuthoriseRequest($type, $typeId, $authoriseParams) - { - // Remove any old sessions the user might have - $this->_dbCall( - 'deleteSession', - $authoriseParams['client_id'], - $type, - $typeId - ); - - // Create the new auth code - $authCode = $this->_newAuthCode( - $authoriseParams['client_id'], - 'user', - $typeId, - $authoriseParams['redirect_uri'], - $authoriseParams['scopes'] - ); - - return $authCode; - } - - /** - * Generate a unique code - * - * Generate a unique code for an authorisation code, or token - * - * @return string A unique code - */ - private function _generateCode() - { - return sha1(uniqid(microtime())); - } - - /** - * Create a new authorisation code - * - * @param string $clientId The client ID - * @param string $type The type of the owner of the session - * @param string $typeId The session owner's ID - * @param string $redirectUri The redirect URI - * @param array $scopes The requested scopes - * @param string $accessToken The access token (default = null) - * @return string An authorisation code - */ - private function _newAuthCode($clientId, $type, $typeId, $redirectUri, $scopes = array()) - { - $authCode = $this->_generateCode(); - - // Delete any existing sessions just to be sure - $this->_dbCall('deleteSession', $clientId, $type, $typeId); - - // Create a new session - $sessionId = $this->_dbCall( - 'newSession', - $clientId, - $redirectUri, - $type, - $typeId, - $authCode, - null, - null, - 'requested' - ); - - // Add the scopes - foreach ($scopes as $key => $scope) { - - $this->_dbCall( - 'addSessionScope', - $sessionId, - $scope['scope'] - ); - - } - - return $authCode; - } - - /** - * Issue an access token - * - * @access public - * - * @param array $authParams Optional array of parsed $_POST keys - * - * @return array Authorise request parameters - */ - public function issueAccessToken($authParams = null) - { - if ( ! isset($authParams['grant_type']) && ! isset($_POST['grant_type'])) { - throw new ClientException(sprintf($this->errors['invalid_request'], 'grant_type'), 0); - } - - $params['grant_type'] = (isset($authParams['grant_type'])) ? - $authParams['grant_type'] : - $_POST['grant_type']; - - // Ensure grant type is one that is recognised and is enabled - if ( ! in_array($params['grant_type'], array_keys($this->_grantTypes)) || $this->_grantTypes[$params['grant_type']] !== true) { - throw new ClientException(sprintf($this->errors['unsupported_grant_type'], $params['grant_type']), 7); - } - - switch ($params['grant_type']) - { - case 'authorization_code': // Authorization code grant - return $this->_completeAuthCodeGrant($authParams, $params); - break; - - case 'client_credentials': // Client credentials grant - return $this->_completeClientCredentialsGrant($authParams, $params); - break; - - case 'password': // Resource owner password credentials grant - return $this->_completeUserCredentialsGrant($authParams, $params); - break; - - case 'refresh_token': // Refresh token grant - return $this->_completeRefreshTokenGrant($authParams, $params); - break; - - // @codeCoverageIgnoreStart - default: // Unsupported - throw new ServerException($this->errors['server_error'] . 'Tried to process an unsuppported grant type.', 5); - break; - } - // @codeCoverageIgnoreEnd - } - - /** - * Complete the authorisation code grant - * - * @access private - * - * @param array $authParams Array of parsed $_POST keys - * @param array $params Generated parameters from issueAccessToken() - * - * @return array Authorise request parameters - */ - private function _completeAuthCodeGrant($authParams = array(), $params = array()) - { - // Client ID - if ( ! isset($authParams['client_id']) && ! isset($_POST['client_id'])) { - throw new ClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0); - } - - $params['client_id'] = (isset($authParams['client_id'])) ? - $authParams['client_id'] : - $_POST['client_id']; - - // Client secret - if ( ! isset($authParams['client_secret']) && ! isset($_POST['client_secret'])) { - throw new ClientException(sprintf($this->errors['invalid_request'], 'client_secret'), 0); - } - - $params['client_secret'] = (isset($authParams['client_secret'])) ? - $authParams['client_secret'] : - $_POST['client_secret']; - - // Redirect URI - if ( ! isset($authParams['redirect_uri']) && ! isset($_POST['redirect_uri'])) { - throw new ClientException(sprintf($this->errors['invalid_request'], 'redirect_uri'), 0); - } - - $params['redirect_uri'] = (isset($authParams['redirect_uri'])) ? - $authParams['redirect_uri'] : - $_POST['redirect_uri']; - - // Validate client ID and redirect URI - $clientDetails = $this->_dbCall( - 'validateClient', - $params['client_id'], - $params['client_secret'], - $params['redirect_uri'] - ); - - if ($clientDetails === false) { - throw new ClientException($this->errors['invalid_client'], 8); - } - - // The authorization code - if ( ! isset($authParams['code']) && ! isset($_POST['code'])) { - throw new ClientException(sprintf($this->errors['invalid_request'], 'code'), 0); - } - - $params['code'] = (isset($authParams['code'])) ? - $authParams['code'] : - $_POST['code']; - - // Verify the authorization code matches the client_id and the request_uri - $session = $this->_dbCall( - 'validateAuthCode', - $params['client_id'], - $params['redirect_uri'], - $params['code'] - ); - - if ( ! $session) { - throw new ClientException(sprintf($this->errors['invalid_grant'], 'code'), 9); - } - - // A session ID was returned so update it with an access token, - // remove the authorisation code, change the stage to 'granted' - - $accessToken = $this->_generateCode(); - $refreshToken = ($this->_grantTypes['refresh_token']) ? - $this->_generateCode() : - null; - - $accessTokenExpires = time() + $this->_config['access_token_ttl']; - $accessTokenExpiresIn = $this->_config['access_token_ttl']; - - $this->_dbCall( - 'updateSession', - $session['id'], - null, - $accessToken, - $refreshToken, - $accessTokenExpires, - 'granted' - ); - - // Update the session's scopes to reference the access token - $this->_dbCall( - 'updateSessionScopeAccessToken', - $session['id'], - $accessToken, - $refreshToken - ); - - $response = array( - 'access_token' => $accessToken, - 'token_type' => 'bearer', - 'expires' => $accessTokenExpires, - 'expires_in' => $accessTokenExpiresIn - ); - - if ($this->_grantTypes['refresh_token']) { - $response['refresh_token'] = $refreshToken; - } - - return $response; - } - - /** - * Complete the resource owner password credentials grant - * - * @access private - * @param array $authParams Array of parsed $_POST keys - * @param array $params Generated parameters from issueAccessToken() - * @return array Authorise request parameters - */ - private function _completeClientCredentialsGrant($authParams = array(), $params = array()) - { - // Client ID - if ( ! isset($authParams['client_id']) && ! isset($_POST['client_id'])) { - // @codeCoverageIgnoreStart - throw new ClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0); - // @codeCoverageIgnoreEnd - } - - $params['client_id'] = (isset($authParams['client_id'])) ? - $authParams['client_id'] : - $_POST['client_id']; - - // Client secret - if ( ! isset($authParams['client_secret']) && ! isset($_POST['client_secret'])) { - // @codeCoverageIgnoreStart - throw new ClientException(sprintf($this->errors['invalid_request'], 'client_secret'), 0); - // @codeCoverageIgnoreEnd - } - - $params['client_secret'] = (isset($authParams['client_secret'])) ? - $authParams['client_secret'] : - $_POST['client_secret']; - - // Validate client ID and client secret - $clientDetails = $this->_dbCall( - 'validateClient', - $params['client_id'], - $params['client_secret'], - null - ); - - if ($clientDetails === false) { - // @codeCoverageIgnoreStart - throw new ClientException($this->errors['invalid_client'], 8); - // @codeCoverageIgnoreEnd - } - - // Generate an access token - $accessToken = $this->_generateCode(); - $refreshToken = ($this->_grantTypes['refresh_token']) ? - $this->_generateCode() : - null; - - $accessTokenExpires = time() + $this->_config['access_token_ttl']; - $accessTokenExpiresIn = $this->_config['access_token_ttl']; - - // Delete any existing sessions just to be sure - $this->_dbCall('deleteSession', $params['client_id'], 'client', $params['client_id']); - - // Create a new session - $this->_dbCall('newSession', $params['client_id'], null, 'client', $params['client_id'], null, $accessToken, $refreshToken, $accessTokenExpires, 'granted'); - - $response = array( - 'access_token' => $accessToken, - 'token_type' => 'bearer', - 'expires' => $accessTokenExpires, - 'expires_in' => $accessTokenExpiresIn - ); - - if ($this->_grantTypes['refresh_token']) { - $response['refresh_token'] = $refreshToken; - } - - return $response; - } - - /** - * Complete the resource owner password credentials grant - * - * @access private - * @param array $authParams Array of parsed $_POST keys - * @param array $params Generated parameters from issueAccessToken() - * @return array Authorise request parameters - */ - private function _completeUserCredentialsGrant($authParams = array(), $params = array()) - { - // Client ID - if ( ! isset($authParams['client_id']) && ! isset($_POST['client_id'])) { - // @codeCoverageIgnoreStart - throw new ClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0); - // @codeCoverageIgnoreEnd - } - - $params['client_id'] = (isset($authParams['client_id'])) ? - $authParams['client_id'] : - $_POST['client_id']; - - // Client secret - if ( ! isset($authParams['client_secret']) && ! isset($_POST['client_secret'])) { - // @codeCoverageIgnoreStart - throw new ClientException(sprintf($this->errors['invalid_request'], 'client_secret'), 0); - // @codeCoverageIgnoreEnd - } - - $params['client_secret'] = (isset($authParams['client_secret'])) ? - $authParams['client_secret'] : - $_POST['client_secret']; - - // Validate client ID and client secret - $clientDetails = $this->_dbCall( - 'validateClient', - $params['client_id'], - $params['client_secret'], - null - ); - - if ($clientDetails === false) { - // @codeCoverageIgnoreStart - throw new ClientException($this->errors['invalid_client'], 8); - // @codeCoverageIgnoreEnd - } - - // User's username - if ( ! isset($authParams['username']) && ! isset($_POST['username'])) { - throw new ClientException(sprintf($this->errors['invalid_request'], 'username'), 0); - } - - $params['username'] = (isset($authParams['username'])) ? - $authParams['username'] : - $_POST['username']; - - // User's password - if ( ! isset($authParams['password']) && ! isset($_POST['password'])) { - throw new ClientException(sprintf($this->errors['invalid_request'], 'password'), 0); - } - - $params['password'] = (isset($authParams['password'])) ? - $authParams['password'] : - $_POST['password']; - - // Check if user's username and password are correct - $userId = call_user_func($this->_grantTypeCallbacks['password'], $params['username'], $params['password']); - - if ($userId === false) { - throw new \Oauth2\Authentication\ClientException($this->errors['invalid_credentials'], 0); - } - - // Generate an access token - $accessToken = $this->_generateCode(); - $refreshToken = ($this->_grantTypes['refresh_token']) ? - $this->_generateCode() : - null; - - $accessTokenExpires = time() + $this->_config['access_token_ttl']; - $accessTokenExpiresIn = $this->_config['access_token_ttl']; - - // Delete any existing sessions just to be sure - $this->_dbCall('deleteSession', $params['client_id'], 'user', $userId); - - // Create a new session - $this->_dbCall('newSession', $params['client_id'], null, 'user', $userId, null, $accessToken, $refreshToken, $accessTokenExpires, 'granted'); - - $response = array( - 'access_token' => $accessToken, - 'token_type' => 'bearer', - 'expires' => $accessTokenExpires, - 'expires_in' => $accessTokenExpiresIn - ); - - if ($this->_grantTypes['refresh_token']) { - $response['refresh_token'] = $refreshToken; - } - - return $response; - } - - /** - * Complete the refresh token grant - * - * @access private - * @param array $authParams Array of parsed $_POST keys - * @param array $params Generated parameters from issueAccessToken() - * @return array Authorise request parameters - */ - private function _completeRefreshTokenGrant($authParams = array(), $params = array()) - { - // Client ID - if ( ! isset($authParams['client_id']) && ! isset($_POST['client_id'])) { - // @codeCoverageIgnoreStart - throw new ClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0); - // @codeCoverageIgnoreEnd - } - - $params['client_id'] = (isset($authParams['client_id'])) ? - $authParams['client_id'] : - $_POST['client_id']; - - // Client secret - if ( ! isset($authParams['client_secret']) && ! isset($_POST['client_secret'])) { - // @codeCoverageIgnoreStart - throw new ClientException(sprintf($this->errors['invalid_request'], 'client_secret'), 0); - // @codeCoverageIgnoreEnd - } - - $params['client_secret'] = (isset($authParams['client_secret'])) ? - $authParams['client_secret'] : - $_POST['client_secret']; - - // Validate client ID and client secret - $clientDetails = $this->_dbCall( - 'validateClient', - $params['client_id'], - $params['client_secret'], - null - ); - - if ($clientDetails === false) { - // @codeCoverageIgnoreStart - throw new ClientException($this->errors['invalid_client'], 8); - // @codeCoverageIgnoreEnd - } - - // Refresh token - if ( ! isset($authParams['refresh_token']) && ! isset($_POST['refresh_token'])) { - throw new ClientException(sprintf($this->errors['invalid_request'], 'refresh_token'), 0); - } - - $params['refresh_token'] = (isset($authParams['refresh_token'])) ? - $authParams['refresh_token'] : - $_POST['refresh_token']; - - // Validate refresh token - $sessionId = $this->_dbCall('validateRefreshToken', $params['refresh_token'], $params['client_id']); - - if ($sessionId === false) { - throw new \Oauth2\Authentication\ClientException($this->errors['invalid_refresh'], 0); - } - - // Generate new tokens - $accessToken = $this->_generateCode(); - $refreshToken = $this->_generateCode(); - - $accessTokenExpires = time() + $this->_config['access_token_ttl']; - $accessTokenExpiresIn = $this->_config['access_token_ttl']; - - // Update the tokens - $this->_dbCall('updateRefreshToken', $sessionId, $accessToken, $refreshToken, $accessTokenExpires); - - return array( - 'access_token' => $accessToken, - 'refresh_token' => $refreshToken, - 'token_type' => 'bearer', - 'expires' => $accessTokenExpires, - 'expires_in' => $accessTokenExpiresIn - ); - } - - /** - * Generates the redirect uri with appended params - * - * @param string $redirectUri The redirect URI - * @param array $params The parameters to be appended to the URL - * @param string $query_delimeter The query string delimiter (default: ?) - * - * @return string The updated redirect URI - */ - public function redirectUri($redirectUri, $params = array(), $queryDelimeter = '?') - { - return (strstr($redirectUri, $queryDelimeter)) ? $redirectUri . '&' . http_build_query($params) : $redirectUri . $queryDelimeter . http_build_query($params); - } - - /** - * Call database methods from the abstractor - * - * @return mixed The query result - */ - private function _dbCall() - { - if ($this->_db === null) { - throw new ServerException('No registered database abstractor'); - } - - if ( ! $this->_db instanceof Database) { - throw new ServerException('Registered database abstractor is not an instance of Oauth2\Authentication\Database'); - } - - $args = func_get_args(); - $method = $args[0]; - unset($args[0]); - $params = array_values($args); - - return call_user_func_array(array($this->_db, $method), $params); - } -} diff --git a/src/Oauth2/Client/IDP.php b/src/Oauth2/Client/IDP.php deleted file mode 100644 index 2dbe182b..00000000 --- a/src/Oauth2/Client/IDP.php +++ /dev/null @@ -1,230 +0,0 @@ -result = $result; - - $code = isset($result['code']) ? $result['code'] : 0; - - if (isset($result['error'])) { - - // OAuth 2.0 Draft 10 style - $message = $result['error']; - - } elseif (isset($result['message'])) { - - // cURL style - $message = $result['message']; - - } else { - - $message = 'Unknown Error.'; - - } - - parent::__construct($message['message'], $message['code']); - } - - public function getType() - { - if (isset($this->result['error'])) { - - $message = $this->result['error']; - - if (is_string($message)) { - // OAuth 2.0 Draft 10 style - return $message; - } - } - - return 'Exception'; - } - - /** - * To make debugging easier. - * - * @returns - * The string representation of the error. - */ - public function __toString() - { - $str = $this->getType() . ': '; - - if ($this->code != 0) { - $str .= $this->code . ': '; - } - - return $str . $this->message; - } - -} - -abstract class IDP { - - public $clientId = ''; - - public $clientSecret = ''; - - public $redirectUri = ''; - - public $name; - - public $uidKey = 'uid'; - - public $scopes = array(); - - public $method = 'post'; - - public $scopeSeperator = ','; - - public $responseType = 'json'; - - public function __construct($options) - { - foreach ($options as $option => $value) { - if (isset($this->{$option})) { - $this->{$option} = $value; - } - } - } - - abstract public function urlAuthorize(); - - abstract public function urlAccessToken(); - - abstract public function urlUserDetails(\Oauth2\Client\Token\Access $token); - - abstract public function userDetails($response, \Oauth2\Client\Token\Access $token); - - public function authorize($options = array()) - { - $state = md5(uniqid(rand(), TRUE)); - setcookie($this->name.'_authorize_state', $state); - - $params = array( - 'client_id' => $this->clientId, - 'redirect_uri' => $this->redirectUri, - 'state' => $state, - 'scope' => is_array($this->scope) ? implode($this->scopeSeperator, $this->scope) : $this->scope, - 'response_type' => isset($options['response_type']) ? $options['response_type'] : 'code', - 'approval_prompt' => 'force' // - google force-recheck - ); - - header('Location: ' . $this->urlAuthorize().'?'.http_build_query($params)); - exit; - } - - public function getAccessToken($code = NULL, $options = array()) - { - if ($code === NULL) { - throw new \BadMethodCallException('Missing authorization code'); - } - - $params = array( - 'client_id' => $this->clientId, - 'client_secret' => $this->clientSecret, - 'grant_type' => isset($options['grantType']) ? $options['grantType'] : 'authorization_code', - ); - - switch ($params['grant_type']) { - - case 'authorization_code': - $params['code'] = $code; - $params['redirect_uri'] = isset($options['redirectUri']) ? $options['redirectUri'] : $this->redirectUri; - break; - - case 'refresh_token': - $params['refresh_token'] = $code; - break; - - } - - try { - - switch ($this->method) { - - case 'get': - - $client = new GuzzleClient($this->urlAccessToken() . '?' . http_build_query($params)); - $request = $client->send(); - $response = $request->getBody(); - - break; - - case 'post': - - $client = new GuzzleClient($this->urlAccessToken()); - $request = $client->post(null, null, $params)->send(); - $response = $request->getBody(); - - break; - - } - - } - - catch (\Guzzle\Http\Exception\BadResponseException $e) - { - $raw_response = explode("\n", $e->getResponse()); - $response = end($raw_response); - } - - switch ($this->responseType) { - - case 'json': - $result = json_decode($response, true); - break; - - case 'string': - parse_str($response, $result); - break; - - } - - if (isset($result['error']) && ! empty($result['error'])) { - - throw new \Oauth2\Client\IDPException($result); - - } - - switch ($params['grant_type']) { - - case 'authorization_code': - return \Oauth2\Client\Token::factory('access', $result); - break; - - case 'refresh_token': - return \Oauth2\Client\Token::factory('refresh', $result); - break; - - } - } - - public function getUserDetails(\Oauth2\Client\Token\Access $token) - { - $url = $this->urlUserDetails($token); - - try { - $client = new GuzzleClient($url); - $request = $client->get()->send(); - $response = $request->getBody(); - - return $this->userDetails(json_decode($response), $token); - } - - catch (\Guzzle\Http\Exception\BadResponseException $e) - { - $raw_response = explode("\n", $e->getResponse()); - throw new \Oauth2\Client\IDPException(end($raw_response)); - } - } - -} \ No newline at end of file diff --git a/src/Oauth2/Client/Provider.php b/src/Oauth2/Client/Provider.php deleted file mode 100755 index 0762d57e..00000000 --- a/src/Oauth2/Client/Provider.php +++ /dev/null @@ -1,19 +0,0 @@ - $token->access_token, - )); - - $user = json_decode(file_get_contents($url)); - - return array( - 'uid' => $user->id, - 'nickname' => $user->username, - 'name' => $user->name, - 'first_name' => $user->first_name, - 'last_name' => $user->last_name, - 'email' => isset($user->email) ? $user->email : null, - 'location' => isset($user->hometown->name) ? $user->hometown->name : null, - 'description' => isset($user->bio) ? $user->bio : null, - 'image' => 'https://graph.facebook.com/me/picture?type=normal&access_token='.$token->access_token, - 'urls' => array( - 'Facebook' => $user->link, - ), - ); - } -} diff --git a/src/Oauth2/Client/Provider/Facebook.php b/src/Oauth2/Client/Provider/Facebook.php deleted file mode 100755 index 53aba543..00000000 --- a/src/Oauth2/Client/Provider/Facebook.php +++ /dev/null @@ -1,49 +0,0 @@ - $token->access_token, - )); - - $user = json_decode(file_get_contents($url)); - - return array( - 'uid' => $user->id, - 'nickname' => isset($user->username) ? $user->username : null, - 'name' => $user->name, - 'first_name' => $user->first_name, - 'last_name' => $user->last_name, - 'email' => isset($user->email) ? $user->email : null, - 'location' => isset($user->hometown->name) ? $user->hometown->name : null, - 'description' => isset($user->bio) ? $user->bio : null, - 'image' => 'https://graph.facebook.com/me/picture?type=normal&access_token='.$token->access_token, - 'urls' => array( - 'Facebook' => $user->link, - ), - ); - } -} diff --git a/src/Oauth2/Client/Provider/Foursquare.php b/src/Oauth2/Client/Provider/Foursquare.php deleted file mode 100755 index ebf81cb1..00000000 --- a/src/Oauth2/Client/Provider/Foursquare.php +++ /dev/null @@ -1,45 +0,0 @@ - $token->access_token, - )); - - $response = json_decode(file_get_contents($url)); - - $user = $response->response->user; - - // Create a response from the request - return array( - 'uid' => $user->id, - 'name' => sprintf('%s %s', $user->firstName, $user->lastName), - 'email' => $user->contact->email, - 'image' => $user->photo, - 'location' => $user->homeCity, - ); - } -} \ No newline at end of file diff --git a/src/Oauth2/Client/Provider/Github.php b/src/Oauth2/Client/Provider/Github.php deleted file mode 100755 index acfd59ff..00000000 --- a/src/Oauth2/Client/Provider/Github.php +++ /dev/null @@ -1,43 +0,0 @@ - $token->access_token, - )); - - $user = json_decode(file_get_contents($url)); - - return array( - 'uid' => $user->id, - 'nickname' => $user->login, - 'name' => $user->name, - 'email' => $user->email, - 'urls' => array( - 'GitHub' => 'http://github.com/'.$user->login, - 'Blog' => $user->blog, - ), - ); - } -} \ No newline at end of file diff --git a/src/Oauth2/Client/Provider/Google.php b/src/Oauth2/Client/Provider/Google.php deleted file mode 100755 index b76d343b..00000000 --- a/src/Oauth2/Client/Provider/Google.php +++ /dev/null @@ -1,84 +0,0 @@ - 'Expected Authorization Code from '.ucfirst($this->name).' is missing')); - } - - return parent::access($code, $options); - } - - public function get_user_info(OAuth2_Token_Access $token) - { - $url = 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json&'.http_build_query(array( - 'access_token' => $token->access_token, - )); - - $user = json_decode(file_get_contents($url), true); - return array( - 'uid' => $user['id'], - 'nickname' => url_title($user['name'], '_', true), - 'name' => $user['name'], - 'first_name' => $user['given_name'], - 'last_name' => $user['family_name'], - 'email' => $user['email'], - 'location' => null, - 'image' => (isset($user['picture'])) ? $user['picture'] : null, - 'description' => null, - 'urls' => array(), - ); - } -} diff --git a/src/Oauth2/Client/Provider/Instagram.php b/src/Oauth2/Client/Provider/Instagram.php deleted file mode 100755 index 39c8bef1..00000000 --- a/src/Oauth2/Client/Provider/Instagram.php +++ /dev/null @@ -1,48 +0,0 @@ -user; - - return array( - 'uid' => $user->id, - 'nickname' => $user->username, - 'name' => $user->full_name, - 'image' => $user->profile_picture, - 'urls' => array( - 'website' => $user->website, - ), - ); - } -} \ No newline at end of file diff --git a/src/Oauth2/Client/Provider/Mailchimp.php b/src/Oauth2/Client/Provider/Mailchimp.php deleted file mode 100755 index 818bd40d..00000000 --- a/src/Oauth2/Client/Provider/Mailchimp.php +++ /dev/null @@ -1,36 +0,0 @@ - $token->access_token, - ); - } -} diff --git a/src/Oauth2/Client/Provider/Mailru.php b/src/Oauth2/Client/Provider/Mailru.php deleted file mode 100755 index 25234a20..00000000 --- a/src/Oauth2/Client/Provider/Mailru.php +++ /dev/null @@ -1,73 +0,0 @@ - $value) { - $params .= "$key=$value"; - } - return md5($params . $secret_key); - } - - public function get_user_info(OAuth2_Token_Access $token) - { - $request_params = array( - 'app_id' => $this->client_id, - 'method' => 'users.getInfo', - 'uids' => $token->uid, - 'access_token' => $token->access_token, - 'secure' => 1 - ); - - $sig = $this->sign_server_server($request_params,$this->client_secret); - $url = 'http://www.appsmail.ru/platform/api?'.http_build_query($request_params).'&sig='.$sig; - - $user = json_decode(file_get_contents($url)); - - return array( - 'uid' => $user[0]->uid, - 'nickname' => $user[0]->nick, - 'name' => $user[0]->first_name.' '.$user[0]->last_name, - 'first_name' => $user[0]->first_name, - 'last_name' => $user[0]->last_name, - 'email' => isset($user[0]->email) ? $user[0]->email : null, - 'image' => isset($user[0]->pic_big) ? $user[0]->pic_big : null, - ); - } - - public function authorize($options = array()) - { - $state = md5(uniqid(rand(), TRUE)); - get_instance()->session->set_userdata('state', $state); - - $params = array( - 'client_id' => $this->client_id, - 'redirect_uri' => isset($options['redirect_uri']) ? $options['redirect_uri'] : $this->redirect_uri, - 'response_type' => 'code', - ); - - redirect($this->url_authorize().'?'.http_build_query($params)); - } -} diff --git a/src/Oauth2/Client/Provider/Paypal.php b/src/Oauth2/Client/Provider/Paypal.php deleted file mode 100755 index 72d065fe..00000000 --- a/src/Oauth2/Client/Provider/Paypal.php +++ /dev/null @@ -1,59 +0,0 @@ - $token->access_token - )); - - $user = json_decode(file_get_contents($url)); - $user = $user->identity; - - return array( - 'uid' => $user['userId'], - 'nickname' => url_title($user['fullName'], '_', true), - 'name' => $user['fullName'], - 'first_name' => $user['firstName'], - 'last_name' => $user['lastName'], - 'email' => $user['emails'][0], - 'location' => $user->addresses[0], - 'image' => null, - 'description' => null, - 'urls' => array( - 'PayPal' => null - ) - ); - } - -} diff --git a/src/Oauth2/Client/Provider/Soundcloud.php b/src/Oauth2/Client/Provider/Soundcloud.php deleted file mode 100755 index 83d9afbc..00000000 --- a/src/Oauth2/Client/Provider/Soundcloud.php +++ /dev/null @@ -1,51 +0,0 @@ - $token->access_token, - )); - - $user = json_decode(file_get_contents($url)); - - // Create a response from the request - return array( - 'uid' => $user->id, - 'nickname' => $user->username, - 'name' => $user->full_name, - 'location' => $user->country.' ,'.$user->country, - 'description' => $user->description, - 'image' => $user->avatar_url, - 'urls' => array( - 'MySpace' => $user->myspace_name, - 'Website' => $user->website, - ), - ); - } -} diff --git a/src/Oauth2/Client/Provider/Vkontakte.php b/src/Oauth2/Client/Provider/Vkontakte.php deleted file mode 100755 index bdad78e4..00000000 --- a/src/Oauth2/Client/Provider/Vkontakte.php +++ /dev/null @@ -1,54 +0,0 @@ - $token->uid, - 'fields' => implode(",",$scope), - 'access_token' => $token->access_token, - )); - - $user = json_decode(file_get_contents($url))->response; - - if(sizeof($user)==0) - return null; - else - $user = $user[0]; - - return array( - 'uid' => $user->uid, - 'nickname' => isset($user->nickname) ? $user->nickname : null, - 'name' => isset($user->name) ? $user->name : null, - 'first_name' => isset($user->first_name) ? $user->first_name : null, - 'last_name' => isset($user->last_name) ? $user->last_name : null, - 'email' => null, - 'location' => null, - 'description' => null, - 'image' => isset($user->photo_big) ? $user->photo_big : null, - 'urls' => array(), - ); - } -} diff --git a/src/Oauth2/Client/Provider/Windowslive.php b/src/Oauth2/Client/Provider/Windowslive.php deleted file mode 100755 index 8d489c3d..00000000 --- a/src/Oauth2/Client/Provider/Windowslive.php +++ /dev/null @@ -1,60 +0,0 @@ - $token->access_token, - )); - - // perform network request - $user = json_decode(file_get_contents($url)); - - // create a response from the request and return it - return array( - 'uid' => $user->id, - 'name' => $user->name, - 'nickname' => url_title($user->name, '_', true), -// 'location' => $user[''], # scope wl.postal_addresses is required - # but won't be implemented by default - 'locale' => $user->locale, - 'urls' => array('Windows Live' => $user->link), - ); - } -} diff --git a/src/Oauth2/Client/Provider/Yandex.php b/src/Oauth2/Client/Provider/Yandex.php deleted file mode 100755 index 7a8f4f80..00000000 --- a/src/Oauth2/Client/Provider/Yandex.php +++ /dev/null @@ -1,115 +0,0 @@ - array( - 'method' => 'GET', - 'header' => 'Authorization: OAuth '.$token->access_token - ) - ); - $_default_opts = stream_context_get_params(stream_context_get_default()); - - $opts = array_merge_recursive($_default_opts['options'], $opts); - $context = stream_context_create($opts); - $url = 'http://api-yaru.yandex.ru/me/?format=json'; - - $user = json_decode(file_get_contents($url,false,$context)); - - preg_match("/\d+$/",$user->id,$uid); - - return array( - 'uid' => $uid[0], - 'nickname' => isset($user->name) ? $user->name : null, - 'name' => isset($user->name) ? $user->name : null, - 'first_name' => isset($user->first_name) ? $user->first_name : null, - 'last_name' => isset($user->last_name) ? $user->last_name : null, - 'email' => isset($user->email) ? $user->email : null, - 'location' => isset($user->hometown->name) ? $user->hometown->name : null, - 'description' => isset($user->bio) ? $user->bio : null, - 'image' => $user->links->userpic, - ); - } - - public function access($code, $options = array()) - { - $params = array( - 'client_id' => $this->client_id, - 'client_secret' => $this->client_secret, - 'grant_type' => isset($options['grant_type']) ? $options['grant_type'] : 'authorization_code', - ); - - switch ($params['grant_type']) - { - case 'authorization_code': - $params['code'] = $code; - $params['redirect_uri'] = isset($options['redirect_uri']) ? $options['redirect_uri'] : $this->redirect_uri; - break; - - case 'refresh_token': - $params['refresh_token'] = $code; - break; - } - - $response = null; - $url = $this->url_access_token(); - - $curl = curl_init($url); - - $headers[] = 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8;'; - curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); - -// curl_setopt($curl, CURLOPT_USERAGENT, 'yamolib-php'); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30); - curl_setopt($curl, CURLOPT_TIMEOUT, 80); - curl_setopt($curl, CURLOPT_POST, true); - curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($params)); - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); - // curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); - // curl_setopt($curl, CURLOPT_CAINFO, dirname(__FILE__) . '/../data/ca-certificate.crt'); - - $response = curl_exec($curl); - curl_close($curl); - - $return = json_decode($response, true); - - if ( ! empty($return['error'])) - { - throw new OAuth2_Exception($return); - } - - switch ($params['grant_type']) - { - case 'authorization_code': - return OAuth2_Token::factory('access', $return); - break; - - case 'refresh_token': - return OAuth2_Token::factory('refresh', $return); - break; - } - } - -} diff --git a/src/Oauth2/Client/Token.php b/src/Oauth2/Client/Token.php deleted file mode 100755 index 2cb15b12..00000000 --- a/src/Oauth2/Client/Token.php +++ /dev/null @@ -1,46 +0,0 @@ -$key; - } - - /** - * Return a boolean if the property is set - * - * @param string variable name - * @return bool - */ - public function __isset($key) - { - return isset($this->$key); - } - -} // End Token diff --git a/src/Oauth2/Client/Token/Access.php b/src/Oauth2/Client/Token/Access.php deleted file mode 100755 index 18d3cd49..00000000 --- a/src/Oauth2/Client/Token/Access.php +++ /dev/null @@ -1,79 +0,0 @@ -accessToken = $options['access_token']; - - // Some providers (not many) give the uid here, so lets take it - isset($options['uid']) and $this->uid = $options['uid']; - - //Vkontakte uses user_id instead of uid - isset($options['user_id']) and $this->uid = $options['user_id']; - - //Mailru uses x_mailru_vid instead of uid - isset($options['x_mailru_vid']) and $this->uid = $options['x_mailru_vid']; - - // We need to know when the token expires, add num. seconds to current time - isset($options['expires_in']) and $this->expires = time() + ((int) $options['expires_in']); - - // Facebook is just being a spec ignoring jerk - isset($options['expires']) and $this->expires = time() + ((int) $options['expires']); - - // Grab a refresh token so we can update access tokens when they expires - isset($options['refresh_token']) and $this->refreshToken = $options['refresh_token']; - } - - /** - * Returns the token key. - * - * @return string - */ - public function __toString() - { - return (string) $this->accessToken; - } - -} \ No newline at end of file diff --git a/src/Oauth2/Client/Token/Authorize.php b/src/Oauth2/Client/Token/Authorize.php deleted file mode 100755 index f9358385..00000000 --- a/src/Oauth2/Client/Token/Authorize.php +++ /dev/null @@ -1,55 +0,0 @@ -code = $options['code']; - $this->redirectUri = $options['redirect_uri']; - } - - /** - * Returns the token key. - * - * @return string - */ - public function __toString() - { - return (string) $this->code; - } - -} \ No newline at end of file diff --git a/src/Oauth2/Resource/Database.php b/src/Oauth2/Resource/Database.php deleted file mode 100644 index 8408df3a..00000000 --- a/src/Oauth2/Resource/Database.php +++ /dev/null @@ -1,59 +0,0 @@ - - * SELECT id, owner_type, owner_id FROM oauth_sessions WHERE access_token = - * $accessToken AND stage = 'granted' AND - * access_token_expires > UNIX_TIMESTAMP(now()) - * - * - * Response: - * - * - * Array - * ( - * [id] => (int) The session ID - * [owner_type] => (string) The session owner type - * [owner_id] => (string) The session owner's ID - * ) - * - * - * @param string $accessToken The access token - * @return array|bool Return an array on success or false on failure - */ - public function validateAccessToken($accessToken); - - /** - * Returns the scopes that the session is authorised with. - * - * Database query: - * - * - * SELECT scope FROM oauth_session_scopes WHERE session_id = - * $sessionId - * - * - * Response: - * - * - * Array - * ( - * [0] => (string) A scope - * [1] => (string) Another scope - * ... - * ) - * - * - * @param int $sessionId The session ID - * @return array A list of scopes - */ - public function sessionScopes($sessionId); -} \ No newline at end of file diff --git a/src/Oauth2/Resource/Server.php b/src/Oauth2/Resource/Server.php deleted file mode 100644 index 9b45bc41..00000000 --- a/src/Oauth2/Resource/Server.php +++ /dev/null @@ -1,253 +0,0 @@ - 'oauth_token' - ); - - /** - * Error codes. - * - * To provide i8ln errors just overwrite the keys - * - * @var array - */ - public $errors = array( - 'missing_access_token' => 'An access token was not presented with the request', - 'invalid_access_token' => 'The access token is not registered with the resource server', - 'missing_access_token_details' => 'The registered database abstractor did not return a valid access token details response', - 'invalid_access_token_scopes' => 'The registered database abstractor did not return a valid access token scopes response', - ); - - /** - * Constructor - * - * @access public - * @return void - */ - public function __construct($options = null) - { - if ($options !== null) { - $this->_config = array_merge($this->_config, $options); - } - } - - /** - * Magic method to test if access token represents a particular owner type - * @param string $method The method name - * @param mixed $arguements The method arguements - * @return bool If method is valid, and access token is owned by the requested party then true, - */ - public function __call($method, $arguements = null) - { - if (substr($method, 0, 2) === 'is') { - - if ($this->_type === strtolower(substr($method, 2))) { - return $this->_typeId; - } - - return false; - } - - trigger_error('Call to undefined function ' . $method . '()'); - } - - /** - * Register a database abstrator class - * - * @access public - * @param object $db A class that implements OAuth2ServerDatabase - * @return void - */ - public function registerDbAbstractor($db) - { - $this->_db = $db; - } - - /** - * Init function - * - * @access public - * @return void - */ - public function init() - { - $accessToken = null; - - $_SERVER['REQUEST_METHOD'] = isset($_SERVER['REQUEST_METHOD']) ? - $_SERVER['REQUEST_METHOD'] : - null; - - // Try and get the access token via an access_token or oauth_token parameter - switch ($_SERVER['REQUEST_METHOD']) - { - case 'POST': - $accessToken = isset($_POST[$this->_config['token_key']]) ? - $_POST[$this->_config['token_key']] : - null; - break; - - default: - $accessToken = isset($_GET[$this->_config['token_key']]) ? - $_GET[$this->_config['token_key']] : - null; - break; - } - - // Try and get an access token from the auth header - if (function_exists('getallheaders')) { - - $headers = getallheaders(); - - if (isset($headers['Authorization'])) { - - $rawToken = base64_decode(str_replace('Bearer ', '', trim($headers['Authorization']))); - - if ( ! empty($rawToken)) { - $accessToken = $rawToken; - } - } - } - - if ($accessToken) { - - $result = $this->_dbCall('validateAccessToken', $accessToken); - - if ($result === false) { - - throw new ClientException($this->errors['invalid_access_token']); - - } else { - - if ( ! array_key_exists('id', $result) || - ! array_key_exists('owner_id', $result) || - ! array_key_exists('owner_type', $result)) { - throw new ServerException($this->errors['missing_access_token_details']); - } - - $this->_accessToken = $accessToken; - $this->_type = $result['owner_type']; - $this->_typeId = $result['owner_id']; - - // Get the scopes - $scopes = $this->_dbCall('sessionScopes', $result['id']); - - if ( ! is_array($scopes)) - { - throw new ServerException($this->errors['invalid_access_token_scopes']); - } - - $this->_scopes = $scopes; - } - - } else { - - throw new ClientException($this->errors['missing_access_token']); - - } - } - - /** - * Test if the access token has a specific scope - * - * @param mixed $scopes Scope(s) to check - * - * @access public - * @return string|bool - */ - public function hasScope($scopes) - { - if (is_string($scopes)) { - - if (in_array($scopes, $this->_scopes)) { - return true; - } - - return false; - - } elseif (is_array($scopes)) { - - foreach ($scopes as $scope) { - - if ( ! in_array($scope, $this->_scopes)) { - return false; - } - - } - - return true; - } - - return false; - } - - /** - * Call database methods from the abstractor - * - * @return mixed The query result - */ - private function _dbCall() - { - if ($this->_db === null) { - throw new ServerException('No registered database abstractor'); - } - - if ( ! $this->_db instanceof Database) { - throw new ServerException('The registered database abstractor is not an instance of Oauth2\Resource\Database'); - } - - $args = func_get_args(); - $method = $args[0]; - unset($args[0]); - $params = array_values($args); - - return call_user_func_array(array($this->_db, $method), $params); - } -} diff --git a/tests/authentication/AuthServerTest.php b/tests/authentication/AuthServerTest.php new file mode 100644 index 00000000..f8f3e35e --- /dev/null +++ b/tests/authentication/AuthServerTest.php @@ -0,0 +1,606 @@ +client = M::mock('OAuth2\Storage\ClientInterface'); + $this->session = M::mock('OAuth2\Storage\SessionInterface'); + $this->scope = M::mock('OAuth2\Storage\ScopeInterface'); + } + + private function returnDefault() + { + return new OAuth2\AuthServer($this->client, $this->session, $this->scope); + } + + /** + * @expectedException PHPUnit_Framework_Error + */ + public function test__construct_NoStorage() + { + $a = new OAuth2\AuthServer; + } + + public function test__contruct_WithStorage() + { + $a = $this->returnDefault(); + } + + public function test_getExceptionMessage() + { + $m = OAuth2\AuthServer::getExceptionMessage('access_denied'); + + $reflector = new ReflectionClass($this->returnDefault()); + $exceptionMessages = $reflector->getProperty('exceptionMessages'); + $exceptionMessages->setAccessible(true); + $v = $exceptionMessages->getValue(); + + $this->assertEquals($v['access_denied'], $m); + } + + public function test_getExceptionCode() + { + $this->assertEquals('access_denied', OAuth2\AuthServer::getExceptionType(2)); + } + + public function test_hasGrantType() + { + $this->assertFalse(OAuth2\AuthServer::hasGrantType('test')); + } + + public function test_addGrantType() + { + $a = $this->returnDefault(); + $grant = M::mock('OAuth2\Grant\GrantTypeInterface'); + $grant->shouldReceive('getResponseType')->andReturn('test'); + $a->addGrantType($grant, 'test'); + + $this->assertTrue(OAuth2\AuthServer::hasGrantType('test')); + } + + public function test_addGrantType_noIdentifier() + { + $a = $this->returnDefault(); + $grant = M::mock('OAuth2\Grant\GrantTypeInterface'); + $grant->shouldReceive('getIdentifier')->andReturn('test'); + $grant->shouldReceive('getResponseType')->andReturn('test'); + $a->addGrantType($grant); + + $this->assertTrue(OAuth2\AuthServer::hasGrantType('test')); + } + + public function test_getScopeDelimeter() + { + $a = $this->returnDefault(); + $this->assertEquals(',', $a->getScopeDelimeter()); + } + + public function test_setScopeDelimeter() + { + $a = $this->returnDefault(); + $a->setScopeDelimeter(';'); + $this->assertEquals(';', $a->getScopeDelimeter()); + } + + public function test_getExpiresIn() + { + $a = $this->returnDefault(); + $a->setExpiresIn(7200); + $this->assertEquals(7200, $a::getExpiresIn()); + } + + public function test_setExpiresIn() + { + $a = $this->returnDefault(); + $a->setScopeDelimeter(';'); + $this->assertEquals(';', $a->getScopeDelimeter()); + } + + public function test_setRequest() + { + $a = $this->returnDefault(); + $request = new OAuth2\Util\Request(); + $a->setRequest($request); + + $reflector = new ReflectionClass($a); + $requestProperty = $reflector->getProperty('request'); + $requestProperty->setAccessible(true); + $v = $requestProperty->getValue(); + + $this->assertTrue($v instanceof OAuth2\Util\RequestInterface); + } + + public function test_getRequest() + { + $a = $this->returnDefault(); + $request = new OAuth2\Util\Request(); + $a->setRequest($request); + $v = $a::getRequest(); + + $this->assertTrue($v instanceof OAuth2\Util\RequestInterface); + } + + public function test_getStorage() + { + $a = $this->returnDefault(); + $this->assertTrue($a->getStorage('session') instanceof OAuth2\Storage\SessionInterface); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_checkAuthoriseParams_noClientId() + { + $a = $this->returnDefault(); + $a->checkAuthoriseParams(); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_checkAuthoriseParams_noRedirectUri() + { + $a = $this->returnDefault(); + $a->checkAuthoriseParams(array( + 'client_id' => 1234 + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 8 + */ + public function test_checkAuthoriseParams_badClient() + { + $this->client->shouldReceive('getClient')->andReturn(false); + + $a = $this->returnDefault(); + $a->checkAuthoriseParams(array( + 'client_id' => 1234, + 'redirect_uri' => 'http://foo/redirect' + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_checkAuthoriseParams_missingResponseType() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $a = $this->returnDefault(); + $a->checkAuthoriseParams(array( + 'client_id' => 1234, + 'redirect_uri' => 'http://foo/redirect' + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 3 + */ + public function test_checkAuthoriseParams_badResponseType() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $a = $this->returnDefault(); + $a->checkAuthoriseParams(array( + 'client_id' => 1234, + 'redirect_uri' => 'http://foo/redirect', + 'response_type' => 'foo' + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_checkAuthoriseParams_missingScopes() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $a->checkAuthoriseParams(array( + 'client_id' => 1234, + 'redirect_uri' => 'http://foo/redirect', + 'response_type' => 'code', + 'scope' => '' + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 4 + */ + public function test_checkAuthoriseParams_badScopes() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->scope->shouldReceive('getScope')->andReturn(false); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $a->checkAuthoriseParams(array( + 'client_id' => 1234, + 'redirect_uri' => 'http://foo/redirect', + 'response_type' => 'code', + 'scope' => 'foo' + )); + } + + public function test_checkAuthoriseParams_passedInput() + { + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->scope->shouldReceive('getScope')->andReturn(array( + 'id' => 1, + 'scope' => 'foo', + 'name' => 'Foo Name', + 'description' => 'Foo Name Description' + )); + + $v = $a->checkAuthoriseParams(array( + 'client_id' => 1234, + 'redirect_uri' => 'http://foo/redirect', + 'response_type' => 'code', + 'scope' => 'foo' + )); + + $this->assertEquals(array( + 'client_id' => 1234, + 'redirect_uri' => 'http://foo/redirect', + 'client_details' => array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + ), + 'response_type' => 'code', + 'scopes' => array( + array( + 'id' => 1, + 'scope' => 'foo', + 'name' => 'Foo Name', + 'description' => 'Foo Name Description' + ) + ) + ), $v); + } + + public function test_checkAuthoriseParams() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->scope->shouldReceive('getScope')->andReturn(array( + 'id' => 1, + 'scope' => 'foo', + 'name' => 'Foo Name', + 'description' => 'Foo Name Description' + )); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $_GET['client_id'] = 1234; + $_GET['redirect_uri'] = 'http://foo/redirect'; + $_GET['response_type'] = 'code'; + $_GET['scope'] = 'foo'; + + $request = new OAuth2\Util\Request($_GET); + $a->setRequest($request); + + $v = $a->checkAuthoriseParams(); + + $this->assertEquals(array( + 'client_id' => 1234, + 'redirect_uri' => 'http://foo/redirect', + 'client_details' => array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + ), + 'response_type' => 'code', + 'scopes' => array( + array( + 'id' => 1, + 'scope' => 'foo', + 'name' => 'Foo Name', + 'description' => 'Foo Name Description' + ) + ) + ), $v); + } + + function test_newAuthoriseRequest() + { + $this->session->shouldReceive('deleteSession')->andReturn(null); + $this->session->shouldReceive('createSession')->andReturn(1); + $this->session->shouldReceive('associateScope')->andReturn(null); + + $a = $this->returnDefault(); + + $params = array( + 'client_id' => 1234, + 'redirect_uri' => 'http://foo/redirect', + 'client_details' => array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + ), + 'response_type' => 'code', + 'scopes' => array( + array( + 'id' => 1, + 'scope' => 'foo', + 'name' => 'Foo Name', + 'description' => 'Foo Name Description' + ) + ) + ); + + $v = $a->newAuthoriseRequest('user', 123, $params); + + $this->assertEquals(40, strlen($v)); + } + + public function test_getGrantType() + { + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $reflector = new ReflectionClass($a); + $method = $reflector->getMethod('getGrantType'); + $method->setAccessible(true); + + $result = $method->invoke($a, 'authorization_code'); + + $this->assertTrue($result instanceof OAuth2\Grant\GrantTypeInterface); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_issueAccessToken_missingGrantType() + { + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $v = $a->issueAccessToken(); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 7 + */ + public function test_issueAccessToken_badGrantType() + { + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $v = $a->issueAccessToken(array('grant_type' => 'foo')); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_issueAccessToken_missingClientId() + { + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'authorization_code' + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_issueAccessToken_missingClientSecret() + { + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'authorization_code', + 'client_id' => 1234 + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_issueAccessToken_missingRedirectUri() + { + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'authorization_code', + 'client_id' => 1234, + 'client_secret' => 5678 + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 8 + */ + public function test_issueAccessToken_badClient() + { + $this->client->shouldReceive('getClient')->andReturn(false); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'authorization_code', + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect' + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_issueAccessToken_missingCode() + { + $this->client->shouldReceive('getClient')->andReturn(array()); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'authorization_code', + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect' + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 9 + */ + public function test_issueAccessToken_badCode() + { + $this->client->shouldReceive('getClient')->andReturn(array()); + $this->session->shouldReceive('validateAuthCode')->andReturn(false); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'authorization_code', + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'code' => 'foobar' + )); + } + + public function test_issueAccessToken_passedInput() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('updateSession')->andReturn(null); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'authorization_code', + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'code' => 'foobar' + )); + + $this->assertArrayHasKey('access_token', $v); + $this->assertArrayHasKey('token_type', $v); + $this->assertArrayHasKey('expires', $v); + $this->assertArrayHasKey('expires_in', $v); + + $this->assertEquals($a::getExpiresIn(), $v['expires_in']); + $this->assertEquals(time()+$a::getExpiresIn(), $v['expires']); + } + + public function test_issueAccessToken() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('updateSession')->andReturn(null); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + + $_POST['grant_type'] = 'authorization_code'; + $_POST['client_id'] = 1234; + $_POST['client_secret'] = 5678; + $_POST['redirect_uri'] = 'http://foo/redirect'; + $_POST['code'] = 'foobar'; + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(); + + $this->assertArrayHasKey('access_token', $v); + $this->assertArrayHasKey('token_type', $v); + $this->assertArrayHasKey('expires', $v); + $this->assertArrayHasKey('expires_in', $v); + + $this->assertEquals($a::getExpiresIn(), $v['expires_in']); + $this->assertEquals(time()+$a::getExpiresIn(), $v['expires']); + } + + public function tearDown() { + M::close(); + } +} \ No newline at end of file diff --git a/tests/authentication/ClientCredentialsGrantTest.php b/tests/authentication/ClientCredentialsGrantTest.php new file mode 100644 index 00000000..72e68919 --- /dev/null +++ b/tests/authentication/ClientCredentialsGrantTest.php @@ -0,0 +1,189 @@ +client = M::mock('OAuth2\Storage\ClientInterface'); + $this->session = M::mock('OAuth2\Storage\SessionInterface'); + $this->scope = M::mock('OAuth2\Storage\ScopeInterface'); + } + + private function returnDefault() + { + return new OAuth2\AuthServer($this->client, $this->session, $this->scope); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_issueAccessToken_clientCredentialsGrant_missingClientId() + { + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\ClientCredentials()); + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'client_credentials' + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_issueAccessToken_clientCredentialsGrant_missingClientPassword() + { + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\ClientCredentials()); + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'client_credentials', + 'client_id' => 1234 + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 8 + */ + public function test_issueAccessToken_clientCredentialsGrant_badClient() + { + $this->client->shouldReceive('getClient')->andReturn(false); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\ClientCredentials()); + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'client_credentials', + 'client_id' => 1234, + 'client_secret' => 5678 + )); + } + + function test_issueAccessToken_clientCredentialsGrant_passedInput() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->client->shouldReceive('validateRefreshToken')->andReturn(1); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('createSession')->andReturn(1); + $this->session->shouldReceive('deleteSession')->andReturn(null); + $this->session->shouldReceive('updateRefreshToken')->andReturn(null); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\ClientCredentials()); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'client_credentials', + 'client_id' => 1234, + 'client_secret' => 5678 + )); + + $this->assertArrayHasKey('access_token', $v); + $this->assertArrayHasKey('token_type', $v); + $this->assertArrayHasKey('expires', $v); + $this->assertArrayHasKey('expires_in', $v); + + $this->assertEquals($a::getExpiresIn(), $v['expires_in']); + $this->assertEquals(time()+$a::getExpiresIn(), $v['expires']); + } + + function test_issueAccessToken_clientCredentialsGrant() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->client->shouldReceive('validateRefreshToken')->andReturn(1); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('createSession')->andReturn(1); + $this->session->shouldReceive('deleteSession')->andReturn(null); + $this->session->shouldReceive('updateRefreshToken')->andReturn(null); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\ClientCredentials()); + + $_POST['grant_type'] = 'client_credentials'; + $_POST['client_id'] = 1234; + $_POST['client_secret'] = 5678; + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(); + + $this->assertArrayHasKey('access_token', $v); + $this->assertArrayHasKey('token_type', $v); + $this->assertArrayHasKey('expires', $v); + $this->assertArrayHasKey('expires_in', $v); + + $this->assertEquals($a::getExpiresIn(), $v['expires_in']); + $this->assertEquals(time()+$a::getExpiresIn(), $v['expires']); + } + + function test_issueAccessToken_clientCredentialsGrant_withRefreshToken() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->client->shouldReceive('validateRefreshToken')->andReturn(1); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('createSession')->andReturn(1); + $this->session->shouldReceive('deleteSession')->andReturn(null); + $this->session->shouldReceive('updateRefreshToken')->andReturn(null); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\ClientCredentials()); + $a->addGrantType(new OAuth2\Grant\RefreshToken()); + + $_POST['grant_type'] = 'client_credentials'; + $_POST['client_id'] = 1234; + $_POST['client_secret'] = 5678; + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(); + + $this->assertArrayHasKey('access_token', $v); + $this->assertArrayHasKey('token_type', $v); + $this->assertArrayHasKey('expires', $v); + $this->assertArrayHasKey('expires_in', $v); + $this->assertArrayHasKey('refresh_token', $v); + + $this->assertEquals($a::getExpiresIn(), $v['expires_in']); + $this->assertEquals(time()+$a::getExpiresIn(), $v['expires']); + } + +} \ No newline at end of file diff --git a/tests/authentication/PasswordGrantTest.php b/tests/authentication/PasswordGrantTest.php new file mode 100644 index 00000000..ac6a7c83 --- /dev/null +++ b/tests/authentication/PasswordGrantTest.php @@ -0,0 +1,347 @@ +client = M::mock('OAuth2\Storage\ClientInterface'); + $this->session = M::mock('OAuth2\Storage\SessionInterface'); + $this->scope = M::mock('OAuth2\Storage\ScopeInterface'); + } + + private function returnDefault() + { + return new OAuth2\AuthServer($this->client, $this->session, $this->scope); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_issueAccessToken_passwordGrant_missingClientId() + { + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\Password()); + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'password' + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_issueAccessToken_passwordGrant_missingClientPassword() + { + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\Password()); + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'password', + 'client_id' => 1234 + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 8 + */ + public function test_issueAccessToken_passwordGrant_badClient() + { + $this->client->shouldReceive('getClient')->andReturn(false); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\Password()); + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'password', + 'client_id' => 1234, + 'client_secret' => 5678 + )); + } + + /** + * @expectedException OAuth2\Exception\InvalidGrantTypeException + */ + function test_issueAccessToken_passwordGrant_invalidCallback() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->client->shouldReceive('validateRefreshToken')->andReturn(1); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('createSession')->andReturn(1); + $this->session->shouldReceive('deleteSession')->andReturn(null); + $this->session->shouldReceive('updateRefreshToken')->andReturn(null); + + $testCredentials = null; + + $a = $this->returnDefault(); + $pgrant = new OAuth2\Grant\Password(); + $pgrant->setVerifyCredentialsCallback($testCredentials); + $a->addGrantType($pgrant); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'password', + 'client_id' => 1234, + 'client_secret' => 5678, + 'username' => 'foo', + 'password' => 'bar' + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + function test_issueAccessToken_passwordGrant_missingUsername() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->client->shouldReceive('validateRefreshToken')->andReturn(1); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('createSession')->andReturn(1); + $this->session->shouldReceive('deleteSession')->andReturn(null); + $this->session->shouldReceive('updateRefreshToken')->andReturn(null); + + $testCredentials = function($u, $p) { return false; }; + + $a = $this->returnDefault(); + $pgrant = new OAuth2\Grant\Password(); + $pgrant->setVerifyCredentialsCallback($testCredentials); + $a->addGrantType($pgrant); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'password', + 'client_id' => 1234, + 'client_secret' => 5678 + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + function test_issueAccessToken_passwordGrant_missingPassword() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->client->shouldReceive('validateRefreshToken')->andReturn(1); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('createSession')->andReturn(1); + $this->session->shouldReceive('deleteSession')->andReturn(null); + $this->session->shouldReceive('updateRefreshToken')->andReturn(null); + + $testCredentials = function($u, $p) { return false; }; + + $a = $this->returnDefault(); + $pgrant = new OAuth2\Grant\Password(); + $pgrant->setVerifyCredentialsCallback($testCredentials); + $a->addGrantType($pgrant); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'password', + 'client_id' => 1234, + 'client_secret' => 5678, + 'username' => 'foo' + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + function test_issueAccessToken_passwordGrant_badCredentials() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->client->shouldReceive('validateRefreshToken')->andReturn(1); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('createSession')->andReturn(1); + $this->session->shouldReceive('deleteSession')->andReturn(null); + $this->session->shouldReceive('updateRefreshToken')->andReturn(null); + + $testCredentials = function($u, $p) { return false; }; + + $a = $this->returnDefault(); + $pgrant = new OAuth2\Grant\Password(); + $pgrant->setVerifyCredentialsCallback($testCredentials); + $a->addGrantType($pgrant); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'password', + 'client_id' => 1234, + 'client_secret' => 5678, + 'username' => 'foo', + 'password' => 'bar' + )); + } + + function test_issueAccessToken_passwordGrant_passedInput() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->client->shouldReceive('validateRefreshToken')->andReturn(1); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('createSession')->andReturn(1); + $this->session->shouldReceive('deleteSession')->andReturn(null); + $this->session->shouldReceive('updateRefreshToken')->andReturn(null); + + $testCredentials = function($u, $p) { return 1; }; + + $a = $this->returnDefault(); + $pgrant = new OAuth2\Grant\Password(); + $pgrant->setVerifyCredentialsCallback($testCredentials); + $a->addGrantType($pgrant); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'password', + 'client_id' => 1234, + 'client_secret' => 5678, + 'username' => 'foo', + 'password' => 'bar' + )); + + $this->assertArrayHasKey('access_token', $v); + $this->assertArrayHasKey('token_type', $v); + $this->assertArrayHasKey('expires', $v); + $this->assertArrayHasKey('expires_in', $v); + + $this->assertEquals($a::getExpiresIn(), $v['expires_in']); + $this->assertEquals(time()+$a::getExpiresIn(), $v['expires']); + } + + function test_issueAccessToken_passwordGrant() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->client->shouldReceive('validateRefreshToken')->andReturn(1); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('createSession')->andReturn(1); + $this->session->shouldReceive('deleteSession')->andReturn(null); + $this->session->shouldReceive('updateRefreshToken')->andReturn(null); + + $testCredentials = function($u, $p) { return 1; }; + + $a = $this->returnDefault(); + $pgrant = new OAuth2\Grant\Password(); + $pgrant->setVerifyCredentialsCallback($testCredentials); + $a->addGrantType($pgrant); + + $_POST['grant_type'] = 'password'; + $_POST['client_id'] = 1234; + $_POST['client_secret'] = 5678; + $_POST['username'] = 'foo'; + $_POST['password'] = 'bar'; + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(); + + $this->assertArrayHasKey('access_token', $v); + $this->assertArrayHasKey('token_type', $v); + $this->assertArrayHasKey('expires', $v); + $this->assertArrayHasKey('expires_in', $v); + + $this->assertEquals($a::getExpiresIn(), $v['expires_in']); + $this->assertEquals(time()+$a::getExpiresIn(), $v['expires']); + } + + function test_issueAccessToken_passwordGrant_withRefreshToken() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->client->shouldReceive('validateRefreshToken')->andReturn(1); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('createSession')->andReturn(1); + $this->session->shouldReceive('deleteSession')->andReturn(null); + $this->session->shouldReceive('updateRefreshToken')->andReturn(null); + + $testCredentials = function($u, $p) { return 1; }; + + $a = $this->returnDefault(); + $pgrant = new OAuth2\Grant\Password(); + $pgrant->setVerifyCredentialsCallback($testCredentials); + $a->addGrantType($pgrant); + $a->addGrantType(new OAuth2\Grant\RefreshToken()); + + $_POST['grant_type'] = 'password'; + $_POST['client_id'] = 1234; + $_POST['client_secret'] = 5678; + $_POST['username'] = 'foo'; + $_POST['password'] = 'bar'; + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(); + + $this->assertArrayHasKey('access_token', $v); + $this->assertArrayHasKey('token_type', $v); + $this->assertArrayHasKey('expires', $v); + $this->assertArrayHasKey('expires_in', $v); + $this->assertArrayHasKey('refresh_token', $v); + + $this->assertEquals($a::getExpiresIn(), $v['expires_in']); + $this->assertEquals(time()+$a::getExpiresIn(), $v['expires']); + } + +} \ No newline at end of file diff --git a/tests/authentication/RefreshTokenTest.php b/tests/authentication/RefreshTokenTest.php new file mode 100644 index 00000000..556477b3 --- /dev/null +++ b/tests/authentication/RefreshTokenTest.php @@ -0,0 +1,233 @@ +client = M::mock('OAuth2\Storage\ClientInterface'); + $this->session = M::mock('OAuth2\Storage\SessionInterface'); + $this->scope = M::mock('OAuth2\Storage\ScopeInterface'); + } + + private function returnDefault() + { + return new OAuth2\AuthServer($this->client, $this->session, $this->scope); + } + + public function test_issueAccessToken_with_refresh_token() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('updateSession')->andReturn(null); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\AuthCode()); + $a->addGrantType(new OAuth2\Grant\RefreshToken()); + + $_POST['grant_type'] = 'authorization_code'; + $_POST['client_id'] = 1234; + $_POST['client_secret'] = 5678; + $_POST['redirect_uri'] = 'http://foo/redirect'; + $_POST['code'] = 'foobar'; + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(); + + $this->assertArrayHasKey('access_token', $v); + $this->assertArrayHasKey('token_type', $v); + $this->assertArrayHasKey('expires', $v); + $this->assertArrayHasKey('expires_in', $v); + $this->assertArrayHasKey('refresh_token', $v); + + $this->assertEquals($a::getExpiresIn(), $v['expires_in']); + $this->assertEquals(time()+$a::getExpiresIn(), $v['expires']); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_issueAccessToken_refreshTokenGrant_missingClientId() + { + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\RefreshToken()); + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'refresh_token' + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_issueAccessToken_refreshTokenGrant_missingClientSecret() + { + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\RefreshToken()); + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'refresh_token', + 'client_id' => 1234 + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 8 + */ + public function test_issueAccessToken_refreshTokenGrant_badClient() + { + $this->client->shouldReceive('getClient')->andReturn(false); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\RefreshToken()); + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'refresh_token', + 'client_id' => 1234, + 'client_secret' => 5678 + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_issueAccessToken_refreshTokenGrant_missingRefreshToken() + { + $this->client->shouldReceive('getClient')->andReturn(array()); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\RefreshToken()); + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'refresh_token', + 'client_id' => 1234, + 'client_secret' => 5678, + //'refresh_token' => + )); + } + + /** + * @expectedException OAuth2\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_issueAccessToken_refreshTokenGrant_badRefreshToken() + { + $this->client->shouldReceive('getClient')->andReturn(array()); + $this->client->shouldReceive('validateRefreshToken')->andReturn(false); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\RefreshToken()); + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'refresh_token', + 'client_id' => 1234, + 'client_secret' => 5678, + 'refresh_token' => 'abcdef' + )); + } + + public function test_issueAccessToken_refreshTokenGrant_passedInput() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->client->shouldReceive('validateRefreshToken')->andReturn(1); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('updateSession')->andReturn(null); + $this->session->shouldReceive('updateRefreshToken')->andReturn(null); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\RefreshToken()); + + $_POST['grant_type'] = 'refresh_token'; + $_POST['client_id'] = 1234; + $_POST['client_secret'] = 5678; + $_POST['refresh_token'] = 'abcdef'; + + $request = new OAuth2\Util\Request(array(), $_POST); + $a->setRequest($request); + + $v = $a->issueAccessToken(); + + $this->assertArrayHasKey('access_token', $v); + $this->assertArrayHasKey('token_type', $v); + $this->assertArrayHasKey('expires', $v); + $this->assertArrayHasKey('expires_in', $v); + $this->assertArrayHasKey('refresh_token', $v); + + $this->assertEquals($a::getExpiresIn(), $v['expires_in']); + $this->assertEquals(time()+$a::getExpiresIn(), $v['expires']); + } + + public function test_issueAccessToken_refreshTokenGrant() + { + $this->client->shouldReceive('getClient')->andReturn(array( + 'client_id' => 1234, + 'client_secret' => 5678, + 'redirect_uri' => 'http://foo/redirect', + 'name' => 'Example Client' + )); + + $this->client->shouldReceive('validateRefreshToken')->andReturn(1); + + $this->session->shouldReceive('validateAuthCode')->andReturn(1); + $this->session->shouldReceive('updateSession')->andReturn(null); + $this->session->shouldReceive('updateRefreshToken')->andReturn(null); + + $a = $this->returnDefault(); + $a->addGrantType(new OAuth2\Grant\RefreshToken()); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'refresh_token', + 'client_id' => 1234, + 'client_secret' => 5678, + 'refresh_token' => 'abcdef', + )); + + $this->assertArrayHasKey('access_token', $v); + $this->assertArrayHasKey('token_type', $v); + $this->assertArrayHasKey('expires', $v); + $this->assertArrayHasKey('expires_in', $v); + $this->assertArrayHasKey('refresh_token', $v); + + $this->assertEquals($a::getExpiresIn(), $v['expires_in']); + $this->assertEquals(time()+$a::getExpiresIn(), $v['expires']); + } +} \ No newline at end of file diff --git a/tests/authentication/database_mock.php b/tests/authentication/database_mock.php deleted file mode 100644 index 594d2665..00000000 --- a/tests/authentication/database_mock.php +++ /dev/null @@ -1,168 +0,0 @@ - array( - 'client_id' => 'test', - 'client_secret' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'name' => 'Test Client' - )); - - private $scopes = array('test' => array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )); - - public function validateClient($clientId, $clientSecret = null, $redirectUri = null) - { - if ($clientId !== $this->clients[0]['client_id']) { - return false; - } - - if ($clientSecret !== null && $clientSecret !== $this->clients[0]['client_secret']) { - return false; - } - - if ($redirectUri !== null && $redirectUri !== $this->clients[0]['redirect_uri']) { - return false; - } - - return $this->clients[0]; - } - - public function newSession($clientId, $redirectUri, $type = 'user', $typeId = null, $authCode = null, $accessToken = null, $refreshToken = null, $accessTokenExpire = null, $stage = 'requested') - { - $id = count($this->sessions); - - $this->sessions[$id] = array( - 'id' => $id, - 'client_id' => $clientId, - 'redirect_uri' => $redirectUri, - 'owner_type' => $type, - 'owner_id' => $typeId, - 'auth_code' => $authCode, - 'access_token' => $accessToken, - 'refresh_token' => $refreshToken, - 'access_token_expire' => $accessTokenExpire, - 'stage' => $stage - ); - - $this->sessions_client_type_id[$clientId . ':' . $type . ':' . $typeId] = $id; - $this->sessions_code[$clientId . ':' . $redirectUri . ':' . $authCode] = $id; - - return $id; - } - - public function updateSession($sessionId, $authCode = null, $accessToken = null, $refreshToken = null, $accessTokenExpire = null, $stage = 'requested') - { - $this->sessions[$sessionId]['auth_code'] = $authCode; - $this->sessions[$sessionId]['access_token'] = $accessToken; - $this->sessions[$sessionId]['refresh_token'] = $refreshToken; - $this->sessions[$sessionId]['access_token_expire'] = $accessTokenExpire; - $this->sessions[$sessionId]['stage'] = $stage; - - return true; - } - - public function deleteSession($clientId, $type, $typeId) - { - $key = $clientId . ':' . $type . ':' . $typeId; - if (isset($this->sessions_client_type_id[$key])) { - unset($this->sessions[$this->sessions_client_type_id[$key]]); - } - return true; - } - - public function refreshToken($currentRefreshToken, $newAccessToken, $newRefreshToken, $accessTokenExpires) - { - die('not implemented refreshToken'); - } - - public function validateAuthCode($clientId, $redirectUri, $authCode) - { - $key = $clientId . ':' . $redirectUri . ':' . $authCode; - - if (isset($this->sessions_code[$key])) { - return $this->sessions[$this->sessions_code[$key]]; - } - - return false; - } - - public function hasSession($type, $typeId, $clientId) - { - die('not implemented hasSession'); - } - - public function getAccessToken($sessionId) - { - die('not implemented getAccessToken'); - } - - public function removeAuthCode($sessionId) - { - die('not implemented removeAuthCode'); - } - - public function setAccessToken($sessionId, $accessToken) - { - die('not implemented setAccessToken'); - } - - public function addSessionScope($sessionId, $scope) - { - if ( ! isset($this->session_scopes[$sessionId])) { - $this->session_scopes[$sessionId] = array(); - } - - $this->session_scopes[$sessionId][] = $scope; - - return true; - } - - public function getScope($scope) - { - if ( ! isset($this->scopes[$scope])) { - return false; - } - - return $this->scopes[$scope]; - } - - public function updateSessionScopeAccessToken($sessionId, $accessToken) - { - return true; - } - - public function accessTokenScopes($accessToken) - { - die('not implemented accessTokenScopes'); - } - - public function validateRefreshToken($refreshToken, $clientId) - { - if ($refreshToken !== $this->sessions[0]['refresh_token']) - { - return false; - } - - return true; - } - - public function updateRefreshToken($sessionId, $newAccessToken, $newRefreshToken, $accessTokenExpires) - { - $this->sessions[$sessionId]['access_token'] = $newAccessToken; - $this->sessions[$sessionId]['refresh_token'] = $newRefreshToken; - $this->sessions[$sessionId]['access_token_expire'] = $accessTokenExpires; - } -} \ No newline at end of file diff --git a/tests/authentication/server_test.php b/tests/authentication/server_test.php deleted file mode 100644 index 36ac49eb..00000000 --- a/tests/authentication/server_test.php +++ /dev/null @@ -1,912 +0,0 @@ -oauth = new Oauth2\Authentication\Server(); - - require_once 'database_mock.php'; - $this->oauthdb = new OAuthdb(); - $this->assertInstanceOf('Oauth2\Authentication\Database', $this->oauthdb); - $this->oauth->registerDbAbstractor($this->oauthdb); - } - - public function test_setupWithOptions() - { - $o = new Oauth2\Authentication\Server(array( - 'access_token_ttl' => 86400 - )); - - $reflector = new ReflectionClass($o); - $param = $reflector->getProperty('_config'); - $param->setAccessible(true); - $array = $param->getValue($o); - - $this->assertEquals(86400, $array['access_token_ttl']); - } - - public function test_generateCode() - { - $reflector = new ReflectionClass($this->oauth); - $method = $reflector->getMethod('_generateCode'); - $method->setAccessible(true); - - $result = $method->invoke($this->oauth); - $result2 = $method->invoke($this->oauth); - - $this->assertEquals(40, strlen($result)); - $this->assertNotEquals($result, $result2); - } - - public function test_redirectUri() - { - $result1 = $this->oauth->redirectUri('http://example.com/foo'); - $result2 = $this->oauth->redirectUri('http://example.com/foo', array('foo' => 'bar')); - $result3 = $this->oauth->redirectUri('http://example.com/foo', array('foo' => 'bar'), '#'); - - $this->assertEquals('http://example.com/foo?', $result1); - $this->assertEquals('http://example.com/foo?foo=bar', $result2); - $this->assertEquals('http://example.com/foo#foo=bar', $result3); - } - - public function test_checkClientAuthoriseParams_GET() - { - $_GET['client_id'] = 'test'; - $_GET['redirect_uri'] = 'http://example.com/test'; - $_GET['response_type'] = 'code'; - $_GET['scope'] = 'test'; - - $expect = array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'client_details' => array( - 'client_id' => 'test', - 'client_secret' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'name' => 'Test Client' - ), - 'response_type' => 'code', - 'scopes' => array( - 0 => array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - ) - ) - ); - - $result = $this->oauth->checkClientAuthoriseParams(); - - $this->assertEquals($expect, $result); - } - - public function test_checkClientAuthoriseParams_PassedParams() - { - unset($_GET['client_id']); - unset($_GET['redirect_uri']); - unset($_GET['response_type']); - unset($_GET['scope']); - - $params = array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'response_type' => 'code', - 'scope' => 'test' - ); - - $this->assertEquals(array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'client_details' => array( - 'client_id' => 'test', - 'client_secret' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'name' => 'Test Client' - ), - 'response_type' => 'code', - 'scopes' => array(0 => array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )) - ), $this->oauth->checkClientAuthoriseParams($params)); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 0 - */ - public function test_checkClientAuthoriseParams_missingClientId() - { - $this->oauth->checkClientAuthoriseParams(); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 0 - */ - public function test_checkClientAuthoriseParams_missingRedirectUri() - { - $_GET['client_id'] = 'test'; - - $this->oauth->checkClientAuthoriseParams(); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 0 - */ - public function test_checkClientAuthoriseParams_missingResponseType() - { - $_GET['client_id'] = 'test'; - $_GET['redirect_uri'] = 'http://example.com/test'; - - $this->oauth->checkClientAuthoriseParams(); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 0 - */ - public function test_checkClientAuthoriseParams_missingScopes() - { - $_GET['client_id'] = 'test'; - $_GET['redirect_uri'] = 'http://example.com/test'; - $_GET['response_type'] = 'code'; - $_GET['scope'] = ' '; - - $this->oauth->checkClientAuthoriseParams(); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 4 - */ - public function test_checkClientAuthoriseParams_invalidScopes() - { - $_GET['client_id'] = 'test'; - $_GET['redirect_uri'] = 'http://example.com/test'; - $_GET['response_type'] = 'code'; - $_GET['scope'] = 'blah'; - - $this->oauth->checkClientAuthoriseParams(); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 8 - */ - public function test_checkClientAuthoriseParams_invalidClient() - { - $_GET['client_id'] = 'test'; - $_GET['redirect_uri'] = 'http://example.com/test2'; - $_GET['response_type'] = 'code'; - $_GET['scope'] = 'blah'; - - $this->oauth->checkClientAuthoriseParams(); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 3 - */ - public function test_checkClientAuthoriseParams_invalidResponseType() - { - $_GET['client_id'] = 'test'; - $_GET['redirect_uri'] = 'http://example.com/test'; - $_GET['response_type'] = 'blah'; - $_GET['scope'] = 'blah'; - - $this->oauth->checkClientAuthoriseParams(); - } - - public function test_newAuthoriseRequest() - { - $result = $this->oauth->newAuthoriseRequest('user', '123', array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'scopes' => array(array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )) - )); - - $this->assertEquals(40, strlen($result)); - } - - public function test_newAuthoriseRequest_isUnique() - { - $result1 = $this->oauth->newAuthoriseRequest('user', '123', array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'scopes' => array(array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )) - )); - - $result2 = $this->oauth->newAuthoriseRequest('user', '123', array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'scopes' => array(array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )) - )); - - $this->assertNotEquals($result1, $result2); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 7 - */ - public function test_issueAccessTokenNoRegisteredGrant() - { - $auth_code = $this->oauth->newAuthoriseRequest('user', '123', array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'scopes' => array(array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )) - )); - - $_POST['client_id'] = 'test'; - $_POST['client_secret'] = 'test'; - $_POST['redirect_uri'] = 'http://example.com/test'; - $_POST['grant_type'] = 'authorization_code'; - $_POST['code'] = $auth_code; - - $result = $this->oauth->issueAccessToken(); - } - - public function test_issueAccessToken_POST_authorization_code() - { - $auth_code = $this->oauth->newAuthoriseRequest('user', '123', array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'scopes' => array(array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )) - )); - - $_POST['client_id'] = 'test'; - $_POST['client_secret'] = 'test'; - $_POST['redirect_uri'] = 'http://example.com/test'; - $_POST['grant_type'] = 'authorization_code'; - $_POST['code'] = $auth_code; - - $this->oauth->enableGrantType('authorization_code'); - $result = $this->oauth->issueAccessToken(); - - $this->assertCount(4, $result); - $this->assertArrayHasKey('access_token', $result); - $this->assertArrayHasKey('token_type', $result); - $this->assertArrayHasKey('expires_in', $result); - $this->assertArrayHasKey('expires', $result); - } - - public function test_issueAccessToken_PassedParams_authorization_code() - { - $auth_code = $this->oauth->newAuthoriseRequest('user', '123', array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'scopes' => array(array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )) - )); - - $params['client_id'] = 'test'; - $params['client_secret'] = 'test'; - $params['redirect_uri'] = 'http://example.com/test'; - $params['grant_type'] = 'authorization_code'; - $params['code'] = $auth_code; - - $this->oauth->enableGrantType('authorization_code'); - $result = $this->oauth->issueAccessToken($params); - - $this->assertCount(4, $result); - $this->assertArrayHasKey('access_token', $result); - $this->assertArrayHasKey('token_type', $result); - $this->assertArrayHasKey('expires_in', $result); - $this->assertArrayHasKey('expires', $result); - } - - public function test_issueAccessToken_refresh_token() - { - $this->oauth->enableGrantType('authorization_code'); - $this->oauth->enableGrantType('refresh_token'); - - $auth_code = $this->oauth->newAuthoriseRequest('user', '123', array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'scopes' => array(array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )) - )); - - $params['client_id'] = 'test'; - $params['client_secret'] = 'test'; - $params['redirect_uri'] = 'http://example.com/test'; - $params['grant_type'] = 'authorization_code'; - $params['code'] = $auth_code; - - $result = $this->oauth->issueAccessToken($params); - - $this->assertCount(5, $result); - $this->assertArrayHasKey('access_token', $result); - $this->assertArrayHasKey('token_type', $result); - $this->assertArrayHasKey('expires_in', $result); - $this->assertArrayHasKey('expires', $result); - $this->assertArrayHasKey('refresh_token', $result); - - // Wait for a few seconds for the access token to age - sleep(1); - - // Refresh the token - $params2['client_id'] = 'test'; - $params2['client_secret'] = 'test'; - $params2['redirect_uri'] = 'http://example.com/test'; - $params2['grant_type'] = 'refresh_token'; - $params2['refresh_token'] = $result['refresh_token']; - - $result2 = $this->oauth->issueAccessToken($params2); - - $this->assertCount(5, $result2); - $this->assertArrayHasKey('access_token', $result2); - $this->assertArrayHasKey('token_type', $result2); - $this->assertArrayHasKey('expires_in', $result2); - $this->assertArrayHasKey('expires', $result2); - $this->assertArrayHasKey('refresh_token', $result2); - - $this->assertNotEquals($result['access_token'], $result2['access_token']); - $this->assertNotEquals($result['refresh_token'], $result2['refresh_token']); - $this->assertNotEquals($result['expires'], $result2['expires']); - $this->assertEquals($result['expires_in'], $result2['expires_in']); - $this->assertEquals($result['token_type'], $result2['token_type']); - } - - public function test_issueAccessToken_client_credentials() - { - $this->oauth->enableGrantType('client_credentials'); - - $auth_code = $this->oauth->newAuthoriseRequest('user', '123', array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'scopes' => array(array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )) - )); - - $params['client_id'] = 'test'; - $params['client_secret'] = 'test'; - $params['redirect_uri'] = 'http://example.com/test'; - $params['grant_type'] = 'client_credentials'; - $params['code'] = $auth_code; - - $result = $this->oauth->issueAccessToken($params); - - $this->assertCount(4, $result); - $this->assertArrayHasKey('access_token', $result); - $this->assertArrayHasKey('token_type', $result); - $this->assertArrayHasKey('expires_in', $result); - $this->assertArrayHasKey('expires', $result); - } - - public function test_issueAccessToken_client_credentialsPOST() - { - $this->oauth->enableGrantType('client_credentials'); - - $auth_code = $this->oauth->newAuthoriseRequest('user', '123', array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'scopes' => array(array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )) - )); - - $_POST['client_id'] = 'test'; - $_POST['client_secret'] = 'test'; - $_POST['redirect_uri'] = 'http://example.com/test'; - $_POST['grant_type'] = 'client_credentials'; - $_POST['code'] = $auth_code; - - $result = $this->oauth->issueAccessToken(); - - $this->assertCount(4, $result); - $this->assertArrayHasKey('access_token', $result); - $this->assertArrayHasKey('token_type', $result); - $this->assertArrayHasKey('expires_in', $result); - $this->assertArrayHasKey('expires', $result); - } - - public function test_issueAccessToken_client_credentials_withRefreshToken() - { - $this->oauth->enableGrantType('client_credentials'); - $this->oauth->enableGrantType('refresh_token'); - - $auth_code = $this->oauth->newAuthoriseRequest('user', '123', array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'scopes' => array(array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )) - )); - - $params['client_id'] = 'test'; - $params['client_secret'] = 'test'; - $params['redirect_uri'] = 'http://example.com/test'; - $params['grant_type'] = 'client_credentials'; - $params['code'] = $auth_code; - - $result = $this->oauth->issueAccessToken($params); - - $this->assertCount(5, $result); - $this->assertArrayHasKey('access_token', $result); - $this->assertArrayHasKey('token_type', $result); - $this->assertArrayHasKey('expires_in', $result); - $this->assertArrayHasKey('expires', $result); - $this->assertArrayHasKey('refresh_token', $result); - } - - public function test_issueAccessToken_refresh_tokenPOST() - { - $this->oauth->enableGrantType('authorization_code'); - $this->oauth->enableGrantType('refresh_token'); - - $auth_code = $this->oauth->newAuthoriseRequest('user', '123', array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'scopes' => array(array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )) - )); - - $_POST['client_id'] = 'test'; - $_POST['client_secret'] = 'test'; - $_POST['redirect_uri'] = 'http://example.com/test'; - $_POST['grant_type'] = 'authorization_code'; - $_POST['code'] = $auth_code; - - $result = $this->oauth->issueAccessToken(); - - $this->assertCount(5, $result); - $this->assertArrayHasKey('access_token', $result); - $this->assertArrayHasKey('token_type', $result); - $this->assertArrayHasKey('expires_in', $result); - $this->assertArrayHasKey('expires', $result); - $this->assertArrayHasKey('refresh_token', $result); - - // Wait for a few seconds for the access token to age - sleep(1); - - // Refresh the token - $_POST['client_id'] = 'test'; - $_POST['client_secret'] = 'test'; - $_POST['redirect_uri'] = 'http://example.com/test'; - $_POST['grant_type'] = 'refresh_token'; - $_POST['refresh_token'] = $result['refresh_token']; - - $result2 = $this->oauth->issueAccessToken(); - - $this->assertCount(5, $result2); - $this->assertArrayHasKey('access_token', $result2); - $this->assertArrayHasKey('token_type', $result2); - $this->assertArrayHasKey('expires_in', $result2); - $this->assertArrayHasKey('expires', $result2); - $this->assertArrayHasKey('refresh_token', $result2); - - $this->assertNotEquals($result['access_token'], $result2['access_token']); - $this->assertNotEquals($result['refresh_token'], $result2['refresh_token']); - $this->assertNotEquals($result['expires'], $result2['expires']); - $this->assertEquals($result['expires_in'], $result2['expires_in']); - $this->assertEquals($result['token_type'], $result2['token_type']); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_refresh_tokenMissingToken() - { - $this->oauth->enableGrantType('authorization_code'); - $this->oauth->enableGrantType('refresh_token'); - - $auth_code = $this->oauth->newAuthoriseRequest('user', '123', array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'scopes' => array(array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )) - )); - - $_POST['client_id'] = 'test'; - $_POST['client_secret'] = 'test'; - $_POST['redirect_uri'] = 'http://example.com/test'; - $_POST['grant_type'] = 'authorization_code'; - $_POST['code'] = $auth_code; - - $result = $this->oauth->issueAccessToken(); - - $this->assertCount(5, $result); - $this->assertArrayHasKey('access_token', $result); - $this->assertArrayHasKey('token_type', $result); - $this->assertArrayHasKey('expires_in', $result); - $this->assertArrayHasKey('expires', $result); - $this->assertArrayHasKey('refresh_token', $result); - - // Wait for a few seconds for the access token to age - sleep(1); - - // Refresh the token - $_POST['client_id'] = 'test'; - $_POST['client_secret'] = 'test'; - $_POST['redirect_uri'] = 'http://example.com/test'; - $_POST['grant_type'] = 'refresh_token'; - - $result2 = $this->oauth->issueAccessToken(); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_invalid_refresh_token() - { - $this->oauth->enableGrantType('authorization_code'); - $this->oauth->enableGrantType('refresh_token'); - - $auth_code = $this->oauth->newAuthoriseRequest('user', '123', array( - 'client_id' => 'test', - 'redirect_uri' => 'http://example.com/test', - 'scopes' => array(array( - 'id' => 1, - 'scope' => 'test', - 'name' => 'test', - 'description' => 'test' - )) - )); - - $params['client_id'] = 'test'; - $params['client_secret'] = 'test'; - $params['redirect_uri'] = 'http://example.com/test'; - $params['grant_type'] = 'authorization_code'; - $params['code'] = $auth_code; - - $result = $this->oauth->issueAccessToken($params); - - $this->assertCount(5, $result); - $this->assertArrayHasKey('access_token', $result); - $this->assertArrayHasKey('token_type', $result); - $this->assertArrayHasKey('expires_in', $result); - $this->assertArrayHasKey('expires', $result); - $this->assertArrayHasKey('refresh_token', $result); - - // Wait for a few seconds for the access token to age - sleep(1); - - // Refresh the token - $params2['client_id'] = 'test'; - $params2['client_secret'] = 'test'; - $params2['redirect_uri'] = 'http://example.com/test'; - $params2['grant_type'] = 'refresh_token'; - $params2['refresh_token'] = 'blah'; - - $result2 = $this->oauth->issueAccessToken($params2); - } - - /** - * @expectedException Oauth2\Authentication\ServerException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_password_grant_Missing_Callback() - { - $this->oauth->enableGrantType('password'); - } - - public function test_issueAccessToken_password_grant() - { - $this->oauth->enableGrantType('password', function(){ - return true; - }); - - $params['client_id'] = 'test'; - $params['client_secret'] = 'test'; - $params['grant_type'] = 'password'; - $params['username'] = 'alexbilbie'; - $params['password'] = 'helloworld'; - - $result = $this->oauth->issueAccessToken($params); - - $this->assertCount(4, $result); - $this->assertArrayHasKey('access_token', $result); - $this->assertArrayHasKey('token_type', $result); - $this->assertArrayHasKey('expires_in', $result); - $this->assertArrayHasKey('expires', $result); - } - - public function test_issueAccessToken_password_grantPOST() - { - $this->oauth->enableGrantType('password', function(){ - return true; - }); - - $_POST['client_id'] = 'test'; - $_POST['client_secret'] = 'test'; - $_POST['grant_type'] = 'password'; - $_POST['username'] = 'alexbilbie'; - $_POST['password'] = 'helloworld'; - - $result = $this->oauth->issueAccessToken(); - - $this->assertCount(4, $result); - $this->assertArrayHasKey('access_token', $result); - $this->assertArrayHasKey('token_type', $result); - $this->assertArrayHasKey('expires_in', $result); - $this->assertArrayHasKey('expires', $result); - } - - public function test_issueAccessToken_password_grant_withRefreshToken() - { - $this->oauth->enableGrantType('password', function(){ - return true; - }); - - $this->oauth->enableGrantType('refresh_token'); - - $params['client_id'] = 'test'; - $params['client_secret'] = 'test'; - $params['grant_type'] = 'password'; - $params['username'] = 'alexbilbie'; - $params['password'] = 'helloworld'; - - $result = $this->oauth->issueAccessToken($params); - - $this->assertCount(5, $result); - $this->assertArrayHasKey('access_token', $result); - $this->assertArrayHasKey('token_type', $result); - $this->assertArrayHasKey('expires_in', $result); - $this->assertArrayHasKey('expires', $result); - $this->assertArrayHasKey('refresh_token', $result); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_password_grant_wrongCreds() - { - $this->oauth->enableGrantType('password', function(){ - return false; - }); - - $params['client_id'] = 'test'; - $params['client_secret'] = 'test'; - $params['grant_type'] = 'password'; - $params['username'] = 'alexbilbie'; - $params['password'] = 'helloworld'; - - $result = $this->oauth->issueAccessToken($params); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_password_grant_missingUsername() - { - $this->oauth->enableGrantType('password', function(){ - return true; - }); - - $params['client_id'] = 'test'; - $params['client_secret'] = 'test'; - $params['grant_type'] = 'password'; - - $result = $this->oauth->issueAccessToken($params); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_password_grant_missingPassword() - { - $this->oauth->enableGrantType('password', function(){ - return true; - }); - - $params['client_id'] = 'test'; - $params['client_secret'] = 'test'; - $params['grant_type'] = 'password'; - $params['username'] = 'alexbilbie'; - - $result = $this->oauth->issueAccessToken($params); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_missingGrantType() - { - $this->oauth->issueAccessToken(); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 7 - */ - public function test_issueAccessToken_unsupportedGrantType() - { - $params['grant_type'] = 'blah'; - - $this->oauth->issueAccessToken($params); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 0 - */ - public function test_completeAuthCodeGrant_missingClientId() - { - $reflector = new ReflectionClass($this->oauth); - $method = $reflector->getMethod('_completeAuthCodeGrant'); - $method->setAccessible(true); - - $method->invoke($this->oauth); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 0 - */ - public function test_completeAuthCodeGrant_missingClientSecret() - { - $reflector = new ReflectionClass($this->oauth); - $method = $reflector->getMethod('_completeAuthCodeGrant'); - $method->setAccessible(true); - - $authParams['client_id'] = 'test'; - - $method->invoke($this->oauth, $authParams); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 0 - */ - public function test_completeAuthCodeGrant_missingRedirectUri() - { - $reflector = new ReflectionClass($this->oauth); - $method = $reflector->getMethod('_completeAuthCodeGrant'); - $method->setAccessible(true); - - $authParams['client_id'] = 'test'; - $authParams['client_secret'] = 'test'; - - $method->invoke($this->oauth, $authParams); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 8 - */ - public function test_completeAuthCodeGrant_invalidClient() - { - $reflector = new ReflectionClass($this->oauth); - $method = $reflector->getMethod('_completeAuthCodeGrant'); - $method->setAccessible(true); - - $authParams['client_id'] = 'test'; - $authParams['client_secret'] = 'test123'; - $authParams['redirect_uri'] = 'http://example.com/test'; - - $method->invoke($this->oauth, $authParams); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 0 - */ - public function test_completeAuthCodeGrant_missingCode() - { - $reflector = new ReflectionClass($this->oauth); - $method = $reflector->getMethod('_completeAuthCodeGrant'); - $method->setAccessible(true); - - $authParams['client_id'] = 'test'; - $authParams['client_secret'] = 'test'; - $authParams['redirect_uri'] = 'http://example.com/test'; - - $method->invoke($this->oauth, $authParams); - } - - /** - * @expectedException Oauth2\Authentication\ClientException - * @expectedExceptionCode 9 - */ - public function test_completeAuthCodeGrant_invalidCode() - { - $reflector = new ReflectionClass($this->oauth); - $method = $reflector->getMethod('_completeAuthCodeGrant'); - $method->setAccessible(true); - - $authParams['client_id'] = 'test'; - $authParams['client_secret'] = 'test'; - $authParams['redirect_uri'] = 'http://example.com/test'; - $authParams['code'] = 'blah'; - - $method->invoke($this->oauth, $authParams); - } - - /** - * @expectedException Oauth2\Authentication\ServerException - * @expectedExceptionMessage No registered database abstractor - */ - public function test_noRegisteredDatabaseAbstractor() - { - $reflector = new ReflectionClass($this->oauth); - $method = $reflector->getMethod('_dbCall'); - $method->setAccessible(true); - - $dbAbstractor = $reflector->getProperty('_db'); - $dbAbstractor->setAccessible(true); - $dbAbstractor->setValue($this->oauth, null); - - $result = $method->invoke($this->oauth); - } - - /** - * @expectedException Oauth2\Authentication\ServerException - * @expectedExceptionMessage Registered database abstractor is not an instance of Oauth2\Authentication\Database - */ - public function test_invalidRegisteredDatabaseAbstractor() - { - $fake = new stdClass; - $this->oauth->registerDbAbstractor($fake); - - $reflector = new ReflectionClass($this->oauth); - $method = $reflector->getMethod('_dbCall'); - $method->setAccessible(true); - - $result = $method->invoke($this->oauth); - } - -} diff --git a/tests/resource/ResourceServerTest.php b/tests/resource/ResourceServerTest.php new file mode 100644 index 00000000..c6bc058d --- /dev/null +++ b/tests/resource/ResourceServerTest.php @@ -0,0 +1,172 @@ +session = M::mock('OAuth2\Storage\SessionInterface'); + } + + private function returnDefault() + { + return new OAuth2\ResourceServer($this->session); + } + + public function test_setRequest() + { + $s = $this->returnDefault(); + $request = new OAuth2\Util\Request(); + $s->setRequest($request); + + $reflector = new ReflectionClass($s); + $requestProperty = $reflector->getProperty('request'); + $requestProperty->setAccessible(true); + $v = $requestProperty->getValue($s); + + $this->assertTrue($v instanceof OAuth2\Util\RequestInterface); + } + + public function test_getRequest() + { + $s = $this->returnDefault(); + $request = new OAuth2\Util\Request(); + $s->setRequest($request); + $v = $s->getRequest(); + + $this->assertTrue($v instanceof OAuth2\Util\RequestInterface); + } + + public function test_getTokenKey() + { + $s = $this->returnDefault(); + $this->assertEquals('access_token', $s->getTokenKey()); + } + + public function test_setTokenKey() + { + $s = $this->returnDefault(); + $s->setTokenKey('oauth_token'); + + $reflector = new ReflectionClass($s); + $requestProperty = $reflector->getProperty('tokenKey'); + $requestProperty->setAccessible(true); + $v = $requestProperty->getValue($s); + + $this->assertEquals('oauth_token', $v); + } + + /** + * @expectedException OAuth2\Exception\InvalidAccessTokenException + */ + public function test_determineAccessToken_missingToken() + { + $_SERVER['HTTP_AUTHORIZATION'] = 'Bearer'; + $request = new OAuth2\Util\Request(array(), array(), array(), array(), $_SERVER); + + $s = $this->returnDefault(); + $s->setRequest($request); + + $reflector = new ReflectionClass($s); + $method = $reflector->getMethod('determineAccessToken'); + $method->setAccessible(true); + + $result = $method->invoke($s); + } + + public function test_determineAccessToken_fromHeader() + { + $request = new OAuth2\Util\Request(); + + $requestReflector = new ReflectionClass($request); + $param = $requestReflector->getProperty('headers'); + $param->setAccessible(true); + $param->setValue($request, array( + 'Authorization' => 'Bearer YWJjZGVm' + )); + $s = $this->returnDefault(); + $s->setRequest($request); + + $reflector = new ReflectionClass($s); + + $method = $reflector->getMethod('determineAccessToken'); + $method->setAccessible(true); + + $result = $method->invoke($s); + + $this->assertEquals('abcdef', $result); + } + + public function test_determineAccessToken_fromMethod() + { + $s = $this->returnDefault(); + + $_GET[$s->getTokenKey()] = 'abcdef'; + $_SERVER['REQUEST_METHOD'] = 'get'; + + $request = new OAuth2\Util\Request($_GET, array(), array(), array(), $_SERVER); + $s->setRequest($request); + + $reflector = new ReflectionClass($s); + $method = $reflector->getMethod('determineAccessToken'); + $method->setAccessible(true); + + $result = $method->invoke($s); + + $this->assertEquals('abcdef', $result); + } + + /** + * @expectedException OAuth2\Exception\InvalidAccessTokenException + */ + public function test_isValid_notValid() + { + $this->session->shouldReceive('validateAccessToken')->andReturn(false); + + $request = new OAuth2\Util\Request(); + $requestReflector = new ReflectionClass($request); + $param = $requestReflector->getProperty('headers'); + $param->setAccessible(true); + $param->setValue($request, array( + 'Authorization' => 'Bearer YWJjZGVm' + )); + $s = $this->returnDefault(); + $s->setRequest($request); + + $s->isValid(); + } + + public function test_isValid_valid() + { + $this->session->shouldReceive('validateAccessToken')->andReturn(array( + 'id' => 1, + 'owner_type' => 'user', + 'owner_id' => 123 + )); + $this->session->shouldReceive('getScopes')->andReturn(array('foo', 'bar')); + + $request = new OAuth2\Util\Request(); + $requestReflector = new ReflectionClass($request); + $param = $requestReflector->getProperty('headers'); + $param->setAccessible(true); + $param->setValue($request, array( + 'Authorization' => 'Bearer YWJjZGVm' + )); + $s = $this->returnDefault(); + $s->setRequest($request); + + $this->assertTrue($s->isValid()); + $this->assertEquals(123, $s->getOwnerId()); + $this->assertEquals('user', $s->getOwnerType()); + $this->assertEquals('abcdef', $s->getAccessToken()); + $this->assertTrue($s->hasScope('foo')); + $this->assertTrue($s->hasScope('bar')); + $this->assertTrue($s->hasScope(array('foo', 'bar'))); + $this->assertFalse($s->hasScope(array('foobar'))); + $this->assertFalse($s->hasScope('foobar')); + $this->assertFalse($s->hasScope(new StdClass)); + } +} \ No newline at end of file diff --git a/tests/resource/database_mock.php b/tests/resource/database_mock.php deleted file mode 100644 index 52c698d8..00000000 --- a/tests/resource/database_mock.php +++ /dev/null @@ -1,31 +0,0 @@ - array( - 'id' => 1, - 'owner_type' => 'user', - 'owner_id' => 123 - ) - ); - - private $sessionScopes = array( - 1 => array( - 'foo', - 'bar' - ) - ); - - public function validateAccessToken($accessToken) - { - return (isset($this->accessTokens[$accessToken])) ? $this->accessTokens[$accessToken] : false; - } - - public function sessionScopes($sessionId) - { - return (isset($this->sessionScopes[$sessionId])) ? $this->sessionScopes[$sessionId] : array(); - } -} \ No newline at end of file diff --git a/tests/resource/server_test.php b/tests/resource/server_test.php deleted file mode 100644 index 7db05113..00000000 --- a/tests/resource/server_test.php +++ /dev/null @@ -1,121 +0,0 @@ -server = new Oauth2\Resource\Server(); - $this->db = new ResourceDB(); - - $this->assertInstanceOf('Oauth2\Resource\Database', $this->db); - $this->server->registerDbAbstractor($this->db); - } - - function test_init_POST() - { - $_SERVER['REQUEST_METHOD'] = 'POST'; - $_POST['oauth_token'] = 'test12345'; - - $this->server->init(); - - $reflector = new ReflectionClass($this->server); - - $_accessToken = $reflector->getProperty('_accessToken'); - $_accessToken->setAccessible(true); - - $_type = $reflector->getProperty('_type'); - $_type->setAccessible(true); - - $_typeId = $reflector->getProperty('_typeId'); - $_typeId->setAccessible(true); - - $_scopes = $reflector->getProperty('_scopes'); - $_scopes->setAccessible(true); - - $this->assertEquals($_accessToken->getValue($this->server), $_POST['oauth_token']); - $this->assertEquals($_type->getValue($this->server), 'user'); - $this->assertEquals($_typeId->getValue($this->server), 123); - $this->assertEquals($_scopes->getValue($this->server), array('foo', 'bar')); - } - - function test_init_GET() - { - $_GET['oauth_token'] = 'test12345'; - - $this->server->init(); - - $reflector = new ReflectionClass($this->server); - - $_accessToken = $reflector->getProperty('_accessToken'); - $_accessToken->setAccessible(true); - - $_type = $reflector->getProperty('_type'); - $_type->setAccessible(true); - - $_typeId = $reflector->getProperty('_typeId'); - $_typeId->setAccessible(true); - - $_scopes = $reflector->getProperty('_scopes'); - $_scopes->setAccessible(true); - - $this->assertEquals($_accessToken->getValue($this->server), $_GET['oauth_token']); - $this->assertEquals($_type->getValue($this->server), 'user'); - $this->assertEquals($_typeId->getValue($this->server), 123); - $this->assertEquals($_scopes->getValue($this->server), array('foo', 'bar')); - } - - function test_init_header() - { - // Test with authorisation header - $this->markTestIncomplete('Authorisation header test has not been implemented yet.'); - } - - /** - * @expectedException \Oauth2\Resource\ClientException - * @expectedExceptionMessage An access token was not presented with the request - */ - function test_init_missingToken() - { - $this->server->init(); - } - - /** - * @expectedException \Oauth2\Resource\ClientException - * @expectedExceptionMessage The access token is not registered with the resource server - */ - function test_init_wrongToken() - { - $_POST['oauth_token'] = 'blah'; - $_SERVER['REQUEST_METHOD'] = 'POST'; - - $this->server->init(); - } - - function test_hasScope() - { - $_POST['oauth_token'] = 'test12345'; - $_SERVER['REQUEST_METHOD'] = 'POST'; - - $this->server->init(); - - $this->assertEquals(true, $this->server->hasScope('foo')); - $this->assertEquals(true, $this->server->hasScope('bar')); - $this->assertEquals(true, $this->server->hasScope(array('foo', 'bar'))); - - $this->assertEquals(false, $this->server->hasScope('foobar')); - $this->assertEquals(false, $this->server->hasScope(array('foobar'))); - } - - function test___call() - { - $_POST['oauth_token'] = 'test12345'; - $_SERVER['REQUEST_METHOD'] = 'POST'; - - $this->server->init(); - - $this->assertEquals(123, $this->server->isUser()); - $this->assertEquals(false, $this->server->isMachine()); - } - -} \ No newline at end of file diff --git a/tests/util/RedirectUriTest.php b/tests/util/RedirectUriTest.php new file mode 100644 index 00000000..6f7aaa1e --- /dev/null +++ b/tests/util/RedirectUriTest.php @@ -0,0 +1,15 @@ +'bar')); + $v2 = OAuth2\Util\RedirectUri::make('https://foobar/', array('foo'=>'bar'), '#'); + $v3 = OAuth2\Util\RedirectUri::make('https://foobar/', array('foo'=>'bar', 'bar' => 'foo')); + + $this->assertEquals('https://foobar/?foo=bar', $v1); + $this->assertEquals('https://foobar/#foo=bar', $v2); + $this->assertEquals('https://foobar/?foo=bar&bar=foo', $v3); + } +} \ No newline at end of file diff --git a/tests/util/RequestTest.php b/tests/util/RequestTest.php new file mode 100644 index 00000000..dd4cd43e --- /dev/null +++ b/tests/util/RequestTest.php @@ -0,0 +1,73 @@ +request = new OAuth2\Util\Request( + array('foo' => 'bar'), + array('foo' => 'bar'), + array('foo' => 'bar'), + array('foo' => 'bar'), + array('HTTP_HOST' => 'foobar.com') + ); + } + + function test_buildFromIndex() + { + $r = new OAuth2\Util\Request(); + $r->buildFromGlobals(); + + $this->assertTrue($r instanceof OAuth2\Util\Request); + } + + function test_get() + { + $this->assertEquals('bar', $this->request->get('foo')); + $this->assertEquals(array('foo' => 'bar'), $this->request->get()); + } + + function test_post() + { + $this->assertEquals('bar', $this->request->post('foo')); + $this->assertEquals(array('foo' => 'bar'), $this->request->post()); + } + + function test_file() + { + $this->assertEquals('bar', $this->request->file('foo')); + $this->assertEquals(array('foo' => 'bar'), $this->request->file()); + } + + function test_cookie() + { + $this->assertEquals('bar', $this->request->cookie('foo')); + $this->assertEquals(array('foo' => 'bar'), $this->request->cookie()); + } + + function test_server() + { + $this->assertEquals('foobar.com', $this->request->server('HTTP_HOST')); + $this->assertEquals(array('HTTP_HOST' => 'foobar.com'), $this->request->server()); + } + + function test_header() + { + $this->assertEquals('foobar.com', $this->request->header('Host')); + $this->assertEquals(array('Host' => 'foobar.com'), $this->request->header()); + } + + /** + * @expectedException InvalidArgumentException + */ + function test_unknownProperty() + { + $reflector = new ReflectionClass($this->request); + $method = $reflector->getMethod('getPropertyValue'); + $method->setAccessible(true); + + $result = $method->invoke($this->request, 'blah'); + } +} \ No newline at end of file diff --git a/tests/util/SecureKeyTest.php b/tests/util/SecureKeyTest.php new file mode 100644 index 00000000..6a46c6c9 --- /dev/null +++ b/tests/util/SecureKeyTest.php @@ -0,0 +1,15 @@ +assertEquals(40, strlen($v1)); + $this->assertTrue($v1 !== $v2); + $this->assertEquals(50, strlen($v3)); + } +} \ No newline at end of file