diff --git a/.gitignore b/.gitignore index f2c4c5c4..a0f2c820 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,12 @@ /vendor /composer.lock -/tests/coverage +/build /docs /testing -build/coverage \ No newline at end of file +/examples/relational/vendor +/examples/relational/config/oauth2.sqlite3 +/examples/nosql/vendor +/examples/nosql/config/oauth2.sqlite3 +/examples/relational/composer.lock +/tests/codecept/tests/_log +oauth2-server.paw \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 30bed1af..1ab246f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,14 +5,25 @@ php: - 5.5 - 5.6 - hhvm - + matrix: allow_failures: - php: hhvm -before_script: composer install --prefer-source -script: phpunit --configuration phpunit.xml.dist - cache: directories: - vendor + +before_script: + - composer self-update + - composer require satooshi/php-coveralls:dev-master --no-update --dev + - composer install + - cd examples/relational && composer install --prefer-dist + - php config/init.php + - php -S localhost:8000 & + - sleep 3 + - cd ../.. + +script: + - mkdir -p build/logs + - phpunit --coverage-text diff --git a/README.md b/README.md index d7cf4f00..7a855434 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,34 @@ # PHP OAuth 2.0 Server -[![Latest Stable Version](https://poser.pugx.org/league/oauth2-server/v/stable.png)](https://packagist.org/packages/league/oauth2-server) [![Coverage Status](https://coveralls.io/repos/thephpleague/oauth2-server/badge.png?branch=master)](https://coveralls.io/r/thephpleague/oauth2-server?branch=master) [![Total Downloads](https://poser.pugx.org/league/oauth2-server/downloads.png)](https://packagist.org/packages/league/oauth2-server) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/thephpleague/oauth2-server/trend.png)](https://bitdeli.com/free "Bitdeli Badge") +[![Latest Stable Version](https://poser.pugx.org/league/oauth2-server/v/stable.png)](https://packagist.org/packages/league/oauth2-server) [![Coverage Status](https://coveralls.io/repos/thephpleague/oauth2-server/badge.png?branch=v4.0.0-WIP)](https://coveralls.io/r/thephpleague/oauth2-server?branch=v4.0.0-WIP) [![Total Downloads](https://poser.pugx.org/league/oauth2-server/downloads.png)](https://packagist.org/packages/league/oauth2-server) A standards compliant [OAuth 2.0](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-v2/) authorization server and resource server written in PHP. ## Package Installation -The framework is provided as a Composer package which can be installed by adding the package to your `composer.json` file: +The framework is provided as a Composer package which can be installed by adding the package to your composer.json file: ```javascript { - "require": { - "league/oauth2-server": "3.*" - } + "require": { + "league/oauth2-server": "4.*" + } } ``` -### Framework Integrations +### Storage Adapters -* [Laravel Service Provider](https://packagist.org/packages/lucadegasperi/oauth2-server-laravel) by @lucadegasperi -* [Laravel Eloquent implementation](https://github.com/ScubaClick/scubaclick-oauth2) by @ScubaClick (under development) +The following adapters have been created by other developers to help you easily integrate this library into your project. + +* [Redis storage adapter](https://github.com/jasonlewis/oauth2-server-redis) by @jasonlewis + +If you want to roll your own adapter check out the docs. --- The library features 100% unit test code coverage. To run the tests yourself run `phpunit` from the project root. -[![Build Status](https://travis-ci.org/thephpleague/oauth2-server.png?branch=master)](https://travis-ci.org/thephpleague/oauth2-server) [master] - -[![Build Status](https://travis-ci.org/thephpleague/oauth2-server.png?branch=develop)](https://travis-ci.org/thephpleague/oauth2-server) [develop] - - ## Current Features ### Authorization Server @@ -42,7 +40,7 @@ The authorization server is a flexible class and the following core specificatio * 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)) -An [overview of the different OAuth 2.0 grants](https://github.com/thephpleague/oauth2-server/wiki/Which-OAuth-2.0-grant-should-I-use%3F) can be found in the [wiki]. +An overview of the different OAuth 2.0 grants can be found in the wiki [https://github.com/php-loep/oauth2-server/wiki/Which-OAuth-2.0-grant-should-I-use%3F](https://github.com/php-loep/oauth2-server/wiki/Which-OAuth-2.0-grant-should-I-use%3F). ### Resource Server @@ -50,35 +48,31 @@ The resource server allows you to secure your API endpoints by checking for a va ### Custom grants -Custom grants can be created easily by implementing an interface. Check out the [custom grant guide](https://github.com/thephpleague/oauth2-server/wiki/Creating-custom-grants). +Custom grants can be created easily by implementing an interface. Check out a guide here [https://github.com/php-loep/oauth2-server/wiki/Creating-custom-grants](https://github.com/php-loep/oauth2-server/wiki/Creating-custom-grants). -## Tutorials and Documentation +## Tutorials and documentation -* **[Wiki]** - The wiki has lots of guides on how to use this library. +The wiki has lots of guides on how to use this library, check it out - [https://github.com/php-loep/oauth2-server/wiki](https://github.com/php-loep/oauth2-server/wiki). -* **[Developing an OAuth-2.0 Authorization Server]** - A simple tutorial on how to use the authorization server. +A simple tutorial on how to use the authorization server can be found at [https://github.com/php-loep/oauth2-server/wiki/Developing-an-OAuth-2.0-authorization-server](https://github.com/php-loep/oauth2-server/wiki/Developing-an-OAuth-2.0-authorization-server). -* **[Securing your API with OAuth 2.0]** - A simple tutorial on how to use the resource server to secure an API server. - -[Wiki]: https://github.com/thephpleague/oauth2-server/wiki -[Securing your API with OAuth 2.0]: https://github.com/thephpleague/oauth2-server/wiki/Securing-your-API-with-OAuth-2.0 -[Developing an OAuth-2.0 Authorization Server]: https://github.com/thephpleague/oauth2-server/wiki/Developing-an-OAuth-2.0-authorization-server +A simple tutorial on how to use the resource server to secure an API server can be found at [https://github.com/php-loep/oauth2-server/wiki/Securing-your-API-with-OAuth-2.0](https://github.com/php-loep/oauth2-server/wiki/Securing-your-API-with-OAuth-2.0). ## Changelog -[See the project releases page](https://github.com/thephpleague/oauth2-server/releases) +[See the project releases page](https://github.com/php-loep/oauth2-server/releases) ## Contributing -Please see [CONTRIBUTING](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) for details. +Please see [CONTRIBUTING](https://github.com/php-loep/oauth2-server/blob/master/CONTRIBUTING.md) for details. ## Support -Bugs and feature request are tracked on [GitHub](https://github.com/thephpleague/oauth2-server/issues) +Bugs and feature request are tracked on [GitHub](https://github.com/php-loep/oauth2-server/issues) ## License -This package is released under the MIT License. See the bundled [LICENSE](https://github.com/thephpleague/oauth2-server/blob/master/LICENSE) file for details. +This package is released under the MIT License. See the bundled [LICENSE](https://github.com/php-loep/oauth2-server/blob/master/LICENSE) file for details. ## Credits @@ -90,8 +84,6 @@ Special thanks to: * [Nick Jackson](https://github.com/jacksonj04) * [Michael Gooden](https://github.com/MichaelGooden) * [Phil Sturgeon](https://github.com/philsturgeon) -* [and all the other contributors](https://github.com/thephpleague/oauth2-server/contributors) +* [and all the other contributors](https://github.com/php-loep/oauth2-server/contributors) -The initial code was developed as part of the [Linkey](http://linkey.blogs.lincoln.ac.uk) project which was funded by [JISC](http://jisc.ac.uk) under the Access and Identity Management programme. - -[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/thephpleague/oauth2-server/trend.png)](https://bitdeli.com/free "Bitdeli Badge") +The initial code was developed as part of the [Linkey](http://linkey.blogs.lincoln.ac.uk) project which was funded by [JISC](http://jisc.ac.uk) under the Access and Identity Management programme. \ No newline at end of file diff --git a/composer.json b/composer.json index ae7a8170..f24d7507 100644 --- a/composer.json +++ b/composer.json @@ -1,26 +1,39 @@ { "name": "league/oauth2-server", "description": "A lightweight and powerful OAuth 2.0 authorization and resource server library with support for all the core specification grants. This library will allow you to secure your API with OAuth and allow your applications users to approve apps that want to access their data from your API.", - "version": "3.2", + "homepage": "https://github.com/php-loep/oauth2-server", "license": "MIT", "require": { - "php": ">=5.4.0" + "php": ">=5.4.0", + "symfony/http-foundation": "~2.1", + "league/event": "0.2.0" }, "require-dev": { - "mockery/mockery": ">=0.7.2", - "league/phpunit-coverage-listener": "~1.0" + "phpunit/phpunit": "~4.0", + "mockery/mockery": "~0.9", + "league/phpunit-coverage-listener": "~1.0", + "squizlabs/php_codesniffer": "1.*", + "codeception/codeception": "2.0.*", + "alexbilbie/fizzfuzz": "dev-develop" }, "repositories": [ { "type": "git", "url": "https://github.com/thephpleague/oauth2-server.git" + },{ + "type": "git", + "url": "https://github.com/alexbilbie/fizzfuzz.git" } ], "keywords": [ "oauth", "oauth2", + "oauth 2", + "oauth 2.0", "server", + "auth", "authorization", + "authorisation", "authentication", "resource", "api", @@ -37,14 +50,17 @@ } ], "replace": { - "lncd/oauth2": "*" + "lncd/oauth2": "*", + "league/oauth2server": "*" }, "autoload": { - "psr-0": { - "League\\OAuth2\\Server": "src/" + "psr-4": { + "League\\OAuth2\\Server\\": "src/" } }, - "suggest": { - + "autoload-dev": { + "psr-4": { + "LeagueTests\\": "tests/unit/" + } } } diff --git a/examples/relational/Model/Users.php b/examples/relational/Model/Users.php new file mode 100644 index 00000000..44af86f3 --- /dev/null +++ b/examples/relational/Model/Users.php @@ -0,0 +1,25 @@ +select(['username', 'password', 'name', 'email', 'photo']); + + if ($username !== null) { + $query->where('username', '=', $username); + } + + $result = $query->get(); + + if (count($result) > 0) { + return $result; + } + + return null; + } +} diff --git a/examples/relational/Storage/AccessTokenStorage.php b/examples/relational/Storage/AccessTokenStorage.php new file mode 100644 index 00000000..faccb46c --- /dev/null +++ b/examples/relational/Storage/AccessTokenStorage.php @@ -0,0 +1,97 @@ +where('access_token', $token) + ->where('expire_time', '>=', time()) + ->get(); + + if (count($result) === 1) { + $token = (new AccessTokenEntity($this->server)) + ->setId($result[0]['access_token']) + ->setExpireTime($result[0]['expire_time']); + + return $token; + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function getScopes(AbstractTokenEntity $token) + { + $result = Capsule::table('oauth_access_token_scopes') + ->select(['oauth_scopes.id', 'oauth_scopes.description']) + ->join('oauth_scopes', 'oauth_access_token_scopes.scope', '=', 'oauth_scopes.id') + ->where('access_token', $token->getId()) + ->get(); + + $response = []; + + if (count($result) > 0) { + foreach ($result as $row) { + $scope = (new ScopeEntity($this->server))->hydrate([ + 'id' => $row['id'], + 'description' => $row['description'] + ]); + $response[] = $scope; + } + } + + return $response; + } + + /** + * {@inheritdoc} + */ + public function create($token, $expireTime, $sessionId) + { + Capsule::table('oauth_access_tokens') + ->insert([ + 'access_token' => $token, + 'session_id' => $sessionId, + 'expire_time' => $expireTime + ]); + } + + /** + * {@inheritdoc} + */ + public function associateScope(AbstractTokenEntity $token, ScopeEntity $scope) + { + Capsule::table('oauth_access_token_scopes') + ->insert([ + 'access_token' => $token->getId(), + 'scope' => $scope->getId() + ]); + } + + /** + * {@inheritdoc} + */ + public function delete(AbstractTokenEntity $token) + { + Capsule::table('oauth_access_token_scopes') + ->where('access_token', $token->getId()) + ->delete(); + } +} diff --git a/examples/relational/Storage/AuthCodeStorage.php b/examples/relational/Storage/AuthCodeStorage.php new file mode 100644 index 00000000..5a840237 --- /dev/null +++ b/examples/relational/Storage/AuthCodeStorage.php @@ -0,0 +1,92 @@ +where('auth_code', $code) + ->where('expire_time', '>=', time()) + ->get(); + + if (count($result) === 1) { + $token = new AuthCodeEntity($this->server); + $token->setId($result[0]['auth_code']); + $token->setRedirectUri($result[0]['client_redirect_uri']); + return $token; + } + + return null; + } + + public function create($token, $expireTime, $sessionId, $redirectUri) + { + Capsule::table('oauth_auth_codes') + ->insert([ + 'auth_code' => $token, + 'client_redirect_uri' => $redirectUri, + 'session_id' => $sessionId, + 'expire_time' => $expireTime + ]); + } + + /** + * {@inheritdoc} + */ + public function getScopes(AuthCodeEntity $token) + { + $result = Capsule::table('oauth_auth_code_scopes') + ->select(['oauth_scopes.id', 'oauth_scopes.description']) + ->join('oauth_scopes', 'oauth_auth_code_scopes.scope', '=', 'oauth_scopes.id') + ->where('auth_code', $token->getId()) + ->get(); + + $response = []; + + if (count($result) > 0) { + foreach ($result as $row) { + $scope = (new ScopeEntity($this->server))->hydrate([ + 'id' => $row['id'], + 'description' => $row['description'] + ]); + $response[] = $scope; + } + } + + return $response; + } + + /** + * {@inheritdoc} + */ + public function associateScope(AuthCodeEntity $token, ScopeEntity $scope) + { + Capsule::table('oauth_auth_code_scopes') + ->insert([ + 'auth_code' => $token->getId(), + 'scope' => $scope->getId() + ]); + } + + /** + * {@inheritdoc} + */ + public function delete(AuthCodeEntity $token) + { + Capsule::table('oauth_auth_codes') + ->where('auth_code', $token->getId()) + ->delete(); + } +} diff --git a/examples/relational/Storage/ClientStorage.php b/examples/relational/Storage/ClientStorage.php new file mode 100644 index 00000000..8db0e0c0 --- /dev/null +++ b/examples/relational/Storage/ClientStorage.php @@ -0,0 +1,71 @@ +select('oauth_clients.*') + ->where('oauth_clients.id', $clientId); + + if ($clientSecret !== null) { + $query->where('oauth_clients.secret', $clientSecret); + } + + if ($redirectUri) { + $query->join('oauth_client_redirect_uris', 'oauth_clients.id', '=', 'oauth_client_redirect_uris.client_id') + ->select(['oauth_clients.*', 'oauth_client_redirect_uris.*']) + ->where('oauth_client_redirect_uris.redirect_uri', $redirectUri); + } + + $result = $query->get(); + + if (count($result) === 1) { + $client = new ClientEntity($this->server); + $client->hydrate([ + 'id' => $result[0]['id'], + 'name' => $result[0]['name'] + ]); + + return $client; + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function getBySession(SessionEntity $session) + { + $result = Capsule::table('oauth_clients') + ->select(['oauth_clients.id', 'oauth_clients.name']) + ->join('oauth_sessions', 'oauth_clients.id', '=', 'oauth_sessions.client_id') + ->where('oauth_sessions.id', $session->getId()) + ->get(); + + if (count($result) === 1) { + $client = new ClientEntity($this->server); + $client->hydrate([ + 'id' => $result[0]['id'], + 'name' => $result[0]['name'] + ]); + + return $client; + } + + return null; + } +} diff --git a/examples/relational/Storage/RefreshTokenStorage.php b/examples/relational/Storage/RefreshTokenStorage.php new file mode 100644 index 00000000..ab291802 --- /dev/null +++ b/examples/relational/Storage/RefreshTokenStorage.php @@ -0,0 +1,58 @@ +where('refresh_token', $token) + ->where('expire_time', '>=', time()) + ->get(); + + if (count($result) === 1) { + $token = (new RefreshTokenEntity($this->server)) + ->setId($result[0]['refresh_token']) + ->setExpireTime($result[0]['expire_time']) + ->setAccessTokenId($result[0]['access_token']); + + return $token; + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function create($token, $expireTime, $accessToken) + { + Capsule::table('oauth_refresh_tokens') + ->insert([ + 'refresh_token' => $token, + 'access_token' => $accessToken, + 'expire_time' => $expireTime + ]); + } + + /** + * {@inheritdoc} + */ + public function delete(RefreshTokenEntity $token) + { + Capsule::table('oauth_refresh_tokens') + ->where('refresh_token', $token->getId()) + ->delete(); + } + +} diff --git a/examples/relational/Storage/ScopeStorage.php b/examples/relational/Storage/ScopeStorage.php new file mode 100644 index 00000000..674fc6d9 --- /dev/null +++ b/examples/relational/Storage/ScopeStorage.php @@ -0,0 +1,31 @@ +where('id', $scope) + ->get(); + + if (count($result) === 0) { + return null; + } + + return (new ScopeEntity($this->server))->hydrate([ + 'id' => $result[0]['id'], + 'description' => $result[0]['description'] + ]); + } +} diff --git a/examples/relational/Storage/SessionStorage.php b/examples/relational/Storage/SessionStorage.php new file mode 100644 index 00000000..9c02b18f --- /dev/null +++ b/examples/relational/Storage/SessionStorage.php @@ -0,0 +1,110 @@ +select(['oauth_sessions.id', 'oauth_sessions.owner_type', 'oauth_sessions.owner_id', 'oauth_sessions.client_id', 'oauth_sessions.client_redirect_uri']) + ->join('oauth_access_tokens', 'oauth_access_tokens.session_id', '=', 'oauth_sessions.id') + ->where('oauth_access_tokens.access_token', $accessToken->getId()) + ->get(); + + if (count($result) === 1) { + $session = new SessionEntity($this->server); + $session->setId($result[0]['id']); + $session->setOwner($result[0]['owner_type'], $result[0]['owner_id']); + + return $session; + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function getByAuthCode(AuthCodeEntity $authCode) + { + $result = Capsule::table('oauth_sessions') + ->select(['oauth_sessions.id', 'oauth_sessions.owner_type', 'oauth_sessions.owner_id', 'oauth_sessions.client_id', 'oauth_sessions.client_redirect_uri']) + ->join('oauth_auth_codes', 'oauth_auth_codes.session_id', '=', 'oauth_sessions.id') + ->where('oauth_auth_codes.auth_code', $authCode->getId()) + ->get(); + + if (count($result) === 1) { + $session = new SessionEntity($this->server); + $session->setId($result[0]['id']); + $session->setOwner($result[0]['owner_type'], $result[0]['owner_id']); + + return $session; + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function getScopes(SessionEntity $session) + { + $result = Capsule::table('oauth_sessions') + ->select('oauth_scopes.*') + ->join('oauth_session_scopes', 'oauth_sessions.id', '=', 'oauth_session_scopes.session_id') + ->join('oauth_scopes', 'oauth_scopes.id', '=', 'oauth_session_scopes.scope') + ->where('oauth_sessions.id', $session->getId()) + ->get(); + + $scopes = []; + + foreach ($result as $scope) { + $scopes[] = (new ScopeEntity($this->server))->hydrate([ + 'id' => $scope['id'], + 'description' => $scope['description'] + ]); + } + + return $scopes; + } + + /** + * {@inheritdoc} + */ + public function create($ownerType, $ownerId, $clientId, $clientRedirectUri = null) + { + $id = Capsule::table('oauth_sessions') + ->insert([ + 'owner_type' => $ownerType, + 'owner_id' => $ownerId, + 'client_id' => $clientId + ]); + + return $id; + } + + /** + * {@inheritdoc} + */ + public function associateScope(SessionEntity $session, ScopeEntity $scope) + { + Capsule::table('oauth_session_scopes') + ->insert([ + 'session_id' => $session->getId(), + 'scope' => $scope->getId() + ]); + } +} diff --git a/examples/relational/api.php b/examples/relational/api.php new file mode 100644 index 00000000..5b62898a --- /dev/null +++ b/examples/relational/api.php @@ -0,0 +1,145 @@ +createFromGlobals(); +$router = new \Orno\Route\RouteCollection; +$router->setStrategy(\Orno\Route\RouteStrategyInterface::RESTFUL_STRATEGY); + +// Set up the OAuth 2.0 resource server +$sessionStorage = new Storage\SessionStorage(); +$accessTokenStorage = new Storage\AccessTokenStorage(); +$clientStorage = new Storage\ClientStorage(); +$scopeStorage = new Storage\ScopeStorage(); + +$server = new ResourceServer( + $sessionStorage, + $accessTokenStorage, + $clientStorage, + $scopeStorage +); + +// Routing setup +$request = (new Request)->createFromGlobals(); +$router = new \Orno\Route\RouteCollection; + +// GET /tokeninfo +$router->get('/tokeninfo', function (Request $request) use ($server) { + + $token = [ + 'owner_id' => $server->getOwnerId(), + 'owner_type' => $server->getOwnerType(), + 'access_token' => $server->getAccessToken(), + 'client_id' => $server->getClientId(), + 'scopes' => $server->getScopes() + ]; + + return new Response(json_encode($token)); + +}); + +// GET /users +$router->get('/users', function (Request $request) use ($server) { + + $results = (new Model\Users())->get(); + + $users = []; + + foreach ($results as $result) { + $user = [ + 'username' => $result['username'], + 'name' => $result['name'] + ]; + + if ($server->hasScope('email')) { + $user['email'] = $result['email']; + } + + if ($server->hasScope('photo')) { + $user['photo'] = $result['photo']; + } + + $users[] = $user; + } + + return new Response(json_encode($users)); +}); + +// GET /users/{username} +$router->get('/users/{username}', function (Request $request, $args) use ($server) { + + $result = (new Model\Users())->get($args['username']); + + if (count($result) === 0) { + throw new NotFoundException(); + } + + $user = [ + 'username' => $result[0]['username'], + 'name' => $result[0]['name'] + ]; + + if ($server->hasScope('email')) { + $user['email'] = $result[0]['email']; + } + + if ($server->hasScope('photo')) { + $user['photo'] = $result[0]['photo']; + } + + return new Response(json_encode($user)); +}); + +$dispatcher = $router->getDispatcher(); + +try { + + // Check that access token is present + $server->isValidRequest(false); + + // A successful response + $response = $dispatcher->dispatch( + $request->getMethod(), + $request->getPathInfo() + ); + +} catch (\Orno\Http\Exception $e) { + + // A failed response + $response = $e->getJsonResponse(); + $response->setContent(json_encode(['status_code' => $e->getStatusCode(), 'message' => $e->getMessage()])); + +} catch (\League\OAuth2\Server\Exception\OAuthException $e) { + + $response = new Response(json_encode([ + 'error' => $e->errorType, + 'message' => $e->getMessage() + ]), $e->httpStatusCode); + + foreach ($e->getHttpHeaders() as $header) { + $response->headers($header); + } + +} catch (\Exception $e) { + + $response = new Orno\Http\Response; + $response->setStatusCode(500); + $response->setContent(json_encode(['status_code' => 500, 'message' => $e->getMessage()])); + +} finally { + + // Return the response + $response->headers->set('Content-type', 'application/json'); + $response->send(); + +} diff --git a/examples/relational/authcode_grant.php b/examples/relational/authcode_grant.php new file mode 100644 index 00000000..51774676 --- /dev/null +++ b/examples/relational/authcode_grant.php @@ -0,0 +1,139 @@ +createFromGlobals(); +$router = new \Orno\Route\RouteCollection; +$router->setStrategy(\Orno\Route\RouteStrategyInterface::RESTFUL_STRATEGY); + +// Set up the OAuth 2.0 authorization server +$server = new \League\OAuth2\Server\AuthorizationServer; +$server->setSessionStorage(new Storage\SessionStorage); +$server->setAccessTokenStorage(new Storage\AccessTokenStorage); +$server->setRefreshTokenStorage(new Storage\RefreshTokenStorage); +$server->setClientStorage(new Storage\ClientStorage); +$server->setScopeStorage(new Storage\ScopeStorage); +$server->setAuthCodeStorage(new Storage\AuthCodeStorage); + +$authCodeGrant = new \League\OAuth2\Server\Grant\AuthCodeGrant(); +$server->addGrantType($authCodeGrant); + +$refrehTokenGrant = new \League\OAuth2\Server\Grant\RefreshTokenGrant(); +$server->addGrantType($refrehTokenGrant); + +// Routing setup +$request = (new Request)->createFromGlobals(); +$router = new \Orno\Route\RouteCollection; + +$router->get('/authorize', function (Request $request) use ($server) { + + // First ensure the parameters in the query string are correct + + try { + + $authParams = $server->getGrantType('authorization_code')->checkAuthorizeParams(); + + } catch (\Exception $e) { + + return new Response( + json_encode([ + 'error' => $e->errorType, + 'message' => $e->getMessage() + ]), + $e->httpStatusCode, + $e->getHttpHeaders() + ); + + } + + // Normally at this point you would show the user a sign-in screen and ask them to authorize the requested scopes + + // ... + + // ... + + // ... + + // Create a new authorize request which will respond with a redirect URI that the user will be redirected to + + $redirectUri = $server->getGrantType('authorization_code')->newAuthorizeRequest('user', 1, $authParams); + + $response = new Response('', 200, [ + 'Location' => $redirectUri + ]); + + return $response; +}); + +$router->post('/access_token', function (Request $request) use ($server) { + + try { + + $response = $server->issueAccessToken(); + return new Response(json_encode($response), 200); + + } catch (\Exception $e) { + + return new Response( + json_encode([ + 'error' => $e->errorType, + 'message' => $e->getMessage() + ]), + $e->httpStatusCode, + $e->getHttpHeaders() + ); + + } + +}); + +$dispatcher = $router->getDispatcher(); + +try { + + // A successful response + $response = $dispatcher->dispatch( + $request->getMethod(), + $request->getPathInfo() + ); + +} catch (\Orno\Http\Exception $e) { + + // A failed response + $response = $e->getJsonResponse(); + $response->setContent(json_encode(['status_code' => $e->getStatusCode(), 'message' => $e->getMessage()])); + +} catch (\League\OAuth2\Server\Exception\OAuthException $e) { + + $response = new Response(json_encode([ + 'error' => $e->errorType, + 'message' => $e->getMessage() + ]), $e->httpStatusCode); + + foreach ($e->getHttpHeaders() as $header) { + $response->headers($header); + } + +} catch (\Exception $e) { + + $response = new Orno\Http\Response; + $response->setStatusCode(500); + $response->setContent(json_encode(['status_code' => 500, 'message' => $e->getMessage()])); + +} finally { + + // Return the response + $response->headers->set('Content-type', 'application/json'); + $response->send(); + +} diff --git a/examples/relational/composer.json b/examples/relational/composer.json new file mode 100644 index 00000000..34bd57cf --- /dev/null +++ b/examples/relational/composer.json @@ -0,0 +1,17 @@ +{ + "require": { + "illuminate/database": "4.1.*", + "orno/route": "1.*", + "ircmaxell/password-compat": "1.0.2", + "league/event": "0.2.0" + }, + "autoload": { + "psr-4": { + "League\\OAuth2\\Server\\": "../../src/", + "RelationalExample\\": "." + }, + "files": [ + "config/db.php" + ] + } +} \ No newline at end of file diff --git a/examples/relational/config/db.php b/examples/relational/config/db.php new file mode 100644 index 00000000..80eaf687 --- /dev/null +++ b/examples/relational/config/db.php @@ -0,0 +1,18 @@ +addConnection([ + 'driver' => 'sqlite', + 'database' => __DIR__.'/oauth2.sqlite3', + 'charset' => 'utf8', + 'collation' => 'utf8_unicode_ci' +]); + +$capsule->setAsGlobal(); diff --git a/examples/relational/config/init.php b/examples/relational/config/init.php new file mode 100644 index 00000000..1f6a5b8f --- /dev/null +++ b/examples/relational/config/init.php @@ -0,0 +1,249 @@ +create('users', function ($table) { + $table->increments('id'); + $table->string('username'); + $table->string('password'); + $table->string('name'); + $table->string('email'); + $table->string('photo'); +}); + +Capsule::table('users')->insert([ + 'username' => 'alexbilbie', + 'password' => password_hash('whisky', PASSWORD_DEFAULT), + 'name' => 'Alex Bilbie', + 'email' => 'hello@alexbilbie.com', + 'photo' => 'https://s.gravatar.com/avatar/14902eb1dac66b8458ebbb481d80f0a3' +]); + +Capsule::table('users')->insert([ + 'username' => 'philsturgeon', + 'password' => password_hash('cider', PASSWORD_DEFAULT), + 'name' => 'Phil Sturgeon', + 'email' => 'email@philsturgeon.co.uk', + 'photo' => 'https://s.gravatar.com/avatar/14df293d6c5cd6f05996dfc606a6a951' +]); + +/******************************************************************************/ + +print 'Creating clients table'.PHP_EOL; + +Capsule::schema()->create('oauth_clients', function ($table) { + $table->string('id'); + $table->string('secret'); + $table->string('name'); + $table->primary('id'); +}); + +Capsule::table('oauth_clients')->insert([ + 'id' => 'testclient', + 'secret' => 'secret', + 'name' => 'Test Client' +]); + +/******************************************************************************/ + +print 'Creating client redirect uris table'.PHP_EOL; + +Capsule::schema()->create('oauth_client_redirect_uris', function ($table) { + $table->increments('id'); + $table->string('client_id'); + $table->string('redirect_uri'); +}); + +Capsule::table('oauth_client_redirect_uris')->insert([ + 'client_id' => 'testclient', + 'redirect_uri' => 'http://example.com/redirect' +]); + +/******************************************************************************/ + +print 'Creating scopes table'.PHP_EOL; + +Capsule::schema()->create('oauth_scopes', function ($table) { + $table->string('id'); + $table->string('description'); + $table->primary('id'); +}); + +Capsule::table('oauth_scopes')->insert([ + 'id' => 'basic', + 'description' => 'Basic details about your account' +]); + +Capsule::table('oauth_scopes')->insert([ + 'id' => 'email', + 'description' => 'Your email address' +]); + +Capsule::table('oauth_scopes')->insert([ + 'id' => 'photo', + 'description' => 'Your photo' +]); + +/******************************************************************************/ + +print 'Creating sessions table'.PHP_EOL; + +Capsule::schema()->create('oauth_sessions', function ($table) { + $table->increments('id'); + $table->string('owner_type'); + $table->string('owner_id'); + $table->string('client_id'); + $table->string('client_redirect_uri')->nullable(); + + $table->foreign('client_id')->references('id')->on('oauth_clients')->onDelete('cascade'); +}); + +Capsule::table('oauth_sessions')->insert([ + 'owner_type' => 'client', + 'owner_id' => 'testclient', + 'client_id' => 'testclient' +]); + +Capsule::table('oauth_sessions')->insert([ + 'owner_type' => 'user', + 'owner_id' => '1', + 'client_id' => 'testclient' +]); + +Capsule::table('oauth_sessions')->insert([ + 'owner_type' => 'user', + 'owner_id' => '2', + 'client_id' => 'testclient' +]); + +/******************************************************************************/ + +print 'Creating access tokens table'.PHP_EOL; + +Capsule::schema()->create('oauth_access_tokens', function ($table) { + $table->string('access_token')->primary(); + $table->integer('session_id'); + $table->integer('expire_time'); + + $table->foreign('session_id')->references('id')->on('oauth_sessions')->onDelete('cascade'); +}); + +Capsule::table('oauth_access_tokens')->insert([ + 'access_token' => 'iamgod', + 'session_id' => '1', + 'expire_time' => time() + 86400 +]); + +Capsule::table('oauth_access_tokens')->insert([ + 'access_token' => 'iamalex', + 'session_id' => '2', + 'expire_time' => time() + 86400 +]); + +Capsule::table('oauth_access_tokens')->insert([ + 'access_token' => 'iamphil', + 'session_id' => '3', + 'expire_time' => time() + 86400 +]); + +/******************************************************************************/ + +print 'Creating refresh tokens table'.PHP_EOL; + +Capsule::schema()->create('oauth_refresh_tokens', function ($table) { + $table->string('refresh_token')->primary(); + $table->integer('expire_time'); + $table->string('access_token'); + + $table->foreign('access_token')->references('id')->on('oauth_access_tokens')->onDelete('cascade'); +}); + +/******************************************************************************/ + +print 'Creating auth codes table'.PHP_EOL; + +Capsule::schema()->create('oauth_auth_codes', function ($table) { + $table->string('auth_code')->primary(); + $table->integer('session_id'); + $table->integer('expire_time'); + $table->string('client_redirect_uri'); + + $table->foreign('session_id')->references('id')->on('oauth_sessions')->onDelete('cascade'); +}); + +/******************************************************************************/ + +print 'Creating oauth access token scopes table'.PHP_EOL; + +Capsule::schema()->create('oauth_access_token_scopes', function ($table) { + $table->increments('id'); + $table->string('access_token'); + $table->string('scope'); + + $table->foreign('access_token')->references('access_token')->on('oauth_access_tokens')->onDelete('cascade'); + $table->foreign('scope')->references('id')->on('oauth_scopes')->onDelete('cascade'); +}); + +Capsule::table('oauth_access_token_scopes')->insert([ + 'access_token' => 'iamgod', + 'scope' => 'basic' +]); + +Capsule::table('oauth_access_token_scopes')->insert([ + 'access_token' => 'iamgod', + 'scope' => 'email' +]); + +Capsule::table('oauth_access_token_scopes')->insert([ + 'access_token' => 'iamgod', + 'scope' => 'photo' +]); + +Capsule::table('oauth_access_token_scopes')->insert([ + 'access_token' => 'iamphil', + 'scope' => 'email' +]); + +Capsule::table('oauth_access_token_scopes')->insert([ + 'access_token' => 'iamalex', + 'scope' => 'photo' +]); + +/******************************************************************************/ + +print 'Creating oauth auth code scopes table'.PHP_EOL; + +Capsule::schema()->create('oauth_auth_code_scopes', function ($table) { + $table->increments('id'); + $table->string('auth_code'); + $table->string('scope'); + + $table->foreign('auth_code')->references('auth_code')->on('oauth_auth_codes')->onDelete('cascade'); + $table->foreign('scope')->references('id')->on('oauth_scopes')->onDelete('cascade'); +}); + +/******************************************************************************/ + +print 'Creating oauth session scopes table'.PHP_EOL; + +Capsule::schema()->create('oauth_session_scopes', function ($table) { + $table->increments('id'); + $table->string('session_id'); + $table->string('scope'); + + $table->foreign('session_id')->references('id')->on('oauth_sessions')->onDelete('cascade'); + $table->foreign('scope')->references('id')->on('oauth_scopes')->onDelete('cascade'); +}); diff --git a/examples/relational/other_grants.php b/examples/relational/other_grants.php new file mode 100644 index 00000000..a1c8c442 --- /dev/null +++ b/examples/relational/other_grants.php @@ -0,0 +1,114 @@ +createFromGlobals(); +$router = new \Orno\Route\RouteCollection; +$router->setStrategy(\Orno\Route\RouteStrategyInterface::RESTFUL_STRATEGY); + +// Set up the OAuth 2.0 authorization server +$server = new \League\OAuth2\Server\AuthorizationServer; +$server->setSessionStorage(new Storage\SessionStorage); +$server->setAccessTokenStorage(new Storage\AccessTokenStorage); +$server->setRefreshTokenStorage(new Storage\RefreshTokenStorage); +$server->setClientStorage(new Storage\ClientStorage); +$server->setScopeStorage(new Storage\ScopeStorage); +$server->setAuthCodeStorage(new Storage\AuthCodeStorage); + +$clientCredentials = new \League\OAuth2\Server\Grant\ClientCredentialsGrant(); +$server->addGrantType($clientCredentials); + +$passwordGrant = new \League\OAuth2\Server\Grant\PasswordGrant(); +$passwordGrant->setVerifyCredentialsCallback(function ($username, $password) { + $result = (new Model\Users())->get($username); + if (count($result) !== 1) { + return false; + } + + if (password_verify($password, $result[0]['password'])) { + return $username; + } + + return false; +}); +$server->addGrantType($passwordGrant); + +$refrehTokenGrant = new \League\OAuth2\Server\Grant\RefreshTokenGrant(); +$server->addGrantType($refrehTokenGrant); + +// Routing setup +$request = (new Request)->createFromGlobals(); +$router = new \Orno\Route\RouteCollection; + +$router->post('/access_token', function (Request $request) use ($server) { + + try { + + $response = $server->issueAccessToken(); + return new Response(json_encode($response), 200); + + } catch (\Exception $e) { + + return new Response( + json_encode([ + 'error' => $e->errorType, + 'message' => $e->getMessage() + ]), + $e->httpStatusCode, + $e->getHttpHeaders() + ); + + } + +}); + +$dispatcher = $router->getDispatcher(); + +try { + + // A successful response + $response = $dispatcher->dispatch( + $request->getMethod(), + $request->getPathInfo() + ); + +} catch (\Orno\Http\Exception $e) { + + // A failed response + $response = $e->getJsonResponse(); + $response->setContent(json_encode(['status_code' => $e->getStatusCode(), 'message' => $e->getMessage()])); + +} catch (\League\OAuth2\Server\Exception\OAuthException $e) { + + $response = new Response(json_encode([ + 'error' => $e->errorType, + 'message' => $e->getMessage() + ]), $e->httpStatusCode); + + foreach ($e->getHttpHeaders() as $header) { + $response->headers($header); + } + +} catch (\Exception $e) { + + $response = new Orno\Http\Response; + $response->setStatusCode(500); + $response->setContent(json_encode(['status_code' => 500, 'message' => $e->getMessage()])); + +} finally { + + // Return the response + $response->headers->set('Content-type', 'application/json'); + $response->send(); + +} diff --git a/license.txt b/license.txt index 15242b16..7bfce8c8 100644 --- a/license.txt +++ b/license.txt @@ -1,6 +1,6 @@ MIT License -Copyright (C) 2013 PHP League of Extraordinary Packages +Copyright (C) 2013 Alex Bilbie Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 00000000..f78850c1 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,17 @@ + + + + + ./tests/unit/ + + + + + src + + + + + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index bc2e166c..00000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,53 +0,0 @@ - - - - - tests/authorization - - - tests/resource - - - tests/util - - - - - PEAR_INSTALL_DIR - PHP_LIBDIR - vendor - tests - testing - - - - - - - - - - - - - - - - - - League\OAuth2\Server - - - DtNuuOrBh1QBXVyRqmVldC2Au11DVti9n - - - https://coveralls.io/api/v1/jobs - - - /tmp - - - - - - diff --git a/sql/index.html b/sql/index.html deleted file mode 100644 index e69de29b..00000000 diff --git a/src/AbstractServer.php b/src/AbstractServer.php new file mode 100644 index 00000000..884e8be7 --- /dev/null +++ b/src/AbstractServer.php @@ -0,0 +1,139 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server; + +use League\OAuth2\Server\Exception; +use League\OAuth2\Server\TokenType\TokenTypeInterface; +use Symfony\Component\HttpFoundation\Request; +use League\Event\Emitter; + +/** + * OAuth 2.0 Resource Server + */ + +abstract class AbstractServer +{ + /** + * The request object + * + * @var Util\RequestInterface + */ + protected $request; + + /** + * Storage classes + * @var array + */ + protected $storages = []; + + /** + * Token type + * @var TokenTypeInterface + */ + protected $tokenType; + + /** + * Event emitter + */ + protected $eventEmitter; + + /** + * Abstract server constructor + */ + public function __construct() + { + $this->setEventEmitter(); + } + + /** + * Set an event emitter + * @param object $emitter Event emitter object + */ + public function setEventEmitter($emitter = null) + { + if ($emitter === null) { + $this->eventEmitter = new Emitter; + } else { + $this->eventEmitter = $emitter; + } + } + + public function addEventListener($eventName, callable $listener) + { + $this->eventEmitter->addListener($eventName, $listener); + } + + public function getEventEmitter() + { + return $this->eventEmitter; + } + + /** + * Sets the Request Object + * @param \Symfony\Component\HttpFoundation\Request The Request Object + * @return self + */ + public function setRequest(Request $request) + { + $this->request = $request; + + return $this; + } + + /** + * Gets the Request object. It will create one from the globals if one is not set. + * @return \Symfony\Component\HttpFoundation\Request + */ + public function getRequest() + { + if ($this->request === null) { + $this->request = Request::createFromGlobals(); + } + + return $this->request; + } + + /** + * Return a storage class + * @param string $obj The class required + * @return Storage\ClientInterface|Storage\ScopeInterface|Storage\SessionInterface + */ + public function getStorage($obj) + { + if (!isset($this->storages[$obj])) { + throw new Exception\ServerErrorException( + 'The `'.$obj.'` storage interface has not been registered with the server' + ); + } + + return $this->storages[$obj]; + } + + /** + * Set the access token type + * @param TokenTypeInterface $tokenType The token type + * @return void + */ + public function setIdType(TokenTypeInterface $tokenType) + { + $this->tokenType = $tokenType; + } + + /** + * Get the access token type + * @return TokenTypeInterface + */ + public function getTokenType() + { + return $this->tokenType; + } +} diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php new file mode 100644 index 00000000..502a811d --- /dev/null +++ b/src/AuthorizationServer.php @@ -0,0 +1,346 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server; + +use League\OAuth2\Server\Grant\GrantTypeInterface; +use League\OAuth2\Server\Storage\ClientInterface; +use League\OAuth2\Server\Storage\AccessTokenInterface; +use League\OAuth2\Server\Storage\AuthCodeInterface; +use League\OAuth2\Server\Storage\RefreshTokenInterface; +use League\OAuth2\Server\Storage\SessionInterface; +use League\OAuth2\Server\Storage\ScopeInterface; +use League\OAuth2\Server\TokenType\Bearer; +use Symfony\Component\HttpFoundation\Request; + +/** + * OAuth 2.0 authorization server class + */ +class AuthorizationServer extends AbstractServer +{ + /** + * The delimeter between scopes specified in the scope query string parameter + * The OAuth 2 specification states it should be a space but most use a comma + * @var string + */ + protected $scopeDelimeter = ' '; + + /** + * The TTL (time to live) of an access token in seconds (default: 3600) + * @var integer + */ + protected $accessTokenTTL = 3600; + + /** + * The registered grant response types + * @var array + */ + protected $responseTypes = []; + + /** + * The registered grant types + * @var array + */ + protected $grantTypes = []; + + /** + * Require the "scope" parameter to be in checkAuthoriseParams() + * @var boolean + */ + protected $requireScopeParam = false; + + /** + * Default scope(s) to be used if none is provided + * @var string|array + */ + protected $defaultScope; + + /** + * Require the "state" parameter to be in checkAuthoriseParams() + * @var boolean + */ + protected $requireStateParam = false; + + /** + * Create a new OAuth2 authorization server + * @return self + */ + public function __construct() + { + $this->storages = []; + + // Set Bearer as the default token type + $this->setIdType(new Bearer); + + parent::__construct(); + + return $this; + } + + /** + * Set the client storage + * @param ClientInterface $storage + * @return self + */ + public function setClientStorage(ClientInterface $storage) + { + $storage->setServer($this); + $this->storages['client'] = $storage; + + return $this; + } + + /** + * Set the session storage + * @param SessionInterface $storage + * @return self + */ + public function setSessionStorage(SessionInterface $storage) + { + $storage->setServer($this); + $this->storages['session'] = $storage; + + return $this; + } + + /** + * Set the access token storage + * @param AccessTokenInterface $storage + * @return self + */ + public function setAccessTokenStorage(AccessTokenInterface $storage) + { + $storage->setServer($this); + $this->storages['access_token'] = $storage; + + return $this; + } + + /** + * Set the refresh token storage + * @param RefreshTokenInteface $storage + * @return self + */ + public function setRefreshTokenStorage(RefreshTokenInterface $storage) + { + $storage->setServer($this); + $this->storages['refresh_token'] = $storage; + + return $this; + } + + /** + * Set the auth code storage + * @param AuthCodeInterface $authCode + * @return self + */ + public function setAuthCodeStorage(AuthCodeInterface $storage) + { + $storage->setServer($this); + $this->storages['auth_code'] = $storage; + + return $this; + } + + /** + * Set the scope storage + * @param ScopeInterface $storage + * @return self + */ + public function setScopeStorage(ScopeInterface $storage) + { + $storage->setServer($this); + $this->storages['scope'] = $storage; + + return $this; + } + + /** + * 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) + * @return self + */ + public function addGrantType(GrantTypeInterface $grantType, $identifier = null) + { + if (is_null($identifier)) { + $identifier = $grantType->getIdentifier(); + } + + // Inject server into grant + $grantType->setAuthorizationServer($this); + + $this->grantTypes[$identifier] = $grantType; + + if (!is_null($grantType->getResponseType())) { + $this->responseTypes[] = $grantType->getResponseType(); + } + + return $this; + } + + /** + * 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 function hasGrantType($identifier) + { + return (array_key_exists($identifier, $this->grantTypes)); + } + + /** + * Returns response types + * @return array + */ + public function getResponseTypes() + { + return $this->responseTypes; + } + + /** + * Require the "scope" paremter in checkAuthoriseParams() + * @param boolean $require + * @return self + */ + public function requireScopeParam($require = true) + { + $this->requireScopeParam = $require; + + return $this; + } + + /** + * Is the scope parameter required? + * @return bool + */ + public function scopeParamRequired() + { + return $this->requireScopeParam; + } + + /** + * Default scope to be used if none is provided and requireScopeParam() is false + * @param string $default Name of the default scope + * @param self + */ + public function setDefaultScope($default = null) + { + $this->defaultScope = $default; + + return $this; + } + + /** + * Default scope to be used if none is provided and requireScopeParam is false + * @return string|null + */ + public function getDefaultScope() + { + return $this->defaultScope; + } + + /** + * Require the "state" paremter in checkAuthoriseParams() + * @param boolean $require + * @return void + */ + public function stateParamRequired() + { + return $this->requireStateParam; + } + + /** + * Require the "state" paremter in checkAuthoriseParams() + * @param boolean $require + * @return void + */ + public function requireStateParam($require = true) + { + $this->requireStateParam = $require; + + return $this; + } + + /** + * 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; + + return $this; + } + + /** + * Get the TTL for an access token + * @return int The TTL + */ + public function getAccessTokenTTL() + { + return $this->accessTokenTTL; + } + + /** + * Set the TTL for an access token + * @param int $accessTokenTTL The new TTL + */ + public function setAccessTokenTTL($accessTokenTTL = 3600) + { + $this->accessTokenTTL = $accessTokenTTL; + + return $this; + } + + /** + * Issue an access token + * @return array Authorise request parameters + */ + public function issueAccessToken() + { + $grantType = $this->getRequest()->request->get('grant_type'); + if (is_null($grantType)) { + throw new Exception\InvalidRequestException('grant_type'); + } + + // Ensure grant type is one that is recognised and is enabled + if (!in_array($grantType, array_keys($this->grantTypes))) { + throw new Exception\UnsupportedGrantTypeException($grantType); + } + + // Complete the flow + return $this->getGrantType($grantType)->completeFlow(); + } + + /** + * Return a grant type class + * @param string $grantType The grant type identifer + * @return Grant\GrantTypeInterface + */ + public function getGrantType($grantType) + { + if (isset($this->grantTypes[$grantType])) { + return $this->grantTypes[$grantType]; + } + + throw new Exception\InvalidGrantException($grantType); + } +} diff --git a/src/Entity/AbstractTokenEntity.php b/src/Entity/AbstractTokenEntity.php new file mode 100644 index 00000000..4712f68d --- /dev/null +++ b/src/Entity/AbstractTokenEntity.php @@ -0,0 +1,173 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entity; + +use League\OAuth2\Server\Util\SecureKey; +use League\OAuth2\Server\AbstractServer; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * Abstract token class + */ +abstract class AbstractTokenEntity +{ + /** + * Token identifier + * @var string + */ + protected $id; + + /** + * Associated session + * @var \League\OAuth2\Server\SessionEntity + */ + protected $session; + + /** + * Session scopes + * @var array Array of ScopeEntity + */ + protected $scopes; + + /** + * Token expire time + * @var int + */ + protected $expireTime = 0; + + /** + * Authorization or resource server + * @var \League\OAuth2\Server\AbstractServer + */ + protected $server; + + /** + * __construct + * @param \League\OAuth2\Server\AbstractServer $server + * @return self + */ + public function __construct(AbstractServer $server) + { + $this->server = $server; + + return $this; + } + + /** + * Set session + * @param \League\OAuth2\Server\SessionEntity $session + * @return self + */ + public function setSession(SessionEntity $session) + { + $this->session = $session; + + return $this; + } + + /** + * Set the expire time of the token + * @param integer $expireTime Unix time stamp + * @return self + */ + public function setExpireTime($expireTime) + { + $this->expireTime = $expireTime; + + return $this; + } + + /** + * Return token expire time + * @return int + */ + public function getExpireTime() + { + return $this->expireTime; + } + + /** + * Set token ID + * @param string $token Token ID + * @return self + */ + public function setId($id = null) + { + $this->id = ($id !== null) ? $id : SecureKey::generate(); + + return $this; + } + + /** + * Get the token ID + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Associate a scope + * @param \League\OAuth2\Server\Entity\ScopeEntity $scope + * @return self + */ + public function associateScope(ScopeEntity $scope) + { + if (!isset($this->scopes[$scope->getId()])) { + $this->scopes[$scope->getId()] = $scope; + } + + return $this; + } + + /** + * Format the local scopes array + * @param array $unformatted Array of \League\OAuth2\Server\Entity\Scope + * @return array + */ + protected function formatScopes($unformatted = []) + { + $scopes = []; + foreach ($unformatted as $scope) { + if ($scope instanceof ScopeEntity) { + $scopes[$scope->getId()] = $scope; + } + } + + return $scopes; + } + + /** + * Returns the token as a string if the object is cast as a string + * @return string + */ + public function __toString() + { + if ($this->id === null) { + return ''; + } + return $this->id; + } + + /** + * Expire the token + * @return void + */ + abstract public function expire(); + + /** + * Save the token + * @return void + */ + abstract public function save(); +} diff --git a/src/Entity/AccessTokenEntity.php b/src/Entity/AccessTokenEntity.php new file mode 100644 index 00000000..76e8093a --- /dev/null +++ b/src/Entity/AccessTokenEntity.php @@ -0,0 +1,89 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entity; + +/** + * Access token entity class + */ +class AccessTokenEntity extends AbstractTokenEntity +{ + /** + * Get session + * @return \League\OAuth2\Server\SessionEntity + */ + public function getSession() + { + if ($this->session instanceof SessionEntity) { + return $this->session; + } + + $this->session = $this->server->getStorage('session')->getByAccessToken($this); + + return $this->session; + } + + /** + * Check if access token has an associated scope + * @param string $scope Scope to check + * @return bool + */ + public function hasScope($scope) + { + if ($this->scopes === null) { + $this->getScopes(); + } + + return isset($this->scopes[$scope]); + } + + /** + * Return all scopes associated with the session + * @return array Array of \League\OAuth2\Server\Entity\Scope + */ + public function getScopes() + { + if ($this->scopes === null) { + $this->scopes = $this->formatScopes( + $this->server->getStorage('access_token')->getScopes($this) + ); + } + + return $this->scopes; + } + + /** + * {@inheritdoc} + */ + public function save() + { + $this->server->getStorage('access_token')->create( + $this->getId(), + $this->getExpireTime(), + $this->getSession()->getId() + ); + + // Associate the scope with the token + foreach ($this->getScopes() as $scope) { + $this->server->getStorage('access_token')->associateScope($this, $scope); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function expire() + { + $this->server->getStorage('access_token')->delete($this); + } +} diff --git a/src/Entity/AuthCodeEntity.php b/src/Entity/AuthCodeEntity.php new file mode 100644 index 00000000..a116af75 --- /dev/null +++ b/src/Entity/AuthCodeEntity.php @@ -0,0 +1,118 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entity; + +/** + * Access token entity class + */ +class AuthCodeEntity extends AbstractTokenEntity +{ + /** + * Redirect URI + * @var string + */ + protected $redirectUri = ''; + + /** + * Set the redirect URI for the authorization request + * @param string $redirectUri + * @return self + */ + public function setRedirectUri($redirectUri) + { + $this->redirectUri = $redirectUri; + + return $this; + } + + /** + * Get the redirect URI + * @return string + */ + public function getRedirectUri() + { + return $this->redirectUri; + } + + /** + * Generate a redirect URI + * @param string $state The state parameter if set by the client + * @param string $queryDelimeter The query delimiter ('?' for auth code grant, '#' for implicit grant) + * @return string + */ + public function generateRedirectUri($state = null, $queryDelimeter = '?') + { + $uri = $this->getRedirectUri(); + $uri .= (strstr($this->getRedirectUri(), $queryDelimeter) === false) ? $queryDelimeter : '&'; + + return $uri.http_build_query([ + 'code' => $this->getId(), + 'state' => $state + ]); + } + + /** + * {@inheritdoc} + */ + public function getSession() + { + if ($this->session instanceof SessionEntity) { + return $this->session; + } + + $this->session = $this->server->getStorage('session')->getByAuthCode($this); + + return $this->session; + } + + /** + * {@inheritdoc} + */ + public function getScopes() + { + if ($this->scopes === null) { + $this->scopes = $this->formatScopes( + $this->server->getStorage('auth_code')->getScopes($this) + ); + } + + return $this->scopes; + } + + /** + * {@inheritdoc} + */ + public function save() + { + $this->server->getStorage('auth_code')->create( + $this->getId(), + $this->getExpireTime(), + $this->getSession()->getId(), + $this->getRedirectUri() + ); + + // Associate the scope with the token + foreach ($this->getScopes() as $scope) { + $this->server->getStorage('auth_code')->associateScope($this, $scope); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function expire() + { + $this->server->getStorage('auth_code')->delete($this); + } +} diff --git a/src/Entity/ClientEntity.php b/src/Entity/ClientEntity.php new file mode 100644 index 00000000..34a3320a --- /dev/null +++ b/src/Entity/ClientEntity.php @@ -0,0 +1,100 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entity; + +use League\OAuth2\Server\AbstractServer; + +/** + * Client entity class + */ +class ClientEntity +{ + use EntityTrait; + + /** + * Client identifier + * @var string + */ + protected $id = null; + + /** + * Client secret + * @var string + */ + protected $secret = null; + + /** + * Client name + * @var string + */ + protected $name = null; + + /** + * Client redirect URI + * @var string + */ + protected $redirectUri = null; + + /** + * Authorization or resource server + * @var \League\OAuth2\Server\AbstractServer + */ + protected $server; + + /** + * __construct + * @param \League\OAuth2\Server\AbstractServer $server + * @return self + */ + public function __construct(AbstractServer $server) + { + $this->server = $server; + + return $this; + } + + /** + * Return the client identifier + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Return the client secret + * @return string + */ + public function getSecret() + { + return $this->secret; + } + + /** + * Get the client name + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returnt the client redirect URI + * @return string + */ + public function getRedirectUri() + { + return $this->redirectUri; + } +} diff --git a/src/Entity/EntityTrait.php b/src/Entity/EntityTrait.php new file mode 100644 index 00000000..80d6138f --- /dev/null +++ b/src/Entity/EntityTrait.php @@ -0,0 +1,30 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entity; + +trait EntityTrait +{ + /** + * Hydrate an entity with properites + * @param array $properties + */ + public function hydrate(array $properties) + { + foreach ($properties as $prop => $val) { + if (property_exists($this, $prop)) { + $this->{$prop} = $val; + } + } + + return $this; + } +} diff --git a/src/Entity/RefreshTokenEntity.php b/src/Entity/RefreshTokenEntity.php new file mode 100644 index 00000000..070fddf4 --- /dev/null +++ b/src/Entity/RefreshTokenEntity.php @@ -0,0 +1,87 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entity; + +/** + * Refresh token entity class + */ +class RefreshTokenEntity extends AbstractTokenEntity +{ + /** + * Access token associated to refresh token + * @var \League\OAuth2\Server\Entity\AccessTokenEntity + */ + protected $accessTokenEntity; + + /** + * Id of the access token + * @var string + */ + protected $accessTokenId; + + /** + * Set the ID of the associated access token + * @param string $accessToken + * @return self + */ + public function setAccessTokenId($accessTokenId) + { + $this->accessTokenId = $accessTokenId; + + return $this; + } + + /** + * Associate an access token + * @param \League\OAuth2\Server\Entity\AccessTokenEntity $accessToken + * @return self + */ + public function setAccessToken(AccessTokenEntity $accessTokenEntity) + { + $this->accessTokenEntity = $accessTokenEntity; + + return $this; + } + + /** + * Return access token + * @return AccessToken + */ + public function getAccessToken() + { + if (! $this->accessTokenEntity instanceof AccessTokenEntity) { + $this->accessTokenEntity = $this->server->getStorage('access_token')->get($this->accessTokenId); + } + + return $this->accessTokenEntity; + } + + /** + * {@inheritdoc} + */ + public function save() + { + $this->server->getStorage('refresh_token')->create( + $this->getId(), + $this->getExpireTime(), + $this->getAccessToken()->getId() + ); + } + + /** + * {@inheritdoc} + */ + public function expire() + { + $this->server->getStorage('refresh_token')->delete($this); + } +} diff --git a/src/Entity/ScopeEntity.php b/src/Entity/ScopeEntity.php new file mode 100644 index 00000000..4b4ed673 --- /dev/null +++ b/src/Entity/ScopeEntity.php @@ -0,0 +1,82 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entity; + +use League\OAuth2\Server\AbstractServer; + +/** + * Scope entity class + */ +class ScopeEntity implements \JsonSerializable +{ + use EntityTrait; + + /** + * Scope identifier + * @var string + */ + protected $id; + + /** + * Scope description + * @var string + */ + protected $description; + + /** + * Authorization or resource server + * @var \League\OAuth2\Server\AbstractServer + */ + protected $server; + + /** + * __construct + * @param \League\OAuth2\Server\AbstractServer $server + * @return self + */ + public function __construct(AbstractServer $server) + { + $this->server = $server; + + return $this; + } + + /** + * Return the scope identifer + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Return the scope's description + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * Returns a JSON object when entity is passed into json_encode + * @return array + */ + public function jsonSerialize() + { + return [ + 'id' => $this->getId(), + 'description' => $this->getDescription() + ]; + } +} diff --git a/src/Entity/SessionEntity.php b/src/Entity/SessionEntity.php new file mode 100644 index 00000000..2d03cc48 --- /dev/null +++ b/src/Entity/SessionEntity.php @@ -0,0 +1,276 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entity; + +use League\OAuth2\Server\AbstractServer; +use League\OAuth2\Server\Event; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * Session entity grant + */ +class SessionEntity +{ + /** + * Session identifier + * @var string + */ + protected $id; + + /** + * Client identifier + * @var string + */ + protected $client; + + /** + * Session owner identifier + * @var string + */ + protected $ownerId; + + /** + * Session owner type (e.g. "user") + * @var string + */ + protected $ownerType; + + /** + * Auth code + * @var \League\OAuth2\Server\Entity\AuthCodeEntity + */ + protected $authCode; + + /** + * Access token + * @var \League\OAuth2\Server\Entity\AccessTokenEntity + */ + protected $accessToken; + + /** + * Refresh token + * @var \League\OAuth2\Server\Entity\RefreshTokenEntity + */ + protected $refreshToken; + + /** + * Session scopes + * @var \Symfony\Component\HttpFoundation\ParameterBag + */ + protected $scopes; + + /** + * Authorization or resource server + * @var \League\OAuth2\Server\Authorization|\League\OAuth2\Server\Resource + */ + protected $server; + + /** + * __construct + * @param \League\OAuth2\Server\AbstractServer $server + * @return self + */ + public function __construct(AbstractServer $server) + { + $this->server = $server; + + return $this; + } + + /** + * Set the session identifier + * @param string $id + * @return self + */ + public function setId($id) + { + $this->id = $id; + + return $this; + } + + /** + * Return the session identifier + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Associate a scope + * @param \League\OAuth2\Server\Entity\ScopeEntity $scope + * @return self + */ + public function associateScope(ScopeEntity $scope) + { + if (!isset($this->scopes[$scope->getId()])) { + $this->scopes[$scope->getId()] = $scope; + } + + return $this; + } + + /** + * Check if access token has an associated scope + * @param string $scope Scope to check + * @return bool + */ + public function hasScope($scope) + { + if ($this->scopes === null) { + $this->getScopes(); + } + + return isset($this->scopes[$scope]); + } + + /** + * Return all scopes associated with the session + * @return array Array of \League\OAuth2\Server\Entity\Scope + */ + public function getScopes() + { + if ($this->scopes === null) { + $this->scopes = $this->formatScopes($this->server->getStorage('session')->getScopes($this)); + } + + return $this->scopes; + } + + /** + * Format the local scopes array + * @param array $unformated Array of Array of \League\OAuth2\Server\Entity\Scope + * @return array + */ + private function formatScopes($unformated = []) + { + $scopes = []; + if (is_array($unformated)) { + foreach ($unformated as $scope) { + if ($scope instanceof ScopeEntity) { + $scopes[$scope->getId()] = $scope; + } + } + } + + return $scopes; + } + + /** + * Associate an access token with the session + * @param \League\OAuth2\Server\Entity\AccessTokenEntity $accessToken + * @return self + */ + public function associateAccessToken(AccessTokenEntity $accessToken) + { + $this->accessToken = $accessToken; + + return $this; + } + + /** + * Associate a refresh token with the session + * @param \League\OAuth2\Server\Entity\RefreshTokenEntity $refreshToken + * @return self + */ + public function associateRefreshToken(RefreshTokenEntity $refreshToken) + { + $this->refreshToken = $refreshToken; + + return $this; + } + + /** + * Associate a client with the session + * @param League\OAuth2\Server\Entity\ClientEntity $client The client + * @return self + */ + public function associateClient(ClientEntity $client) + { + $this->client = $client; + + return $this; + } + + /** + * Return the session client + * @return League\OAuth2\Server\Entity\Client + */ + public function getClient() + { + if ($this->client instanceof ClientEntity) { + return $this->client; + } + + $this->client = $this->server->getStorage('client')->getBySession($this); + + return $this->client; + } + + /** + * Set the session owner + * @param string $type The type of the owner (e.g. user, app) + * @param string $id The identifier of the owner + * @return self + */ + public function setOwner($type, $id) + { + $this->ownerType = $type; + $this->ownerId = $id; + + $this->server->getEventEmitter()->emit(new Event\SessionOwnerEvent($this)); + + return $this; + } + + /** + * Return session owner identifier + * @return string + */ + public function getOwnerId() + { + return $this->ownerId; + } + + /** + * Return session owner type + * @return string + */ + public function getOwnerType() + { + return $this->ownerType; + } + + /** + * Save the session + * @return void + */ + public function save() + { + // Save the session and get an identifier + $id = $this->server->getStorage('session')->create( + $this->getOwnerType(), + $this->getOwnerId(), + $this->getClient()->getId(), + $this->getClient()->getRedirectUri() + ); + + $this->setId($id); + + // Associate the scope with the session + foreach ($this->getScopes() as $scope) { + $this->server->getStorage('session')->associateScope($this, $scope); + } + } +} diff --git a/src/Event/SessionOwnerEvent.php b/src/Event/SessionOwnerEvent.php new file mode 100644 index 00000000..621d0a83 --- /dev/null +++ b/src/Event/SessionOwnerEvent.php @@ -0,0 +1,51 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Event; + +use League\Event\AbstractEvent; +use League\OAuth2\Server\Entity\SessionEntity; + +class SessionOwnerEvent extends AbstractEvent +{ + /** + * Session entity + * @var \League\OAuth2\Server\Entity\SessionEntity + */ + private $session; + + /** + * Init the event with a session + * @param \League\OAuth2\Server\Entity\SessionEntity $session + */ + public function __construct(SessionEntity $session) + { + $this->session = $session; + } + + /** + * The name of the event + * @return string + */ + public function getName() + { + return 'session.owner'; + } + + /** + * Return session + * @return \League\OAuth2\Server\Entity\SessionEntity + */ + public function getSession() + { + return $this->session; + } +} diff --git a/src/Exception/AccessDeniedException.php b/src/Exception/AccessDeniedException.php new file mode 100644 index 00000000..e5d84b61 --- /dev/null +++ b/src/Exception/AccessDeniedException.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Exception; + +/** + * Exception class + */ +class AccessDeniedException extends OAuthException +{ + /** + * {@inheritdoc} + */ + public $httpStatusCode = 401; + + /** + * {@inheritdoc} + */ + public $errorType = 'access_denied'; + + /** + * {@inheritdoc} + */ + public function __construct() + { + parent::__construct('The resource owner or authorization server denied the request.'); + } +} diff --git a/src/Exception/InvalidClientException.php b/src/Exception/InvalidClientException.php new file mode 100644 index 00000000..ba01d277 --- /dev/null +++ b/src/Exception/InvalidClientException.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Exception; + +/** + * Exception class + */ +class InvalidClientException extends OAuthException +{ + /** + * {@inheritdoc} + */ + public $httpStatusCode = 401; + + /** + * {@inheritdoc} + */ + public $errorType = 'invalid_client'; + + /** + * {@inheritdoc} + */ + public function __construct() + { + parent::__construct('Client authentication failed.'); + } +} diff --git a/src/Exception/InvalidCredentialsException.php b/src/Exception/InvalidCredentialsException.php new file mode 100644 index 00000000..40c4ce7e --- /dev/null +++ b/src/Exception/InvalidCredentialsException.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Exception; + +/** + * Exception class + */ +class InvalidCredentialsException extends OAuthException +{ + /** + * {@inheritdoc} + */ + public $httpStatusCode = 401; + + /** + * {@inheritdoc} + */ + public $errorType = 'invalid_credentials'; + + /** + * {@inheritdoc} + */ + public function __construct() + { + parent::__construct('The user credentials were incorrect.'); + } +} diff --git a/src/Exception/InvalidGrantException.php b/src/Exception/InvalidGrantException.php new file mode 100644 index 00000000..051330e9 --- /dev/null +++ b/src/Exception/InvalidGrantException.php @@ -0,0 +1,42 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Exception; + +/** + * Exception class + */ +class InvalidGrantException extends OAuthException +{ + /** + * {@inheritdoc} + */ + public $httpStatusCode = 400; + + /** + * {@inheritdoc} + */ + public $errorType = 'invalid_grant'; + + /** + * {@inheritdoc} + */ + + public function __construct($parameter) + { + parent::__construct( + sprintf( + '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.', + $parameter + ) + ); + } +} diff --git a/src/Exception/InvalidRefreshException.php b/src/Exception/InvalidRefreshException.php new file mode 100644 index 00000000..5ca3d921 --- /dev/null +++ b/src/Exception/InvalidRefreshException.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Exception; + +/** + * Exception class + */ +class InvalidRefreshException extends OAuthException +{ + /** + * {@inheritdoc} + */ + public $httpStatusCode = 400; + + /** + * {@inheritdoc} + */ + public $errorType = 'invalid_request'; + + /** + * {@inheritdoc} + */ + public function __construct() + { + parent::__construct('The refresh token is invalid.'); + } +} diff --git a/src/Exception/InvalidRequestException.php b/src/Exception/InvalidRequestException.php new file mode 100644 index 00000000..56dd75df --- /dev/null +++ b/src/Exception/InvalidRequestException.php @@ -0,0 +1,42 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Exception; + +/** + * Exception class + */ +class InvalidRequestException extends OAuthException +{ + /** + * {@inheritdoc} + */ + public $httpStatusCode = 400; + + /** + * {@inheritdoc} + */ + public $errorType = 'invalid_request'; + + /** + * {@inheritdoc} + */ + + public function __construct($parameter) + { + parent::__construct( + sprintf( + '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.', + $parameter + ) + ); + } +} diff --git a/src/Exception/InvalidScopeException.php b/src/Exception/InvalidScopeException.php new file mode 100644 index 00000000..eb74213d --- /dev/null +++ b/src/Exception/InvalidScopeException.php @@ -0,0 +1,42 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Exception; + +/** + * Exception class + */ +class InvalidScopeException extends OAuthException +{ + /** + * {@inheritdoc} + */ + public $httpStatusCode = 400; + + /** + * {@inheritdoc} + */ + public $errorType = 'invalid_scope'; + + /** + * {@inheritdoc} + */ + + public function __construct($parameter) + { + parent::__construct( + sprintf( + 'The requested scope is invalid, unknown, or malformed. Check the "%s" scope.', + $parameter + ) + ); + } +} diff --git a/src/Exception/OAuthException.php b/src/Exception/OAuthException.php new file mode 100644 index 00000000..5cbb0b3a --- /dev/null +++ b/src/Exception/OAuthException.php @@ -0,0 +1,93 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Exception; + +use Symfony\Component\HttpFoundation\Request; + +/** + * Exception class + */ +class OAuthException extends \Exception +{ + /** + * The HTTP status code for this exception that should be sent in the response + */ + public $httpStatusCode = 400; + + /** + * The exception type + */ + public $errorType = ''; + + /** + * Throw a new exception + */ + public function __construct($msg = 'An error occured') + { + parent::__construct($msg); + } + + /** + * Get all headers that have to be send with the error response + * @return array Array with header values + */ + public function getHttpHeaders() + { + $headers = []; + switch ($this->httpStatusCode) { + case 401: + $headers[] = 'HTTP/1.1 401 Unauthorized'; + break; + case 500: + $headers[] = 'HTTP/1.1 500 Internal Server Error'; + break; + case 501: + $headers[] = 'HTTP/1.1 501 Not Implemented'; + break; + case 400: + default: + $headers[] = 'HTTP/1.1 400 Bad Request'; + break; + } + + // Add "WWW-Authenticate" header + // + // RFC 6749, section 5.2.: + // "If the client attempted to authenticate via the 'Authorization' + // request header field, the authorization server MUST + // respond with an HTTP 401 (Unauthorized) status code and + // include the "WWW-Authenticate" response header field + // matching the authentication scheme used by the client. + // @codeCoverageIgnoreStart + if ($this->errorType === 'invalid_client') { + $authScheme = null; + $request = new Request(); + if ($request->getUser() !== null) { + $authScheme = 'Basic'; + } else { + $authHeader = $request->headers->get('Authorization'); + if ($authHeader !== null) { + if (strpos($authHeader, 'Bearer') === 0) { + $authScheme = 'Bearer'; + } elseif (strpos($authHeader, 'Basic') === 0) { + $authScheme = 'Basic'; + } + } + } + if ($authScheme !== null) { + $headers[] = 'WWW-Authenticate: '.$authScheme.' realm=""'; + } + } + // @codeCoverageIgnoreEnd + return $headers; + } +} diff --git a/src/Exception/ServerErrorException.php b/src/Exception/ServerErrorException.php new file mode 100644 index 00000000..fe5a7df1 --- /dev/null +++ b/src/Exception/ServerErrorException.php @@ -0,0 +1,37 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Exception; + +/** + * Exception class + */ +class ServerErrorException extends OAuthException +{ + /** + * {@inheritdoc} + */ + public $httpStatusCode = 500; + + /** + * {@inheritdoc} + */ + public $errorType = 'server_error'; + + /** + * {@inheritdoc} + */ + public function __construct($parameter = null) + { + $parameter = is_null($parameter) ? 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.' : $parameter; + parent::__construct($parameter); + } +} diff --git a/src/Exception/UnauthorizedClientException.php b/src/Exception/UnauthorizedClientException.php new file mode 100644 index 00000000..fd1f18c3 --- /dev/null +++ b/src/Exception/UnauthorizedClientException.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Exception; + +/** + * Exception class + */ +class UnauthorizedClientException extends OAuthException +{ + /** + * {@inheritdoc} + */ + public $httpStatusCode = 400; + + /** + * {@inheritdoc} + */ + public $errorType = 'unauthorized_client'; + + /** + * {@inheritdoc} + */ + public function __construct() + { + parent::__construct('The client is not authorized to request an access token using this method.'); + } +} diff --git a/src/Exception/UnsupportedGrantTypeException.php b/src/Exception/UnsupportedGrantTypeException.php new file mode 100644 index 00000000..5a4f1d90 --- /dev/null +++ b/src/Exception/UnsupportedGrantTypeException.php @@ -0,0 +1,42 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Exception; + +/** + * Exception class + */ +class UnsupportedGrantTypeException extends OAuthException +{ + /** + * {@inheritdoc} + */ + public $httpStatusCode = 400; + + /** + * {@inheritdoc} + */ + public $errorType = 'unsupported_grant_type'; + + /** + * {@inheritdoc} + */ + + public function __construct($parameter) + { + parent::__construct( + sprintf( + 'The authorization grant type "%s" is not supported by the authorization server.', + $parameter + ) + ); + } +} diff --git a/src/Exception/UnsupportedResponseTypeException.php b/src/Exception/UnsupportedResponseTypeException.php new file mode 100644 index 00000000..05677686 --- /dev/null +++ b/src/Exception/UnsupportedResponseTypeException.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Exception; + +/** + * Exception class + */ +class UnsupportedResponseTypeException extends OAuthException +{ + /** + * {@inheritdoc} + */ + public $httpStatusCode = 400; + + /** + * {@inheritdoc} + */ + public $errorType = 'unsupported_response_type'; + + /** + * {@inheritdoc} + */ + public function __construct($parameter) + { + parent::__construct('The authorization server does not support obtaining an access token using this method.'); + } +} diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php new file mode 100644 index 00000000..10195c79 --- /dev/null +++ b/src/Grant/AbstractGrant.php @@ -0,0 +1,190 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Grant; + +use League\OAuth2\Server\AuthorizationServer; +use League\OAuth2\Server\Entity\ScopeEntity; +use League\OAuth2\Server\Exception; + +/** + * Abstract grant class + */ +abstract class AbstractGrant implements GrantTypeInterface +{ + /** + * Grant identifier + * @var string + */ + protected $identifier = ''; + + /** + * Response type + * @var string + */ + protected $responseType; + + /** + * Callback to authenticate a user's name and password + * @var function + */ + protected $callback; + + /** + * AuthServer instance + * @var AuthServer + */ + protected $server; + + /** + * Access token expires in override + * @var int + */ + protected $accessTokenTTL; + + /** + * Return the identifier + * @return string + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * Return the identifier + * @param string $identifier + * @return self + */ + public function setIdentifier($identifier) + { + $this->identifier = $identifier; + + return $this; + } + + /** + * Return the response type + * @return string + */ + public function getResponseType() + { + return $this->responseType; + } + + /** + * Override the default access token expire time + * @param int $accessTokenTTL + * @return self + */ + public function setAccessTokenTTL($accessTokenTTL) + { + $this->accessTokenTTL = $accessTokenTTL; + + return $this; + } + + /** + * Inject the authorization server into the grant + * @param AuthorizationServer $server The authorization server instance + * @return self + */ + public function setAuthorizationServer(AuthorizationServer $server) + { + $this->server = $server; + + return $this; + } + + /** + * Given a list of scopes, validate them and return an arrary of Scope entities + * @param string $scopeParam A string of scopes (e.g. "profile email birthday") + * @return array + * @throws ClientException If scope is invalid, or no scopes passed when required + */ + public function validateScopes($scopeParam = '') + { + $scopesList = explode($this->server->getScopeDelimeter(), $scopeParam); + + for ($i = 0; $i < count($scopesList); $i++) { + $scopesList[$i] = trim($scopesList[$i]); + if ($scopesList[$i] === '') { + unset($scopesList[$i]); // Remove any junk scopes + } + } + + if ( + $this->server->scopeParamRequired() === true + && $this->server->getDefaultScope() === null + && count($scopesList) === 0 + ) { + throw new Exception\InvalidRequestException('scope'); + } elseif (count($scopesList) === 0 && $this->server->getDefaultScope() !== null) { + if (is_array($this->server->getDefaultScope())) { + $scopesList = $this->server->getDefaultScope(); + } else { + $scopesList = [0 => $this->server->getDefaultScope()]; + } + } + + $scopes = []; + + foreach ($scopesList as $scopeItem) { + $scope = $this->server->getStorage('scope')->get( + $scopeItem, + $this->getIdentifier() + ); + + if (($scope instanceof ScopeEntity) === false) { + throw new Exception\InvalidScopeException($scopeItem); + } + + $scopes[$scope->getId()] = $scope; + } + + return $scopes; + } + + /** + * Format the local scopes array + * @param array $unformated Array of Array of \League\OAuth2\Server\Entity\ScopeEntity + * @return array + */ + protected function formatScopes($unformated = []) + { + $scopes = []; + foreach ($unformated as $scope) { + if ($scope instanceof ScopeEntity) { + $scopes[$scope->getId()] = $scope; + } + } + + return $scopes; + } + + /** + * 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
+     *  )
+     * 
+ * + * @return array An array of parameters to be passed back to the client + */ + abstract public function completeFlow(); +} diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php new file mode 100644 index 00000000..b7fee22c --- /dev/null +++ b/src/Grant/AuthCodeGrant.php @@ -0,0 +1,252 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Grant; + +use League\OAuth2\Server\Request; +use League\OAuth2\Server\Exception; +use League\OAuth2\Server\Entity\ClientEntity; +use League\OAuth2\Server\Entity\RefreshTokenEntity; +use League\OAuth2\Server\Entity\SessionEntity; +use League\OAuth2\Server\Entity\AccessTokenEntity; +use League\OAuth2\Server\Entity\AuthCodeEntity; +use League\OAuth2\Server\Util\SecureKey; + +/** + * Auth code grant class + */ +class AuthCodeGrant extends AbstractGrant +{ + /** + * Grant identifier + * @var string + */ + protected $identifier = 'authorization_code'; + + /** + * Response type + * @var string + */ + protected $responseType = 'code'; + + /** + * AuthServer instance + * @var AuthServer + */ + protected $server = null; + + /** + * Access token expires in override + * @var int + */ + protected $accessTokenTTL = null; + + /** + * The TTL of the auth token + * @var integer + */ + protected $authTokenTTL = 600; + + /** + * Override the default access token expire time + * @param int $authTokenTTL + * @return void + */ + public function setAuthTokenTTL($authTokenTTL) + { + $this->authTokenTTL = $authTokenTTL; + } + + /** + * Check authorize parameters + * + * @return array Authorize request parameters + */ + public function checkAuthorizeParams() + { + // Get required params + $clientId = $this->server->getRequest()->query->get('client_id', null); + if (is_null($clientId)) { + throw new Exception\InvalidRequestException('client_id'); + } + + $redirectUri = $this->server->getRequest()->query->get('redirect_uri', null); + if (is_null($redirectUri)) { + throw new Exception\InvalidRequestException('redirect_uri'); + } + + $state = $this->server->getRequest()->query->get('state', null); + if ($this->server->stateParamRequired() === true && is_null($state)) { + throw new Exception\InvalidRequestException('state'); + } + + $responseType = $this->server->getRequest()->query->get('response_type', null); + if (is_null($responseType)) { + throw new Exception\InvalidRequestException('response_type'); + } + + // Ensure response type is one that is recognised + if (!in_array($responseType, $this->server->getResponseTypes())) { + throw new Exception\UnsupportedResponseTypeException($responseType); + } + + // Validate client ID and redirect URI + $client = $this->server->getStorage('client')->get( + $clientId, + null, + $redirectUri, + $this->getIdentifier() + ); + + if (($client instanceof ClientEntity) === false) { + throw new Exception\InvalidClientException(); + } + + // Validate any scopes that are in the request + $scopeParam = $this->server->getRequest()->query->get('scope', ''); + $scopes = $this->validateScopes($scopeParam); + + return [ + 'client' => $client, + 'redirect_uri' => $redirectUri, + 'state' => $state, + 'response_type' => $responseType, + 'scopes' => $scopes + ]; + } + + /** + * Parse a new authorize request + * + * @param string $type The session owner's type + * @param string $typeId The session owner's ID + * @param array $authParams The authorize request $_GET parameters + * @return string An authorisation code + */ + public function newAuthorizeRequest($type, $typeId, $authParams = []) + { + // Create a new session + $session = new SessionEntity($this->server); + $session->setOwner($type, $typeId); + $session->associateClient($authParams['client']); + $session->save(); + + // Create a new auth code + $authCode = new AuthCodeEntity($this->server); + $authCode->setId(SecureKey::generate()); + $authCode->setRedirectUri($authParams['redirect_uri']); + $authCode->setExpireTime(time() + $this->authTokenTTL); + + foreach ($authParams['scopes'] as $scope) { + $authCode->associateScope($scope); + } + + $authCode->setSession($session); + $authCode->save(); + + return $authCode->generateRedirectUri($authParams['state']); + } + + /** + * Complete the auth code grant + * @return array + */ + public function completeFlow() + { + // Get the required params + $clientId = $this->server->getRequest()->request->get('client_id', null); + if (is_null($clientId)) { + $clientId = $this->server->getRequest()->getUser(); + if (is_null($clientId)) { + throw new Exception\InvalidRequestException('client_id'); + } + } + + $clientSecret = $this->server->getRequest()->request->get('client_secret', null); + if (is_null($clientSecret)) { + $clientSecret = $this->server->getRequest()->getPassword(); + if (is_null($clientSecret)) { + throw new Exception\InvalidRequestException('client_secret'); + } + } + + $redirectUri = $this->server->getRequest()->request->get('redirect_uri', null); + if (is_null($redirectUri)) { + throw new Exception\InvalidRequestException('redirect_uri'); + } + + // Validate client ID and client secret + $client = $this->server->getStorage('client')->get( + $clientId, + $clientSecret, + $redirectUri, + $this->getIdentifier() + ); + + if (($client instanceof ClientEntity) === false) { + throw new Exception\InvalidClientException(); + } + + // Validate the auth code + $authCode = $this->server->getRequest()->request->get('code', null); + if (is_null($authCode)) { + throw new Exception\InvalidRequestException('code'); + } + + $code = $this->server->getStorage('auth_code')->get($authCode); + if (($code instanceof AuthCodeEntity) === false) { + throw new Exception\InvalidRequestException('code'); + } + + // Check redirect URI presented matches redirect URI originally used in authorize request + if ($code->getRedirectUri() !== $redirectUri) { + throw new Exception\InvalidRequestException('redirect_uri'); + } + + $session = $code->getSession(); + $authCodeScopes = $code->getScopes(); + + // Generate the access token + $accessToken = new AccessTokenEntity($this->server); + $accessToken->setId(SecureKey::generate()); + $accessToken->setExpireTime($this->server->getAccessTokenTTL() + time()); + + foreach ($authCodeScopes as $authCodeScope) { + $session->associateScope($authCodeScope); + } + + $this->server->getTokenType()->set('access_token', $accessToken->getId()); + $this->server->getTokenType()->set('expires_in', $this->server->getAccessTokenTTL()); + + // Associate a refresh token if set + if ($this->server->hasGrantType('refresh_token')) { + $refreshToken = new RefreshTokenEntity($this->server); + $refreshToken->setId(SecureKey::generate()); + $refreshToken->setExpireTime($this->server->getGrantType('refresh_token')->getRefreshTokenTTL() + time()); + $this->server->getTokenType()->set('refresh_token', $refreshToken->getId()); + } + + // Expire the auth code + $code->expire(); + + // Save all the things + $session->save(); + $accessToken->setSession($session); + $accessToken->save(); + + if ($this->server->hasGrantType('refresh_token')) { + $refreshToken->setAccessToken($accessToken); + $refreshToken->save(); + } + + return $this->server->getTokenType()->generateResponse(); + } +} diff --git a/src/Grant/ClientCredentialsGrant.php b/src/Grant/ClientCredentialsGrant.php new file mode 100644 index 00000000..146bd694 --- /dev/null +++ b/src/Grant/ClientCredentialsGrant.php @@ -0,0 +1,115 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Grant; + +use League\OAuth2\Server\Entity\AccessTokenEntity; +use League\OAuth2\Server\Entity\ClientEntity; +use League\OAuth2\Server\Entity\SessionEntity; +use League\OAuth2\Server\Exception; +use League\OAuth2\Server\Util\SecureKey; + +/** + * Client credentials grant class + */ +class ClientCredentialsGrant extends AbstractGrant +{ + /** + * Grant identifier + * @var string + */ + protected $identifier = 'client_credentials'; + + /** + * Response type + * @var string + */ + protected $responseType = null; + + /** + * AuthServer instance + * @var AuthServer + */ + protected $server = null; + + /** + * Access token expires in override + * @var int + */ + protected $accessTokenTTL = null; + + /** + * Complete the client credentials grant + * @param null|array $inputParams + * @return array + */ + public function completeFlow() + { + // Get the required params + $clientId = $this->server->getRequest()->request->get('client_id', null); + if (is_null($clientId)) { + $clientId = $this->server->getRequest()->getUser(); + if (is_null($clientId)) { + throw new Exception\InvalidRequestException('client_id'); + } + } + + $clientSecret = $this->server->getRequest()->request->get('client_secret', null); + if (is_null($clientSecret)) { + $clientSecret = $this->server->getRequest()->getPassword(); + if (is_null($clientSecret)) { + throw new Exception\InvalidRequestException('client_secret'); + } + } + + // Validate client ID and client secret + $client = $this->server->getStorage('client')->get( + $clientId, + $clientSecret, + null, + $this->getIdentifier() + ); + + if (($client instanceof ClientEntity) === false) { + throw new Exception\InvalidClientException(); + } + + // Validate any scopes that are in the request + $scopeParam = $this->server->getRequest()->request->get('scope', ''); + $scopes = $this->validateScopes($scopeParam); + + // Create a new session + $session = new SessionEntity($this->server); + $session->setOwner('client', $client->getId()); + $session->associateClient($client); + + // Generate an access token + $accessToken = new AccessTokenEntity($this->server); + $accessToken->setId(SecureKey::generate()); + $accessToken->setExpireTime($this->server->getAccessTokenTTL() + time()); + + // Associate scopes with the session and access token + foreach ($scopes as $scope) { + $accessToken->associateScope($scope); + $session->associateScope($scope); + } + + // Save everything + $session->save($this->server->getStorage('session')); + $accessToken->setSession($session); + $accessToken->save($this->server->getStorage('access_token')); + + $this->server->getTokenType()->set('access_token', $accessToken->getId()); + $this->server->getTokenType()->set('expires_in', $this->server->getAccessTokenTTL()); + + return $this->server->getTokenType()->generateResponse(); + } +} diff --git a/src/Grant/GrantTypeInterface.php b/src/Grant/GrantTypeInterface.php new file mode 100644 index 00000000..5bad669c --- /dev/null +++ b/src/Grant/GrantTypeInterface.php @@ -0,0 +1,24 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Grant; + +/** + * Grant type interface + */ +interface GrantTypeInterface +{ + /** + * Complete the grant flow + * @return array + */ + public function completeFlow(); +} diff --git a/src/Grant/PasswordGrant.php b/src/Grant/PasswordGrant.php new file mode 100644 index 00000000..43d8b7af --- /dev/null +++ b/src/Grant/PasswordGrant.php @@ -0,0 +1,168 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Grant; + +use League\OAuth2\Server\Entity\ClientEntity; +use League\OAuth2\Server\Entity\AccessTokenEntity; +use League\OAuth2\Server\Entity\RefreshTokenEntity; +use League\OAuth2\Server\Entity\SessionEntity; +use League\OAuth2\Server\Exception; +use League\OAuth2\Server\Util\SecureKey; + +/** + * Password grant class + */ +class PasswordGrant extends AbstractGrant +{ + /** + * Grant identifier + * @var string + */ + protected $identifier = 'password'; + + /** + * Response type + * @var string + */ + protected $responseType; + + /** + * Callback to authenticate a user's name and password + * @var function + */ + protected $callback; + + /** + * Access token expires in override + * @var int + */ + protected $accessTokenTTL; + + /** + * Set the callback to verify a user's username and password + * @param callable $callback The callback function + * @return void + */ + public function setVerifyCredentialsCallback(callable $callback) + { + $this->callback = $callback; + } + + /** + * Return the callback function + * @return callable + */ + protected function getVerifyCredentialsCallback() + { + if (is_null($this->callback) || ! is_callable($this->callback)) { + throw new Exception\ServerErrorException('Null or non-callable callback set on Password grant'); + } + + return $this->callback; + } + + /** + * Complete the password grant + * @return array + */ + public function completeFlow() + { + // Get the required params + $clientId = $this->server->getRequest()->request->get('client_id', null); + if (is_null($clientId)) { + $clientId = $this->server->getRequest()->getUser(); + if (is_null($clientId)) { + throw new Exception\InvalidRequestException('client_id'); + } + } + + $clientSecret = $this->server->getRequest()->request->get('client_secret', null); + if (is_null($clientSecret)) { + $clientSecret = $this->server->getRequest()->getPassword(); + if (is_null($clientSecret)) { + throw new Exception\InvalidRequestException('client_secret'); + } + } + + // Validate client ID and client secret + $client = $this->server->getStorage('client')->get( + $clientId, + $clientSecret, + null, + $this->getIdentifier() + ); + + if (($client instanceof ClientEntity) === false) { + throw new Exception\InvalidClientException(); + } + + $username = $this->server->getRequest()->request->get('username', null); + if (is_null($username)) { + throw new Exception\InvalidRequestException('username'); + } + + $password = $this->server->getRequest()->request->get('password', null); + if (is_null($password)) { + throw new Exception\InvalidRequestException('password'); + } + + // Check if user's username and password are correct + $userId = call_user_func($this->getVerifyCredentialsCallback(), $username, $password); + + if ($userId === false) { + throw new Exception\InvalidCredentialsException(); + } + + // Validate any scopes that are in the request + $scopeParam = $this->server->getRequest()->request->get('scope', ''); + $scopes = $this->validateScopes($scopeParam); + + // Create a new session + $session = new SessionEntity($this->server); + $session->setOwner('user', $userId); + $session->associateClient($client); + + // Generate an access token + $accessToken = new AccessTokenEntity($this->server); + $accessToken->setId(SecureKey::generate()); + $accessToken->setExpireTime($this->server->getAccessTokenTTL() + time()); + + // Associate scopes with the session and access token + foreach ($scopes as $scope) { + $accessToken->associateScope($scope); + $session->associateScope($scope); + } + + $this->server->getTokenType()->set('access_token', $accessToken->getId()); + $this->server->getTokenType()->set('expires_in', $this->server->getAccessTokenTTL()); + + // Associate a refresh token if set + if ($this->server->hasGrantType('refresh_token')) { + $refreshToken = new RefreshTokenEntity($this->server); + $refreshToken->setId(SecureKey::generate()); + $refreshToken->setExpireTime($this->server->getGrantType('refresh_token')->getRefreshTokenTTL() + time()); + $this->server->getTokenType()->set('refresh_token', $refreshToken->getId()); + } + + // Save everything + $session->save(); + $accessToken->setSession($session); + $accessToken->save(); + + if ($this->server->hasGrantType('refresh_token')) { + $refreshToken->setAccessToken($accessToken); + $refreshToken->save(); + } + + return $this->server->getTokenType()->generateResponse(); + } +} diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php new file mode 100644 index 00000000..58283cc2 --- /dev/null +++ b/src/Grant/RefreshTokenGrant.php @@ -0,0 +1,157 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Grant; + +use League\OAuth2\Server\Request; +use League\OAuth2\Server\Exception; +use League\OAuth2\Server\Util\SecureKey; +use League\OAuth2\Server\Entity\RefreshTokenEntity; +use League\OAuth2\Server\Entity\AccessTokenEntity; +use League\OAuth2\Server\Entity\ClientEntity; + +/** + * Referesh token grant + */ +class RefreshTokenGrant extends AbstractGrant +{ + /** + * {@inheritdoc} + */ + protected $identifier = 'refresh_token'; + + /** + * Refresh token TTL (default = 604800 | 1 week) + * @var integer + */ + protected $refreshTokenTTL = 604800; + + /** + * Set the TTL of the refresh token + * @param int $refreshTokenTTL + * @return void + */ + public function setRefreshTokenTTL($refreshTokenTTL) + { + $this->refreshTokenTTL = $refreshTokenTTL; + } + + /** + * Get the TTL of the refresh token + * @return int + */ + public function getRefreshTokenTTL() + { + return $this->refreshTokenTTL; + } + + /** + * {@inheritdoc} + */ + public function completeFlow() + { + $clientId = $this->server->getRequest()->request->get('client_id', null); + if (is_null($clientId)) { + $clientId = $this->server->getRequest()->getUser(); + if (is_null($clientId)) { + throw new Exception\InvalidRequestException('client_id'); + } + } + + $clientSecret = $this->server->getRequest()->request->get('client_secret', null); + if (is_null($clientSecret)) { + $clientSecret = $this->server->getRequest()->getPassword(); + if (is_null($clientSecret)) { + throw new Exception\InvalidRequestException('client_secret'); + } + } + + // Validate client ID and client secret + $client = $this->server->getStorage('client')->get( + $clientId, + $clientSecret, + null, + $this->getIdentifier() + ); + + if (($client instanceof ClientEntity) === false) { + throw new Exception\InvalidClientException(); + } + + $oldRefreshTokenParam = $this->server->getRequest()->request->get('refresh_token', null); + if ($oldRefreshTokenParam === null) { + throw new Exception\InvalidRequestException('refresh_token'); + } + + // Validate refresh token + $oldRefreshToken = $this->server->getStorage('refresh_token')->get($oldRefreshTokenParam); + + if (($oldRefreshToken instanceof RefreshTokenEntity) === false) { + throw new Exception\InvalidRefreshException(); + } + + $oldAccessToken = $oldRefreshToken->getAccessToken(); + + // Get the scopes for the original session + $session = $oldAccessToken->getSession(); + $scopes = $this->formatScopes($session->getScopes()); + + // Get and validate any requested scopes + $requestedScopesString = $this->server->getRequest()->request->get('scope', ''); + $requestedScopes = $this->validateScopes($requestedScopesString); + + // If no new scopes are requested then give the access token the original session scopes + if (count($requestedScopes) === 0) { + $newScopes = $scopes; + } else { + // The OAuth spec says that a refreshed access token can have the original scopes or fewer so ensure + // the request doesn't include any new scopes + foreach ($requestedScopes as $requestedScope) { + if (!isset($scopes[$requestedScope->getId()])) { + throw new Exception\InvalidScopeException($requestedScope->getId()); + } + } + + $newScopes = $requestedScopes; + } + + // Generate a new access token and assign it the correct sessions + $newAccessToken = new AccessTokenEntity($this->server); + $newAccessToken->setId(SecureKey::generate()); + $newAccessToken->setExpireTime($this->server->getAccessTokenTTL() + time()); + $newAccessToken->setSession($session); + + foreach ($newScopes as $newScope) { + $newAccessToken->associateScope($newScope); + } + + // Expire the old token and save the new one + $oldAccessToken->expire($this->server->getStorage('access_token')); + $newAccessToken->save($this->server->getStorage('access_token')); + + $this->server->getTokenType()->set('access_token', $newAccessToken->getId()); + $this->server->getTokenType()->set('expires_in', $this->server->getAccessTokenTTL()); + + // Expire the old refresh token + $oldRefreshToken->expire($this->server->getStorage('refresh_token')); + + // Generate a new refresh token + $newRefreshToken = new RefreshTokenEntity($this->server); + $newRefreshToken->setId(SecureKey::generate()); + $newRefreshToken->setExpireTime($this->getRefreshTokenTTL() + time()); + $newRefreshToken->setAccessToken($newAccessToken); + $newRefreshToken->save($this->server->getStorage('refresh_token')); + + $this->server->getTokenType()->set('refresh_token', $newRefreshToken->getId()); + + return $this->server->getTokenType()->generateResponse(); + } +} diff --git a/src/League/OAuth2/Server/Authorization.php b/src/League/OAuth2/Server/Authorization.php deleted file mode 100644 index c5d63d2e..00000000 --- a/src/League/OAuth2/Server/Authorization.php +++ /dev/null @@ -1,479 +0,0 @@ - - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages - * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server - */ - -namespace League\OAuth2\Server; - -use League\OAuth2\Server\Util\Request; -use League\OAuth2\Server\Util\SecureKey; -use League\OAuth2\Server\Storage\SessionInterface; -use League\OAuth2\Server\Storage\ClientInterface; -use League\OAuth2\Server\Storage\ScopeInterface; -use League\OAuth2\Server\Grant\GrantTypeInterface; - -/** - * OAuth 2.0 authorization server class - */ -class Authorization -{ - /** - * The delimeter between scopes specified in the scope query string parameter - * - * The OAuth 2 specification states it should be a space but most use a comma - * @var string - */ - protected $scopeDelimeter = ' '; - - /** - * The TTL (time to live) of an access token in seconds (default: 3600) - * @var integer - */ - protected $accessTokenTTL = 3600; - - /** - * The registered grant response types - * @var array - */ - protected $responseTypes = array(); - - /** - * The client, scope and session storage classes - * @var array - */ - protected $storages = array(); - - /** - * The registered grant types - * @var array - */ - protected $grantTypes = array(); - - /** - * Require the "scope" parameter to be in checkAuthoriseParams() - * @var boolean - */ - protected $requireScopeParam = false; - - /** - * Default scope(s) to be used if none is provided - * @var string|array - */ - protected $defaultScope = null; - - /** - * Require the "state" parameter to be in checkAuthoriseParams() - * @var boolean - */ - protected $requireStateParam = false; - - /** - * The request object - * @var Util\RequestInterface - */ - 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 - */ - protected static $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.', - ); - - /** - * Exception error HTTP status codes - * @var array - * - * RFC 6749, section 4.1.2.1.: - * No 503 status code for 'temporarily_unavailable', because - * "a 503 Service Unavailable HTTP status code cannot be - * returned to the client via an HTTP redirect" - */ - protected static $exceptionHttpStatusCodes = array( - 'invalid_request' => 400, - 'unauthorized_client' => 400, - 'access_denied' => 401, - 'unsupported_response_type' => 400, - 'invalid_scope' => 400, - 'server_error' => 500, - 'temporarily_unavailable' => 400, - 'unsupported_grant_type' => 501, - 'invalid_client' => 401, - 'invalid_grant' => 400, - 'invalid_credentials' => 400, - 'invalid_refresh' => 400, - ); - - /** - * Get all headers that have to be send with the error response - * - * @param string $error The error message key - * @return array Array with header values - */ - public static function getExceptionHttpHeaders($error) - { - $headers = array(); - switch (self::$exceptionHttpStatusCodes[$error]) { - case 401: - $headers[] = 'HTTP/1.1 401 Unauthorized'; - break; - case 500: - $headers[] = 'HTTP/1.1 500 Internal Server Error'; - break; - case 501: - $headers[] = 'HTTP/1.1 501 Not Implemented'; - break; - case 400: - default: - $headers[] = 'HTTP/1.1 400 Bad Request'; - } - - // Add "WWW-Authenticate" header - // - // RFC 6749, section 5.2.: - // "If the client attempted to authenticate via the 'Authorization' - // request header field, the authorization server MUST - // respond with an HTTP 401 (Unauthorized) status code and - // include the "WWW-Authenticate" response header field - // matching the authentication scheme used by the client. - // @codeCoverageIgnoreStart - if ($error === 'invalid_client') { - $authScheme = null; - $request = new Request(); - if ($request->server('PHP_AUTH_USER') !== null) { - $authScheme = 'Basic'; - } else { - $authHeader = $request->header('Authorization'); - if ($authHeader !== null) { - if (strpos($authHeader, 'Bearer') === 0) { - $authScheme = 'Bearer'; - } elseif (strpos($authHeader, 'Basic') === 0) { - $authScheme = 'Basic'; - } - } - } - if ($authScheme !== null) { - $headers[] = 'WWW-Authenticate: '.$authScheme.' realm=""'; - } - } - // @codeCoverageIgnoreEnd - - return $headers; - } - - /** - * 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 authorization 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) - { - $this->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(); - } - - // Inject server into grant - $grantType->setAuthorizationServer($this); - - $this->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 function hasGrantType($identifier) - { - return (array_key_exists($identifier, $this->grantTypes)); - } - - /** - * Returns response types - * - * @return array - */ - public function getResponseTypes() - { - return $this->responseTypes; - } - - /** - * Require the "scope" paremter in checkAuthoriseParams() - * @param boolean $require - * @return void - */ - public function requireScopeParam($require = true) - { - $this->requireScopeParam = $require; - } - - /** - * Is the scope parameter required? - * @return bool - */ - public function scopeParamRequired() - { - return $this->requireScopeParam; - } - - /** - * Default scope to be used if none is provided and requireScopeParam is false - * @param string|array $default - */ - public function setDefaultScope($default = null) - { - $this->defaultScope = $default; - return $this; - } - - /** - * Default scope to be used if none is provided and requireScopeParam is false - * @return string|null - */ - public function getDefaultScope() - { - return $this->defaultScope; - } - - /** - * Require the "state" paremter in checkAuthoriseParams() - * @param boolean $require - * @return void - */ - public function stateParamRequired() - { - return $this->requireStateParam; - } - - /** - * Require the "state" paremter in checkAuthoriseParams() - * @param boolean $require - * @return void - */ - public function requireStateParam($require = true) - { - $this->requireStateParam = $require; - return $this; - } - - /** - * 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; - return $this; - } - - /** - * Get the TTL for an access token - * @return int The TTL - */ - public function getAccessTokenTTL() - { - return $this->accessTokenTTL; - } - - /** - * Set the TTL for an access token - * @param int $accessTokenTTL The new TTL - */ - public function setAccessTokenTTL($accessTokenTTL = 3600) - { - $this->accessTokenTTL = $accessTokenTTL; - return $this; - } - - /** - * Sets the Request Object - * - * @param Util\RequestInterface The Request Object - */ - public function setRequest(Util\RequestInterface $request) - { - $this->request = $request; - return $this; - } - - /** - * 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; - } - - /** - * Return a storage class - * @param string $obj The class required - * @return Storage\ClientInterface|Storage\ScopeInterface|Storage\SessionInterface - */ - public function getStorage($obj) - { - return $this->storages[$obj]; - } - - /** - * Issue an access token - * - * @param array $inputParams Optional array of parsed $_POST keys - * @return array Authorise request parameters - */ - public function issueAccessToken($inputParams = array()) - { - $grantType = $this->getParam('grant_type', 'post', $inputParams); - - 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($this->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 Grant\AuthCode|Grant\ClientCredentials|Grant\Implict|Grant\Password|Grant\RefreshToken - */ - public function getGrantType($grantType) - { - if (isset($this->grantTypes[$grantType])) { - return $this->grantTypes[$grantType]; - } - - throw new Exception\InvalidGrantTypeException(sprintf(self::$exceptionMessages['unsupported_grant_type'], $grantType), 9); - } - - /** - * Get a parameter from passed input parameters or the Request class - * @param string|array $param Required parameter - * @param string $method Get/put/post/delete - * @param array $inputParams Passed input parameters - * @return mixed 'Null' if parameter is missing - */ - public function getParam($param = '', $method = 'get', $inputParams = array(), $default = null) - { - if (is_string($param)) { - if (isset($inputParams[$param])) { - return $inputParams[$param]; - } elseif ($param === 'client_id' && ! is_null($clientId = $this->getRequest()->server('PHP_AUTH_USER'))) { - return $clientId; - } elseif ($param === 'client_secret' && ! is_null($clientSecret = $this->getRequest()->server('PHP_AUTH_PW'))) { - return $clientSecret; - } else { - return $this->getRequest()->{$method}($param, $default); - } - } else { - $response = array(); - foreach ($param as $p) { - $response[$p] = $this->getParam($p, $method, $inputParams); - } - return $response; - } - } - -} diff --git a/src/League/OAuth2/Server/Exception/ClientException.php b/src/League/OAuth2/Server/Exception/ClientException.php deleted file mode 100644 index e9839a74..00000000 --- a/src/League/OAuth2/Server/Exception/ClientException.php +++ /dev/null @@ -1,20 +0,0 @@ - - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages - * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server - */ - -namespace League\OAuth2\Server\Exception; - -/** - * ClientException Exception - */ -class ClientException extends OAuth2Exception -{ - -} \ No newline at end of file diff --git a/src/League/OAuth2/Server/Exception/InvalidAccessTokenException.php b/src/League/OAuth2/Server/Exception/InvalidAccessTokenException.php deleted file mode 100644 index b9bb5462..00000000 --- a/src/League/OAuth2/Server/Exception/InvalidAccessTokenException.php +++ /dev/null @@ -1,20 +0,0 @@ - - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages - * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server - */ - -namespace League\OAuth2\Server\Exception; - -/** - * InvalidAccessToken Exception - */ -class InvalidAccessTokenException extends OAuth2Exception -{ - -} diff --git a/src/League/OAuth2/Server/Exception/InvalidGrantTypeException.php b/src/League/OAuth2/Server/Exception/InvalidGrantTypeException.php deleted file mode 100644 index c6318f8f..00000000 --- a/src/League/OAuth2/Server/Exception/InvalidGrantTypeException.php +++ /dev/null @@ -1,20 +0,0 @@ - - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages - * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server - */ - -namespace League\OAuth2\Server\Exception; - -/** - * InvalidGrantTypeException Exception - */ -class InvalidGrantTypeException extends OAuth2Exception -{ - -} diff --git a/src/League/OAuth2/Server/Exception/OAuth2Exception.php b/src/League/OAuth2/Server/Exception/OAuth2Exception.php deleted file mode 100644 index 0b8f68a8..00000000 --- a/src/League/OAuth2/Server/Exception/OAuth2Exception.php +++ /dev/null @@ -1,20 +0,0 @@ - - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages - * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server - */ - -namespace League\OAuth2\Server\Exception; - -/** - * Exception class - */ -class OAuth2Exception extends \Exception -{ - -} diff --git a/src/League/OAuth2/Server/Grant/AuthCode.php b/src/League/OAuth2/Server/Grant/AuthCode.php deleted file mode 100644 index bf968980..00000000 --- a/src/League/OAuth2/Server/Grant/AuthCode.php +++ /dev/null @@ -1,259 +0,0 @@ - - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages - * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server - */ - -namespace League\OAuth2\Server\Grant; - -use League\OAuth2\Server\Request; -use League\OAuth2\Server\Authorization; -use League\OAuth2\Server\Exception; -use League\OAuth2\Server\Util\SecureKey; -use League\OAuth2\Server\Storage\SessionInterface; -use League\OAuth2\Server\Storage\ClientInterface; -use League\OAuth2\Server\Storage\ScopeInterface; - -/** - * Auth code grant class - */ -class AuthCode implements GrantTypeInterface { - - use GrantTrait; - - /** - * Grant identifier - * @var string - */ - protected $identifier = 'authorization_code'; - - /** - * Response type - * @var string - */ - protected $responseType = 'code'; - - /** - * AuthServer instance - * @var AuthServer - */ - protected $authServer = null; - - /** - * Access token expires in override - * @var int - */ - protected $accessTokenTTL = null; - - /** - * The TTL of the auth token - * @var integer - */ - protected $authTokenTTL = 600; - - /** - * Override the default access token expire time - * @param int $authTokenTTL - * @return void - */ - public function setAuthTokenTTL($authTokenTTL) - { - $this->authTokenTTL = $authTokenTTL; - } - - /** - * 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()) - { - // Auth params - $authParams = $this->authServer->getParam(array('client_id', 'redirect_uri', 'response_type', 'scope', 'state'), 'get', $inputParams); - - if (is_null($authParams['client_id'])) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'client_id'), 0); - } - - if (is_null($authParams['redirect_uri'])) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'redirect_uri'), 0); - } - - if ($this->authServer->stateParamRequired() === true && is_null($authParams['state'])) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'state'), 0); - } - - // Validate client ID and redirect URI - $clientDetails = $this->authServer->getStorage('client')->getClient($authParams['client_id'], null, $authParams['redirect_uri'], $this->identifier); - - if ($clientDetails === false) { - throw new Exception\ClientException($this->authServer->getExceptionMessage('invalid_client'), 8); - } - - $authParams['client_details'] = $clientDetails; - - if (is_null($authParams['response_type'])) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'response_type'), 0); - } - - // Ensure response type is one that is recognised - if ( ! in_array($authParams['response_type'], $this->authServer->getResponseTypes())) { - throw new Exception\ClientException($this->authServer->getExceptionMessage('unsupported_response_type'), 3); - } - - // Validate scopes - $scopes = explode($this->authServer->getScopeDelimeter(), $authParams['scope']); - - for ($i = 0; $i < count($scopes); $i++) { - $scopes[$i] = trim($scopes[$i]); - if ($scopes[$i] === '') unset($scopes[$i]); // Remove any junk scopes - } - - if ($this->authServer->scopeParamRequired() === true && $this->authServer->getDefaultScope() === null && count($scopes) === 0) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'scope'), 0); - } elseif (count($scopes) === 0 && $this->authServer->getDefaultScope() !== null) { - if (is_array($this->authServer->getDefaultScope())) { - $scopes = $this->authServer->getDefaultScope(); - } else { - $scopes = array($this->authServer->getDefaultScope()); - } - } - - $authParams['scopes'] = array(); - - foreach ($scopes as $scope) { - $scopeDetails = $this->authServer->getStorage('scope')->getScope($scope, $authParams['client_id'], $this->identifier); - - if ($scopeDetails === false) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('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 - $this->authServer->getStorage('session')->deleteSession($authParams['client_id'], $type, $typeId); - - // Create a new session - $sessionId = $this->authServer->getStorage('session')->createSession($authParams['client_id'], $type, $typeId); - - // Associate a redirect URI - $this->authServer->getStorage('session')->associateRedirectUri($sessionId, $authParams['redirect_uri']); - - // Associate the auth code - $authCodeId = $this->authServer->getStorage('session')->associateAuthCode($sessionId, $authCode, time() + $this->authTokenTTL); - - // Associate the scopes to the auth code - foreach ($authParams['scopes'] as $scope) { - $this->authServer->getStorage('session')->associateAuthCodeScope($authCodeId, $scope['id']); - } - - return $authCode; - } - - /** - * Complete the auth code grant - * @param null|array $inputParams - * @return array - */ - public function completeFlow($inputParams = null) - { - // Get the required params - $authParams = $this->authServer->getParam(array('client_id', 'client_secret', 'redirect_uri', 'code'), 'post', $inputParams); - - if (is_null($authParams['client_id'])) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'client_id'), 0); - } - - if (is_null($authParams['client_secret'])) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'client_secret'), 0); - } - - if (is_null($authParams['redirect_uri'])) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'redirect_uri'), 0); - } - - // Validate client ID and redirect URI - $clientDetails = $this->authServer->getStorage('client')->getClient($authParams['client_id'], $authParams['client_secret'], $authParams['redirect_uri'], $this->identifier); - - if ($clientDetails === false) { - throw new Exception\ClientException($this->authServer->getExceptionMessage('invalid_client'), 8); - } - - $authParams['client_details'] = $clientDetails; - - // Validate the authorization code - if (is_null($authParams['code'])) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'code'), 0); - } - - // Verify the authorization code matches the client_id and the request_uri - $authCodeDetails = $this->authServer->getStorage('session')->validateAuthCode($authParams['client_id'], $authParams['redirect_uri'], $authParams['code']); - - if ( ! $authCodeDetails) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_grant'), 'code'), 9); - } - - // Get any associated scopes - $scopes = $this->authServer->getStorage('session')->getAuthCodeScopes($authCodeDetails['authcode_id']); - - // A session ID was returned so update it with an access token and remove the authorisation code - $accessToken = SecureKey::make(); - $accessTokenExpiresIn = ($this->accessTokenTTL !== null) ? $this->accessTokenTTL : $this->authServer->getAccessTokenTTL(); - $accessTokenExpires = time() + $accessTokenExpiresIn; - - // Remove the auth code - $this->authServer->getStorage('session')->removeAuthCode($authCodeDetails['session_id']); - - // Create an access token - $accessTokenId = $this->authServer->getStorage('session')->associateAccessToken($authCodeDetails['session_id'], $accessToken, $accessTokenExpires); - - // Associate scopes with the access token - if (count($scopes) > 0) { - foreach ($scopes as $scope) { - $this->authServer->getStorage('session')->associateScope($accessTokenId, $scope['scope_id']); - } - } - - $response = array( - 'access_token' => $accessToken, - 'token_type' => 'Bearer', - 'expires' => $accessTokenExpires, - 'expires_in' => $accessTokenExpiresIn - ); - - // Associate a refresh token if set - if ($this->authServer->hasGrantType('refresh_token')) { - $refreshToken = SecureKey::make(); - $refreshTokenTTL = time() + $this->authServer->getGrantType('refresh_token')->getRefreshTokenTTL(); - $this->authServer->getStorage('session')->associateRefreshToken($accessTokenId, $refreshToken, $refreshTokenTTL, $authParams['client_id']); - $response['refresh_token'] = $refreshToken; - } - - return $response; - } - -} diff --git a/src/League/OAuth2/Server/Grant/ClientCredentials.php b/src/League/OAuth2/Server/Grant/ClientCredentials.php deleted file mode 100644 index aa072d09..00000000 --- a/src/League/OAuth2/Server/Grant/ClientCredentials.php +++ /dev/null @@ -1,166 +0,0 @@ - - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages - * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server - */ - -namespace League\OAuth2\Server\Grant; - -use League\OAuth2\Server\Request; -use League\OAuth2\Server\Authorization; -use League\OAuth2\Server\Exception; -use League\OAuth2\Server\Util\SecureKey; -use League\OAuth2\Server\Storage\SessionInterface; -use League\OAuth2\Server\Storage\ClientInterface; -use League\OAuth2\Server\Storage\ScopeInterface; - -/** - * Client credentials grant class - */ -class ClientCredentials implements GrantTypeInterface { - - use GrantTrait; - - /** - * Grant identifier - * @var string - */ - protected $identifier = 'client_credentials'; - - /** - * Response type - * @var string - */ - protected $responseType = null; - - /** - * AuthServer instance - * @var AuthServer - */ - protected $authServer = null; - - /** - * Access token expires in override - * @var int - */ - protected $accessTokenTTL = null; - - /** - * Return the identifier - * @return string - */ - public function getIdentifier() - { - return $this->identifier; - } - - /** - * Return the response type - * @return string - */ - public function getResponseType() - { - return $this->responseType; - } - - /** - * Override the default access token expire time - * @param int $accessTokenTTL - * @return void - */ - public function setAccessTokenTTL($accessTokenTTL) - { - $this->accessTokenTTL = $accessTokenTTL; - } - - /** - * Complete the client credentials grant - * @param null|array $inputParams - * @return array - */ - public function completeFlow($inputParams = null) - { - // Get the required params - $authParams = $this->authServer->getParam(array('client_id', 'client_secret'), 'post', $inputParams); - - if (is_null($authParams['client_id'])) { - throw new Exception\ClientException(sprintf(Authorization::getExceptionMessage('invalid_request'), 'client_id'), 0); - } - - if (is_null($authParams['client_secret'])) { - throw new Exception\ClientException(sprintf(Authorization::getExceptionMessage('invalid_request'), 'client_secret'), 0); - } - - // Validate client ID and client secret - $clientDetails = $this->authServer->getStorage('client')->getClient($authParams['client_id'], $authParams['client_secret'], null, $this->identifier); - - if ($clientDetails === false) { - throw new Exception\ClientException(Authorization::getExceptionMessage('invalid_client'), 8); - } - - $authParams['client_details'] = $clientDetails; - - // Validate any scopes that are in the request - $scope = $this->authServer->getParam('scope', 'post', $inputParams, ''); - $scopes = explode($this->authServer->getScopeDelimeter(), $scope); - - for ($i = 0; $i < count($scopes); $i++) { - $scopes[$i] = trim($scopes[$i]); - if ($scopes[$i] === '') unset($scopes[$i]); // Remove any junk scopes - } - - if ($this->authServer->scopeParamRequired() === true && $this->authServer->getDefaultScope() === null && count($scopes) === 0) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'scope'), 0); - } elseif (count($scopes) === 0 && $this->authServer->getDefaultScope() !== null) { - if (is_array($this->authServer->getDefaultScope())) { - $scopes = $this->authServer->getDefaultScope(); - } else { - $scopes = array($this->authServer->getDefaultScope()); - } - } - - $authParams['scopes'] = array(); - - foreach ($scopes as $scope) { - $scopeDetails = $this->authServer->getStorage('scope')->getScope($scope, $authParams['client_id'], $this->identifier); - - if ($scopeDetails === false) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_scope'), $scope), 4); - } - - $authParams['scopes'][] = $scopeDetails; - } - - // Generate an access token - $accessToken = SecureKey::make(); - $accessTokenExpiresIn = ($this->accessTokenTTL !== null) ? $this->accessTokenTTL : $this->authServer->getAccessTokenTTL(); - $accessTokenExpires = time() + $accessTokenExpiresIn; - - // Create a new session - $sessionId = $this->authServer->getStorage('session')->createSession($authParams['client_id'], 'client', $authParams['client_id']); - - // Add the access token - $accessTokenId = $this->authServer->getStorage('session')->associateAccessToken($sessionId, $accessToken, $accessTokenExpires); - - // Associate scopes with the new session - foreach ($authParams['scopes'] as $scope) - { - $this->authServer->getStorage('session')->associateScope($accessTokenId, $scope['id']); - } - - $response = array( - 'access_token' => $accessToken, - 'token_type' => 'Bearer', - 'expires' => $accessTokenExpires, - 'expires_in' => $accessTokenExpiresIn - ); - - return $response; - } - -} diff --git a/src/League/OAuth2/Server/Grant/GrantTrait.php b/src/League/OAuth2/Server/Grant/GrantTrait.php deleted file mode 100644 index 0d555319..00000000 --- a/src/League/OAuth2/Server/Grant/GrantTrait.php +++ /dev/null @@ -1,85 +0,0 @@ - - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages - * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server - */ - -namespace League\OAuth2\Server\Grant; - -use League\OAuth2\Server\Authorization; - -trait GrantTrait { - - /** - * Constructor - * @param Authorization $authServer Authorization server instance - * @return void - */ - public function __construct(Authorization $authServer = null) - { - // @codeCoverageIgnoreStart - if ($authServer instanceof Authorization) { - trigger_error( - 'Server is now automatically injected into grant as of v3.1 of this library', - E_USER_DEPRECATED - ); - } // @codeCoverageIgnoreEnd - } - - /** - * Return the identifier - * @return string - */ - public function getIdentifier() - { - return $this->identifier; - } - - /** - * Return the identifier - * @param string $identifier - * @return self - */ - public function setIdentifier($identifier) - { - $this->identifier = $identifier; - return $this; - } - - /** - * Return the response type - * @return string - */ - public function getResponseType() - { - return $this->responseType; - } - - /** - * Override the default access token expire time - * @param int $accessTokenTTL - * @return self - */ - public function setAccessTokenTTL($accessTokenTTL) - { - $this->accessTokenTTL = $accessTokenTTL; - return $this; - } - - /** - * Inject the authorization server into the grant - * @param Authorization $authServer The authorization server instance - * @return self - */ - public function setAuthorizationServer(Authorization $authServer) - { - $this->authServer = $authServer; - return $this; - } - -} diff --git a/src/League/OAuth2/Server/Grant/GrantTypeInterface.php b/src/League/OAuth2/Server/Grant/GrantTypeInterface.php deleted file mode 100644 index 2301bd4e..00000000 --- a/src/League/OAuth2/Server/Grant/GrantTypeInterface.php +++ /dev/null @@ -1,48 +0,0 @@ - - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages - * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server - */ - -namespace League\OAuth2\Server\Grant; - -use League\OAuth2\Server\Request; -use League\OAuth2\Server\Authorization; -use League\OAuth2\Server\Exception; -use League\OAuth2\Server\Util\SecureKey; -use League\OAuth2\Server\Storage\SessionInterface; -use League\OAuth2\Server\Storage\ClientInterface; -use League\OAuth2\Server\Storage\ScopeInterface; - -interface GrantTypeInterface -{ - /** - * Constructor - * @return void - */ - public function __construct(Authorization $authServer = null); - - /** - * 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/League/OAuth2/Server/Grant/Implicit.php b/src/League/OAuth2/Server/Grant/Implicit.php deleted file mode 100644 index a41c05a6..00000000 --- a/src/League/OAuth2/Server/Grant/Implicit.php +++ /dev/null @@ -1,91 +0,0 @@ - - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages - * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server - */ - -namespace League\OAuth2\Server\Grant; - -use League\OAuth2\Server\Request; -use League\OAuth2\Server\Authorization; -use League\OAuth2\Server\Exception; -use League\OAuth2\Server\Util\SecureKey; -use League\OAuth2\Server\Storage\SessionInterface; -use League\OAuth2\Server\Storage\ClientInterface; -use League\OAuth2\Server\Storage\ScopeInterface; - -/** - * Client credentials grant class - */ -class Implicit implements GrantTypeInterface { - - use GrantTrait; - - /** - * Grant identifier - * @var string - */ - protected $identifier = 'implicit'; - - /** - * Response type - * @var string - */ - protected $responseType = 'token'; - - /** - * AuthServer instance - * @var AuthServer - */ - protected $authServer = null; - - /** - * Access token expires in override - * @var int - */ - protected $accessTokenTTL = null; - - /** - * Complete the client credentials grant - * @param null|array $inputParams - * @return array - */ - public function completeFlow($authParams = null) - { - // Remove any old sessions the user might have - $this->authServer->getStorage('session')->deleteSession($authParams['client_id'], 'user', $authParams['user_id']); - - // Generate a new access token - $accessToken = SecureKey::make(); - - // Compute expiry time - $accessTokenExpiresIn = ($this->accessTokenTTL !== null) ? $this->accessTokenTTL : $this->authServer->getAccessTokenTTL(); - $accessTokenExpires = time() + $accessTokenExpiresIn; - - // Create a new session - $sessionId = $this->authServer->getStorage('session')->createSession($authParams['client_id'], 'user', $authParams['user_id']); - - // Create an access token - $accessTokenId = $this->authServer->getStorage('session')->associateAccessToken($sessionId, $accessToken, $accessTokenExpires); - - // Associate scopes with the access token - foreach ($authParams['scopes'] as $scope) { - $this->authServer->getStorage('session')->associateScope($accessTokenId, $scope['id']); - } - - $response = array( - 'access_token' => $accessToken, - 'token_type' => 'Bearer', - 'expires' => $accessTokenExpires, - 'expires_in' => $accessTokenExpiresIn, - ); - - return $response; - } - -} diff --git a/src/League/OAuth2/Server/Grant/Password.php b/src/League/OAuth2/Server/Grant/Password.php deleted file mode 100644 index 54410586..00000000 --- a/src/League/OAuth2/Server/Grant/Password.php +++ /dev/null @@ -1,189 +0,0 @@ - - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages - * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server - */ - -namespace League\OAuth2\Server\Grant; - -use League\OAuth2\Server\Request; -use League\OAuth2\Server\Authorization; -use League\OAuth2\Server\Exception; -use League\OAuth2\Server\Util\SecureKey; -use League\OAuth2\Server\Storage\SessionInterface; -use League\OAuth2\Server\Storage\ClientInterface; -use League\OAuth2\Server\Storage\ScopeInterface; - -/** - * Password grant class - */ -class Password implements GrantTypeInterface { - - use GrantTrait; - - /** - * 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; - - /** - * AuthServer instance - * @var AuthServer - */ - protected $authServer = null; - - /** - * Access token expires in override - * @var int - */ - protected $accessTokenTTL = null; - - /** - * Set the callback to verify a user's username and password - * @param callable $callback The callback function - * @return void - */ - public function setVerifyCredentialsCallback($callback) - { - $this->callback = $callback; - } - - /** - * Return the callback function - * @return callable - */ - 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 = $this->authServer->getParam(array('client_id', 'client_secret', 'username', 'password'), 'post', $inputParams); - - if (is_null($authParams['client_id'])) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'client_id'), 0); - } - - if (is_null($authParams['client_secret'])) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'client_secret'), 0); - } - - // Validate client credentials - $clientDetails = $this->authServer->getStorage('client')->getClient($authParams['client_id'], $authParams['client_secret'], null, $this->identifier); - - if ($clientDetails === false) { - throw new Exception\ClientException($this->authServer->getExceptionMessage('invalid_client'), 8); - } - - $authParams['client_details'] = $clientDetails; - - if (is_null($authParams['username'])) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'username'), 0); - } - - if (is_null($authParams['password'])) { - throw new Exception\ClientException(sprintf($this->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($this->authServer->getExceptionMessage('invalid_credentials'), 0); - } - - // Validate any scopes that are in the request - $scope = $this->authServer->getParam('scope', 'post', $inputParams, ''); - $scopes = explode($this->authServer->getScopeDelimeter(), $scope); - - for ($i = 0; $i < count($scopes); $i++) { - $scopes[$i] = trim($scopes[$i]); - if ($scopes[$i] === '') unset($scopes[$i]); // Remove any junk scopes - } - - if ($this->authServer->scopeParamRequired() === true && $this->authServer->getDefaultScope() === null && count($scopes) === 0) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'scope'), 0); - } elseif (count($scopes) === 0 && $this->authServer->getDefaultScope() !== null) { - if (is_array($this->authServer->getDefaultScope())) { - $scopes = $this->authServer->getDefaultScope(); - } else { - $scopes = array($this->authServer->getDefaultScope()); - } - } - - $authParams['scopes'] = array(); - - foreach ($scopes as $scope) { - $scopeDetails = $this->authServer->getStorage('scope')->getScope($scope, $authParams['client_id'], $this->identifier); - - if ($scopeDetails === false) { - throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_scope'), $scope), 4); - } - - $authParams['scopes'][] = $scopeDetails; - } - - // Generate an access token - $accessToken = SecureKey::make(); - $accessTokenExpiresIn = ($this->accessTokenTTL !== null) ? $this->accessTokenTTL : $this->authServer->getAccessTokenTTL(); - $accessTokenExpires = time() + $accessTokenExpiresIn; - - // Create a new session - $sessionId = $this->authServer->getStorage('session')->createSession($authParams['client_id'], 'user', $userId); - - // Associate an access token with the session - $accessTokenId = $this->authServer->getStorage('session')->associateAccessToken($sessionId, $accessToken, $accessTokenExpires); - - // Associate scopes with the access token - foreach ($authParams['scopes'] as $scope) { - $this->authServer->getStorage('session')->associateScope($accessTokenId, $scope['id']); - } - - $response = array( - 'access_token' => $accessToken, - 'token_type' => 'Bearer', - 'expires' => $accessTokenExpires, - 'expires_in' => $accessTokenExpiresIn - ); - - // Associate a refresh token if set - if ($this->authServer->hasGrantType('refresh_token')) { - $refreshToken = SecureKey::make(); - $refreshTokenTTL = time() + $this->authServer->getGrantType('refresh_token')->getRefreshTokenTTL(); - $this->authServer->getStorage('session')->associateRefreshToken($accessTokenId, $refreshToken, $refreshTokenTTL, $authParams['client_id']); - $response['refresh_token'] = $refreshToken; - } - - return $response; - } - -} diff --git a/src/League/OAuth2/Server/Storage/ClientInterface.php b/src/League/OAuth2/Server/Storage/ClientInterface.php deleted file mode 100644 index ac1a485c..00000000 --- a/src/League/OAuth2/Server/Storage/ClientInterface.php +++ /dev/null @@ -1,60 +0,0 @@ - - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages - * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server - */ - -namespace League\OAuth2\Server\Storage; - -interface ClientInterface -{ - /** - * Validate a client - * - * Example SQL query: - * - * - * # Client ID + redirect URI - * SELECT oauth_clients.id, oauth_clients.secret, oauth_client_endpoints.redirect_uri, oauth_clients.name, - * oauth_clients.auto_approve - * FROM oauth_clients LEFT JOIN oauth_client_endpoints ON oauth_client_endpoints.client_id = oauth_clients.id - * WHERE oauth_clients.id = :clientId AND oauth_client_endpoints.redirect_uri = :redirectUri - * - * # Client ID + client secret - * SELECT oauth_clients.id, oauth_clients.secret, oauth_clients.name, oauth_clients.auto_approve FROM oauth_clients - * WHERE oauth_clients.id = :clientId AND oauth_clients.secret = :clientSecret - * - * # Client ID + client secret + redirect URI - * SELECT oauth_clients.id, oauth_clients.secret, oauth_client_endpoints.redirect_uri, oauth_clients.name, - * oauth_clients.auto_approve FROM oauth_clients LEFT JOIN oauth_client_endpoints - * ON oauth_client_endpoints.client_id = oauth_clients.id - * WHERE oauth_clients.id = :clientId AND oauth_clients.secret = :clientSecret AND - * oauth_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 - * [auto_approve] => (bool) Whether the client should auto approve - * ) - * - * - * @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") - * @param string $grantType The grant type used in the request (default = "null") - * @return bool|array Returns false if the validation fails, array on success - */ - public function getClient($clientId, $clientSecret = null, $redirectUri = null, $grantType = null); -} diff --git a/src/League/OAuth2/Server/Storage/ScopeInterface.php b/src/League/OAuth2/Server/Storage/ScopeInterface.php deleted file mode 100644 index 60efd9ca..00000000 --- a/src/League/OAuth2/Server/Storage/ScopeInterface.php +++ /dev/null @@ -1,43 +0,0 @@ - - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages - * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server - */ - -namespace League\OAuth2\Server\Storage; - -interface ScopeInterface -{ - /** - * Return information about a scope - * - * Example SQL query: - * - * - * SELECT * FROM oauth_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 - * @param string $clientId The client ID (default = "null") - * @param string $grantType The grant type used in the request (default = "null") - * @return bool|array If the scope doesn't exist return false - */ - public function getScope($scope, $clientId = null, $grantType = null); -} diff --git a/src/League/OAuth2/Server/Util/RequestInterface.php b/src/League/OAuth2/Server/Util/RequestInterface.php deleted file mode 100644 index 00b8dc8e..00000000 --- a/src/League/OAuth2/Server/Util/RequestInterface.php +++ /dev/null @@ -1,29 +0,0 @@ - - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages - * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server - */ - -namespace League\OAuth2\Server\Util; - -interface RequestInterface -{ - - public function get($index = null); - - public function post($index = null); - - public function cookie($index = null); - - public function file($index = null); - - public function server($index = null); - - public function header($index = null); - -} diff --git a/src/ResourceServer.php b/src/ResourceServer.php new file mode 100644 index 00000000..654dbb41 --- /dev/null +++ b/src/ResourceServer.php @@ -0,0 +1,219 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server; + +use League\OAuth2\Server\Storage\ClientInterface; +use League\OAuth2\Server\Storage\AccessTokenInterface; +use League\OAuth2\Server\Storage\SessionInterface; +use League\OAuth2\Server\Storage\ScopeInterface; +use League\OAuth2\Server\Entity\AccessTokenEntity; +use League\OAuth2\Server\TokenType\Bearer; +use League\OAuth2\Server\Exception; +use Symfony\Component\HttpFoundation\Request; + +/** + * OAuth 2.0 Resource Server + */ +class ResourceServer extends AbstractServer +{ + /** + * The access token + * @var League\OAuth2\Server\AccessToken + */ + protected $accessToken; + + /** + * The query string key which is used by clients to present the access token (default: access_token) + * @var string + */ + protected $tokenKey = 'access_token'; + + /** + * Initialise the resource server + * @param SessionInterface $sessionStorage + * @param AccessTokenInteface $accessTokenStorage + * @param ClientInterface $clientStorage + * @param ScopeInterface $scopeStorage + * @return self + */ + public function __construct( + SessionInterface $sessionStorage, + AccessTokenInterface $accessTokenStorage, + ClientInterface $clientStorage, + ScopeInterface $scopeStorage + ) { + $sessionStorage->setServer($this); + $this->setStorage('session', $sessionStorage); + + $accessTokenStorage->setServer($this); + $this->setStorage('access_token', $accessTokenStorage); + + $clientStorage->setServer($this); + $this->setStorage('client', $clientStorage); + + $scopeStorage->setServer($this); + $this->setStorage('scope', $scopeStorage); + + // Set Bearer as the default token type + $this->setIdType(new Bearer); + + parent::__construct(); + + return $this; + } + + /** + * Set the storage + * @param string $type Storage type + * @param mixed $storage Storage class + * @return self + */ + protected function setStorage($type, $storage) + { + $storage->setServer($this); + $this->storages[$type] = $storage; + + return $this; + } + + /** + * Returns the query string key for the access token. + * @return string + */ + public function getIdKey() + { + return $this->tokenKey; + } + + /** + * Sets the query string key for the access token. + * @param $key The new query string key + * @return self + */ + public function setIdKey($key) + { + $this->tokenKey = $key; + + return $this; + } + + /** + * Gets the access token owner ID + * @return string + */ + public function getOwnerId() + { + return $this->accessToken->getSession()->getOwnerId(); + } + + /** + * Gets the owner type + * @return string + */ + public function getOwnerType() + { + return $this->accessToken->getSession()->getOwnerType(); + } + + /** + * Gets the access token + * @return string + */ + public function getAccessToken() + { + return $this->accessToken->getId(); + } + + /** + * Gets the client ID that created the session + * @return string + */ + public function getClientId() + { + return $this->accessToken->getSession()->getClient()->getId(); + } + + /** + * Get the session scopes + * @return array + */ + public function getScopes() + { + return $this->accessToken->getScopes(); + } + + /** + * Checks if the presented access token has the given scope(s) + * @param array|string $scopes 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)) { + return $this->accessToken->hasScope($scopes); + } + + if (is_array($scopes)) { + foreach ($scopes as $scope) { + if (!$this->accessToken->hasScope($scope)) { + return false; + } + } + } + + return true; + } + + /** + * Checks if the access token is valid or not + * @param $headersOnly Limit Access Token to Authorization header only + * @return bool + */ + public function isValidRequest($headersOnly = true, $accessToken = null) + { + $accessTokenString = ($accessToken !== null) + ? $accessToken + : $this->determineAccessToken($headersOnly, $accessToken); + + // Set the access token + $this->accessToken = $this->storages['access_token']->get($accessTokenString); + + if (!$this->accessToken instanceof AccessTokenEntity) { + throw new Exception\AccessDeniedException; + } + + return true; + } + + /** + * Reads in the access token from the headers + * @param $headersOnly Limit Access Token to Authorization header only + * @throws Exception\MissingAccessTokenException Thrown if there is no access token presented + * @return string + */ + public function determineAccessToken($headersOnly = false) + { + if ($this->getRequest()->headers->get('Authorization') !== null) { + $accessToken = $this->getTokenType()->determineAccessTokenInHeader($this->getRequest()); + } elseif ($headersOnly === false) { + $accessToken = ($this->getRequest()->server->get('REQUEST_METHOD') === 'GET') + ? $this->getRequest()->query->get($this->tokenKey) + : $this->getRequest()->request->get($this->tokenKey); + } + + if (empty($accessToken)) { + throw new Exception\InvalidRequestException('access token'); + } + + return $accessToken; + } +} diff --git a/src/Storage/AccessTokenInterface.php b/src/Storage/AccessTokenInterface.php new file mode 100644 index 00000000..fefa1fc1 --- /dev/null +++ b/src/Storage/AccessTokenInterface.php @@ -0,0 +1,61 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Storage; + +use League\OAuth2\Server\Entity\AccessTokenEntity; +use League\OAuth2\Server\Entity\AbstractTokenEntity; +use League\OAuth2\Server\Entity\RefreshTokenEntity; +use League\OAuth2\Server\Entity\ScopeEntity; + +/** + * Access token interface + */ +interface AccessTokenInterface +{ + /** + * Get an instance of Entity\AccessTokenEntity + * @param string $token The access token + * @return \League\OAuth2\Server\Entity\AccessTokenEntity + */ + public function get($token); + + /** + * Get the scopes for an access token + * @param \League\OAuth2\Server\Entity\AbstractTokenEntity $token The access token + * @return array Array of \League\OAuth2\Server\Entity\ScopeEntity + */ + public function getScopes(AbstractTokenEntity $token); + + /** + * Creates a new access token + * @param string $token The access token + * @param integer $expireTime The expire time expressed as a unix timestamp + * @param string|integer $sessionId The session ID + * @return \League\OAuth2\Server\Entity\AccessToken + */ + public function create($token, $expireTime, $sessionId); + + /** + * Associate a scope with an acess token + * @param \League\OAuth2\Server\Entity\AbstractTokenEntity $token The access token + * @param \League\OAuth2\Server\Entity\ScopeEntity $scope The scope + * @return void + */ + public function associateScope(AbstractTokenEntity $token, ScopeEntity $scope); + + /** + * Delete an access token + * @param \League\OAuth2\Server\Entity\AbstractTokenEntity $token The access token to delete + * @return void + */ + public function delete(AbstractTokenEntity $token); +} diff --git a/src/Storage/Adapter.php b/src/Storage/Adapter.php new file mode 100644 index 00000000..e9f3f1d6 --- /dev/null +++ b/src/Storage/Adapter.php @@ -0,0 +1,46 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Storage; + +use League\OAuth2\Server\AbstractServer; + +/** + * Storage adapter class + */ +class Adapter +{ + /** + * Server + * @var \League\OAuth2\Server\AbstractServer $server + */ + protected $server; + + /** + * Set the server + * @param \League\OAuth2\Server\AbstractServer $server + */ + public function setServer(AbstractServer $server) + { + $this->server = $server; + + return $this; + } + + /** + * Return the server + * @return \League\OAuth2\Server\AbstractServer + */ + protected function getServer() + { + return $this->server; + } +} diff --git a/src/Storage/AuthCodeInterface.php b/src/Storage/AuthCodeInterface.php new file mode 100644 index 00000000..fab3ee2f --- /dev/null +++ b/src/Storage/AuthCodeInterface.php @@ -0,0 +1,61 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Storage; + +use League\OAuth2\Server\Entity\AuthCodeEntity; +use League\OAuth2\Server\Entity\ScopeEntity; + +/** + * Auth code storage interface + */ +interface AuthCodeInterface +{ + /** + * Get the auth code + * @param string $code + * @return \League\OAuth2\Server\Entity\AuthCodeEntity + */ + public function get($code); + + /** + * Create an auth code. + * @param string $token The token ID + * @param integer $expireTime Token expire time + * @param integer $sessionId Session identifier + * @param string $redirectUri Client redirect uri + * + * @return void + */ + public function create($token, $expireTime, $sessionId, $redirectUri); + + /** + * Get the scopes for an access token + * @param \League\OAuth2\Server\Entity\AuthCodeEntity $token The auth code + * @return array Array of \League\OAuth2\Server\Entity\ScopeEntity + */ + public function getScopes(AuthCodeEntity $token); + + /** + * Associate a scope with an acess token + * @param \League\OAuth2\Server\Entity\AuthCodeEntity $token The auth code + * @param \League\OAuth2\Server\Entity\ScopeEntity $scope The scope + * @return void + */ + public function associateScope(AuthCodeEntity $token, ScopeEntity $scope); + + /** + * Delete an access token + * @param \League\OAuth2\Server\Entity\AuthCodeEntity $token The access token to delete + * @return void + */ + public function delete(AuthCodeEntity $token); +} diff --git a/src/Storage/ClientInterface.php b/src/Storage/ClientInterface.php new file mode 100644 index 00000000..202e4b02 --- /dev/null +++ b/src/Storage/ClientInterface.php @@ -0,0 +1,37 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Storage; + +use League\OAuth2\Server\Entity\SessionEntity; + +/** + * Client storage interface + */ +interface ClientInterface +{ + /** + * Validate a 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") + * @param string $grantType The grant type used (default = "null") + * @return League\OAuth2\Server\Entity\ClientEntity + */ + public function get($clientId, $clientSecret = null, $redirectUri = null, $grantType = null); + + /** + * Get the client associated with a session + * @param \League\OAuth2\Server\Entity\SessionEntity $session The session + * @return \League\OAuth2\Server\Entity\ClientEntity + */ + public function getBySession(SessionEntity $session); +} diff --git a/src/Storage/RefreshTokenInterface.php b/src/Storage/RefreshTokenInterface.php new file mode 100644 index 00000000..38621295 --- /dev/null +++ b/src/Storage/RefreshTokenInterface.php @@ -0,0 +1,43 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Storage; + +use League\OAuth2\Server\Entity\RefreshTokenEntity; + +/** + * Refresh token interface + */ +interface RefreshTokenInterface +{ + /** + * Return a new instance of \League\OAuth2\Server\Entity\RefreshTokenEntity + * @param string $token + * @return \League\OAuth2\Server\Entity\RefreshTokenEntity + */ + public function get($token); + + /** + * Create a new refresh token_name + * @param string $token + * @param integer $expireTime + * @param string $accessToken + * @return \League\OAuth2\Server\Entity\RefreshTokenEntity + */ + public function create($token, $expireTime, $accessToken); + + /** + * Delete the refresh token + * @param \League\OAuth2\Server\Entity\RefreshTokenEntity $token + * @return void + */ + public function delete(RefreshTokenEntity $token); +} diff --git a/src/Storage/ScopeInterface.php b/src/Storage/ScopeInterface.php new file mode 100644 index 00000000..e533a777 --- /dev/null +++ b/src/Storage/ScopeInterface.php @@ -0,0 +1,26 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Storage; + +/** + * Scope interface + */ +interface ScopeInterface +{ + /** + * Return information about a scope + * @param string $scope The scope + * @param string $grantType The grant type used in the request (default = "null") + * @return \League\OAuth2\Server\Entity\ScopeEntity + */ + public function get($scope, $grantType = null); +} diff --git a/src/Storage/SessionInterface.php b/src/Storage/SessionInterface.php new file mode 100644 index 00000000..3a1b1c33 --- /dev/null +++ b/src/Storage/SessionInterface.php @@ -0,0 +1,62 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Storage; + +use League\OAuth2\Server\Entity\AccessTokenEntity; +use League\OAuth2\Server\Entity\AuthCodeEntity; +use League\OAuth2\Server\Entity\SessionEntity; +use League\OAuth2\Server\Entity\ScopeEntity; + +/** + * Session storage interface + */ +interface SessionInterface +{ + /** + * Get a session from an access token + * @param \League\OAuth2\Server\Entity\AccessTokenEntity $accessToken The access token + * @return \League\OAuth2\Server\Entity\SessionEntity + */ + public function getByAccessToken(AccessTokenEntity $accessToken); + + /** + * Get a session from an auth code + * @param \League\OAuth2\Server\Entity\AuthCodeEntity $authCode The auth code + * @return \League\OAuth2\Server\Entity\SessionEntity + */ + public function getByAuthCode(AuthCodeEntity $authCode); + + /** + * Get a session's scopes + * @param \League\OAuth2\Server\Entity\SessionEntity + * @return array Array of \League\OAuth2\Server\Entity\ScopeEntity + */ + public function getScopes(SessionEntity $session); + + /** + * Create a new session + * @param string $ownerType Session owner's type (user, client) + * @param string $ownerId Session owner's ID + * @param string $clientId Client ID + * @param string $clientRedirectUri Client redirect URI (default = null) + * @return integer The session's ID + */ + public function create($ownerType, $ownerId, $clientId, $clientRedirectUri = null); + + /** + * Associate a scope with a session + * @param \League\OAuth2\Server\Entity\SessionEntity $scope The scope + * @param \League\OAuth2\Server\Entity\ScopeEntity $scope The scope + * @return void + */ + public function associateScope(SessionEntity $session, ScopeEntity $scope); +} diff --git a/src/TokenType/AbstractTokenType.php b/src/TokenType/AbstractTokenType.php new file mode 100644 index 00000000..c106b546 --- /dev/null +++ b/src/TokenType/AbstractTokenType.php @@ -0,0 +1,50 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\TokenType; + +use Symfony\Component\HttpFoundation\Request; + +abstract class AbstractTokenType +{ + /** + * Response array + * @var array + */ + protected $response = []; + + /** + * Set a key/value response pair + * @param string $key + * @param mixed $value + */ + public function set($key, $value) + { + $this->response[$key] = $value; + } + + /** + * Get a key from the response array + * @param string $key + * @return mixed + */ + public function get($key) + { + return isset($this->response[$key]) ? $this->response[$key] : null; + } + + /** + * Determine the access token in the authorization header + * @param \Symfony\Component\HttpFoundation\Request $request + * @return string + */ + abstract public function determineAccessTokenInHeader(Request $request); +} diff --git a/src/TokenType/Bearer.php b/src/TokenType/Bearer.php new file mode 100644 index 00000000..e6e92a29 --- /dev/null +++ b/src/TokenType/Bearer.php @@ -0,0 +1,46 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\TokenType; + +use Symfony\Component\HttpFoundation\Request; + +class Bearer extends AbstractTokenType implements TokenTypeInterface +{ + /** + * {@inheritdoc} + */ + public function generateResponse() + { + $return = [ + 'access_token' => $this->get('access_token'), + 'token_type' => 'Bearer', + 'expires_in' => $this->get('expires_in') + ]; + + if (!is_null($this->get('refresh_token'))) { + $return['refresh_token'] = $this->get('refresh_token'); + } + + return $return; + } + + /** + * {@inheritdoc} + */ + public function determineAccessTokenInHeader(Request $request) + { + $header = $request->headers->get('Authorization'); + $accessToken = trim(preg_replace('/^(?:\s+)?Bearer\s/', '', $header)); + + return ($accessToken === 'Bearer') ? '' : $accessToken; + } +} diff --git a/src/TokenType/TokenTypeInterface.php b/src/TokenType/TokenTypeInterface.php new file mode 100644 index 00000000..c0116ba8 --- /dev/null +++ b/src/TokenType/TokenTypeInterface.php @@ -0,0 +1,21 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\TokenType; + +interface TokenTypeInterface +{ + /** + * Generate a response + * @return array + */ + public function generateResponse(); +} diff --git a/src/Util/KeyAlgorithm/DefaultAlgorithm.php b/src/Util/KeyAlgorithm/DefaultAlgorithm.php new file mode 100644 index 00000000..ec691c7e --- /dev/null +++ b/src/Util/KeyAlgorithm/DefaultAlgorithm.php @@ -0,0 +1,46 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Util\KeyAlgorithm; + +class DefaultAlgorithm implements KeyAlgorithmInterface +{ + protected static $algorithm; + + /** + * {@inheritdoc} + */ + public function generate($len = 40) + { + return self::getAlgorithm()->make($len); + } + + /** + * @param KeyAlgorithmInterface $algorithm + */ + public static function setAlgorithm(KeyAlgorithmInterface $algorithm) + { + self::$algorithm = $algorithm; + } + + /** + * @return KeyAlgorithmInterface + */ + public static function getAlgorithm() + { + if (!self::$algorithm) { + + self::$algorithm = new DefaultAlgorithm(); + } + + return self::$algorithm; + } +} diff --git a/src/Util/KeyAlgorithm/KeyAlgorithmInterface.php b/src/Util/KeyAlgorithm/KeyAlgorithmInterface.php new file mode 100644 index 00000000..a2ba7f51 --- /dev/null +++ b/src/Util/KeyAlgorithm/KeyAlgorithmInterface.php @@ -0,0 +1,22 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Util\KeyAlgorithm; + +interface KeyAlgorithmInterface +{ + /** + * Generate a new unique code + * @param integer $len Length of the generated code + * @return string + */ + public function generate($len = 40); +} diff --git a/src/League/OAuth2/Server/Util/RedirectUri.php b/src/Util/RedirectUri.php similarity index 50% rename from src/League/OAuth2/Server/Util/RedirectUri.php rename to src/Util/RedirectUri.php index ced04113..b05b92bc 100644 --- a/src/League/OAuth2/Server/Util/RedirectUri.php +++ b/src/Util/RedirectUri.php @@ -2,11 +2,11 @@ /** * OAuth 2.0 Redirect URI generator * - * @package php-loep/oauth2-server + * @package league/oauth2-server * @author Alex Bilbie - * @copyright Copyright (c) 2013 PHP League of Extraordinary Packages + * @copyright Copyright (c) Alex Bilbie * @license http://mit-license.org/ - * @link http://github.com/php-loep/oauth2-server + * @link https://github.com/thephpleague/oauth2-server */ namespace League\OAuth2\Server\Util; @@ -16,16 +16,17 @@ namespace League\OAuth2\Server\Util; */ 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 - */ + /** + * 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/League/OAuth2/Server/Util/SecureKey.php b/src/Util/SecureKey.php similarity index 88% rename from src/League/OAuth2/Server/Util/SecureKey.php rename to src/Util/SecureKey.php index cd1fffd5..51767d7c 100644 --- a/src/League/OAuth2/Server/Util/SecureKey.php +++ b/src/Util/SecureKey.php @@ -26,9 +26,9 @@ class SecureKey * @param integer $len Length of the generated code * @return string */ - public static function make($len = 40) + public static function generate($len = 40) { - return self::getAlgorithm()->make($len); + return self::getAlgorithm()->generate($len); } /** @@ -44,11 +44,10 @@ class SecureKey */ public static function getAlgorithm() { - if (!self::$algorithm) { - + if (is_null(self::$algorithm)) { self::$algorithm = new DefaultAlgorithm(); } return self::$algorithm; } -} \ No newline at end of file +} diff --git a/tests/authorization/AuthCodeGrantTest.php b/tests/authorization/AuthCodeGrantTest.php deleted file mode 100644 index fadf90f2..00000000 --- a/tests/authorization/AuthCodeGrantTest.php +++ /dev/null @@ -1,428 +0,0 @@ -client = M::mock('League\OAuth2\Server\Storage\ClientInterface'); - $this->session = M::mock('League\OAuth2\Server\Storage\SessionInterface'); - $this->scope = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); - } - - private function returnDefault() - { - return new League\OAuth2\Server\Authorization($this->client, $this->session, $this->scope); - } - - /** - * @expectedException PHPUnit_Framework_Error - */ - public function test__construct() - { - $a = $this->returnDefault(); - $grant = new League\OAuth2\Server\Grant\AuthCode($a); - } - - public function test_setIdentifier() - { - $grant = new League\OAuth2\Server\Grant\AuthCode(); - $grant->setIdentifier('foobar'); - $this->assertEquals($grant->getIdentifier(), 'foobar'); - } - - public function test_setAuthTokenTTL() - { - $a = $this->returnDefault(); - $grant = new League\OAuth2\Server\Grant\AuthCode(); - $grant->setAuthTokenTTL(30); - - $reflector = new ReflectionClass($grant); - $requestProperty = $reflector->getProperty('authTokenTTL'); - $requestProperty->setAccessible(true); - $v = $requestProperty->getValue($grant); - - $this->assertEquals(30, $v); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_checkAuthoriseParams_noClientId() - { - $a = $this->returnDefault(); - $g = new League\OAuth2\Server\Grant\AuthCode(); - $a->addGrantType($g); - $g->checkAuthoriseParams(); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_checkAuthoriseParams_noRedirectUri() - { - $a = $this->returnDefault(); - $g = new League\OAuth2\Server\Grant\AuthCode(); - $a->addGrantType($g); - $g->checkAuthoriseParams(array( - 'client_id' => 1234 - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_checkAuthoriseParams_noRequiredState() - { - $a = $this->returnDefault(); - $g = new League\OAuth2\Server\Grant\AuthCode(); - $a->addGrantType($g); - $a->requireStateParam(true); - $g->checkAuthoriseParams(array( - 'client_id' => 1234, - 'redirect_uri' => 'http://foo/redirect' - )); - } - - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 8 - */ - public function test_checkAuthoriseParams_badClient() - { - $this->client->shouldReceive('getClient')->andReturn(false); - - $a = $this->returnDefault(); - $g = new League\OAuth2\Server\Grant\AuthCode(); - $a->addGrantType($g); - $g->checkAuthoriseParams(array( - 'client_id' => 1234, - 'redirect_uri' => 'http://foo/redirect' - )); - } - - /** - * @expectedException League\OAuth2\Server\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(); - $g = new League\OAuth2\Server\Grant\AuthCode(); - $a->addGrantType($g); - $g->checkAuthoriseParams(array( - 'client_id' => 1234, - 'redirect_uri' => 'http://foo/redirect' - )); - } - - /** - * @expectedException League\OAuth2\Server\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(); - $g = new League\OAuth2\Server\Grant\AuthCode(); - $a->addGrantType($g); - $g->checkAuthoriseParams(array( - 'client_id' => 1234, - 'redirect_uri' => 'http://foo/redirect', - 'response_type' => 'foo' - )); - } - - /** - * @expectedException League\OAuth2\Server\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(); - $g = new League\OAuth2\Server\Grant\AuthCode(); - $a->addGrantType($g); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - $a->requireScopeParam(true); - - $g->checkAuthoriseParams(array( - 'client_id' => 1234, - 'redirect_uri' => 'http://foo/redirect', - 'response_type' => 'code', - 'scope' => '' - )); - } - - public function test_checkAuthoriseParams_defaultScope() - { - $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(); - $g = new League\OAuth2\Server\Grant\AuthCode(); - $a->addGrantType($g); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - $a->setDefaultScope('test.scope'); - $a->requireScopeParam(false); - - $params = $g->checkAuthoriseParams(array( - 'client_id' => 1234, - 'redirect_uri' => 'http://foo/redirect', - 'response_type' => 'code', - 'scope' => '' - )); - - $this->assertArrayHasKey('scopes', $params); - $this->assertEquals(1, count($params['scopes'])); - } - - public function test_checkAuthoriseParams_defaultScopeArray() - { - $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(); - $g = new League\OAuth2\Server\Grant\AuthCode(); - $a->addGrantType($g); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - $a->setDefaultScope(array('test.scope', 'test.scope2')); - $a->requireScopeParam(false); - - $params = $g->checkAuthoriseParams(array( - 'client_id' => 1234, - 'redirect_uri' => 'http://foo/redirect', - 'response_type' => 'code', - 'scope' => '' - )); - - $this->assertArrayHasKey('scopes', $params); - $this->assertEquals(2, count($params['scopes'])); - } - - /** - * @expectedException League\OAuth2\Server\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(); - $g = new League\OAuth2\Server\Grant\AuthCode(); - $a->addGrantType($g); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - - $g->checkAuthoriseParams(array( - 'client_id' => 1234, - 'redirect_uri' => 'http://foo/redirect', - 'response_type' => 'code', - 'scope' => 'foo' - )); - } - - public function test_checkAuthoriseParams_passedInput() - { - $a = $this->returnDefault(); - $g = new League\OAuth2\Server\Grant\AuthCode(); - $a->addGrantType($g); - $a->addGrantType(new League\OAuth2\Server\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 = $g->checkAuthoriseParams(array( - 'client_id' => 1234, - 'redirect_uri' => 'http://foo/redirect', - 'response_type' => 'code', - 'scope' => 'foo', - 'state' => 'xyz' - )); - - $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' - ) - ), - 'scope' => 'foo', - 'state' => 'xyz' - ), $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(); - $g = new League\OAuth2\Server\Grant\AuthCode(); - $a->addGrantType($g); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - - $_GET['client_id'] = 1234; - $_GET['redirect_uri'] = 'http://foo/redirect'; - $_GET['response_type'] = 'code'; - $_GET['scope'] = 'foo'; - $_GET['state'] = 'xyz'; - - $request = new League\OAuth2\Server\Util\Request($_GET); - $a->setRequest($request); - - $v = $g->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' - ) - ), - 'scope' => 'foo', - 'state' => 'xyz' - ), $v); - } - - - function test_newAuthoriseRequest() - { - $this->session->shouldReceive('deleteSession')->andReturn(null); - $this->session->shouldReceive('createSession')->andReturn(1); - $this->session->shouldReceive('associateScope')->andReturn(null); - $this->session->shouldReceive('associateRedirectUri')->andReturn(null); - $this->session->shouldReceive('associateAuthCode')->andReturn(1); - $this->session->shouldReceive('associateAuthCodeScope')->andReturn(null); - - $a = $this->returnDefault(); - $g = new League\OAuth2\Server\Grant\AuthCode(); - $a->addGrantType($g); - - $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 = $g->newAuthoriseRequest('user', 123, $params); - - $this->assertEquals(40, strlen($v)); - } - - -} \ No newline at end of file diff --git a/tests/authorization/AuthServerTest.php b/tests/authorization/AuthServerTest.php deleted file mode 100644 index 2646fc75..00000000 --- a/tests/authorization/AuthServerTest.php +++ /dev/null @@ -1,513 +0,0 @@ -client = M::mock('League\OAuth2\Server\Storage\ClientInterface'); - $this->session = M::mock('League\OAuth2\Server\Storage\SessionInterface'); - $this->scope = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); - } - - private function returnDefault() - { - return new League\OAuth2\Server\Authorization($this->client, $this->session, $this->scope); - } - - /** - * @expectedException PHPUnit_Framework_Error - */ - public function test__construct_NoStorage() - { - new League\OAuth2\Server\Authorization; - } - - public function test__contruct_WithStorage() - { - $this->returnDefault(); - } - - public function test_getExceptionMessage() - { - $m = League\OAuth2\Server\Authorization::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', League\OAuth2\Server\Authorization::getExceptionType(2)); - } - - public function test_getExceptionHttpHeaders() - { - $this->assertEquals(array('HTTP/1.1 401 Unauthorized'), League\OAuth2\Server\Authorization::getExceptionHttpHeaders('access_denied')); - $this->assertEquals(array('HTTP/1.1 500 Internal Server Error'), League\OAuth2\Server\Authorization::getExceptionHttpHeaders('server_error')); - $this->assertEquals(array('HTTP/1.1 501 Not Implemented'), League\OAuth2\Server\Authorization::getExceptionHttpHeaders('unsupported_grant_type')); - $this->assertEquals(array('HTTP/1.1 400 Bad Request'), League\OAuth2\Server\Authorization::getExceptionHttpHeaders('invalid_refresh')); - } - - public function test_hasGrantType() - { - $a = $this->returnDefault(); - $this->assertFalse($a->hasGrantType('test')); - } - - public function test_addGrantType() - { - $a = $this->returnDefault(); - $grant = M::mock('League\OAuth2\Server\Grant\GrantTypeInterface'); - $grant->shouldReceive('getResponseType')->andReturn('test'); - $grant->shouldReceive('setAuthorizationServer')->andReturn($grant); - $a->addGrantType($grant, 'test'); - - $this->assertTrue($a->hasGrantType('test')); - } - - public function test_addGrantType_noIdentifier() - { - $a = $this->returnDefault(); - $grant = M::mock('League\OAuth2\Server\Grant\GrantTypeInterface'); - $grant->shouldReceive('getIdentifier')->andReturn('test'); - $grant->shouldReceive('getResponseType')->andReturn('test'); - $grant->shouldReceive('setAuthorizationServer')->andReturn($grant); - $a->addGrantType($grant); - - $this->assertTrue($a->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_requireScopeParam() - { - $a = $this->returnDefault(); - $a->requireScopeParam(false); - - $reflector = new ReflectionClass($a); - $requestProperty = $reflector->getProperty('requireScopeParam'); - $requestProperty->setAccessible(true); - $v = $requestProperty->getValue($a); - - $this->assertFalse($v); - } - - public function test_scopeParamRequired() - { - $a = $this->returnDefault(); - $a->requireScopeParam(false); - - $this->assertFalse($a->scopeParamRequired()); - } - - public function test_setDefaultScope() - { - $a = $this->returnDefault(); - $a->setDefaultScope('test.default'); - - $reflector = new ReflectionClass($a); - $requestProperty = $reflector->getProperty('defaultScope'); - $requestProperty->setAccessible(true); - $v = $requestProperty->getValue($a); - - $this->assertEquals('test.default', $v); - } - - public function test_getDefaultScope() - { - $a = $this->returnDefault(); - $a->setDefaultScope('test.default'); - $this->assertEquals('test.default', $a->getDefaultScope()); - } - - public function test_requireStateParam() - { - $a = $this->returnDefault(); - $a->requireStateParam(true); - - $reflector = new ReflectionClass($a); - $requestProperty = $reflector->getProperty('requireStateParam'); - $requestProperty->setAccessible(true); - $v = $requestProperty->getValue($a); - - $this->assertTrue($v); - } - - public function test_getAccessTokenTTL() - { - $a = $this->returnDefault(); - $a->setAccessTokenTTL(7200); - $this->assertEquals(7200, $a->getAccessTokenTTL()); - } - - public function test_setAccessTokenTTL() - { - $a = $this->returnDefault(); - $a->setScopeDelimeter(';'); - $this->assertEquals(';', $a->getScopeDelimeter()); - } - - public function test_setRequest() - { - $a = $this->returnDefault(); - $request = new League\OAuth2\Server\Util\Request(); - $a->setRequest($request); - - $reflector = new ReflectionClass($a); - $requestProperty = $reflector->getProperty('request'); - $requestProperty->setAccessible(true); - $v = $requestProperty->getValue($a); - - $this->assertTrue($v instanceof League\OAuth2\Server\Util\RequestInterface); - } - - public function test_getRequest() - { - $a = $this->returnDefault(); - $request = new League\OAuth2\Server\Util\Request(); - $a->setRequest($request); - $v = $a->getRequest(); - - $this->assertTrue($v instanceof League\OAuth2\Server\Util\RequestInterface); - } - - public function test_getStorage() - { - $a = $this->returnDefault(); - $this->assertTrue($a->getStorage('session') instanceof League\OAuth2\Server\Storage\SessionInterface); - } - - public function test_getGrantType() - { - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - - $reflector = new ReflectionClass($a); - $method = $reflector->getMethod('getGrantType'); - $method->setAccessible(true); - - $result = $method->invoke($a, 'authorization_code'); - - $this->assertTrue($result instanceof League\OAuth2\Server\Grant\GrantTypeInterface); - } - - /** - * @expectedException League\OAuth2\Server\Exception\InvalidGrantTypeException - * @expectedExceptionCode 9 - */ - public function test_getGrantType_fail() - { - $a = $this->returnDefault(); - $a->getGrantType('blah'); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_missingGrantType() - { - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - - $a->issueAccessToken(); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 7 - */ - public function test_issueAccessToken_badGrantType() - { - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - - $a->issueAccessToken(array('grant_type' => 'foo')); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_missingClientId() - { - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - - $a->issueAccessToken(array( - 'grant_type' => 'authorization_code' - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_missingClientSecret() - { - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - - $a->issueAccessToken(array( - 'grant_type' => 'authorization_code', - 'client_id' => 1234 - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_missingRedirectUri() - { - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - - $a->issueAccessToken(array( - 'grant_type' => 'authorization_code', - 'client_id' => 1234, - 'client_secret' => 5678 - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 8 - */ - public function test_issueAccessToken_badClient() - { - $this->client->shouldReceive('getClient')->andReturn(false); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - - $a->issueAccessToken(array( - 'grant_type' => 'authorization_code', - 'client_id' => 1234, - 'client_secret' => 5678, - 'redirect_uri' => 'http://foo/redirect' - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_missingCode() - { - $this->client->shouldReceive('getClient')->andReturn(array()); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - - $a->issueAccessToken(array( - 'grant_type' => 'authorization_code', - 'client_id' => 1234, - 'client_secret' => 5678, - 'redirect_uri' => 'http://foo/redirect' - )); - } - - /** - * @expectedException League\OAuth2\Server\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 League\OAuth2\Server\Grant\AuthCode()); - - $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(array( - 'session_id' => 1, - 'authcode_id' => 1 - )); - $this->session->shouldReceive('updateSession')->andReturn(null); - $this->session->shouldReceive('removeAuthCode')->andReturn(null); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - $this->session->shouldReceive('associateScope')->andReturn(null); - $this->session->shouldReceive('getAuthCodeScopes')->andReturn(array('scope_id' => 1)); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\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->getAccessTokenTTL(), $v['expires_in']); - } - - 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); - $this->session->shouldReceive('removeAuthCode')->andReturn(null); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - $this->session->shouldReceive('getAuthCodeScopes')->andReturn(array('scope_id' => 1)); - $this->session->shouldReceive('associateScope')->andReturn(null); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\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 League\OAuth2\Server\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->getAccessTokenTTL(), $v['expires_in']); - } - - public function test_issueAccessToken_customExpiresIn() - { - $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); - $this->session->shouldReceive('removeAuthCode')->andReturn(null); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - $this->session->shouldReceive('getAuthCodeScopes')->andReturn(array('scope_id' => 1)); - $this->session->shouldReceive('associateScope')->andReturn(null); - - $a = $this->returnDefault(); - $grant = new League\OAuth2\Server\Grant\AuthCode(); - $grant->setAccessTokenTTL(30); - $a->addGrantType($grant); - - $_POST['grant_type'] = 'authorization_code'; - $_POST['client_id'] = 1234; - $_POST['client_secret'] = 5678; - $_POST['redirect_uri'] = 'http://foo/redirect'; - $_POST['code'] = 'foobar'; - - $request = new League\OAuth2\Server\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->assertNotEquals($a->getAccessTokenTTL(), $v['expires_in']); - $this->assertNotEquals(time()+$a->getAccessTokenTTL(), $v['expires']); - $this->assertEquals(30, $v['expires_in']); - $this->assertEquals(time()+30, $v['expires']); - } - - public function test_issueAccessToken_HTTP_auth() - { - $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); - $this->session->shouldReceive('removeAuthCode')->andReturn(null); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - $this->session->shouldReceive('getAuthCodeScopes')->andReturn(array('scope_id' => 1)); - $this->session->shouldReceive('associateScope')->andReturn(null); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - - $_POST['grant_type'] = 'authorization_code'; - $_SERVER['PHP_AUTH_USER'] = 1234; - $_SERVER['PHP_AUTH_PW'] = 5678; - $_POST['redirect_uri'] = 'http://foo/redirect'; - $_POST['code'] = 'foobar'; - - $request = new League\OAuth2\Server\Util\Request(array(), $_POST, array(), array(), $_SERVER); - $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->getAccessTokenTTL(), $v['expires_in']); - } - - public function tearDown() { - M::close(); - } -} \ No newline at end of file diff --git a/tests/authorization/ClientCredentialsGrantTest.php b/tests/authorization/ClientCredentialsGrantTest.php deleted file mode 100644 index 0883a825..00000000 --- a/tests/authorization/ClientCredentialsGrantTest.php +++ /dev/null @@ -1,411 +0,0 @@ -client = M::mock('League\OAuth2\Server\Storage\ClientInterface'); - $this->session = M::mock('League\OAuth2\Server\Storage\SessionInterface'); - $this->scope = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); - } - - private function returnDefault() - { - return new League\OAuth2\Server\Authorization($this->client, $this->session, $this->scope); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_clientCredentialsGrant_missingClientId() - { - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\ClientCredentials()); - - $request = new League\OAuth2\Server\Util\Request(array(), $_POST); - $a->setRequest($request); - - $a->issueAccessToken(array( - 'grant_type' => 'client_credentials' - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_clientCredentialsGrant_missingClientPassword() - { - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\ClientCredentials()); - - $request = new League\OAuth2\Server\Util\Request(array(), $_POST); - $a->setRequest($request); - - $a->issueAccessToken(array( - 'grant_type' => 'client_credentials', - 'client_id' => 1234 - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 8 - */ - public function test_issueAccessToken_clientCredentialsGrant_badClient() - { - $this->client->shouldReceive('getClient')->andReturn(false); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\ClientCredentials()); - - $request = new League\OAuth2\Server\Util\Request(array(), $_POST); - $a->setRequest($request); - - $a->issueAccessToken(array( - 'grant_type' => 'client_credentials', - 'client_id' => 1234, - 'client_secret' => 5678 - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_clientCredentialsGrant_missingScopes() - { - $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); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\ClientCredentials()); - $a->requireScopeParam(true); - - $a->issueAccessToken(array( - 'grant_type' => 'client_credentials', - 'client_id' => 1234, - 'client_secret' => 5678 - )); - } - - public function test_issueAccessToken_clientCredentialsGrant_defaultScope() - { - $this->scope->shouldReceive('getScope')->andReturn(array( - 'id' => 1, - 'key' => 'foo', - 'name' => 'Foo Name', - 'description' => 'Foo Name Description' - )); - - $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('associateScope')->andReturn(null); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\ClientCredentials()); - $a->requireScopeParam(false); - $a->setDefaultScope('foobar'); - - $v = $a->issueAccessToken(array( - 'grant_type' => 'client_credentials', - 'client_id' => 1234, - 'client_secret' => 5678, - 'scope' => '' - )); - - $this->assertArrayHasKey('access_token', $v); - $this->assertArrayHasKey('token_type', $v); - $this->assertArrayHasKey('expires', $v); - $this->assertArrayHasKey('expires_in', $v); - } - - public function test_issueAccessToken_clientCredentialsGrant_defaultScopeArray() - { - $this->scope->shouldReceive('getScope')->andReturn(array( - 'id' => 1, - 'key' => 'foo', - 'name' => 'Foo Name', - 'description' => 'Foo Name Description' - )); - - $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('associateScope')->andReturn(null); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\ClientCredentials()); - $a->requireScopeParam(false); - $a->setDefaultScope(array('foobar', 'barfoo')); - - $v = $a->issueAccessToken(array( - 'grant_type' => 'client_credentials', - 'client_id' => 1234, - 'client_secret' => 5678, - 'scope' => '' - )); - - $this->assertArrayHasKey('access_token', $v); - $this->assertArrayHasKey('token_type', $v); - $this->assertArrayHasKey('expires', $v); - $this->assertArrayHasKey('expires_in', $v); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 4 - */ - public function test_issueAccessToken_clientCredentialsGrant_badScope() - { - $this->scope->shouldReceive('getScope')->andReturn(false); - - $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('associateScope')->andReturn(null); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\ClientCredentials()); - - $a->issueAccessToken(array( - 'grant_type' => 'client_credentials', - 'client_id' => 1234, - 'client_secret' => 5678, - 'scope' => 'blah' - )); - } - - public function test_issueAccessToken_clientCredentialsGrant_goodScope() - { - $this->scope->shouldReceive('getScope')->andReturn(array( - 'id' => 1, - 'key' => 'foo', - 'name' => 'Foo Name', - 'description' => 'Foo Name Description' - )); - - $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('associateScope')->andReturn(null); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\ClientCredentials()); - - $v = $a->issueAccessToken(array( - 'grant_type' => 'client_credentials', - 'client_id' => 1234, - 'client_secret' => 5678, - 'scope' => 'blah' - )); - - $this->assertArrayHasKey('access_token', $v); - $this->assertArrayHasKey('token_type', $v); - $this->assertArrayHasKey('expires', $v); - $this->assertArrayHasKey('expires_in', $v); - } - - 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('associateAccessToken')->andReturn(1); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\ClientCredentials()); - $a->requireScopeParam(false); - - $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->getAccessTokenTTL(), $v['expires_in']); - } - - 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('associateAccessToken')->andReturn(1); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\ClientCredentials()); - $a->requireScopeParam(false); - - $_POST['grant_type'] = 'client_credentials'; - $_POST['client_id'] = 1234; - $_POST['client_secret'] = 5678; - - $request = new League\OAuth2\Server\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->getAccessTokenTTL(), $v['expires_in']); - } - - function test_issueAccessToken_clientCredentialsGrant_customExpiresIn() - { - $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('associateAccessToken')->andReturn(1); - - $a = $this->returnDefault(); - $grant = new League\OAuth2\Server\Grant\ClientCredentials(); - $grant->setAccessTokenTTL(30); - $a->addGrantType($grant); - $a->requireScopeParam(false); - - $_POST['grant_type'] = 'client_credentials'; - $_POST['client_id'] = 1234; - $_POST['client_secret'] = 5678; - - $request = new League\OAuth2\Server\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->assertNotEquals($a->getAccessTokenTTL(), $v['expires_in']); - $this->assertNotEquals(time()+$a->getAccessTokenTTL(), $v['expires']); - $this->assertEquals(30, $v['expires_in']); - $this->assertEquals(time()+30, $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('associateAccessToken')->andReturn(1); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\ClientCredentials()); - $a->requireScopeParam(false); - - $_POST['grant_type'] = 'client_credentials'; - $_POST['client_id'] = 1234; - $_POST['client_secret'] = 5678; - - $request = new League\OAuth2\Server\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->getAccessTokenTTL(), $v['expires_in']); - } - -} \ No newline at end of file diff --git a/tests/authorization/PasswordGrantTest.php b/tests/authorization/PasswordGrantTest.php deleted file mode 100644 index fcd45c7a..00000000 --- a/tests/authorization/PasswordGrantTest.php +++ /dev/null @@ -1,613 +0,0 @@ -client = M::mock('League\OAuth2\Server\Storage\ClientInterface'); - $this->session = M::mock('League\OAuth2\Server\Storage\SessionInterface'); - $this->scope = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); - } - - private function returnDefault() - { - return new League\OAuth2\Server\Authorization($this->client, $this->session, $this->scope); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_passwordGrant_missingClientId() - { - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\Password()); - - $request = new League\OAuth2\Server\Util\Request(array(), $_POST); - $a->setRequest($request); - - $a->issueAccessToken(array( - 'grant_type' => 'password' - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_passwordGrant_missingClientPassword() - { - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\Password()); - - $request = new League\OAuth2\Server\Util\Request(array(), $_POST); - $a->setRequest($request); - - $a->issueAccessToken(array( - 'grant_type' => 'password', - 'client_id' => 1234 - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 8 - */ - public function test_issueAccessToken_passwordGrant_badClient() - { - $this->client->shouldReceive('getClient')->andReturn(false); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\Password()); - - $request = new League\OAuth2\Server\Util\Request(array(), $_POST); - $a->setRequest($request); - - $a->issueAccessToken(array( - 'grant_type' => 'password', - 'client_id' => 1234, - 'client_secret' => 5678 - )); - } - - /** - * @expectedException League\OAuth2\Server\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 League\OAuth2\Server\Grant\Password(); - $pgrant->setVerifyCredentialsCallback($testCredentials); - $a->addGrantType($pgrant); - - $a->issueAccessToken(array( - 'grant_type' => 'password', - 'client_id' => 1234, - 'client_secret' => 5678, - 'username' => 'foo', - 'password' => 'bar' - )); - } - - /** - * @expectedException League\OAuth2\Server\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() { return false; }; - - $a = $this->returnDefault(); - $pgrant = new League\OAuth2\Server\Grant\Password(); - $pgrant->setVerifyCredentialsCallback($testCredentials); - $a->addGrantType($pgrant); - - $a->issueAccessToken(array( - 'grant_type' => 'password', - 'client_id' => 1234, - 'client_secret' => 5678 - )); - } - - /** - * @expectedException League\OAuth2\Server\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() { return false; }; - - $a = $this->returnDefault(); - $pgrant = new League\OAuth2\Server\Grant\Password(); - $pgrant->setVerifyCredentialsCallback($testCredentials); - $a->addGrantType($pgrant); - - $a->issueAccessToken(array( - 'grant_type' => 'password', - 'client_id' => 1234, - 'client_secret' => 5678, - 'username' => 'foo' - )); - } - - /** - * @expectedException League\OAuth2\Server\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() { return false; }; - - $a = $this->returnDefault(); - $pgrant = new League\OAuth2\Server\Grant\Password(); - $pgrant->setVerifyCredentialsCallback($testCredentials); - $a->addGrantType($pgrant); - - $a->issueAccessToken(array( - 'grant_type' => 'password', - 'client_id' => 1234, - 'client_secret' => 5678, - 'username' => 'foo', - 'password' => 'bar' - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 4 - */ - public function test_issueAccessToken_passwordGrant_badScopes() - { - $this->scope->shouldReceive('getScope')->andReturn(false); - - $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() { return 1; }; - - $a = $this->returnDefault(); - $pgrant = new League\OAuth2\Server\Grant\Password(); - $pgrant->setVerifyCredentialsCallback($testCredentials); - $a->addGrantType($pgrant); - - $a->issueAccessToken(array( - 'grant_type' => 'password', - 'client_id' => 1234, - 'client_secret' => 5678, - 'username' => 'foo', - 'password' => 'bar', - 'scope' => 'blah' - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_passwordGrant_missingScopes() - { - $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() { return 1; }; - - $a = $this->returnDefault(); - $pgrant = new League\OAuth2\Server\Grant\Password(); - $pgrant->setVerifyCredentialsCallback($testCredentials); - $a->addGrantType($pgrant); - $a->requireScopeParam(true); - - $a->issueAccessToken(array( - 'grant_type' => 'password', - 'client_id' => 1234, - 'client_secret' => 5678, - 'username' => 'foo', - 'password' => 'bar' - )); - } - - public function test_issueAccessToken_passwordGrant_defaultScope() - { - $this->scope->shouldReceive('getScope')->andReturn(array( - 'id' => 1, - 'scope' => 'foo', - 'name' => 'Foo Name', - 'description' => 'Foo Name Description' - )); - - $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); - $this->session->shouldReceive('associateScope')->andReturn(null); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - - $testCredentials = function() { return 1; }; - - $a = $this->returnDefault(); - $pgrant = new League\OAuth2\Server\Grant\Password(); - $pgrant->setVerifyCredentialsCallback($testCredentials); - $a->addGrantType($pgrant); - $a->requireScopeParam(false); - $a->setDefaultScope('foobar'); - - $v = $a->issueAccessToken(array( - 'grant_type' => 'password', - 'client_id' => 1234, - 'client_secret' => 5678, - 'username' => 'foo', - 'password' => 'bar', - 'scope' => '' - )); - - $this->assertArrayHasKey('access_token', $v); - $this->assertArrayHasKey('token_type', $v); - $this->assertArrayHasKey('expires', $v); - $this->assertArrayHasKey('expires_in', $v); - } - - public function test_issueAccessToken_passwordGrant_defaultScopeArray() - { - $this->scope->shouldReceive('getScope')->andReturn(array( - 'id' => 1, - 'scope' => 'foo', - 'name' => 'Foo Name', - 'description' => 'Foo Name Description' - )); - - $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); - $this->session->shouldReceive('associateScope')->andReturn(null); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - - $testCredentials = function() { return 1; }; - - $a = $this->returnDefault(); - $pgrant = new League\OAuth2\Server\Grant\Password(); - $pgrant->setVerifyCredentialsCallback($testCredentials); - $a->addGrantType($pgrant); - $a->requireScopeParam(false); - $a->setDefaultScope(array('foobar', 'barfoo')); - - $v = $a->issueAccessToken(array( - 'grant_type' => 'password', - 'client_id' => 1234, - 'client_secret' => 5678, - 'username' => 'foo', - 'password' => 'bar', - 'scope' => '' - )); - - $this->assertArrayHasKey('access_token', $v); - $this->assertArrayHasKey('token_type', $v); - $this->assertArrayHasKey('expires', $v); - $this->assertArrayHasKey('expires_in', $v); - } - - public function test_issueAccessToken_passwordGrant_goodScope() - { - $this->scope->shouldReceive('getScope')->andReturn(array( - 'id' => 1, - 'scope' => 'foo', - 'name' => 'Foo Name', - 'description' => 'Foo Name Description' - )); - - $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); - $this->session->shouldReceive('associateScope')->andReturn(null); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - - $testCredentials = function() { return 1; }; - - $a = $this->returnDefault(); - $pgrant = new League\OAuth2\Server\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', - 'scope' => 'blah' - )); - - $this->assertArrayHasKey('access_token', $v); - $this->assertArrayHasKey('token_type', $v); - $this->assertArrayHasKey('expires', $v); - $this->assertArrayHasKey('expires_in', $v); - } - - 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); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - - $testCredentials = function() { return 1; }; - - $a = $this->returnDefault(); - $pgrant = new League\OAuth2\Server\Grant\Password(); - $pgrant->setVerifyCredentialsCallback($testCredentials); - $a->addGrantType($pgrant); - $a->requireScopeParam(false); - - $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->getAccessTokenTTL(), $v['expires_in']); - } - - 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); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - - $testCredentials = function() { return 1; }; - - $a = $this->returnDefault(); - $pgrant = new League\OAuth2\Server\Grant\Password(); - $pgrant->setVerifyCredentialsCallback($testCredentials); - $a->addGrantType($pgrant); - $a->requireScopeParam(false); - - $_POST['grant_type'] = 'password'; - $_POST['client_id'] = 1234; - $_POST['client_secret'] = 5678; - $_POST['username'] = 'foo'; - $_POST['password'] = 'bar'; - - $request = new League\OAuth2\Server\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->getAccessTokenTTL(), $v['expires_in']); - } - - function test_issueAccessToken_passwordGrant_customExpiresIn() - { - $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); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - - $testCredentials = function() { return 1; }; - - $a = $this->returnDefault(); - $pgrant = new League\OAuth2\Server\Grant\Password(); - $pgrant->setVerifyCredentialsCallback($testCredentials); - $pgrant->setAccessTokenTTL(30); - $a->addGrantType($pgrant); - $a->requireScopeParam(false); - - $_POST['grant_type'] = 'password'; - $_POST['client_id'] = 1234; - $_POST['client_secret'] = 5678; - $_POST['username'] = 'foo'; - $_POST['password'] = 'bar'; - - $request = new League\OAuth2\Server\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->assertNotEquals($a->getAccessTokenTTL(), $v['expires_in']); - $this->assertNotEquals(time()+$a->getAccessTokenTTL(), $v['expires']); - $this->assertEquals(30, $v['expires_in']); - $this->assertEquals(time()+30, $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); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - $this->session->shouldReceive('associateRefreshToken')->andReturn(null); - - $testCredentials = function() { return 1; }; - - $a = $this->returnDefault(); - $pgrant = new League\OAuth2\Server\Grant\Password(); - $pgrant->setVerifyCredentialsCallback($testCredentials); - $a->addGrantType($pgrant); - $a->addGrantType(new League\OAuth2\Server\Grant\RefreshToken()); - $a->requireScopeParam(false); - - $_POST['grant_type'] = 'password'; - $_POST['client_id'] = 1234; - $_POST['client_secret'] = 5678; - $_POST['username'] = 'foo'; - $_POST['password'] = 'bar'; - - $request = new League\OAuth2\Server\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->getAccessTokenTTL(), $v['expires_in']); - } -} \ No newline at end of file diff --git a/tests/authorization/RefreshTokenTest.php b/tests/authorization/RefreshTokenTest.php deleted file mode 100644 index 572b50cb..00000000 --- a/tests/authorization/RefreshTokenTest.php +++ /dev/null @@ -1,421 +0,0 @@ -client = M::mock('League\OAuth2\Server\Storage\ClientInterface'); - $this->session = M::mock('League\OAuth2\Server\Storage\SessionInterface'); - $this->scope = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); - } - - private function returnDefault() - { - return new League\OAuth2\Server\Authorization($this->client, $this->session, $this->scope); - } - - public function test_setRefreshTokenTTL() - { - $a = $this->returnDefault(); - $rt = new League\OAuth2\Server\Grant\RefreshToken(); - $rt->setRefreshTokenTTL(30); - $this->assertEquals(30, $rt->getRefreshTokenTTL()); - } - - 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); - $this->session->shouldReceive('removeAuthCode')->andReturn(null); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - $this->session->shouldReceive('associateRefreshToken')->andReturn(1); - $this->session->shouldReceive('associateScope')->andReturn(null); - $this->session->shouldReceive('getAuthCodeScopes')->andReturn(array('scope_id' => 1)); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\AuthCode()); - $a->addGrantType(new League\OAuth2\Server\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 League\OAuth2\Server\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->getAccessTokenTTL(), $v['expires_in']); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_refreshTokenGrant_missingClientId() - { - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\RefreshToken()); - - $request = new League\OAuth2\Server\Util\Request(array(), $_POST); - $a->setRequest($request); - - $a->issueAccessToken(array( - 'grant_type' => 'refresh_token' - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_refreshTokenGrant_missingClientSecret() - { - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\RefreshToken()); - - $request = new League\OAuth2\Server\Util\Request(array(), $_POST); - $a->setRequest($request); - - $a->issueAccessToken(array( - 'grant_type' => 'refresh_token', - 'client_id' => 1234 - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 8 - */ - public function test_issueAccessToken_refreshTokenGrant_badClient() - { - $this->client->shouldReceive('getClient')->andReturn(false); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\RefreshToken()); - - $request = new League\OAuth2\Server\Util\Request(array(), $_POST); - $a->setRequest($request); - - $a->issueAccessToken(array( - 'grant_type' => 'refresh_token', - 'client_id' => 1234, - 'client_secret' => 5678 - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_refreshTokenGrant_missingRefreshToken() - { - $this->client->shouldReceive('getClient')->andReturn(array()); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\RefreshToken()); - - $request = new League\OAuth2\Server\Util\Request(array(), $_POST); - $a->setRequest($request); - - $a->issueAccessToken(array( - 'grant_type' => 'refresh_token', - 'client_id' => 1234, - 'client_secret' => 5678 - )); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_refreshTokenGrant_badRefreshToken() - { - $this->client->shouldReceive('getClient')->andReturn(array()); - $this->session->shouldReceive('validateRefreshToken')->andReturn(false); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\RefreshToken()); - - $request = new League\OAuth2\Server\Util\Request(array(), $_POST); - $a->setRequest($request); - - $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->session->shouldReceive('validateRefreshToken')->andReturn(1); - $this->session->shouldReceive('validateAuthCode')->andReturn(1); - $this->session->shouldReceive('updateSession')->andReturn(null); - $this->session->shouldReceive('updateRefreshToken')->andReturn(null); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - $this->session->shouldReceive('associateRefreshToken')->andReturn(1); - $this->session->shouldReceive('removeRefreshToken')->andReturn(1); - $this->session->shouldReceive('getAccessToken')->andReturn(null); - $this->session->shouldReceive('getScopes')->andReturn(array()); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\Grant\RefreshToken()); - - $_POST['grant_type'] = 'refresh_token'; - $_POST['client_id'] = 1234; - $_POST['client_secret'] = 5678; - $_POST['refresh_token'] = 'abcdef'; - - $request = new League\OAuth2\Server\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->getAccessTokenTTL(), $v['expires_in']); - } - - 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->session->shouldReceive('validateRefreshToken')->andReturn(1); - $this->session->shouldReceive('validateAuthCode')->andReturn(1); - $this->session->shouldReceive('updateSession')->andReturn(null); - $this->session->shouldReceive('updateRefreshToken')->andReturn(null); - $this->session->shouldReceive('getAccessToken')->andReturn(null); - $this->session->shouldReceive('getScopes')->andReturn(array('id' => 1)); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - $this->session->shouldReceive('associateRefreshToken')->andReturn(1); - $this->session->shouldReceive('removeRefreshToken')->andReturn(1); - $this->session->shouldReceive('associateScope')->andReturn(null); - - $a = $this->returnDefault(); - $a->addGrantType(new League\OAuth2\Server\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->assertEquals($a->getAccessTokenTTL(), $v['expires_in']); - } - - public function test_issueAccessToken_refreshTokenGrant_rotateTokens() - { - $this->client->shouldReceive('getClient')->andReturn(array( - 'client_id' => 1234, - 'client_secret' => 5678, - 'redirect_uri' => 'http://foo/redirect', - 'name' => 'Example Client' - )); - - $this->session->shouldReceive('validateRefreshToken')->andReturn(1); - $this->session->shouldReceive('validateAuthCode')->andReturn(1); - $this->session->shouldReceive('updateSession')->andReturn(null); - $this->session->shouldReceive('updateRefreshToken')->andReturn(null); - $this->session->shouldReceive('getAccessToken')->andReturn(null); - $this->session->shouldReceive('getScopes')->andReturn(array('id' => 1)); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - $this->session->shouldReceive('associateRefreshToken')->andReturn(1); - $this->session->shouldReceive('removeRefreshToken')->andReturn(1); - $this->session->shouldReceive('associateScope')->andReturn(null); - - $a = $this->returnDefault(); - - $rt = new League\OAuth2\Server\Grant\RefreshToken(); - $rt->rotateRefreshTokens(true); - $a->addGrantType($rt); - - $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->getAccessTokenTTL(), $v['expires_in']); - } - - public function test_issueAccessToken_refreshTokenGrant_customExpiresIn() - { - $this->client->shouldReceive('getClient')->andReturn(array( - 'client_id' => 1234, - 'client_secret' => 5678, - 'redirect_uri' => 'http://foo/redirect', - 'name' => 'Example Client' - )); - - $this->session->shouldReceive('validateRefreshToken')->andReturn(1); - $this->session->shouldReceive('validateAuthCode')->andReturn(1); - $this->session->shouldReceive('updateSession')->andReturn(null); - $this->session->shouldReceive('updateRefreshToken')->andReturn(null); - $this->session->shouldReceive('getAccessToken')->andReturn(null); - $this->session->shouldReceive('getScopes')->andReturn(array('id' => 1)); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - $this->session->shouldReceive('associateRefreshToken')->andReturn(1); - $this->session->shouldReceive('removeRefreshToken')->andReturn(1); - $this->session->shouldReceive('associateScope')->andReturn(null); - - $a = $this->returnDefault(); - $grant = new League\OAuth2\Server\Grant\RefreshToken(); - $grant->setAccessTokenTTL(30); - $a->addGrantType($grant); - - $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->assertNotEquals($a->getAccessTokenTTL(), $v['expires_in']); - $this->assertNotEquals(time()+$a->getAccessTokenTTL(), $v['expires']); - $this->assertEquals(30, $v['expires_in']); - $this->assertEquals(time()+30, $v['expires']); - } - - public function test_issueAccessToken_refreshTokenGrant_newScopes() - { - $this->client->shouldReceive('getClient')->andReturn(array( - 'client_id' => 1234, - 'client_secret' => 5678, - 'redirect_uri' => 'http://foo/redirect', - 'name' => 'Example Client' - )); - - $this->session->shouldReceive('validateRefreshToken')->andReturn(1); - $this->session->shouldReceive('validateAuthCode')->andReturn(1); - $this->session->shouldReceive('updateSession')->andReturn(null); - $this->session->shouldReceive('updateRefreshToken')->andReturn(null); - $this->session->shouldReceive('getAccessToken')->andReturn(null); - $this->session->shouldReceive('getScopes')->andReturn(array(array('id' => 1, 'scope' => 'foo'), array('id' => 2, 'scope' => 'bar'))); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - $this->session->shouldReceive('associateRefreshToken')->andReturn(1); - $this->session->shouldReceive('removeRefreshToken')->andReturn(1); - $this->session->shouldReceive('associateScope')->andReturn(null); - $this->scope->shouldReceive('getScope')->andReturn(array('id' => 1, 'scope' => 'foo')); - - $a = $this->returnDefault(); - $grant = new League\OAuth2\Server\Grant\RefreshToken(); - $grant->setAccessTokenTTL(30); - $grant->rotateRefreshTokens(true); - $a->addGrantType($grant); - - $v = $a->issueAccessToken(array( - 'grant_type' => 'refresh_token', - 'client_id' => 1234, - 'client_secret' => 5678, - 'refresh_token' => 'abcdef', - 'scope' => 'foo' - )); - - $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->assertNotEquals($a->getAccessTokenTTL(), $v['expires_in']); - $this->assertNotEquals(time()+$a->getAccessTokenTTL(), $v['expires']); - $this->assertEquals(30, $v['expires_in']); - $this->assertEquals(time()+30, $v['expires']); - } - - /** - * @expectedException League\OAuth2\Server\Exception\ClientException - * @expectedExceptionCode 0 - */ - public function test_issueAccessToken_refreshTokenGrant_badNewScopes() - { - $this->client->shouldReceive('getClient')->andReturn(array( - 'client_id' => 1234, - 'client_secret' => 5678, - 'redirect_uri' => 'http://foo/redirect', - 'name' => 'Example Client' - )); - - $this->session->shouldReceive('validateRefreshToken')->andReturn(1); - $this->session->shouldReceive('validateAuthCode')->andReturn(1); - $this->session->shouldReceive('updateSession')->andReturn(null); - $this->session->shouldReceive('updateRefreshToken')->andReturn(null); - $this->session->shouldReceive('getAccessToken')->andReturn(null); - $this->session->shouldReceive('getScopes')->andReturn(array(array('id' => 1, 'scope' => 'foo'), array('id' => 2, 'scope' => 'bar'))); - $this->session->shouldReceive('associateAccessToken')->andReturn(1); - $this->session->shouldReceive('associateRefreshToken')->andReturn(1); - $this->session->shouldReceive('removeRefreshToken')->andReturn(1); - $this->session->shouldReceive('associateScope')->andReturn(null); - $this->scope->shouldReceive('getScope')->andReturn(array('id' => 1, 'scope' => 'foo')); - - $a = $this->returnDefault(); - $grant = new League\OAuth2\Server\Grant\RefreshToken(); - $grant->setAccessTokenTTL(30); - $grant->rotateRefreshTokens(true); - $a->addGrantType($grant); - - $a->issueAccessToken(array( - 'grant_type' => 'refresh_token', - 'client_id' => 1234, - 'client_secret' => 5678, - 'refresh_token' => 'abcdef', - 'scope' => 'foobar' - )); - } -} \ No newline at end of file diff --git a/tests/fuzz/grant-authcode.yml b/tests/fuzz/grant-authcode.yml new file mode 100644 index 00000000..7c740a9d --- /dev/null +++ b/tests/fuzz/grant-authcode.yml @@ -0,0 +1,9 @@ +url: 'http://localhost:8000/authcode_grant.php/authorize?client_id=testclient&redirect_uri=http%3A%2F%2Fexample.com%2Fredirect&response_type=code&scope=basic' +request: + method: GET +response: + statusCode: 200 + headers: + - + key: Location + valueRegex: /http:\/\/example.com\/redirect\?code=([a-zA-Z0-9]*)/ \ No newline at end of file diff --git a/tests/fuzz/grant-client-credentials.yml b/tests/fuzz/grant-client-credentials.yml new file mode 100644 index 00000000..e56d903f --- /dev/null +++ b/tests/fuzz/grant-client-credentials.yml @@ -0,0 +1,67 @@ +url: 'http://localhost:8000/other_grants.php/access_token' +request: + method: POST + body: + - + key: client_id + value: testclient + missing: + response.statusCode: 400 + headers.content-type: "application/json" + body.error: invalid_request + body.message: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the \"client_id\" parameter." + invalid: + response.statusCode: 401 + headers.content-type: "application/json" + body.error: invalid_client + body.message: "Client authentication failed." + - + key: client_secret + value: secret + missing: + response.statusCode: 400 + headers.content-type: "application/json" + body.error: invalid_request + body.message: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the \"client_secret\" parameter." + invalid: + response.statusCode: 401 + headers.content-type: "application/json" + body.error: invalid_client + body.message: "Client authentication failed." + - + key: grant_type + value: client_credentials + missing: + response.statusCode: 400 + headers.content-type: "application/json" + body.error: invalid_request + body.message: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the \"grant_type\" parameter." + invalid: + response.statusCode: 400 + headers.content-type: "application/json" + body.error: unsupported_grant_type + #body.message: "The authorization grant type XXX is not supported by the authorization server." + - + key: scope + value: "basic" + invalid: + response.statusCode: 400 + headers.content-type: "application/json" + body.error: invalid_scope + border.message: fooooooooo +response: + statusCode: 200 + headers: + - + key: Content-type + value: application/json + body: + - + key: expires_in + valueType: integer + - + key: access_token + valueRegex: /([a-zA-Z0-9]*)/ + - + key: token_type + value: Bearer \ No newline at end of file diff --git a/tests/fuzz/grant-password.yml b/tests/fuzz/grant-password.yml new file mode 100644 index 00000000..0348c5f2 --- /dev/null +++ b/tests/fuzz/grant-password.yml @@ -0,0 +1,88 @@ +url: 'http://localhost:8000/other_grants.php/access_token' +request: + method: POST + body: + - + key: client_id + value: testclient + missing: + response.statusCode: 400 + headers.content-type: "application/json" + body.error: invalid_request + body.message: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the \"client_id\" parameter." + invalid: + response.statusCode: 401 + headers.content-type: "application/json" + body.error: invalid_client + body.message: "Client authentication failed." + - + key: client_secret + value: secret + missing: + response.statusCode: 400 + headers.content-type: "application/json" + body.error: invalid_request + body.message: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the \"client_secret\" parameter." + invalid: + response.statusCode: 401 + headers.content-type: "application/json" + body.error: invalid_client + body.message: "Client authentication failed." + - + key: username + value: alexbilbie + missing: + response.statusCode: 400 + headers.content-type: "application/json" + body.error: invalid_request + body.message: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the \"username\" parameter." + invalid: + response.statusCode: 401 + headers.content-type: "application/json" + body.error: invalid_credentials + body.message: "The user credentials were incorrect." + - + key: password + value: whisky + missing: + response.statusCode: 400 + headers.content-type: "application/json" + body.error: invalid_request + body.message: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the \"password\" parameter." + invalid: + response.statusCode: 401 + headers.content-type: "application/json" + body.error: invalid_credentials + body.message: "The user credentials were incorrect." + - + key: grant_type + value: password + missing: + response.statusCode: 400 + headers.content-type: "application/json" + body.error: invalid_request + body.message: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the \"grant_type\" parameter." + invalid: + response.statusCode: 400 + headers.content-type: "application/json" + body.error: unsupported_grant_type + #body.message: "The authorization grant type XXX is not supported by the authorization server." +response: + statusCode: 200 + headers: + - + key: Content-type + value: application/json + body: + - + key: expires_in + valueType: integer + - + key: access_token + valueRegex: /([a-zA-Z0-9]*)/ + - + key: refresh_token + valueRegex: /([a-zA-Z0-9]*)/ + - + key: token_type + value: Bearer \ No newline at end of file diff --git a/tests/fuzz/tokeninfo-no-access-token.yml b/tests/fuzz/tokeninfo-no-access-token.yml new file mode 100644 index 00000000..3d084cf7 --- /dev/null +++ b/tests/fuzz/tokeninfo-no-access-token.yml @@ -0,0 +1,16 @@ +url: 'http://localhost:8000/api.php/tokeninfo' +request: + method: GET +response: + statusCode: 400 + headers: + - + key: Content-type + value: application/json + body: + - + key: error + value: "invalid_request" + - + key: message + value: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the \"access token\" parameter." \ No newline at end of file diff --git a/tests/fuzz/tokeninfo-no-invalid-token-query-string.yml b/tests/fuzz/tokeninfo-no-invalid-token-query-string.yml new file mode 100644 index 00000000..bfe07737 --- /dev/null +++ b/tests/fuzz/tokeninfo-no-invalid-token-query-string.yml @@ -0,0 +1,16 @@ +url: 'http://localhost:8000/api.php/tokeninfo?access_token=foobar' +request: + method: GET +response: + statusCode: 401 + headers: + - + key: Content-type + value: application/json + body: + - + key: error + value: "access_denied" + - + key: message + value: "The resource owner or authorization server denied the request." \ No newline at end of file diff --git a/tests/fuzz/tokeninfo-no-invalid-token.yml b/tests/fuzz/tokeninfo-no-invalid-token.yml new file mode 100644 index 00000000..8c4535e3 --- /dev/null +++ b/tests/fuzz/tokeninfo-no-invalid-token.yml @@ -0,0 +1,20 @@ +url: 'http://localhost:8000/api.php/tokeninfo' +request: + method: GET + headers: + - + key: Authorization + value: Bearer foobar +response: + statusCode: 401 + headers: + - + key: Content-type + value: application/json + body: + - + key: error + value: "access_denied" + - + key: message + value: "The resource owner or authorization server denied the request." \ No newline at end of file diff --git a/tests/fuzz/tokeninfo-valid-token-header.yml b/tests/fuzz/tokeninfo-valid-token-header.yml new file mode 100644 index 00000000..c30d819e --- /dev/null +++ b/tests/fuzz/tokeninfo-valid-token-header.yml @@ -0,0 +1,26 @@ +url: 'http://localhost:8000/api.php/tokeninfo' +request: + method: GET + headers: + - + key: Authorization + value: "Bearer iamgod" +response: + statusCode: 200 + headers: + - + key: Content-type + value: application/json + body: + - + key: owner_id + value: testclient + - + key: owner_type + value: client + - + key: access_token + value: iamgod + - + key: client_id + value: testclient \ No newline at end of file diff --git a/tests/fuzz/tokeninfo-valid-token.yml b/tests/fuzz/tokeninfo-valid-token.yml new file mode 100644 index 00000000..fb160249 --- /dev/null +++ b/tests/fuzz/tokeninfo-valid-token.yml @@ -0,0 +1,22 @@ +url: 'http://localhost:8000/api.php/tokeninfo?access_token=iamgod' +request: + method: GET +response: + statusCode: 200 + headers: + - + key: Content-type + value: application/json + body: + - + key: owner_id + value: testclient + - + key: owner_type + value: client + - + key: access_token + value: iamgod + - + key: client_id + value: testclient \ No newline at end of file diff --git a/tests/fuzz/users-token-iamalex.yml b/tests/fuzz/users-token-iamalex.yml new file mode 100644 index 00000000..629de493 --- /dev/null +++ b/tests/fuzz/users-token-iamalex.yml @@ -0,0 +1,32 @@ +url: 'http://localhost:8000/api.php/users' +request: + method: GET + headers: + - + key: Authorization + value: Bearer iamalex +response: + statusCode: 200 + headers: + - + key: Content-type + value: application/json + body: + - + key: 0.username + value: alexbilbie + - + key: 0.name + value: Alex Bilbie + - + key: 0.photo + valueType: string + - + key: 1.username + value: philsturgeon + - + key: 1.name + value: Phil Sturgeon + - + key: 1.photo + valueType: string \ No newline at end of file diff --git a/tests/fuzz/users-token-iamphil.yml b/tests/fuzz/users-token-iamphil.yml new file mode 100644 index 00000000..6f1b7b57 --- /dev/null +++ b/tests/fuzz/users-token-iamphil.yml @@ -0,0 +1,32 @@ +url: 'http://localhost:8000/api.php/users' +request: + method: GET + headers: + - + key: Authorization + value: Bearer iamphil +response: + statusCode: 200 + headers: + - + key: Content-type + value: application/json + body: + - + key: 0.username + value: alexbilbie + - + key: 0.name + value: Alex Bilbie + - + key: 0.email + valueType: string + - + key: 1.username + value: philsturgeon + - + key: 1.name + value: Phil Sturgeon + - + key: 1.email + valueType: string \ No newline at end of file diff --git a/tests/unit/AbstractServerTest.php b/tests/unit/AbstractServerTest.php new file mode 100644 index 00000000..8933e137 --- /dev/null +++ b/tests/unit/AbstractServerTest.php @@ -0,0 +1,35 @@ +addEventListener('event.name', function() use ($var) { + $var++; + $this->assertSame(1, $var); + }); + $server->getEventEmitter()->emit('event.name'); + $this->assertTrue($server->getRequest() instanceof \Symfony\Component\HttpFoundation\Request); + $this->assertTrue($server->getEventEmitter() instanceof \League\Event\Emitter); + + + $server2 = new StubAbstractServer(); + $server2->setRequest((new \Symfony\Component\HttpFoundation\Request)); + $server2->setEventEmitter(1); + $this->assertTrue($server2->getRequest() instanceof \Symfony\Component\HttpFoundation\Request); + + } + + public function testGetStorageException() + { + $this->setExpectedException('League\OAuth2\Server\Exception\ServerErrorException'); + $server = new StubAbstractServer(); + $server->getStorage('foobar'); + } +} diff --git a/tests/unit/AuthorizationServerTest.php b/tests/unit/AuthorizationServerTest.php new file mode 100644 index 00000000..90298d3a --- /dev/null +++ b/tests/unit/AuthorizationServerTest.php @@ -0,0 +1,82 @@ +requireScopeParam(true); + $server->requireStateParam(true); + $server->setDefaultScope('foobar'); + $server->setScopeDelimeter(','); + $server->setAccessTokenTTL(1); + + $grant = M::mock('League\OAuth2\Server\Grant\GrantTypeInterface'); + $grant->shouldReceive('getIdentifier')->andReturn('foobar'); + $grant->shouldReceive('getResponseType')->andReturn('foobar'); + $grant->shouldReceive('setAuthorizationServer'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + + $server->addGrantType($grant); + $server->setScopeStorage($scopeStorage); + + $this->assertTrue($server->hasGrantType('foobar')); + $this->assertTrue($server->getGrantType('foobar') instanceof GrantTypeInterface); + $this->assertSame($server->getResponseTypes(), ['foobar']); + $this->assertTrue($server->scopeParamRequired()); + $this->assertTrue($server->stateParamRequired()); + $this->assertTrue($server->getStorage('scope') instanceof ScopeInterface); + $this->assertEquals('foobar', $server->getDefaultScope()); + $this->assertEquals(',', $server->getScopeDelimeter()); + $this->assertEquals(1, $server->getAccessTokenTTL()); + } + + public function testInvalidGrantType() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidGrantException'); + $server = new AuthorizationServer; + $server->getGrantType('foobar'); + } + + public function testIssueAccessToken() + { + $grant = M::mock('League\OAuth2\Server\Grant\GrantTypeInterface'); + $grant->shouldReceive('getIdentifier')->andReturn('foobar'); + $grant->shouldReceive('getResponseType')->andReturn('foobar'); + $grant->shouldReceive('setAuthorizationServer'); + $grant->shouldReceive('completeFlow')->andReturn(true); + + $_POST['grant_type'] = 'foobar'; + + $server = new AuthorizationServer; + $server->addGrantType($grant); + + $this->assertTrue($server->issueAccessToken()); + } + + public function testIssueAccessTokenEmptyGrantType() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + $server = new AuthorizationServer; + $this->assertTrue($server->issueAccessToken()); + } + + public function testIssueAccessTokenInvalidGrantType() + { + $this->setExpectedException('League\OAuth2\Server\Exception\UnsupportedGrantTypeException'); + + $_POST['grant_type'] = 'foobar'; + + $server = new AuthorizationServer; + $this->assertTrue($server->issueAccessToken()); + } +} diff --git a/tests/Bootstrap.php b/tests/unit/Bootstrap.php similarity index 72% rename from tests/Bootstrap.php rename to tests/unit/Bootstrap.php index ff9fb28e..e6d3d782 100644 --- a/tests/Bootstrap.php +++ b/tests/unit/Bootstrap.php @@ -1,6 +1,5 @@ wget http://getcomposer.org/composer.phar\n> php composer.phar install\n"); -} \ No newline at end of file +} diff --git a/tests/unit/Entity/AbstractTokenEntityTest.php b/tests/unit/Entity/AbstractTokenEntityTest.php new file mode 100644 index 00000000..f3bb290a --- /dev/null +++ b/tests/unit/Entity/AbstractTokenEntityTest.php @@ -0,0 +1,116 @@ +setId('foobar'); + $entity->setExpireTime($time); + $entity->setSession((new SessionEntity($server))); + $entity->associateScope((new ScopeEntity($server))->hydrate(['id' => 'foo'])); + + $this->assertEquals('foobar', $entity->getId()); + $this->assertEquals($time, $entity->getExpireTime()); + // $this->assertTrue($entity->getSession() instanceof SessionEntity); + // $this->assertTrue($entity->hasScope('foo')); + + // $result = $entity->getScopes(); + // $this->assertTrue(isset($result['foo'])); + } + + /*public function testGetSession() + { + $server = M::mock('League\OAuth2\Server\AuthorizationServer'); + $server->shouldReceive('setSessionStorage'); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('getByAccessToken')->andReturn( + (new SessionEntity($server)) + ); + $sessionStorage->shouldReceive('setServer'); + + $server->shouldReceive('getStorage')->andReturn($sessionStorage); + + $server->setSessionStorage($sessionStorage); + + $entity = new StubAbstractTokenEntity($server); + $this->assertTrue($entity->getSession() instanceof SessionEntity); + }*/ + + /*public function testGetScopes() + { + $server = M::mock('League\OAuth2\Server\AuthorizationServer'); + $server->shouldReceive('setAccessTokenStorage'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn( + [] + ); + $accessTokenStorage->shouldReceive('setServer'); + + $server->setAccessTokenStorage($accessTokenStorage); + + $entity = new StubAbstractTokenEntity($server); + $this->assertEquals($entity->getScopes(), []); + }*/ + + /*public function testHasScopes() + { + $server = M::mock('League\OAuth2\Server\AuthorizationServer'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn( + [] + ); + $accessTokenStorage''>shouldReceive('setServer'); + + $server->setAccessTokenStorage($accessTokenStorage); + + $entity = new StubAbstractTokenEntity($server); + $this->assertFalse($entity->hasScope('foo')); + }*/ + + public function testFormatScopes() + { + $server = M::mock('League\OAuth2\Server\AbstractServer'); + + $entity = new StubAbstractTokenEntity($server); + $reflectedEntity = new \ReflectionClass('LeagueTests\Stubs\StubAbstractTokenEntity'); + $method = $reflectedEntity->getMethod('formatScopes'); + $method->setAccessible(true); + + $scopes = [ + (new ScopeEntity($server))->hydrate(['id' => 'scope1', 'description' => 'foo']), + (new ScopeEntity($server))->hydrate(['id' => 'scope2', 'description' => 'bar']) + ]; + + $result = $method->invokeArgs($entity, [$scopes]); + + $this->assertTrue(isset($result['scope1'])); + $this->assertTrue(isset($result['scope2'])); + $this->assertTrue($result['scope1'] instanceof ScopeEntity); + $this->assertTrue($result['scope2'] instanceof ScopeEntity); + } + + public function test__toString() + { + $server = M::mock('League\OAuth2\Server\AbstractServer'); + + $entity = new StubAbstractTokenEntity($server); + $this->assertEquals('', (string) $entity); + $entity->setId('foobar'); + $this->assertEquals('foobar', (string) $entity); + } +} diff --git a/tests/unit/Entity/AccessTokenEntityTest.php b/tests/unit/Entity/AccessTokenEntityTest.php new file mode 100644 index 00000000..f034d4c5 --- /dev/null +++ b/tests/unit/Entity/AccessTokenEntityTest.php @@ -0,0 +1,59 @@ +shouldReceive('setAccessTokenStorage'); + $server->shouldReceive('setSessionStorage'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('associateScope'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('getByAccessToken')->andReturn( + (new SessionEntity($server)) + ); + $sessionStorage->shouldReceive('setServer'); + + $server->shouldReceive('getStorage')->with('session')->andReturn($sessionStorage); + $server->shouldReceive('getStorage')->with('access_token')->andReturn($accessTokenStorage); + + $server->setAccessTokenStorage($accessTokenStorage); + $server->setSessionStorage($sessionStorage); + + $entity = new AccessTokenEntity($server); + $this->assertTrue($entity->save() instanceof AccessTokenEntity); + } + + public function testExpire() + { + $server = M::mock('League\OAuth2\Server\AbstractServer'); + + $server->shouldReceive('setAccessTokenStorage'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('delete'); + $accessTokenStorage->shouldReceive('setServer'); + + $server->shouldReceive('getStorage')->with('access_token')->andReturn($accessTokenStorage); + + $server->setAccessTokenStorage($accessTokenStorage); + + $entity = new AccessTokenEntity($server); + $this->assertSame($entity->expire(), null); + } +} diff --git a/tests/unit/Entity/AuthCodeEntityTest.php b/tests/unit/Entity/AuthCodeEntityTest.php new file mode 100644 index 00000000..56205e44 --- /dev/null +++ b/tests/unit/Entity/AuthCodeEntityTest.php @@ -0,0 +1,73 @@ +setRedirectUri('http://foo/bar'); + $code->setId('foobar'); + $code->setSession($session); + + $this->assertEquals('http://foo/bar', $code->getRedirectUri()); + $this->assertEquals('http://foo/bar?code=foobar', $code->generateRedirectUri()); + $this->assertTrue($code->getSession() instanceof \League\OAuth2\Server\Entity\SessionEntity); + } + + public function testSave() + { + $server = M::mock('League\OAuth2\Server\AbstractServer'); + $server->shouldReceive('setAuthCodeStorage'); + $server->shouldReceive('setSessionStorage'); + + $authCodeStorage = M::mock('League\OAuth2\Server\Storage\AuthCodeInterface'); + $authCodeStorage->shouldReceive('create'); + $authCodeStorage->shouldReceive('associateScope'); + $authCodeStorage->shouldReceive('setServer'); + $authCodeStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + + $server->shouldReceive('getStorage')->with('auth_code')->andReturn($authCodeStorage); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('getByAuthCode')->andReturn( + (new SessionEntity($server)) + ); + $sessionStorage->shouldReceive('setServer'); + + $server->shouldReceive('getStorage')->with('session')->andReturn($sessionStorage); + + $server->setAuthCodeStorage($authCodeStorage); + $server->setSessionStorage($sessionStorage); + + $entity = new AuthCodeEntity($server); + $this->assertTrue($entity->save() instanceof AuthCodeEntity); + } + + public function testExpire() + { + $server = new AuthorizationServer(); + + $authCodeStorage = M::mock('League\OAuth2\Server\Storage\AuthCodeInterface'); + $authCodeStorage->shouldReceive('delete'); + $authCodeStorage->shouldReceive('setServer'); + + $server->setAuthCodeStorage($authCodeStorage); + + $entity = new AuthCodeEntity($server); + $this->assertSame($entity->expire(), null); + } +} diff --git a/tests/unit/Entity/ClientEntityTest.php b/tests/unit/Entity/ClientEntityTest.php new file mode 100644 index 00000000..7521ad7a --- /dev/null +++ b/tests/unit/Entity/ClientEntityTest.php @@ -0,0 +1,25 @@ +hydrate([ + 'id' => 'foobar', + 'secret' => 'barfoo', + 'name' => 'Test Client', + 'redirectUri' => 'http://foo/bar' + ]); + + $this->assertEquals('foobar', $client->getId()); + $this->assertEquals('barfoo', $client->getSecret()); + $this->assertEquals('Test Client', $client->getName()); + $this->assertEquals('http://foo/bar', $client->getRedirectUri()); + } +} diff --git a/tests/unit/Entity/RefreshTokenEntityTest.php b/tests/unit/Entity/RefreshTokenEntityTest.php new file mode 100644 index 00000000..6a57436d --- /dev/null +++ b/tests/unit/Entity/RefreshTokenEntityTest.php @@ -0,0 +1,94 @@ +setAccessTokenId('foobar'); + + $reflector = new \ReflectionClass($entity); + $accessTokenProperty = $reflector->getProperty('accessTokenId'); + $accessTokenProperty->setAccessible(true); + + $this->assertSame($accessTokenProperty->getValue($entity), 'foobar'); + } + + public function testSetAccessToken() + { + $server = M::mock('League\OAuth2\Server\AbstractServer'); + $entity = new RefreshTokenEntity($server); + $entity->setAccessToken((new AccessTokenEntity($server))); + + $reflector = new \ReflectionClass($entity); + $accessTokenProperty = $reflector->getProperty('accessTokenEntity'); + $accessTokenProperty->setAccessible(true); + + $this->assertTrue($accessTokenProperty->getValue($entity) instanceof AccessTokenEntity); + } + + public function testSave() + { + $server = M::mock('League\OAuth2\Server\AbstractServer'); + $server->shouldReceive('setAccessTokenStorage'); + $server->shouldReceive('setRefreshTokenStorage'); + + $refreshTokenStorage = M::mock('League\OAuth2\Server\Storage\RefreshTokenInterface'); + $refreshTokenStorage->shouldReceive('create'); + $refreshTokenStorage->shouldReceive('setServer'); + $refreshTokenStorage->shouldReceive('associateScope'); + + $server->shouldReceive('getStorage')->with('refresh_token')->andReturn($refreshTokenStorage); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('get')->andReturn( + (new AccessTokenEntity($server))->setId('foobar') + ); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + + $server->shouldReceive('getStorage')->with('access_token')->andReturn($accessTokenStorage); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('getByAccessToken')->andReturn( + (new SessionEntity($server)) + ); + $sessionStorage->shouldReceive('setServer'); + + $server->shouldReceive('getStorage')->with('session')->andReturn($sessionStorage); + + $server->setAccessTokenStorage($accessTokenStorage); + $server->setRefreshTokenStorage($refreshTokenStorage); + + $entity = new RefreshTokenEntity($server); + $this->assertSame(null, $entity->save()); + } + + public function testExpire() + { + $server = M::mock('League\OAuth2\Server\AbstractServer'); + $server->shouldReceive('setRefreshTokenStorage'); + + $refreshTokenStorage = M::mock('League\OAuth2\Server\Storage\RefreshTokenInterface'); + $refreshTokenStorage->shouldReceive('delete'); + $refreshTokenStorage->shouldReceive('setServer'); + + $server->shouldReceive('getStorage')->with('refresh_token')->andReturn($refreshTokenStorage); + + $server->setRefreshTokenStorage($refreshTokenStorage); + + $entity = new RefreshTokenEntity($server); + $this->assertSame($entity->expire(), null); + } +} diff --git a/tests/unit/Entity/ScopeEntityTest.php b/tests/unit/Entity/ScopeEntityTest.php new file mode 100644 index 00000000..a9730089 --- /dev/null +++ b/tests/unit/Entity/ScopeEntityTest.php @@ -0,0 +1,23 @@ +hydrate([ + 'id' => 'foobar', + 'description' => 'barfoo' + ]); + + $this->assertEquals('foobar', $scope->getId()); + $this->assertEquals('barfoo', $scope->getDescription()); + + $this->assertTrue(is_array($scope->jsonSerialize())); + } +} diff --git a/tests/unit/Entity/SessionEntityTest.php b/tests/unit/Entity/SessionEntityTest.php new file mode 100644 index 00000000..e02dae85 --- /dev/null +++ b/tests/unit/Entity/SessionEntityTest.php @@ -0,0 +1,154 @@ +shouldReceive('emit'); + $server = M::mock('League\OAuth2\Server\AbstractServer'); + $server->shouldReceive('setEventEmitter'); + $server->shouldReceive('getEventEmitter')->andReturn($emitter); + $server->setEventEmitter($emitter); + + $entity = new SessionEntity($server); + $entity->setId('foobar'); + $entity->setOwner('user', 123); + $entity->associateAccessToken((new AccessTokenEntity($server))); + $entity->associateRefreshToken((new RefreshTokenEntity($server))); + $entity->associateClient((new ClientEntity($server))); + $entity->associateScope( + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ); + // $entity->associateAuthCode((new AuthCode($server))); + + $this->assertEquals('foobar', $entity->getId()); + $this->assertEquals('user', $entity->getOwnerType()); + $this->assertEquals(123, $entity->getOwnerId()); + $this->assertTrue($entity->getClient() instanceof ClientEntity); + $this->assertTrue($entity->hasScope('foo')); + + $reflector = new \ReflectionClass($entity); + $accessTokenProperty = $reflector->getProperty('accessToken'); + $accessTokenProperty->setAccessible(true); + $refreshTokenProperty = $reflector->getProperty('refreshToken'); + $refreshTokenProperty->setAccessible(true); + + $this->assertTrue($accessTokenProperty->getValue($entity) instanceof AccessTokenEntity); + $this->assertTrue($refreshTokenProperty->getValue($entity) instanceof RefreshTokenEntity); + // $this->assertTrue($reader($entity, 'authCode') instanceof AuthCode); + } + + public function testFormatScopes() + { + $server = M::mock('League\OAuth2\Server\AbstractServer'); + + $entity = new SessionEntity($server); + $reflectedEntity = new \ReflectionClass('League\OAuth2\Server\Entity\SessionEntity'); + $method = $reflectedEntity->getMethod('formatScopes'); + $method->setAccessible(true); + + $scopes = [ + (new ScopeEntity($server))->hydrate(['id' => 'scope1']), + (new ScopeEntity($server))->hydrate(['id' => 'scope2']) + ]; + + $result = $method->invokeArgs($entity, [$scopes]); + + $this->assertTrue(isset($result['scope1'])); + $this->assertTrue(isset($result['scope2'])); + $this->assertTrue($result['scope1'] instanceof ScopeEntity); + $this->assertTrue($result['scope2'] instanceof ScopeEntity); + } + + public function testGetScopes() + { + $server = M::mock('League\OAuth2\Server\AuthorizationServer'); + $server->shouldReceive('setAccessTokenStorage'); + $server->shouldReceive('setSessionStorage'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $server->setAccessTokenStorage($accessTokenStorage); + + $server->shouldReceive('getStorage')->with('access_token')->andReturn($accessTokenStorage); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('getScopes')->andReturn( + [] + ); + $sessionStorage->shouldReceive('setServer'); + $server->setSessionStorage($sessionStorage); + + $server->shouldReceive('getStorage')->with('session')->andReturn($sessionStorage); + + $entity = new SessionEntity($server); + $this->assertEquals($entity->getScopes(), []); + } + + public function testHasScopes() + { + $server = M::mock('League\OAuth2\Server\AuthorizationServer'); + $server->shouldReceive('setAccessTokenStorage'); + $server->shouldReceive('setSessionStorage'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $server->setAccessTokenStorage($accessTokenStorage); + + $server->shouldReceive('getStorage')->with('access_token')->andReturn($accessTokenStorage); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('getScopes')->andReturn( + [] + ); + $sessionStorage->shouldReceive('setServer'); + $server->setSessionStorage($sessionStorage); + + $server->shouldReceive('getStorage')->with('session')->andReturn($sessionStorage); + + $entity = new SessionEntity($server); + $this->assertFalse($entity->hasScope('foo')); + } + + public function testSave() + { + $server = M::mock('League\OAuth2\Server\AuthorizationServer'); + $server->shouldReceive('setSessionStorage'); + $server->shouldReceive('setClientStorage'); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('create'); + $sessionStorage->shouldReceive('associateScope'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + + $server->shouldReceive('getStorage')->with('session')->andReturn($sessionStorage); + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('getBySession')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'foo']) + ); + $clientStorage->shouldReceive('setServer'); + + $server->shouldReceive('getStorage')->with('client')->andReturn($clientStorage); + + $server->setSessionStorage($sessionStorage); + $server->setClientStorage($clientStorage); + + $entity = new SessionEntity($server); + $this->assertEquals(null, $entity->save()); + } +} diff --git a/tests/unit/Exception/OAuthExceptionTest.php b/tests/unit/Exception/OAuthExceptionTest.php new file mode 100644 index 00000000..be2d0e92 --- /dev/null +++ b/tests/unit/Exception/OAuthExceptionTest.php @@ -0,0 +1,25 @@ +httpStatusCode = 400; + $this->assertSame($exception->getHttpHeaders(), ['HTTP/1.1 400 Bad Request']); + + $exception->httpStatusCode = 401; + $this->assertSame($exception->getHttpHeaders(), ['HTTP/1.1 401 Unauthorized']); + + $exception->httpStatusCode = 500; + $this->assertSame($exception->getHttpHeaders(), ['HTTP/1.1 500 Internal Server Error']); + + $exception->httpStatusCode = 501; + $this->assertSame($exception->getHttpHeaders(), ['HTTP/1.1 501 Not Implemented']); + } +} diff --git a/tests/unit/Grant/AbstractGrantTest.php b/tests/unit/Grant/AbstractGrantTest.php new file mode 100644 index 00000000..15168bf0 --- /dev/null +++ b/tests/unit/Grant/AbstractGrantTest.php @@ -0,0 +1,149 @@ +setIdentifier('foobar'); + $grant->setAccessTokenTTL(300); + $grant->setAuthorizationServer($server); + + $this->assertEquals('foobar', $grant->getIdentifier()); + $this->assertEquals('foobar', $grant->getResponseType()); + $this->assertEquals(300, $grant->getAccessTokenTTL()); + $this->assertTrue($grant->getAuthorizationServer() instanceof AuthorizationServer); + } + + public function testFormatScopes() + { + $server = M::mock('League\OAuth2\Server\AbstractServer'); + + $grant = new StubAbstractGrant; + $reflectedGrant = new \ReflectionClass('LeagueTests\Stubs\StubAbstractGrant'); + $method = $reflectedGrant->getMethod('formatScopes'); + $method->setAccessible(true); + + $scopes = [ + (new ScopeEntity($server))->hydrate(['id' => 'scope1', 'description' => 'foo']), + (new ScopeEntity($server))->hydrate(['id' => 'scope2', 'description' => 'bar']) + ]; + + $result = $method->invokeArgs($grant, [$scopes]); + + $this->assertTrue(isset($result['scope1'])); + $this->assertTrue(isset($result['scope2'])); + $this->assertTrue($result['scope1'] instanceof ScopeEntity); + $this->assertTrue($result['scope2'] instanceof ScopeEntity); + } + + public function testValidateScopes() + { + $server = new AuthorizationServer; + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn( + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ); + + $server->setScopeStorage($scopeStorage); + + $grant = new StubAbstractGrant; + $grant->setAuthorizationServer($server); + + $this->assertEquals( + [ + 'foo' => (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ], + $grant->validateScopes('foo') + ); + } + + public function testValidateScopesMissingScope() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + + $server = new AuthorizationServer; + $server->requireScopeParam(true); + $server->setScopeStorage($scopeStorage); + + $grant = new StubAbstractGrant; + $grant->setAuthorizationServer($server); + + $grant->validateScopes(); + } + + public function testValidateScopesInvalidScope() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidScopeException'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn(null); + + $server = new AuthorizationServer; + $server->setScopeStorage($scopeStorage); + + $grant = new StubAbstractGrant; + $grant->setAuthorizationServer($server); + + $grant->validateScopes('blah'); + } + + public function testValidateScopesDefaultScope() + { + $server = new AuthorizationServer; + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn( + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ); + $server->setScopeStorage($scopeStorage); + + $server->requireScopeParam(true); + $server->setScopeStorage($scopeStorage); + $server->setDefaultScope('foo'); + + $grant = new StubAbstractGrant; + $grant->setAuthorizationServer($server); + + $grant->validateScopes(); + } + + public function testValidateScopesDefaultScopeArray() + { + $server = new AuthorizationServer; + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn( + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ); + $server->setScopeStorage($scopeStorage); + + $server->requireScopeParam(true); + $server->setScopeStorage($scopeStorage); + $server->setDefaultScope(['foo', 'bar']); + + $grant = new StubAbstractGrant; + $grant->setAuthorizationServer($server); + + $grant->validateScopes(); + } +} diff --git a/tests/unit/Grant/AuthCodeGrantTest.php b/tests/unit/Grant/AuthCodeGrantTest.php new file mode 100644 index 00000000..c0641438 --- /dev/null +++ b/tests/unit/Grant/AuthCodeGrantTest.php @@ -0,0 +1,620 @@ +setAuthTokenTTL(100); + + $class = new \ReflectionClass($grant); + $property = $class->getProperty('authTokenTTL'); + $property->setAccessible(true); + $this->assertEquals(100, $property->getValue($grant)); + } + + public function testCheckAuthoriseParamsMissingClientId() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_GET = []; + $server = new AuthorizationServer; + + $grant = new AuthCodeGrant; + + $server->addGrantType($grant); + $grant->checkAuthorizeParams(); + + } + + public function testCheckAuthoriseParamsMissingRedirectUri() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $server = new AuthorizationServer; + $_GET = [ + 'client_id' => 'testapp' + ]; + + $grant = new AuthCodeGrant; + + $server->addGrantType($grant); + $grant->checkAuthorizeParams(); + } + + public function testCheckAuthoriseParamsMissingStateParam() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_GET = [ + 'client_id' => 'testapp', + 'redirect_uri' => 'http://foo/bar' + ]; + $server = new AuthorizationServer; + + $grant = new AuthCodeGrant; + $server->requireStateParam(true); + + $server->addGrantType($grant); + $grant->checkAuthorizeParams(); + } + + public function testCheckAuthoriseParamsMissingResponseType() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_GET = [ + 'client_id' => 'testapp', + 'redirect_uri' => 'http://foo/bar' + ]; + $server = new AuthorizationServer; + + $grant = new AuthCodeGrant; + + $server->addGrantType($grant); + $grant->checkAuthorizeParams(); + } + + public function testCheckAuthoriseParamsInvalidResponseType() + { + $this->setExpectedException('League\OAuth2\Server\Exception\UnsupportedResponseTypeException'); + + $_GET = [ + 'client_id' => 'testapp', + 'redirect_uri' => 'http://foo/bar', + 'response_type' => 'foobar' + ]; + $server = new AuthorizationServer; + + $grant = new AuthCodeGrant; + + $server->addGrantType($grant); + $grant->checkAuthorizeParams(); + } + + public function testCheckAuthoriseParamsInvalidClient() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidClientException'); + + $_GET = [ + 'client_id' => 'testapp', + 'redirect_uri' => 'http://foo/bar', + 'response_type' => 'code' + ]; + $server = new AuthorizationServer; + + $grant = new AuthCodeGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn(null); + + $server->setClientStorage($clientStorage); + + $server->addGrantType($grant); + $grant->checkAuthorizeParams(); + } + + public function testCheckAuthoriseParamsInvalidScope() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidScopeException'); + + $_GET = [ + 'response_type' => 'code', + 'client_id' => 'testapp', + 'redirect_uri' => 'http://foo/bar', + 'scope' => 'foo' + ]; + + $server = new AuthorizationServer; + $grant = new AuthCodeGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create'); + $sessionStorage->shouldReceive('getScopes')->andReturn([]); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([]); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn(null); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + + $server->addGrantType($grant); + $grant->checkAuthorizeParams(); + } + + public function testCheckAuthoriseParams() + { + $_GET = [ + 'response_type' => 'code', + 'client_id' => 'testapp', + 'redirect_uri' => 'http://foo/bar', + 'scope' => 'foo' + ]; + + $server = new AuthorizationServer; + $grant = new AuthCodeGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create')->andreturn(123); + $sessionStorage->shouldReceive('getScopes')->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + $sessionStorage->shouldReceive('associateScope'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + $accessTokenStorage->shouldReceive('associateScope'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn( + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + + $server->addGrantType($grant); + + $result = $grant->checkAuthorizeParams(); + + $this->assertTrue($result['client'] instanceof ClientEntity); + $this->assertTrue($result['redirect_uri'] === $_GET['redirect_uri']); + $this->assertTrue($result['state'] === null); + $this->assertTrue($result['response_type'] === 'code'); + $this->assertTrue($result['scopes']['foo'] instanceof ScopeEntity); + } + + public function testNewAuthoriseRequest() + { + $server = new AuthorizationServer; + $client = (new ClientEntity($server))->hydrate(['id' => 'testapp']); + $scope = (new ScopeEntity($server))->hydrate(['id' => 'foo']); + + $grant = new AuthCodeGrant; + $server->addGrantType($grant); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create')->andreturn(123); + $sessionStorage->shouldReceive('getScopes')->shouldReceive('getScopes')->andReturn([$scope]); + $sessionStorage->shouldReceive('associateScope'); + $server->setSessionStorage($sessionStorage); + + $authCodeStorage = M::mock('League\OAuth2\Server\Storage\AuthCodeInterface'); + $authCodeStorage->shouldReceive('setServer'); + $authCodeStorage->shouldReceive('get'); + $authCodeStorage->shouldReceive('create'); + $authCodeStorage->shouldReceive('associateScope'); + $server->setAuthCodeStorage($authCodeStorage); + + $grant->newAuthorizeRequest('user', 123, [ + 'client' => $client, + 'redirect_uri' => 'http://foo/bar', + 'scopes' => [$scope], + 'state' => 'foobar' + ]); + } + + public function testCompleteFlowMissingClientId() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST['grant_type'] = 'authorization_code'; + + $server = new AuthorizationServer; + $grant = new AuthCodeGrant; + + $server->addGrantType($grant); + $server->issueAccessToken(); + + } + + public function testCompleteFlowMissingClientSecret() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST = [ + 'grant_type' => 'authorization_code', + 'client_id' => 'testapp' + ]; + + $server = new AuthorizationServer; + $grant = new AuthCodeGrant; + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowMissingRedirectUri() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST = [ + 'grant_type' => 'authorization_code', + 'client_id' => 'testapp', + 'client_secret' => 'foobar' + ]; + + $server = new AuthorizationServer; + $grant = new AuthCodeGrant; + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowInvalidClient() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidClientException'); + + $_POST = [ + 'grant_type' => 'authorization_code', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'redirect_uri' => 'http://foo/bar' + ]; + + $server = new AuthorizationServer; + $grant = new AuthCodeGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn(null); + + $server->setClientStorage($clientStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowMissingCode() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST = [ + 'grant_type' => 'authorization_code', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'redirect_uri' => 'http://foo/bar' + ]; + + $server = new AuthorizationServer; + $grant = new AuthCodeGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create'); + $sessionStorage->shouldReceive('getScopes')->andReturn([]); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([]); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn(null); + + $authCodeStorage = M::mock('League\OAuth2\Server\Storage\AuthCodeInterface'); + $authCodeStorage->shouldReceive('setServer'); + $authCodeStorage->shouldReceive('get'); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + $server->setAuthCodeStorage($authCodeStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowInvalidCode() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST = [ + 'grant_type' => 'authorization_code', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'redirect_uri' => 'http://foo/bar', + 'code' => 'foobar' + ]; + + $server = new AuthorizationServer; + $grant = new AuthCodeGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create'); + $sessionStorage->shouldReceive('getScopes')->andReturn([]); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([]); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn(null); + + $authCodeStorage = M::mock('League\OAuth2\Server\Storage\AuthCodeInterface'); + $authCodeStorage->shouldReceive('setServer'); + $authCodeStorage->shouldReceive('get'); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + $server->setAuthCodeStorage($authCodeStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowRedirectUriMismatch() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST = [ + 'grant_type' => 'authorization_code', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'redirect_uri' => 'http://foo/bar', + 'code' => 'foobar' + ]; + + $server = new AuthorizationServer; + $grant = new AuthCodeGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create'); + $sessionStorage->shouldReceive('getScopes')->andReturn([]); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([]); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn(null); + + $authCodeStorage = M::mock('League\OAuth2\Server\Storage\AuthCodeInterface'); + $authCodeStorage->shouldReceive('setServer'); + $authCodeStorage->shouldReceive('get')->andReturn( + (new AuthCodeEntity($server))->setId('foobar')->setRedirectUri('http://fail/face') + ); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + $server->setAuthCodeStorage($authCodeStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlow() + { + $_POST = [ + 'grant_type' => 'authorization_code', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'redirect_uri' => 'http://foo/bar', + 'code' => 'foo' + ]; + + $server = new AuthorizationServer; + $grant = new AuthCodeGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('getBySession')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create')->andreturn(123); + $sessionStorage->shouldReceive('associateScope'); + $sessionStorage->shouldReceive('getByAuthCode')->andReturn( + (new SessionEntity($server))->setId('foobar') + ); + $sessionStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('associateScope'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn( + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ); + + $authCodeStorage = M::mock('League\OAuth2\Server\Storage\AuthCodeInterface'); + $authCodeStorage->shouldReceive('setServer'); + $authCodeStorage->shouldReceive('delete'); + $authCodeStorage->shouldReceive('get')->andReturn( + (new AuthCodeEntity($server))->setId('foobar')->setRedirectUri('http://foo/bar') + ); + $authCodeStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + $server->setAuthCodeStorage($authCodeStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowWithRefreshToken() + { + $_POST = [ + 'grant_type' => 'authorization_code', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'redirect_uri' => 'http://foo/bar', + 'code' => 'foo' + ]; + + $server = new AuthorizationServer; + $grant = new AuthCodeGrant; + $rtgrant = new RefreshTokenGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('getBySession')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create')->andreturn(123); + $sessionStorage->shouldReceive('associateScope'); + $sessionStorage->shouldReceive('getByAuthCode')->andReturn( + (new SessionEntity($server))->setId('foobar') + ); + $sessionStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('associateScope'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn( + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ); + + $authCodeStorage = M::mock('League\OAuth2\Server\Storage\AuthCodeInterface'); + $authCodeStorage->shouldReceive('setServer'); + $authCodeStorage->shouldReceive('delete'); + $authCodeStorage->shouldReceive('get')->andReturn( + (new AuthCodeEntity($server))->setId('foobar')->setRedirectUri('http://foo/bar') + ); + $authCodeStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + + $refreshTokenStorage = M::mock('League\OAuth2\Server\Storage\RefreshTokenInterface'); + $refreshTokenStorage->shouldReceive('setServer'); + $refreshTokenStorage->shouldReceive('create'); + $refreshTokenStorage->shouldReceive('associateScope'); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + $server->setAuthCodeStorage($authCodeStorage); + $server->setRefreshTokenStorage($refreshTokenStorage); + + $server->addGrantType($grant); + $server->addGrantType($rtgrant); + $server->issueAccessToken(); + } +} diff --git a/tests/unit/Grant/ClientCredentialsGrantTest.php b/tests/unit/Grant/ClientCredentialsGrantTest.php new file mode 100644 index 00000000..1741cd83 --- /dev/null +++ b/tests/unit/Grant/ClientCredentialsGrantTest.php @@ -0,0 +1,242 @@ +setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST['grant_type'] = 'client_credentials'; + + $server = new AuthorizationServer; + $grant = new ClientCredentialsGrant; + + $server->addGrantType($grant); + $server->issueAccessToken(); + + } + + public function testCompleteFlowMissingClientSecret() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST = [ + 'grant_type' => 'client_credentials', + 'client_id' => 'testapp' + ]; + + $server = new AuthorizationServer; + $grant = new ClientCredentialsGrant; + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowInvalidClient() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidClientException'); + + $_POST = [ + 'grant_type' => 'client_credentials', + 'client_id' => 'testapp', + 'client_secret' => 'foobar' + ]; + + $server = new AuthorizationServer; + $grant = new ClientCredentialsGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn(null); + + $server->setClientStorage($clientStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowInvalidScope() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidScopeException'); + + $_POST = [ + 'grant_type' => 'client_credentials', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'scope' => 'foo' + ]; + + $server = new AuthorizationServer; + $grant = new ClientCredentialsGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create'); + $sessionStorage->shouldReceive('getScopes')->andReturn([]); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([]); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn(null); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowNoScopes() + { + $_POST = [ + 'grant_type' => 'client_credentials', + 'client_id' => 'testapp', + 'client_secret' => 'foobar' + ]; + + $server = new AuthorizationServer; + $grant = new ClientCredentialsGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create')->andreturn(123); + $sessionStorage->shouldReceive('getScopes')->shouldReceive('getScopes')->andReturn([]); + $sessionStorage->shouldReceive('associateScope'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([]); + $accessTokenStorage->shouldReceive('associateScope'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + // $scopeStorage->shouldReceive('get')->andReturn( + // // (new ScopeEntity($server))->hydrate(['id' => 'foo']) + // ); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlow() + { + $_POST = [ + 'grant_type' => 'client_credentials', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'scope' => 'foo' + ]; + + $server = new AuthorizationServer; + $grant = new ClientCredentialsGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create')->andreturn(123); + $sessionStorage->shouldReceive('getScopes')->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + $sessionStorage->shouldReceive('associateScope'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + $accessTokenStorage->shouldReceive('associateScope'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn( + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testClientNotAuthorizedToUseGrant() + { + $this->setExpectedException('\League\OAuth2\Server\Exception\UnauthorizedClientException'); + + $_POST = [ + 'grant_type' => 'client_credentials', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'scope' => 'foo' + ]; + + $server = new AuthorizationServer; + $grant = new ClientCredentialsGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andThrow( + new \League\OAuth2\Server\Exception\UnauthorizedClientException + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn( + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } +} diff --git a/tests/unit/Grant/PasswordGrantTest.php b/tests/unit/Grant/PasswordGrantTest.php new file mode 100644 index 00000000..c3648d10 --- /dev/null +++ b/tests/unit/Grant/PasswordGrantTest.php @@ -0,0 +1,473 @@ +setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST['grant_type'] = 'password'; + + $server = new AuthorizationServer; + $grant = new PasswordGrant; + + $server->addGrantType($grant); + $server->issueAccessToken(); + + } + + public function testCompleteFlowMissingClientSecret() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST = [ + 'grant_type' => 'password', + 'client_id' => 'testapp' + ]; + + $server = new AuthorizationServer; + $grant = new PasswordGrant; + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowInvalidClient() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidClientException'); + + $_POST = [ + 'grant_type' => 'password', + 'client_id' => 'testapp', + 'client_secret' => 'foobar' + ]; + + $server = new AuthorizationServer; + $grant = new PasswordGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn(null); + + $server->setClientStorage($clientStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testNoUsername() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST = [ + 'grant_type' => 'password', + 'client_id' => 'testapp', + 'client_secret' => 'foobar' + ]; + + $server = new AuthorizationServer; + $grant = new PasswordGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create'); + $sessionStorage->shouldReceive('getScopes')->andReturn([]); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([]); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn(null); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testNoPassword() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST = [ + 'grant_type' => 'password', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'username' => 'foo' + ]; + + $server = new AuthorizationServer; + $grant = new PasswordGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create'); + $sessionStorage->shouldReceive('getScopes')->andReturn([]); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([]); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn(null); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testNoCallable() + { + $this->setExpectedException('League\OAuth2\Server\Exception\ServerErrorException'); + + $_POST = [ + 'grant_type' => 'password', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'username' => 'foo', + 'password' => 'foobar' + ]; + + $server = new AuthorizationServer; + $grant = new PasswordGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create'); + $sessionStorage->shouldReceive('getScopes')->andReturn([]); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([]); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn(null); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowInvalidScope() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidScopeException'); + + $_POST = [ + 'grant_type' => 'password', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'username' => 'foo', + 'password' => 'foobar', + 'scope' => 'foo' + ]; + + $server = new AuthorizationServer; + $grant = new PasswordGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create'); + $sessionStorage->shouldReceive('getScopes')->andReturn([]); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([]); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn(null); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + $grant->setVerifyCredentialsCallback(function () { + return 123; + }); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowNoScopes() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST = [ + 'grant_type' => 'password', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'username' => 'username', + 'password' => 'password' + ]; + + $server = new AuthorizationServer; + $grant = new PasswordGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create')->andreturn(123); + $sessionStorage->shouldReceive('getScopes')->shouldReceive('getScopes')->andReturn([]); + $sessionStorage->shouldReceive('associateScope'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([]); + $accessTokenStorage->shouldReceive('associateScope'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + $server->requireScopeParam(true); + $grant->setVerifyCredentialsCallback(function () { + return 123; + }); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowInvalidCredentials() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidCredentialsException'); + + $_POST = [ + 'grant_type' => 'password', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'scope' => 'foo', + 'username' => 'username', + 'password' => 'password' + ]; + + $server = new AuthorizationServer; + $grant = new PasswordGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create')->andreturn(123); + $sessionStorage->shouldReceive('getScopes')->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + $sessionStorage->shouldReceive('associateScope'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + $accessTokenStorage->shouldReceive('associateScope'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn( + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + $grant->setVerifyCredentialsCallback(function () { + return false; + }); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlow() + { + $_POST = [ + 'grant_type' => 'password', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'scope' => 'foo', + 'username' => 'username', + 'password' => 'password' + ]; + + $server = new AuthorizationServer; + $grant = new PasswordGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create')->andreturn(123); + $sessionStorage->shouldReceive('getScopes')->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + $sessionStorage->shouldReceive('associateScope'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + $accessTokenStorage->shouldReceive('associateScope'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn( + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + $grant->setVerifyCredentialsCallback(function () { + return 123; + }); + + $server->addGrantType($grant); + $response = $server->issueAccessToken(); + + $this->assertTrue(array_key_exists('access_token', $response)); + $this->assertTrue(array_key_exists('token_type', $response)); + $this->assertTrue(array_key_exists('expires_in', $response)); + } + + public function testCompleteFlowRefreshToken() + { + $_POST = [ + 'grant_type' => 'password', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'scope' => 'foo', + 'username' => 'username', + 'password' => 'password' + ]; + + $server = new AuthorizationServer; + $grant = new PasswordGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('create')->andreturn(123); + $sessionStorage->shouldReceive('getScopes')->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + $sessionStorage->shouldReceive('associateScope'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + $accessTokenStorage->shouldReceive('associateScope'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn( + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ); + + $refreshTokenStorage = M::mock('League\OAuth2\Server\Storage\RefreshTokenInterface'); + $refreshTokenStorage->shouldReceive('setServer'); + $refreshTokenStorage->shouldReceive('create'); + $refreshTokenStorage->shouldReceive('associateScope'); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + $server->setRefreshTokenStorage($refreshTokenStorage); + + $grant->setVerifyCredentialsCallback(function () { + return 123; + }); + + $server->addGrantType($grant); + $server->addGrantType(new RefreshTokenGrant); + $response = $server->issueAccessToken(); + + $this->assertTrue(array_key_exists('access_token', $response)); + // $this->assertTrue(array_key_exists('refresh_token', $response)); + $this->assertTrue(array_key_exists('token_type', $response)); + $this->assertTrue(array_key_exists('expires_in', $response)); + } +} diff --git a/tests/unit/Grant/RefreshTokenGrantTest.php b/tests/unit/Grant/RefreshTokenGrantTest.php new file mode 100644 index 00000000..d11555b9 --- /dev/null +++ b/tests/unit/Grant/RefreshTokenGrantTest.php @@ -0,0 +1,356 @@ +setRefreshTokenTTL(86400); + + $property = new \ReflectionProperty($grant, 'refreshTokenTTL'); + $property->setAccessible(true); + + $this->assertEquals(86400, $property->getValue($grant)); + } + + public function testCompleteFlowMissingClientId() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST['grant_type'] = 'refresh_token'; + + $server = new AuthorizationServer; + $grant = new RefreshTokenGrant; + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowMissingClientSecret() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST = [ + 'grant_type' => 'refresh_token', + 'client_id' => 'testapp' + ]; + + $server = new AuthorizationServer; + $grant = new RefreshTokenGrant; + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowInvalidClient() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidClientException'); + + $_POST = [ + 'grant_type' => 'refresh_token', + 'client_id' => 'testapp', + 'client_secret' => 'foobar' + ]; + + $server = new AuthorizationServer; + $grant = new RefreshTokenGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn(null); + + $server->setClientStorage($clientStorage); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowMissingRefreshToken() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $_POST = [ + 'grant_type' => 'refresh_token', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + ]; + + $server = new AuthorizationServer; + $grant = new RefreshTokenGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->requireScopeParam(true); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowInvalidRefreshToken() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRefreshException'); + + $_POST = [ + 'grant_type' => 'refresh_token', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'refresh_token' => 'meh' + ]; + + $server = new AuthorizationServer; + $grant = new RefreshTokenGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $refreshTokenStorage = M::mock('League\OAuth2\Server\Storage\RefreshTokenInterface'); + $refreshTokenStorage->shouldReceive('get'); + $refreshTokenStorage->shouldReceive('setServer'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setRefreshTokenStorage($refreshTokenStorage); + $server->requireScopeParam(true); + + $server->addGrantType($grant); + $server->issueAccessToken(); + } + + public function testCompleteFlowExistingScopes() + { + $_POST = [ + 'grant_type' => 'refresh_token', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'refresh_token' => 'refresh_token' + ]; + + $server = new AuthorizationServer; + $grant = new RefreshTokenGrant; + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('getScopes')->shouldReceive('getScopes')->andReturn([]); + $sessionStorage->shouldReceive('associateScope'); + $sessionStorage->shouldReceive('getByAccessToken')->andReturn( + (new SessionEntity($server)) + ); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('get')->andReturn( + (new AccessTokenEntity($server)) + ); + $accessTokenStorage->shouldReceive('delete'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + $accessTokenStorage->shouldReceive('associateScope'); + + $refreshTokenStorage = M::mock('League\OAuth2\Server\Storage\RefreshTokenInterface'); + $refreshTokenStorage->shouldReceive('setServer'); + $refreshTokenStorage->shouldReceive('associateScope'); + $refreshTokenStorage->shouldReceive('delete'); + $refreshTokenStorage->shouldReceive('create'); + $refreshTokenStorage->shouldReceive('get')->andReturn( + (new RefreshTokenEntity($server)) + ); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn( + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + $server->setRefreshTokenStorage($refreshTokenStorage); + + $server->addGrantType($grant); + $response = $server->issueAccessToken(); + + $this->assertTrue(array_key_exists('access_token', $response)); + $this->assertTrue(array_key_exists('refresh_token', $response)); + $this->assertTrue(array_key_exists('token_type', $response)); + $this->assertTrue(array_key_exists('expires_in', $response)); + } + + public function testCompleteFlowRequestScopes() + { + $_POST = [ + 'grant_type' => 'refresh_token', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'refresh_token' => 'refresh_token', + 'scope' => 'foo' + ]; + + $server = new AuthorizationServer; + $grant = new RefreshTokenGrant; + + $oldSession = (new SessionEntity($server))->associateScope((new ScopeEntity($server))->hydrate(['id' => 'foo'])); + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('getScopes')->shouldReceive('getScopes')->andReturn([]); + $sessionStorage->shouldReceive('associateScope'); + $sessionStorage->shouldReceive('getByAccessToken')->andReturn( + $oldSession + ); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('get')->andReturn( + (new AccessTokenEntity($server)) + ); + $accessTokenStorage->shouldReceive('delete'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + $accessTokenStorage->shouldReceive('associateScope'); + + $refreshTokenStorage = M::mock('League\OAuth2\Server\Storage\RefreshTokenInterface'); + $refreshTokenStorage->shouldReceive('setServer'); + $refreshTokenStorage->shouldReceive('associateScope'); + $refreshTokenStorage->shouldReceive('delete'); + $refreshTokenStorage->shouldReceive('create'); + $refreshTokenStorage->shouldReceive('get')->andReturn( + (new RefreshTokenEntity($server)) + ); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn( + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + $server->setRefreshTokenStorage($refreshTokenStorage); + + $server->addGrantType($grant); + $response = $server->issueAccessToken(); + + $this->assertTrue(isset($response['access_token'])); + $this->assertTrue(isset($response['refresh_token'])); + $this->assertTrue(isset($response['token_type'])); + $this->assertTrue(isset($response['expires_in'])); + } + + public function testCompleteFlowRequestScopesInvalid() + { + $_POST = [ + 'grant_type' => 'refresh_token', + 'client_id' => 'testapp', + 'client_secret' => 'foobar', + 'refresh_token' => 'refresh_token', + 'scope' => 'blah' + ]; + + $server = new AuthorizationServer; + $grant = new RefreshTokenGrant; + + $oldSession = (new SessionEntity($server))->associateScope((new ScopeEntity($server))->hydrate(['id' => 'foo'])); + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $clientStorage->shouldReceive('get')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $sessionStorage->shouldReceive('getScopes')->shouldReceive('getScopes')->andReturn([]); + $sessionStorage->shouldReceive('associateScope'); + $sessionStorage->shouldReceive('getByAccessToken')->andReturn( + $oldSession + ); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('get')->andReturn( + (new AccessTokenEntity($server)) + ); + $accessTokenStorage->shouldReceive('delete'); + $accessTokenStorage->shouldReceive('create'); + $accessTokenStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']) + ]); + $accessTokenStorage->shouldReceive('associateScope'); + + $refreshTokenStorage = M::mock('League\OAuth2\Server\Storage\RefreshTokenInterface'); + $refreshTokenStorage->shouldReceive('setServer'); + $refreshTokenStorage->shouldReceive('associateScope'); + $refreshTokenStorage->shouldReceive('delete'); + $refreshTokenStorage->shouldReceive('create'); + $refreshTokenStorage->shouldReceive('get')->andReturn( + (new RefreshTokenEntity($server)) + ); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + $scopeStorage->shouldReceive('get')->andReturn( + (new ScopeEntity($server))->hydrate(['id' => 'blah']) + ); + + $server->setClientStorage($clientStorage); + $server->setScopeStorage($scopeStorage); + $server->setSessionStorage($sessionStorage); + $server->setAccessTokenStorage($accessTokenStorage); + $server->setRefreshTokenStorage($refreshTokenStorage); + + $server->addGrantType($grant); + + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidScopeException'); + + $server->issueAccessToken(); + } +} diff --git a/tests/unit/ResourceServerTest.php b/tests/unit/ResourceServerTest.php new file mode 100644 index 00000000..7fce1600 --- /dev/null +++ b/tests/unit/ResourceServerTest.php @@ -0,0 +1,181 @@ +shouldReceive('setServer'); + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + + $server = new ResourceServer( + $sessionStorage, + $accessTokenStorage, + $clientStorage, + $scopeStorage + ); + + return $server; + } + + public function testGetSet() + { + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + + $server = new ResourceServer( + $sessionStorage, + $accessTokenStorage, + $clientStorage, + $scopeStorage + ); + } + + public function testDetermineAccessTokenMissingToken() + { + $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); + + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('get')->andReturn(false); + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + + $server = new ResourceServer( + $sessionStorage, + $accessTokenStorage, + $clientStorage, + $scopeStorage + ); + + $request = new \Symfony\Component\HttpFoundation\Request(); + $request->headers = new \Symfony\Component\HttpFoundation\ParameterBag([ + 'HTTP_AUTHORIZATION' => 'Bearer' + ]); + $server->setRequest($request); + + $reflector = new \ReflectionClass($server); + $method = $reflector->getMethod('determineAccessToken'); + $method->setAccessible(true); + + $method->invoke($server); + } + + public function testIsValidNotValid() + { + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + $accessTokenStorage->shouldReceive('get')->andReturn(false); + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + + $server = new ResourceServer( + $sessionStorage, + $accessTokenStorage, + $clientStorage, + $scopeStorage + ); + + $this->setExpectedException('League\OAuth2\Server\Exception\AccessDeniedException'); + $server->isValidRequest(false, 'foobar'); + } + + public function testIsValid() + { + $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); + $sessionStorage->shouldReceive('setServer'); + + $accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface'); + $accessTokenStorage->shouldReceive('setServer'); + + $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); + $clientStorage->shouldReceive('setServer'); + + $scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface'); + $scopeStorage->shouldReceive('setServer'); + + $server = new ResourceServer( + $sessionStorage, + $accessTokenStorage, + $clientStorage, + $scopeStorage + ); + + $server->setIdKey('at'); + + $server->addEventListener('session.owner', function($event) { + $this->assertTrue($event->getSession() instanceof \League\OAuth2\Server\Entity\SessionEntity); + }); + + $accessTokenStorage->shouldReceive('get')->andReturn( + (new AccessTokenEntity($server))->setId('abcdef') + ); + + $accessTokenStorage->shouldReceive('getScopes')->andReturn([ + (new ScopeEntity($server))->hydrate(['id' => 'foo']), + (new ScopeEntity($server))->hydrate(['id' => 'bar']) + ]); + + $sessionStorage->shouldReceive('getByAccessToken')->andReturn( + (new SessionEntity($server))->setId('foobar')->setOwner('user', 123) + ); + + $clientStorage->shouldReceive('getBySession')->andReturn( + (new ClientEntity($server))->hydrate(['id' => 'testapp']) + ); + + $request = new \Symfony\Component\HttpFoundation\Request(); + $request->headers = new \Symfony\Component\HttpFoundation\ParameterBag([ + 'Authorization' => 'Bearer abcdef' + ]); + $server->setRequest($request); + + $this->assertTrue($server->isValidRequest()); + $this->assertEquals('at', $server->getIdKey()); + $this->assertEquals(123, $server->getOwnerId()); + $this->assertEquals('user', $server->getOwnerType()); + $this->assertEquals('abcdef', $server->getAccessToken()); + $this->assertEquals('testapp', $server->getClientId()); + $this->assertTrue($server->hasScope('foo')); + $this->assertTrue($server->hasScope('bar')); + $this->assertTrue($server->hasScope(['foo', 'bar'])); + $this->assertTrue(isset($server->getScopes()['foo'])); + $this->assertTrue(isset($server->getScopes()['bar'])); + $this->assertFalse($server->hasScope(['foobar'])); + $this->assertFalse($server->hasScope('foobar')); + } +} diff --git a/tests/unit/Storage/AdapterTest.php b/tests/unit/Storage/AdapterTest.php new file mode 100644 index 00000000..1610b4d0 --- /dev/null +++ b/tests/unit/Storage/AdapterTest.php @@ -0,0 +1,23 @@ +getMethod('setServer'); + $setMethod->setAccessible(true); + $setMethod->invokeArgs($adapter, [new StubAbstractServer]); + $getMethod = $reflector->getMethod('getServer'); + $getMethod->setAccessible(true); + + $this->assertTrue($getMethod->invoke($adapter) instanceof StubAbstractServer); + } +} diff --git a/tests/unit/Stubs/StubAbstractGrant.php b/tests/unit/Stubs/StubAbstractGrant.php new file mode 100644 index 00000000..b409500c --- /dev/null +++ b/tests/unit/Stubs/StubAbstractGrant.php @@ -0,0 +1,23 @@ +accessTokenTTL; + } + + public function getAuthorizationServer() + { + return $this->server; + } +} diff --git a/tests/unit/Stubs/StubAbstractServer.php b/tests/unit/Stubs/StubAbstractServer.php new file mode 100644 index 00000000..70d78d9e --- /dev/null +++ b/tests/unit/Stubs/StubAbstractServer.php @@ -0,0 +1,8 @@ +'bar')); + $v2 = RedirectUri::make('https://foobar/', array('foo'=>'bar'), '#'); + $v3 = 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); + } +} diff --git a/tests/unit/util/SecureKeyTest.php b/tests/unit/util/SecureKeyTest.php new file mode 100644 index 00000000..edfbb538 --- /dev/null +++ b/tests/unit/util/SecureKeyTest.php @@ -0,0 +1,36 @@ +assertEquals(40, strlen($v1)); + $this->assertTrue($v1 !== $v2); + $this->assertEquals(50, strlen($v3)); + } + + public function testGenerateWithDifferentAlgorithm() + { + $algorithm = $this->getMock('League\OAuth2\Server\Util\KeyAlgorithm\KeyAlgorithmInterface'); + + $result = 'dasdsdsaads'; + $algorithm + ->expects($this->once()) + ->method('generate') + ->with(11) + ->will($this->returnValue($result)) + ; + + SecureKey::setAlgorithm($algorithm); + $this->assertSame($algorithm, SecureKey::getAlgorithm()); + $this->assertEquals($result, SecureKey::generate(11)); + } +}