diff --git a/.gitattributes b/.gitattributes index 3819f5cdd..d85a794e7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,13 @@ -tests/ export-ignore -phpunit.xml export-ignore -build.xml export-ignore -test export-ignore -.travis.yml export-ignore \ No newline at end of file +* text=auto + +/examples export-ignore +/tests export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.travis.yml export-ignore +.travis.yml export-ignore +.scrutinizer.yml export-ignore +/phpunit.xml.dist export-ignore +/CHANGELOG.md export-ignore +/CONTRIBUTING.md export-ignore +/README.md export-ignore \ No newline at end of file diff --git a/.gitignore b/.gitignore index 941a7dcf2..897c80b18 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,8 @@ /vendor /composer.lock -/build -/docs -/testing -/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 -/output_*/ -/_site -.idea \ No newline at end of file +phpunit.xml +.idea +/examples/vendor +examples/public.key +examples/private.key +build diff --git a/.styleci.yml b/.styleci.yml new file mode 100644 index 000000000..6caf80c55 --- /dev/null +++ b/.styleci.yml @@ -0,0 +1,53 @@ +preset: psr2 + +enabled: + - binary_operator_spaces + - blank_line_before_return + - concat_with_spaces + - function_typehint_space + - hash_to_slash_comment + - include + - lowercase_cast + - method_separation + - native_function_casing + - no_blank_lines_after_class_opening + - no_blank_lines_between_uses + - no_duplicate_semicolons + - no_leading_import_slash + - no_leading_namespace_whitespace + - no_multiline_whitespace_before_semicolons + - no_php4_constructor + - no_short_bool_cast + - no_singleline_whitespace_before_semicolons + - no_trailing_comma_in_singleline_array + - no_unreachable_default_argument_value + - no_unused_imports + - no_whitespace_before_comma_in_array + - ordered_imports + - phpdoc_align + - phpdoc_indent + - phpdoc_inline_tag + - phpdoc_no_access + - phpdoc_no_simplified_null_return + - phpdoc_order + - phpdoc_property + - phpdoc_scalar + - phpdoc_separation + - phpdoc_to_comment + - phpdoc_trim + - phpdoc_type_to_var + - phpdoc_types + - phpdoc_var_without_name + - print_to_echo + - short_array_syntax + - short_scalar_cast + - simplified_null_return + - single_quote + - spaces_cast + - standardize_not_equal + - ternary_operator_spaces + - trailing_comma_in_multiline_array + - trim_array_spaces + - unary_operator_spaces + - whitespace_after_comma_in_array + - whitespacy_lines diff --git a/.travis.yml b/.travis.yml index 5f97eaf95..eda7549ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,20 +7,18 @@ cache: - vendor php: - - 5.4 + - 5.5.9 - 5.5 - 5.6 - 7.0 - hhvm - -matrix: - allow_failures: - - php: 7.0 - fast_finish: true install: - travis_retry composer install --no-interaction --prefer-source script: - - mkdir -p build/logs - - phpunit --coverage-text --verbose --coverage-clover=coverage.clover --coverage-html coverage \ No newline at end of file + - vendor/bin/phpunit + +branches: + only: + - master \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f9843d2b..4d2cba7af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,81 @@ # Changelog +## 5.0.0 (release 2016-04-17) + +Version 5 is a complete code rewrite. + +* JWT support +* PSR-7 support +* Improved exception errors +* Replace all occurrences of the term "Storage" with "Repository" +* Simplify repositories +* Entities conform to interfaces and use traits +* Auth code grant updated + * Allow support for public clients + * Add support for #439 +* Client credentials grant updated +* Password grant updated + * Allow support for public clients +* Refresh token grant updated +* Implement Implicit grant +* Bearer token output type +* Remove MAC token output type +* Authorization server rewrite +* Resource server class moved to PSR-7 middleware +* Tests +* Much much better documentation + +Changes since RC2: + +* Renamed Server class to AuthorizationServer +* Added ResourceServer class +* Run unit tests again PHP 5.5.9 as it's the minimum supported version +* Enable PHPUnit 5.0 support +* Improved examples and documentation +* Make it clearer that the implicit grant doesn't support refresh tokens +* Improved refresh token validation errors +* Fixed refresh token expiry date + +## 5.0.0-RC2 (released 2016-04-10) + +Changes since RC1: + +* Allow multiple client redirect URIs (Issue #511) +* Remove unused mac token interface (Issue #503) +* Handle RSA key passphrase (Issue #502) +* Remove access token repository from response types (Issue #501) +* Remove unnecessary methods from entity interfaces (Issue #490) +* Ensure incoming JWT hasn't expired (Issue #509) +* Fix client identifier passed where user identifier is expected (Issue #498) +* Removed built-in entities; added traits to for quick re-use (Issue #504) +* Redirect uri is required only if the "redirect_uri" parameter was included in the authorization request (Issue #514) +* Removed templating for auth code and implicit grants (Issue #499) + +## 5.0.0-RC1 (release 2016-03-24) + +Version 5 is a complete code rewrite. + +* JWT support +* PSR-7 support +* Improved exception errors +* Replace all occurrences of the term "Storage" with "Repository" +* Simplify repositories +* Entities conform to interfaces and use traits +* Auth code grant updated + * Allow support for public clients + * Add support for #439 +* Client credentials grant updated +* Password grant updated + * Allow support for public clients +* Refresh token grant updated +* Implement Implicit grant +* Bearer token output type +* Remove MAC token output type +* Authorization server rewrite +* Resource server class moved to PSR-7 middleware +* Tests +* Much much better documentation + ## 4.1.5 (released 2016-01-04) * Enable Symfony 3.0 support (#412) @@ -159,7 +235,7 @@ * Included a PDO driver which implements the storage interfaces so the library is more "get up and go" * Further normalised the database structure so all sessions no longer contain infomation related to authorization grant (which may or may not be enabled) * A session can have multiple associated access tokens -* Induvidual grants can have custom expire times for access tokens +* Individual grants can have custom expire times for access tokens * Authorization codes now have a TTL of 10 minutes by default (can be manually set) * Refresh tokens now have a TTL of one week by default (can be manually set) * The client credentials grant will no longer gives out refresh tokens as per the specification diff --git a/CONDUCT.md b/CONDUCT.md new file mode 100644 index 000000000..ea00abfca --- /dev/null +++ b/CONDUCT.md @@ -0,0 +1,22 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery +* Personal attacks +* Trolling or insulting/derogatory comments +* Public or private harassment +* Publishing other's private information, such as physical or electronic addresses, without explicit permission +* Other unethical or unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. + +This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community in a direct capacity. Personal views, beliefs and values of individuals do not necessarily reflect those of the organisation or affiliated individuals and organisations. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. + +This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) \ No newline at end of file diff --git a/license.txt b/LICENSE similarity index 100% rename from license.txt rename to LICENSE diff --git a/README.md b/README.md index e153dcd62..3e4157ab4 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,39 @@ -# PHP OAuth 2.0 Server by [@alexbilbie](https://twitter.com/alexbilbie) +# PHP OAuth 2.0 Server [![Latest Version](http://img.shields.io/packagist/v/league/oauth2-server.svg?style=flat-square)](https://github.com/thephpleague/oauth2-server/releases) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) [![Build Status](https://img.shields.io/travis/thephpleague/oauth2-server/master.svg?style=flat-square)](https://travis-ci.org/thephpleague/oauth2-server) [![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/thephpleague/oauth2-server.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth2-server/code-structure) [![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/oauth2-server.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth2-server) -[![Total Downloads](https://img.shields.io/packagist/dt/league/oauth2-server.svg?style=flat-square)](https://packagist.org/packages/league/oauth2-server) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/thephpleague/oauth2-server?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +[![Total Downloads](https://img.shields.io/packagist/dt/league/oauth2-server.svg?style=flat-square)](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 which makes working with OAuth 2.0 trivial. You can easily configure an OAuth 2.0 server to protect your API with access tokens, or allow clients to request new access tokens and refresh them. +`league/oauth2-server` is a a standards compliant implementation of an [OAuth 2.0](https://tools.ietf.org/html/rfc6749) authorization server written in PHP which makes working with OAuth 2.0 trivial. You can easily configure an OAuth 2.0 server to protect your API with access tokens, or allow clients to request new access tokens and refresh them. It supports out of the box the following grants: * Authorization code grant +* Implicit grant * Client credentials grant * Resource owner password credentials grant * Refresh grant -You can also define your own grants. - -In addition it supports the following token types: - -* Bearer tokens -* MAC tokens -* JSON web tokens (coming soon) - -You can also create you own tokens. - +This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](https://twitter.com/alexbilbie). ## Requirements The following versions of PHP are supported: -* PHP 5.4 -* PHP 5.5 +* PHP 5.5 (>=5.5.9) * PHP 5.6 +* PHP 7.0 * HHVM -## Documentation +The `openssl` extension is also required. -This library has [full documentation](http://oauth2.thephpleague.com), powered by [Jekyll](http://jekyllrb.com/). +## Documentation -Contribute to this documentation in the [gh-pages branch](https://github.com/thephpleague/oauth2-server/tree/gh-pages/). +The library documentation can be found at [https://oauth2.thephpleague.com](https://oauth2.thephpleague.com). +You can contribute to the documentation in the [gh-pages branch](https://github.com/thephpleague/oauth2-server/tree/gh-pages/). ## Changelog @@ -49,7 +41,7 @@ Contribute to this documentation in the [gh-pages branch](https://github.com/the ## Contributing -Please see [CONTRIBUTING](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) for details. +Please see [CONTRIBUTING.md](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) and [CONDUCT.md](https://github.com/thephpleague/oauth2-server/blob/master/CONDUCT.md) for details. ## Integration @@ -58,7 +50,9 @@ Please see [CONTRIBUTING](https://github.com/thephpleague/oauth2-server/blob/mas ## 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/thephpleague/oauth2-server/issues). + +If you have any questions about OAuth _please_ open a ticket here; please **don't** email the address below. ## Security @@ -72,12 +66,6 @@ This package is released under the MIT License. See the bundled [LICENSE](https: This code is principally developed and maintained by [Alex Bilbie](https://twitter.com/alexbilbie). -Special thanks to: - -* [Dan Horrigan](https://github.com/dandoescode) -* [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) +Special thanks to [all of these awesome contributors](https://github.com/thephpleague/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. diff --git a/composer.json b/composer.json index 62a01ec98..de38ee810 100644 --- a/composer.json +++ b/composer.json @@ -1,59 +1,67 @@ { - "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.", - "homepage": "http://oauth2.thephpleague.com/", - "license": "MIT", - "require": { - "php": ">=5.4.0", - "symfony/http-foundation": "~2.4|~3.0", - "league/event": "~2.1" - }, - "require-dev": { - "phpunit/phpunit": "4.3.*", - "mockery/mockery": "0.9.*" - }, - "repositories": [ - { - "type": "git", - "url": "https://github.com/thephpleague/oauth2-server.git" - } - ], - "keywords": [ - "oauth", - "oauth2", - "oauth 2", - "oauth 2.0", - "server", - "auth", - "authorization", - "authorisation", - "authentication", - "resource", - "api", - "auth", - "protect", - "secure" - ], - "authors": [ - { - "name": "Alex Bilbie", - "email": "hello@alexbilbie.com", - "homepage": "http://www.alexbilbie.com", - "role": "Developer" - } - ], - "replace": { - "lncd/oauth2": "*", - "league/oauth2server": "*" - }, - "autoload": { - "psr-4": { - "League\\OAuth2\\Server\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "LeagueTests\\": "tests/unit/" - } - } + "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.", + "homepage": "https://oauth2.thephpleague.com/", + "license": "MIT", + "require": { + "php": ">=5.5.9", + "ext-openssl": "*", + "league/event": "^2.1", + "lcobucci/jwt": "^3.1", + "paragonie/random_compat": "^1.1", + "psr/http-message": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8 || ^5.0", + "zendframework/zend-diactoros": "^1.0" + }, + "repositories": [ + { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-server.git" + } + ], + "keywords": [ + "oauth", + "oauth2", + "oauth 2", + "oauth 2.0", + "server", + "auth", + "authorization", + "authorisation", + "authentication", + "resource", + "api", + "auth", + "protect", + "secure" + ], + "authors": [ + { + "name": "Alex Bilbie", + "email": "hello@alexbilbie.com", + "homepage": "http://www.alexbilbie.com", + "role": "Developer" + } + ], + "replace": { + "lncd/oauth2": "*", + "league/oauth2server": "*" + }, + "autoload": { + "psr-4": { + "League\\OAuth2\\Server\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "LeagueTests\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-V5-WIP": "5.0-dev" + } + } } diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..bc785ee6b --- /dev/null +++ b/examples/README.md @@ -0,0 +1,53 @@ +# Example implementations + +## Installation + +0. Run `composer install` in this directory to install dependencies +0. Create a private key `openssl genrsa -out private.key 1024` +0. Create a public key `openssl rsa -in private.key -pubout > public.key` +0. `cd` into the public directory +0. Start a PHP server `php -S localhost:4444` + +## Testing the client credentials grant example + +Send the following cURL request: + +``` +curl -X "POST" "http://localhost:4444/client_credentials.php/access_token" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -H "Accept: 1.0" \ + --data-urlencode "grant_type=client_credentials" \ + --data-urlencode "client_id=myawesomeapp" \ + --data-urlencode "client_secret=abc123" \ + --data-urlencode "scope=basic email" +``` + +## Testing the password grant example + +Send the following cURL request: + +``` +curl -X "POST" "http://localhost:4444/password.php/access_token" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -H "Accept: 1.0" \ + --data-urlencode "grant_type=password" \ + --data-urlencode "client_id=myawesomeapp" \ + --data-urlencode "client_secret=abc123" \ + --data-urlencode "username=alex" \ + --data-urlencode "password=whisky" \ + --data-urlencode "scope=basic email" +``` + +## Testing the refresh token grant example + +Send the following cURL request. Replace `{{REFRESH_TOKEN}}` with a refresh token from another grant above: + +``` +curl -X "POST" "http://localhost:4444/refresh_token.php/access_token" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -H "Accept: 1.0" \ + --data-urlencode "grant_type=refresh_token" \ + --data-urlencode "client_id=myawesomeapp" \ + --data-urlencode "client_secret=abc123" \ + --data-urlencode "refresh_token={{REFRESH_TOKEN}}" +``` diff --git a/examples/composer.json b/examples/composer.json new file mode 100644 index 000000000..3c6e550b8 --- /dev/null +++ b/examples/composer.json @@ -0,0 +1,17 @@ +{ + "require": { + "slim/slim": "3.0.*" + }, + "require-dev": { + "league/event": "^2.1", + "lcobucci/jwt": "^3.1", + "paragonie/random_compat": "^1.1", + "psr/http-message": "^1.0" + }, + "autoload": { + "psr-4": { + "OAuth2ServerExamples\\": "src/", + "League\\OAuth2\\Server\\": "../src/" + } + } +} diff --git a/examples/composer.lock b/examples/composer.lock new file mode 100644 index 000000000..9c6c83cba --- /dev/null +++ b/examples/composer.lock @@ -0,0 +1,407 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "48bcb7a3514d7c7f271c554ba1440124", + "content-hash": "e41be75973527cb9d63f27ad14ac8624", + "packages": [ + { + "name": "container-interop/container-interop", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/container-interop/container-interop.git", + "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e", + "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Interop\\Container\\": "src/Interop/Container/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", + "time": "2014-12-30 15:22:37" + }, + { + "name": "nikic/fast-route", + "version": "v0.6.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/FastRoute.git", + "reference": "31fa86924556b80735f98b294a7ffdfb26789f22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/31fa86924556b80735f98b294a7ffdfb26789f22", + "reference": "31fa86924556b80735f98b294a7ffdfb26789f22", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "FastRoute\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Fast request router for PHP", + "keywords": [ + "router", + "routing" + ], + "time": "2015-06-18 19:15:47" + }, + { + "name": "pimple/pimple", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/silexphp/Pimple.git", + "reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a30f7d6e57565a2e1a316e1baf2a483f788b258a", + "reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Pimple": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Pimple, a simple Dependency Injection Container", + "homepage": "http://pimple.sensiolabs.org", + "keywords": [ + "container", + "dependency injection" + ], + "time": "2015-09-11 15:10:35" + }, + { + "name": "psr/http-message", + "version": "1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", + "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2015-05-04 20:22:00" + }, + { + "name": "slim/slim", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Slim.git", + "reference": "3b06f0f2d84dabbe81b6cea46ace46a3e883253e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/3b06f0f2d84dabbe81b6cea46ace46a3e883253e", + "reference": "3b06f0f2d84dabbe81b6cea46ace46a3e883253e", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.1", + "nikic/fast-route": "^0.6", + "php": ">=5.5.0", + "pimple/pimple": "^3.0", + "psr/http-message": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\": "Slim" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "https://joshlockhart.com" + }, + { + "name": "Gabriel Manricks", + "email": "gmanricks@me.com", + "homepage": "http://gabrielmanricks.com" + }, + { + "name": "Andrew Smith", + "email": "a.smith@silentworks.co.uk", + "homepage": "http://silentworks.co.uk" + } + ], + "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs", + "homepage": "http://slimframework.com", + "keywords": [ + "api", + "framework", + "micro", + "router" + ], + "time": "2015-12-07 14:11:09" + } + ], + "packages-dev": [ + { + "name": "lcobucci/jwt", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/jwt.git", + "reference": "afea8e682e911a21574fd8519321b32522fa25b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/afea8e682e911a21574fd8519321b32522fa25b5", + "reference": "afea8e682e911a21574fd8519321b32522fa25b5", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">=5.5" + }, + "require-dev": { + "mdanter/ecc": "~0.3", + "mikey179/vfsstream": "~1.5", + "phpmd/phpmd": "~2.2", + "phpunit/php-invoker": "~1.1", + "phpunit/phpunit": "~4.5", + "squizlabs/php_codesniffer": "~2.3" + }, + "suggest": { + "mdanter/ecc": "Required to use Elliptic Curves based algorithms." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Lcobucci\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Luís Otávio Cobucci Oblonczyk", + "email": "lcobucci@gmail.com", + "role": "Developer" + } + ], + "description": "A simple library to work with JSON Web Token and JSON Web Signature", + "keywords": [ + "JWS", + "jwt" + ], + "time": "2016-03-24 22:46:13" + }, + { + "name": "league/event", + "version": "2.1.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/event.git", + "reference": "e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/event/zipball/e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd", + "reference": "e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "~1.0.1", + "phpspec/phpspec": "~2.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Event\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Event package", + "keywords": [ + "emitter", + "event", + "listener" + ], + "time": "2015-05-21 12:24:47" + }, + { + "name": "paragonie/random_compat", + "version": "v1.4.1", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "c7e26a21ba357863de030f0b9e701c7d04593774" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/c7e26a21ba357863de030f0b9e701c7d04593774", + "reference": "c7e26a21ba357863de030f0b9e701c7d04593774", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2016-03-18 20:34:03" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/examples/public/api.php b/examples/public/api.php new file mode 100644 index 000000000..2df5d1cf2 --- /dev/null +++ b/examples/public/api.php @@ -0,0 +1,78 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +use League\OAuth2\Server\ResourceServer; +use OAuth2ServerExamples\Repositories\AccessTokenRepository; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Slim\App; + +include __DIR__ . '/../vendor/autoload.php'; + +$app = new App([ + 'settings' => [ + 'displayErrorDetails' => true, + ], + ResourceServer::class => function () { + // Setup the authorization server + $server = new ResourceServer( + new AccessTokenRepository(), + 'file://' . __DIR__ . '/../public.key' + ); + + return $server; + }, +]); + +$app->add( + new \League\OAuth2\Server\Middleware\ResourceServerMiddleware( + $app->getContainer()->get(ResourceServer::class) + ) +); + +$app->get('/users', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) { + + $users = [ + [ + 'id' => 123, + 'name' => 'Alex', + 'email' => 'alex@thephpleague.com', + ], + [ + 'id' => 124, + 'name' => 'Frank', + 'email' => 'frank@thephpleague.com', + ], + [ + 'id' => 125, + 'name' => 'Phil', + 'email' => 'phil@thephpleague.com', + ], + ]; + + // If the access token doesn't have the `basic` scope hide users' names + if (in_array('basic', $request->getAttribute('oauth_scopes')) === false) { + for ($i = 0; $i < count($users); $i++) { + unset($users[$i]['name']); + } + } + + // If the access token doesn't have the `emal` scope hide users' email addresses + if (in_array('email', $request->getAttribute('oauth_scopes')) === false) { + for ($i = 0; $i < count($users); $i++) { + unset($users[$i]['email']); + } + } + + $response->getBody()->write(json_encode($users)); + + return $response->withStatus(200); +}); + +$app->run(); diff --git a/examples/public/auth_code.php b/examples/public/auth_code.php new file mode 100644 index 000000000..e014f55a2 --- /dev/null +++ b/examples/public/auth_code.php @@ -0,0 +1,108 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +use League\OAuth2\Server\AuthorizationServer; +use League\OAuth2\Server\Exception\OAuthServerException; +use League\OAuth2\Server\Grant\AuthCodeGrant; +use OAuth2ServerExamples\Entities\UserEntity; +use OAuth2ServerExamples\Repositories\AccessTokenRepository; +use OAuth2ServerExamples\Repositories\AuthCodeRepository; +use OAuth2ServerExamples\Repositories\ClientRepository; +use OAuth2ServerExamples\Repositories\RefreshTokenRepository; +use OAuth2ServerExamples\Repositories\ScopeRepository; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Slim\App; +use Zend\Diactoros\Stream; + +include __DIR__ . '/../vendor/autoload.php'; + +$app = new App([ + 'settings' => [ + 'displayErrorDetails' => true, + ], + AuthorizationServer::class => function () { + // Init our repositories + $clientRepository = new ClientRepository(); + $scopeRepository = new ScopeRepository(); + $accessTokenRepository = new AccessTokenRepository(); + $authCodeRepository = new AuthCodeRepository(); + $refreshTokenRepository = new RefreshTokenRepository(); + + $privateKeyPath = 'file://' . __DIR__ . '/../private.key'; + $publicKeyPath = 'file://' . __DIR__ . '/../public.key'; + + // Setup the authorization server + $server = new AuthorizationServer( + $clientRepository, + $accessTokenRepository, + $scopeRepository, + $privateKeyPath, + $publicKeyPath + ); + + // Enable the authentication code grant on the server with a token TTL of 1 hour + $server->enableGrantType( + new AuthCodeGrant( + $authCodeRepository, + $refreshTokenRepository, + new \DateInterval('PT10M') + ), + new \DateInterval('PT1H') + ); + + return $server; + }, +]); + +$app->get('/authorize', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) { + /* @var \League\OAuth2\Server\AuthorizationServer $server */ + $server = $app->getContainer()->get(AuthorizationServer::class); + + try { + // Validate the HTTP request and return an AuthorizationRequest object. + // The auth request object can be serialized into a user's session + $authRequest = $server->validateAuthorizationRequest($request); + + // Once the user has logged in set the user on the AuthorizationRequest + $authRequest->setUser(new UserEntity()); + + // Once the user has approved or denied the client update the status + // (true = approved, false = denied) + $authRequest->setAuthorizationApproved(true); + + // Return the HTTP redirect response + return $server->completeAuthorizationRequest($authRequest, $response); + } catch (OAuthServerException $exception) { + return $exception->generateHttpResponse($response); + } catch (\Exception $exception) { + $body = new Stream('php://temp', 'r+'); + $body->write($exception->getMessage()); + + return $response->withStatus(500)->withBody($body); + } +}); + +$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) { + /* @var \League\OAuth2\Server\AuthorizationServer $server */ + $server = $app->getContainer()->get(AuthorizationServer::class); + + try { + return $server->respondToAccessTokenRequest($request, $response); + } catch (OAuthServerException $exception) { + return $exception->generateHttpResponse($response); + } catch (\Exception $exception) { + $body = new Stream('php://temp', 'r+'); + $body->write($exception->getMessage()); + + return $response->withStatus(500)->withBody($body); + } +}); + +$app->run(); diff --git a/examples/public/client_credentials.php b/examples/public/client_credentials.php new file mode 100644 index 000000000..0825b61a4 --- /dev/null +++ b/examples/public/client_credentials.php @@ -0,0 +1,79 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +use League\OAuth2\Server\AuthorizationServer; +use League\OAuth2\Server\Exception\OAuthServerException; +use OAuth2ServerExamples\Repositories\AccessTokenRepository; +use OAuth2ServerExamples\Repositories\ClientRepository; +use OAuth2ServerExamples\Repositories\ScopeRepository; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Slim\App; +use Zend\Diactoros\Stream; + +include __DIR__ . '/../vendor/autoload.php'; + +$app = new App([ + 'settings' => [ + 'displayErrorDetails' => true, + ], + AuthorizationServer::class => function () { + // Init our repositories + $clientRepository = new ClientRepository(); // instance of ClientRepositoryInterface + $scopeRepository = new ScopeRepository(); // instance of ScopeRepositoryInterface + $accessTokenRepository = new AccessTokenRepository(); // instance of AccessTokenRepositoryInterface + + // Path to public and private keys + $privateKey = 'file://path/to/private.key'; + //$privateKey = new CryptKey('file://path/to/private.key', 'passphrase'); // if private key has a pass phrase + $publicKey = 'file://path/to/public.key'; + + // Setup the authorization server + $server = new AuthorizationServer( + $clientRepository, + $accessTokenRepository, + $scopeRepository, + $privateKey, + $publicKey + ); + + // Enable the client credentials grant on the server + $server->enableGrantType( + new \League\OAuth2\Server\Grant\ClientCredentialsGrant(), + new \DateInterval('PT1H') // access tokens will expire after 1 hour + ); + + return $server; + }, +]); + +$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) { + + /* @var \League\OAuth2\Server\AuthorizationServer $server */ + $server = $app->getContainer()->get(AuthorizationServer::class); + + try { + + // Try to respond to the request + return $server->respondToAccessTokenRequest($request, $response); + } catch (OAuthServerException $exception) { + + // All instances of OAuthServerException can be formatted into a HTTP response + return $exception->generateHttpResponse($response); + } catch (\Exception $exception) { + + // Unknown exception + $body = new Stream('php://temp', 'r+'); + $body->write($exception->getMessage()); + + return $response->withStatus(500)->withBody($body); + } +}); + +$app->run(); diff --git a/examples/public/implicit.php b/examples/public/implicit.php new file mode 100644 index 000000000..2a82097fd --- /dev/null +++ b/examples/public/implicit.php @@ -0,0 +1,81 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +use League\OAuth2\Server\AuthorizationServer; +use League\OAuth2\Server\Exception\OAuthServerException; +use League\OAuth2\Server\Grant\ImplicitGrant; +use OAuth2ServerExamples\Entities\UserEntity; +use OAuth2ServerExamples\Repositories\AccessTokenRepository; +use OAuth2ServerExamples\Repositories\ClientRepository; +use OAuth2ServerExamples\Repositories\ScopeRepository; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Slim\App; +use Zend\Diactoros\Stream; + +include __DIR__ . '/../vendor/autoload.php'; + +$app = new App([ + 'settings' => [ + 'displayErrorDetails' => true, + ], + AuthorizationServer::class => function () { + // Init our repositories + $clientRepository = new ClientRepository(); + $scopeRepository = new ScopeRepository(); + $accessTokenRepository = new AccessTokenRepository(); + + $privateKeyPath = 'file://' . __DIR__ . '/../private.key'; + $publicKeyPath = 'file://' . __DIR__ . '/../public.key'; + + // Setup the authorization server + $server = new AuthorizationServer( + $clientRepository, + $accessTokenRepository, + $scopeRepository, + $privateKeyPath, + $publicKeyPath + ); + + // Enable the implicit grant on the server with a token TTL of 1 hour + $server->enableGrantType(new ImplicitGrant(new \DateInterval('PT1H'))); + + return $server; + }, +]); + +$app->get('/authorize', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) { + /* @var \League\OAuth2\Server\AuthorizationServer $server */ + $server = $app->getContainer()->get(AuthorizationServer::class); + + try { + // Validate the HTTP request and return an AuthorizationRequest object. + // The auth request object can be serialized into a user's session + $authRequest = $server->validateAuthorizationRequest($request); + + // Once the user has logged in set the user on the AuthorizationRequest + $authRequest->setUser(new UserEntity()); + + // Once the user has approved or denied the client update the status + // (true = approved, false = denied) + $authRequest->setAuthorizationApproved(true); + + // Return the HTTP redirect response + return $server->completeAuthorizationRequest($authRequest, $response); + } catch (OAuthServerException $exception) { + return $exception->generateHttpResponse($response); + } catch (\Exception $exception) { + $body = new Stream('php://temp', 'r+'); + $body->write($exception->getMessage()); + + return $response->withStatus(500)->withBody($body); + } +}); + +$app->run(); diff --git a/examples/public/middleware_use.php b/examples/public/middleware_use.php new file mode 100644 index 000000000..2dfe18341 --- /dev/null +++ b/examples/public/middleware_use.php @@ -0,0 +1,99 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +use League\OAuth2\Server\AuthorizationServer; +use League\OAuth2\Server\Grant\AuthCodeGrant; +use League\OAuth2\Server\Grant\RefreshTokenGrant; +use League\OAuth2\Server\Middleware\AuthorizationServerMiddleware; +use League\OAuth2\Server\Middleware\ResourceServerMiddleware; +use OAuth2ServerExamples\Repositories\AccessTokenRepository; +use OAuth2ServerExamples\Repositories\AuthCodeRepository; +use OAuth2ServerExamples\Repositories\ClientRepository; +use OAuth2ServerExamples\Repositories\RefreshTokenRepository; +use OAuth2ServerExamples\Repositories\ScopeRepository; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Slim\App; +use Zend\Diactoros\Stream; + +include __DIR__ . '/../vendor/autoload.php'; + +$app = new App([ + 'settings' => [ + 'displayErrorDetails' => true, + ], + AuthorizationServer::class => function () { + // Init our repositories + $clientRepository = new ClientRepository(); + $accessTokenRepository = new AccessTokenRepository(); + $scopeRepository = new ScopeRepository(); + $authCodeRepository = new AuthCodeRepository(); + $refreshTokenRepository = new RefreshTokenRepository(); + + $privateKeyPath = 'file://' . __DIR__ . '/../private.key'; + $publicKeyPath = 'file://' . __DIR__ . '/../public.key'; + + // Setup the authorization server + $server = new AuthorizationServer( + $clientRepository, + $accessTokenRepository, + $scopeRepository, + $privateKeyPath, + $publicKeyPath + ); + + // Enable the authentication code grant on the server with a token TTL of 1 hour + $server->enableGrantType( + new AuthCodeGrant( + $authCodeRepository, + $refreshTokenRepository, + new \DateInterval('PT10M') + ), + new \DateInterval('PT1H') + ); + + // Enable the refresh token grant on the server with a token TTL of 1 month + $server->enableGrantType( + new RefreshTokenGrant($refreshTokenRepository), + new \DateInterval('PT1M') + ); + + return $server; + }, +]); + +// Access token issuer +$app->post('/access_token', function () { +})->add(new AuthorizationServerMiddleware($app->getContainer()->get(AuthorizationServer::class))); + +// Secured API +$app->group('/api', function () { + $this->get('/user', function (ServerRequestInterface $request, ResponseInterface $response) { + $params = []; + + if (in_array('basic', $request->getAttribute('oauth_scopes', []))) { + $params = [ + 'id' => 1, + 'name' => 'Alex', + 'city' => 'London', + ]; + } + + if (in_array('email', $request->getAttribute('oauth_scopes', []))) { + $params['email'] = 'alex@example.com'; + } + + $body = new Stream('php://temp', 'r+'); + $body->write(json_encode($params)); + + return $response->withBody($body); + }); +})->add(new ResourceServerMiddleware($app->getContainer()->get(AuthorizationServer::class))); + +$app->run(); diff --git a/examples/public/password.php b/examples/public/password.php new file mode 100644 index 000000000..18eb6ddf5 --- /dev/null +++ b/examples/public/password.php @@ -0,0 +1,75 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +use League\OAuth2\Server\AuthorizationServer; +use League\OAuth2\Server\Exception\OAuthServerException; +use League\OAuth2\Server\Grant\PasswordGrant; +use OAuth2ServerExamples\Repositories\AccessTokenRepository; +use OAuth2ServerExamples\Repositories\ClientRepository; +use OAuth2ServerExamples\Repositories\RefreshTokenRepository; +use OAuth2ServerExamples\Repositories\ScopeRepository; +use OAuth2ServerExamples\Repositories\UserRepository; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Slim\App; +use Zend\Diactoros\Stream; + +include __DIR__ . '/../vendor/autoload.php'; + +$app = new App([ + 'settings' => [ + 'displayErrorDetails' => true, + ], + AuthorizationServer::class => function () { + // Init our repositories + $clientRepository = new ClientRepository(); + $accessTokenRepository = new AccessTokenRepository(); + $scopeRepository = new ScopeRepository(); + $userRepository = new UserRepository(); + $refreshTokenRepository = new RefreshTokenRepository(); + + $privateKeyPath = 'file://' . __DIR__ . '/../private.key'; + $publicKeyPath = 'file://' . __DIR__ . '/../public.key'; + + // Setup the authorization server + $server = new AuthorizationServer( + $clientRepository, + $accessTokenRepository, + $scopeRepository, + $privateKeyPath, + $publicKeyPath + ); + + // Enable the password grant on the server with a token TTL of 1 hour + $server->enableGrantType( + new PasswordGrant($userRepository, $refreshTokenRepository), + new \DateInterval('PT1H') + ); + + return $server; + }, +]); + +$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) { + /* @var \League\OAuth2\Server\AuthorizationServer $server */ + $server = $app->getContainer()->get(AuthorizationServer::class); + + try { + return $server->respondToAccessTokenRequest($request, $response); + } catch (OAuthServerException $exception) { + return $exception->generateHttpResponse($response); + } catch (\Exception $exception) { + $body = new Stream('php://temp', 'r+'); + $body->write($exception->getMessage()); + + return $response->withStatus(500)->withBody($body); + } +}); + +$app->run(); diff --git a/examples/public/refresh_token.php b/examples/public/refresh_token.php new file mode 100644 index 000000000..0649c117a --- /dev/null +++ b/examples/public/refresh_token.php @@ -0,0 +1,76 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +use League\OAuth2\Server\AuthorizationServer; +use League\OAuth2\Server\Exception\OAuthServerException; +use League\OAuth2\Server\Grant\RefreshTokenGrant; +use OAuth2ServerExamples\Repositories\AccessTokenRepository; +use OAuth2ServerExamples\Repositories\ClientRepository; +use OAuth2ServerExamples\Repositories\RefreshTokenRepository; +use OAuth2ServerExamples\Repositories\ScopeRepository; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Slim\App; +use Zend\Diactoros\Stream; + +include __DIR__ . '/../vendor/autoload.php'; + +$app = new App([ + 'settings' => [ + 'displayErrorDetails' => true, + ], + AuthorizationServer::class => function () { + // Init our repositories + $clientRepository = new ClientRepository(); + $accessTokenRepository = new AccessTokenRepository(); + $scopeRepository = new ScopeRepository(); + $refreshTokenRepository = new RefreshTokenRepository(); + + $privateKeyPath = 'file://' . __DIR__ . '/../private.key'; + $publicKeyPath = 'file://' . __DIR__ . '/../public.key'; + + // Setup the authorization server + $server = new AuthorizationServer( + $clientRepository, + $accessTokenRepository, + $scopeRepository, + $privateKeyPath, + $publicKeyPath + ); + + // Enable the refresh token grant on the server + $grant = new RefreshTokenGrant($refreshTokenRepository); + $grant->setRefreshTokenTTL(new \DateInterval('P1M')); // The refresh token will expire in 1 month + + $server->enableGrantType( + $grant, + new \DateInterval('PT1H') // The new access token will expire after 1 hour + ); + + return $server; + }, +]); + +$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) { + /* @var \League\OAuth2\Server\AuthorizationServer $server */ + $server = $app->getContainer()->get(AuthorizationServer::class); + + try { + return $server->respondToAccessTokenRequest($request, $response); + } catch (OAuthServerException $exception) { + return $exception->generateHttpResponse($response); + } catch (\Exception $exception) { + $body = new Stream('php://temp', 'r+'); + $body->write($exception->getMessage()); + + return $response->withStatus(500)->withBody($body); + } +}); + +$app->run(); diff --git a/examples/relational/Model/Users.php b/examples/relational/Model/Users.php deleted file mode 100644 index 76caab858..000000000 --- a/examples/relational/Model/Users.php +++ /dev/null @@ -1,25 +0,0 @@ -select(['username', 'password', 'name', 'email', 'photo']); - - if ($username !== null) { - $query->where('username', '=', $username); - } - - $result = $query->get(); - - if (count($result) > 0) { - return $result; - } - - return; - } -} diff --git a/examples/relational/Storage/AccessTokenStorage.php b/examples/relational/Storage/AccessTokenStorage.php deleted file mode 100644 index bd1a5ca8f..000000000 --- a/examples/relational/Storage/AccessTokenStorage.php +++ /dev/null @@ -1,93 +0,0 @@ -where('access_token', $token) - ->get(); - - if (count($result) === 1) { - $token = (new AccessTokenEntity($this->server)) - ->setId($result[0]['access_token']) - ->setExpireTime($result[0]['expire_time']); - - return $token; - } - - return; - } - - /** - * {@inheritdoc} - */ - public function getScopes(AccessTokenEntity $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(AccessTokenEntity $token, ScopeEntity $scope) - { - Capsule::table('oauth_access_token_scopes') - ->insert([ - 'access_token' => $token->getId(), - 'scope' => $scope->getId(), - ]); - } - - /** - * {@inheritdoc} - */ - public function delete(AccessTokenEntity $token) - { - Capsule::table('oauth_access_tokens') - ->where('access_token', $token->getId()) - ->delete(); - } -} diff --git a/examples/relational/Storage/AuthCodeStorage.php b/examples/relational/Storage/AuthCodeStorage.php deleted file mode 100644 index c0f84d9aa..000000000 --- a/examples/relational/Storage/AuthCodeStorage.php +++ /dev/null @@ -1,93 +0,0 @@ -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']); - $token->setExpireTime($result[0]['expire_time']); - - return $token; - } - - return; - } - - 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 deleted file mode 100644 index 9d62263df..000000000 --- a/examples/relational/Storage/ClientStorage.php +++ /dev/null @@ -1,70 +0,0 @@ -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; - } - - /** - * {@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; - } -} diff --git a/examples/relational/Storage/RefreshTokenStorage.php b/examples/relational/Storage/RefreshTokenStorage.php deleted file mode 100644 index 580efaf16..000000000 --- a/examples/relational/Storage/RefreshTokenStorage.php +++ /dev/null @@ -1,55 +0,0 @@ -where('refresh_token', $token) - ->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; - } - - /** - * {@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 deleted file mode 100644 index 4a6dd20e5..000000000 --- a/examples/relational/Storage/ScopeStorage.php +++ /dev/null @@ -1,30 +0,0 @@ -where('id', $scope) - ->get(); - - if (count($result) === 0) { - return; - } - - 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 deleted file mode 100644 index 612b14892..000000000 --- a/examples/relational/Storage/SessionStorage.php +++ /dev/null @@ -1,109 +0,0 @@ -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; - } - - /** - * {@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; - } - - /** - * {@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') - ->insertGetId([ - '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 deleted file mode 100644 index d05d225ad..000000000 --- a/examples/relational/api.php +++ /dev/null @@ -1,130 +0,0 @@ -createFromGlobals(); -$router = new \Orno\Route\RouteCollection(); - -// GET /tokeninfo -$router->get('/tokeninfo', function (Request $request) use ($server) { - - $accessToken = $server->getAccessToken(); - $session = $server->getSessionStorage()->getByAccessToken($accessToken); - $token = [ - 'owner_id' => $session->getOwnerId(), - 'owner_type' => $session->getOwnerType(), - 'access_token' => $accessToken, - 'client_id' => $session->getClient()->getId(), - 'scopes' => $accessToken->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->getAccessToken()->hasScope('email')) { - $user['email'] = $result['email']; - } - - if ($server->getAccessToken()->hasScope('photo')) { - $user['photo'] = $result['photo']; - } - - $users[] = $user; - } - - return new Response(json_encode($users)); -}); - -// GET /users/{username} -$router->get('/users/{username}', function (Request $request, Response $response, array $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->getAccessToken()->hasScope('email')) { - $user['email'] = $result[0]['email']; - } - - if ($server->getAccessToken()->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 deleted file mode 100644 index 380025e63..000000000 --- a/examples/relational/authcode_grant.php +++ /dev/null @@ -1,117 +0,0 @@ -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 deleted file mode 100644 index 34bd57cf3..000000000 --- a/examples/relational/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "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 deleted file mode 100644 index de883979d..000000000 --- a/examples/relational/config/db.php +++ /dev/null @@ -1,18 +0,0 @@ -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 deleted file mode 100644 index 85ba40f02..000000000 --- a/examples/relational/config/init.php +++ /dev/null @@ -1,249 +0,0 @@ -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')->unsigned(); - $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')->unsigned(); - $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('access_token')->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')->unsigned(); - $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')->unsigned(); - $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')->unsigned(); - $table->integer('session_id')->unsigned(); - $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 deleted file mode 100644 index a59d201e9..000000000 --- a/examples/relational/other_grants.php +++ /dev/null @@ -1,97 +0,0 @@ -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/examples/src/Entities/AccessTokenEntity.php b/examples/src/Entities/AccessTokenEntity.php new file mode 100644 index 000000000..f55246b33 --- /dev/null +++ b/examples/src/Entities/AccessTokenEntity.php @@ -0,0 +1,20 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace OAuth2ServerExamples\Entities; + +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\Traits\AccessTokenTrait; +use League\OAuth2\Server\Entities\Traits\EntityTrait; +use League\OAuth2\Server\Entities\Traits\TokenEntityTrait; + +class AccessTokenEntity implements AccessTokenEntityInterface +{ + use AccessTokenTrait, TokenEntityTrait, EntityTrait; +} diff --git a/examples/src/Entities/AuthCodeEntity.php b/examples/src/Entities/AuthCodeEntity.php new file mode 100644 index 000000000..acfbc3b60 --- /dev/null +++ b/examples/src/Entities/AuthCodeEntity.php @@ -0,0 +1,20 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace OAuth2ServerExamples\Entities; + +use League\OAuth2\Server\Entities\AuthCodeEntityInterface; +use League\OAuth2\Server\Entities\Traits\AuthCodeTrait; +use League\OAuth2\Server\Entities\Traits\EntityTrait; +use League\OAuth2\Server\Entities\Traits\TokenEntityTrait; + +class AuthCodeEntity implements AuthCodeEntityInterface +{ + use EntityTrait, TokenEntityTrait, AuthCodeTrait; +} diff --git a/examples/src/Entities/ClientEntity.php b/examples/src/Entities/ClientEntity.php new file mode 100644 index 000000000..9d682a454 --- /dev/null +++ b/examples/src/Entities/ClientEntity.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace OAuth2ServerExamples\Entities; + +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Entities\Traits\ClientTrait; +use League\OAuth2\Server\Entities\Traits\EntityTrait; + +class ClientEntity implements ClientEntityInterface +{ + use EntityTrait, ClientTrait; + + public function setName($name) + { + $this->name = $name; + } + + public function setRedirectUri($uri) + { + $this->redirectUri = $uri; + } +} diff --git a/examples/src/Entities/RefreshTokenEntity.php b/examples/src/Entities/RefreshTokenEntity.php new file mode 100644 index 000000000..60109c029 --- /dev/null +++ b/examples/src/Entities/RefreshTokenEntity.php @@ -0,0 +1,19 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace OAuth2ServerExamples\Entities; + +use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; +use League\OAuth2\Server\Entities\Traits\EntityTrait; +use League\OAuth2\Server\Entities\Traits\RefreshTokenTrait; + +class RefreshTokenEntity implements RefreshTokenEntityInterface +{ + use RefreshTokenTrait, EntityTrait; +} diff --git a/examples/src/Entities/ScopeEntity.php b/examples/src/Entities/ScopeEntity.php new file mode 100644 index 000000000..ec83cf51e --- /dev/null +++ b/examples/src/Entities/ScopeEntity.php @@ -0,0 +1,23 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace OAuth2ServerExamples\Entities; + +use League\OAuth2\Server\Entities\ScopeEntityInterface; +use League\OAuth2\Server\Entities\Traits\EntityTrait; + +class ScopeEntity implements ScopeEntityInterface +{ + use EntityTrait; + + public function jsonSerialize() + { + return $this->getIdentifier(); + } +} diff --git a/examples/src/Entities/UserEntity.php b/examples/src/Entities/UserEntity.php new file mode 100644 index 000000000..22c1b4e55 --- /dev/null +++ b/examples/src/Entities/UserEntity.php @@ -0,0 +1,25 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace OAuth2ServerExamples\Entities; + +use League\OAuth2\Server\Entities\UserEntityInterface; + +class UserEntity implements UserEntityInterface +{ + /** + * Return the user's identifier. + * + * @return mixed + */ + public function getIdentifier() + { + return 1; + } +} diff --git a/examples/src/Repositories/AccessTokenRepository.php b/examples/src/Repositories/AccessTokenRepository.php new file mode 100644 index 000000000..d7736c763 --- /dev/null +++ b/examples/src/Repositories/AccessTokenRepository.php @@ -0,0 +1,57 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace OAuth2ServerExamples\Repositories; + +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; +use OAuth2ServerExamples\Entities\AccessTokenEntity; + +class AccessTokenRepository implements AccessTokenRepositoryInterface +{ + /** + * {@inheritdoc} + */ + public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity) + { + // Some logic here to save the access token to a database + } + + /** + * {@inheritdoc} + */ + public function revokeAccessToken($tokenId) + { + // Some logic here to revoke the access token + } + + /** + * {@inheritdoc} + */ + public function isAccessTokenRevoked($tokenId) + { + return false; // Access token hasn't been revoked + } + + /** + * {@inheritdoc} + */ + public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null) + { + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($clientEntity); + foreach ($scopes as $scope) { + $accessToken->addScope($scope); + } + $accessToken->setUserIdentifier($userIdentifier); + + return $accessToken; + } +} diff --git a/examples/src/Repositories/AuthCodeRepository.php b/examples/src/Repositories/AuthCodeRepository.php new file mode 100644 index 000000000..d3ca4825c --- /dev/null +++ b/examples/src/Repositories/AuthCodeRepository.php @@ -0,0 +1,49 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace OAuth2ServerExamples\Repositories; + +use League\OAuth2\Server\Entities\AuthCodeEntityInterface; +use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface; +use OAuth2ServerExamples\Entities\AuthCodeEntity; + +class AuthCodeRepository implements AuthCodeRepositoryInterface +{ + /** + * {@inheritdoc} + */ + public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity) + { + // Some logic to persist the auth code to a database + } + + /** + * {@inheritdoc} + */ + public function revokeAuthCode($codeId) + { + // Some logic to revoke the auth code in a database + } + + /** + * {@inheritdoc} + */ + public function isAuthCodeRevoked($codeId) + { + return false; // The auth code has not been revoked + } + + /** + * {@inheritdoc} + */ + public function getNewAuthCode() + { + return new AuthCodeEntity(); + } +} diff --git a/examples/src/Repositories/ClientRepository.php b/examples/src/Repositories/ClientRepository.php new file mode 100644 index 000000000..f3f57fa10 --- /dev/null +++ b/examples/src/Repositories/ClientRepository.php @@ -0,0 +1,42 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace OAuth2ServerExamples\Repositories; + +use League\OAuth2\Server\Repositories\ClientRepositoryInterface; +use OAuth2ServerExamples\Entities\ClientEntity; + +class ClientRepository implements ClientRepositoryInterface +{ + /** + * {@inheritdoc} + */ + public function getClientEntity($clientIdentifier, $clientSecret = null, $redirectUri = null, $grantType = null) + { + $clients = [ + 'myawesomeapp' => [ + 'secret' => password_hash('abc123', PASSWORD_BCRYPT), + 'name' => 'My Awesome App', + 'redirect_uri' => 'http://foo/bar', + ], + ]; + + // Check if client is registered + if (array_key_exists($clientIdentifier, $clients) === false) { + return; + } + + $client = new ClientEntity(); + $client->setIdentifier($clientIdentifier); + $client->setName($clients[$clientIdentifier]['name']); + $client->setRedirectUri($clients[$clientIdentifier]['redirect_uri']); + + return $client; + } +} diff --git a/examples/src/Repositories/RefreshTokenRepository.php b/examples/src/Repositories/RefreshTokenRepository.php new file mode 100644 index 000000000..39a0b8cd1 --- /dev/null +++ b/examples/src/Repositories/RefreshTokenRepository.php @@ -0,0 +1,49 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace OAuth2ServerExamples\Repositories; + +use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; +use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface; +use OAuth2ServerExamples\Entities\RefreshTokenEntity; + +class RefreshTokenRepository implements RefreshTokenRepositoryInterface +{ + /** + * {@inheritdoc} + */ + public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntityInterface) + { + // Some logic to persist the refresh token in a database + } + + /** + * {@inheritdoc} + */ + public function revokeRefreshToken($tokenId) + { + // Some logic to revoke the refresh token in a database + } + + /** + * {@inheritdoc} + */ + public function isRefreshTokenRevoked($tokenId) + { + return false; // The refresh token has not been revoked + } + + /** + * {@inheritdoc} + */ + public function getNewRefreshToken() + { + return new RefreshTokenEntity(); + } +} diff --git a/examples/src/Repositories/ScopeRepository.php b/examples/src/Repositories/ScopeRepository.php new file mode 100644 index 000000000..fbd64b3e6 --- /dev/null +++ b/examples/src/Repositories/ScopeRepository.php @@ -0,0 +1,53 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace OAuth2ServerExamples\Repositories; + +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; +use OAuth2ServerExamples\Entities\ScopeEntity; + +class ScopeRepository implements ScopeRepositoryInterface +{ + /** + * {@inheritdoc} + */ + public function getScopeEntityByIdentifier($scopeIdentifier) + { + $scopes = [ + 'basic' => [ + 'description' => 'Basic details about you', + ], + 'email' => [ + 'description' => 'Your email address', + ], + ]; + + if (array_key_exists($scopeIdentifier, $scopes) === false) { + return; + } + + $scope = new ScopeEntity(); + $scope->setIdentifier($scopeIdentifier); + + return $scope; + } + + /** + * {@inheritdoc} + */ + public function finalizeScopes( + array $scopes, + $grantType, + ClientEntityInterface $clientEntity, + $userIdentifier = null + ) { + return $scopes; + } +} diff --git a/examples/src/Repositories/UserRepository.php b/examples/src/Repositories/UserRepository.php new file mode 100644 index 000000000..86f999700 --- /dev/null +++ b/examples/src/Repositories/UserRepository.php @@ -0,0 +1,38 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace OAuth2ServerExamples\Repositories; + +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Repositories\UserRepositoryInterface; +use OAuth2ServerExamples\Entities\ScopeEntity; +use OAuth2ServerExamples\Entities\UserEntity; + +class UserRepository implements UserRepositoryInterface +{ + /** + * {@inheritdoc} + */ + public function getUserEntityByUserCredentials( + $username, + $password, + $grantType, + ClientEntityInterface $clientEntity + ) { + if ($username === 'alex' && $password === 'whisky') { + $scope = new ScopeEntity(); + $scope->setIdentifier('email'); + $scopes[] = $scope; + + return new UserEntity(); + } + + return; + } +} diff --git a/phpunit.xml b/phpunit.xml deleted file mode 100644 index f78850c10..000000000 --- a/phpunit.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - ./tests/unit/ - - - - - src - - - - - - - diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 000000000..92564086b --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,24 @@ + + + + + ./tests/ + + + + + src + + src/ResponseTypes/DefaultTemplates + src/TemplateRenderer + + + + + + + + diff --git a/src/AbstractServer.php b/src/AbstractServer.php deleted file mode 100644 index ec4b2ae88..000000000 --- a/src/AbstractServer.php +++ /dev/null @@ -1,358 +0,0 @@ - - * @copyright Copyright (c) Alex Bilbie - * @license http://mit-license.org/ - * @link https://github.com/thephpleague/oauth2-server - */ - -namespace League\OAuth2\Server; - -use League\Event\Emitter; -use League\OAuth2\Server\Storage\AccessTokenInterface; -use League\OAuth2\Server\Storage\AuthCodeInterface; -use League\OAuth2\Server\Storage\ClientInterface; -use League\OAuth2\Server\Storage\MacTokenInterface; -use League\OAuth2\Server\Storage\RefreshTokenInterface; -use League\OAuth2\Server\Storage\ScopeInterface; -use League\OAuth2\Server\Storage\SessionInterface; -use League\OAuth2\Server\TokenType\TokenTypeInterface; -use Symfony\Component\HttpFoundation\Request; - -/** - * OAuth 2.0 Resource Server - */ -abstract class AbstractServer -{ - /** - * The request object - * - * @var \Symfony\Component\HttpFoundation\Request - */ - protected $request; - - /** - * Session storage - * - * @var \League\OAuth2\Server\Storage\SessionInterface - */ - protected $sessionStorage; - - /** - * Access token storage - * - * @var \League\OAuth2\Server\Storage\AccessTokenInterface - */ - protected $accessTokenStorage; - - /** - * Refresh token storage - * - * @var \League\OAuth2\Server\Storage\RefreshTokenInterface - */ - protected $refreshTokenStorage; - - /** - * Auth code storage - * - * @var \League\OAuth2\Server\Storage\AuthCodeInterface - */ - protected $authCodeStorage; - - /** - * Scope storage - * - * @var \League\OAuth2\Server\Storage\ScopeInterface - */ - protected $scopeStorage; - - /** - * Client storage - * - * @var \League\OAuth2\Server\Storage\ClientInterface - */ - protected $clientStorage; - - /** - * @var \League\OAuth2\Server\Storage\MacTokenInterface - */ - protected $macStorage; - - /** - * Token type - * - * @var \League\OAuth2\Server\TokenType\TokenTypeInterface - */ - protected $tokenType; - - /** - * Event emitter - * - * @var \League\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; - } - } - - /** - * Add an event listener to the event emitter - * - * @param string $eventName Event name - * @param callable $listener Callable function or method - * @param int $priority Priority of event listener - */ - public function addEventListener($eventName, callable $listener, $priority = Emitter::P_NORMAL) - { - $this->eventEmitter->addListener($eventName, $listener, $priority); - } - - /** - * Returns the event emitter - * - * @return \League\Event\Emitter - */ - public function getEventEmitter() - { - return $this->eventEmitter; - } - - /** - * Sets the Request Object - * - * @param \Symfony\Component\HttpFoundation\Request The Request Object - * - * @return self - */ - public function setRequest($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; - } - - /** - * Set the client storage - * - * @param \League\OAuth2\Server\Storage\ClientInterface $storage - * - * @return self - */ - public function setClientStorage(ClientInterface $storage) - { - $storage->setServer($this); - $this->clientStorage = $storage; - - return $this; - } - - /** - * Set the session storage - * - * @param \League\OAuth2\Server\Storage\SessionInterface $storage - * - * @return self - */ - public function setSessionStorage(SessionInterface $storage) - { - $storage->setServer($this); - $this->sessionStorage = $storage; - - return $this; - } - - /** - * Set the access token storage - * - * @param \League\OAuth2\Server\Storage\AccessTokenInterface $storage - * - * @return self - */ - public function setAccessTokenStorage(AccessTokenInterface $storage) - { - $storage->setServer($this); - $this->accessTokenStorage = $storage; - - return $this; - } - - /** - * Set the refresh token storage - * - * @param \League\OAuth2\Server\Storage\RefreshTokenInterface $storage - * - * @return self - */ - public function setRefreshTokenStorage(RefreshTokenInterface $storage) - { - $storage->setServer($this); - $this->refreshTokenStorage = $storage; - - return $this; - } - - /** - * Set the auth code storage - * - * @param \League\OAuth2\Server\Storage\AuthCodeInterface $storage - * - * @return self - */ - public function setAuthCodeStorage(AuthCodeInterface $storage) - { - $storage->setServer($this); - $this->authCodeStorage = $storage; - - return $this; - } - - /** - * Set the scope storage - * - * @param \League\OAuth2\Server\Storage\ScopeInterface $storage - * - * @return self - */ - public function setScopeStorage(ScopeInterface $storage) - { - $storage->setServer($this); - $this->scopeStorage = $storage; - - return $this; - } - - /** - * Return the client storage - * - * @return \League\OAuth2\Server\Storage\ClientInterface - */ - public function getClientStorage() - { - return $this->clientStorage; - } - - /** - * Return the scope storage - * - * @return \League\OAuth2\Server\Storage\ScopeInterface - */ - public function getScopeStorage() - { - return $this->scopeStorage; - } - - /** - * Return the session storage - * - * @return \League\OAuth2\Server\Storage\SessionInterface - */ - public function getSessionStorage() - { - return $this->sessionStorage; - } - - /** - * Return the refresh token storage - * - * @return \League\OAuth2\Server\Storage\RefreshTokenInterface - */ - public function getRefreshTokenStorage() - { - return $this->refreshTokenStorage; - } - - /** - * Return the access token storage - * - * @return \League\OAuth2\Server\Storage\AccessTokenInterface - */ - public function getAccessTokenStorage() - { - return $this->accessTokenStorage; - } - - /** - * Return the auth code storage - * - * @return \League\OAuth2\Server\Storage\AuthCodeInterface - */ - public function getAuthCodeStorage() - { - return $this->authCodeStorage; - } - - /** - * Set the access token type - * - * @param TokenTypeInterface $tokenType The token type - * - * @return void - */ - public function setTokenType(TokenTypeInterface $tokenType) - { - $tokenType->setServer($this); - $this->tokenType = $tokenType; - } - - /** - * Get the access token type - * - * @return TokenTypeInterface - */ - public function getTokenType() - { - return $this->tokenType; - } - - /** - * @return MacTokenInterface - */ - public function getMacStorage() - { - return $this->macStorage; - } - - /** - * @param MacTokenInterface $macStorage - */ - public function setMacStorage(MacTokenInterface $macStorage) - { - $this->macStorage = $macStorage; - } -} diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 452ee4e19..b0ba7598f 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -1,295 +1,214 @@ * @copyright Copyright (c) Alex Bilbie * @license http://mit-license.org/ + * * @link https://github.com/thephpleague/oauth2-server */ namespace League\OAuth2\Server; +use DateInterval; +use League\Event\EmitterAwareInterface; +use League\Event\EmitterAwareTrait; +use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Grant\GrantTypeInterface; -use League\OAuth2\Server\TokenType\Bearer; - -/** - * OAuth 2.0 authorization server class - */ -class AuthorizationServer extends AbstractServer +use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; +use League\OAuth2\Server\Repositories\ClientRepositoryInterface; +use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; +use League\OAuth2\Server\RequestTypes\AuthorizationRequest; +use League\OAuth2\Server\ResponseTypes\BearerTokenResponse; +use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; + +class AuthorizationServer implements EmitterAwareInterface { - /** - * The delimiter 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 $scopeDelimiter = ' '; + use EmitterAwareTrait; /** - * The TTL (time to live) of an access token in seconds (default: 3600) - * - * @var integer + * @var \League\OAuth2\Server\Grant\GrantTypeInterface[] */ - protected $accessTokenTTL = 3600; + protected $enabledGrantTypes = []; /** - * The registered grant response types - * - * @var array + * @var \DateInterval[] */ - protected $responseTypes = []; + protected $grantTypeAccessTokenTTL = []; /** - * The registered grant types - * - * @var array + * @var \League\OAuth2\Server\CryptKey */ - protected $grantTypes = []; + protected $privateKey; /** - * Require the "scope" parameter to be in checkAuthoriseParams() - * - * @var boolean + * @var \League\OAuth2\Server\CryptKey */ - protected $requireScopeParam = false; + protected $publicKey; /** - * Default scope(s) to be used if none is provided - * - * @var string|array + * @var ResponseTypeInterface */ - protected $defaultScope; + protected $responseType; /** - * Require the "state" parameter to be in checkAuthoriseParams() - * - * @var boolean + * @var \League\OAuth2\Server\Repositories\ClientRepositoryInterface */ - protected $requireStateParam = false; + private $clientRepository; /** - * Create a new OAuth2 authorization server - * - * @return self + * @var \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface */ - public function __construct() - { - // Set Bearer as the default token type - $this->setTokenType(new Bearer()); - - parent::__construct(); - - return $this; - } + private $accessTokenRepository; /** - * 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 + * @var \League\OAuth2\Server\Repositories\ScopeRepositoryInterface */ - 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; - } + private $scopeRepository; /** - * Check if a grant type has been enabled - * - * @param string $identifier The grant type identifier + * New server instance. * - * @return boolean Returns "true" if enabled, "false" if not + * @param \League\OAuth2\Server\Repositories\ClientRepositoryInterface $clientRepository + * @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository + * @param \League\OAuth2\Server\Repositories\ScopeRepositoryInterface $scopeRepository + * @param \League\OAuth2\Server\CryptKey|string $privateKey + * @param \League\OAuth2\Server\CryptKey|string $publicKey + * @param null|\League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType */ - public function hasGrantType($identifier) - { - return (array_key_exists($identifier, $this->grantTypes)); - } + public function __construct( + ClientRepositoryInterface $clientRepository, + AccessTokenRepositoryInterface $accessTokenRepository, + ScopeRepositoryInterface $scopeRepository, + $privateKey, + $publicKey, + ResponseTypeInterface $responseType = null + ) { + $this->clientRepository = $clientRepository; + $this->accessTokenRepository = $accessTokenRepository; + $this->scopeRepository = $scopeRepository; - /** - * Returns response types - * - * @return array - */ - public function getResponseTypes() - { - return $this->responseTypes; - } - - /** - * Require the "scope" parameter in checkAuthoriseParams() - * - * @param boolean $require - * - * @return self - */ - public function requireScopeParam($require = true) - { - $this->requireScopeParam = $require; + if (!$privateKey instanceof CryptKey) { + $privateKey = new CryptKey($privateKey); + } + $this->privateKey = $privateKey; - return $this; - } + if (!$publicKey instanceof CryptKey) { + $publicKey = new CryptKey($publicKey); + } + $this->publicKey = $publicKey; - /** - * Is the scope parameter required? - * - * @return bool - */ - public function scopeParamRequired() - { - return $this->requireScopeParam; + $this->responseType = $responseType; } /** - * Default scope to be used if none is provided and requireScopeParam() is false - * - * @param string $default Name of the default scope + * Enable a grant type on the server. * - * @return self + * @param \League\OAuth2\Server\Grant\GrantTypeInterface $grantType + * @param \DateInterval $accessTokenTTL */ - public function setDefaultScope($default = null) + public function enableGrantType(GrantTypeInterface $grantType, DateInterval $accessTokenTTL = null) { - $this->defaultScope = $default; + if ($accessTokenTTL instanceof DateInterval === false) { + $accessTokenTTL = new \DateInterval('PT1H'); + } - return $this; - } + $grantType->setAccessTokenRepository($this->accessTokenRepository); + $grantType->setClientRepository($this->clientRepository); + $grantType->setScopeRepository($this->scopeRepository); + $grantType->setPrivateKey($this->privateKey); + $grantType->setPublicKey($this->publicKey); + $grantType->setEmitter($this->getEmitter()); - /** - * Default scope to be used if none is provided and requireScopeParam is false - * - * @return string|null - */ - public function getDefaultScope() - { - return $this->defaultScope; + $this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType; + $this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] = $accessTokenTTL; } /** - * Require the "state" parameter in checkAuthoriseParams() + * Validate an authorization request * - * @return bool - */ - public function stateParamRequired() - { - return $this->requireStateParam; - } - - /** - * Require the "state" parameter in checkAuthoriseParams() + * @param \Psr\Http\Message\ServerRequestInterface $request * - * @param boolean $require + * @throws \League\OAuth2\Server\Exception\OAuthServerException * - * @return self + * @return \League\OAuth2\Server\RequestTypes\AuthorizationRequest|null */ - public function requireStateParam($require = true) + public function validateAuthorizationRequest(ServerRequestInterface $request) { - $this->requireStateParam = $require; + $authRequest = null; + $enabledGrantTypes = $this->enabledGrantTypes; + while ($authRequest === null && $grantType = array_shift($enabledGrantTypes)) { + /** @var \League\OAuth2\Server\Grant\GrantTypeInterface $grantType */ + if ($grantType->canRespondToAuthorizationRequest($request)) { + $authRequest = $grantType->validateAuthorizationRequest($request); - return $this; - } + return $authRequest; + } + } - /** - * Get the scope delimiter - * - * @return string The scope delimiter (default: " ") - */ - public function getScopeDelimiter() - { - return $this->scopeDelimiter; + throw OAuthServerException::unsupportedGrantType(); } /** - * Set the scope delimiter + * Complete an authorization request * - * @param string $scopeDelimiter + * @param \League\OAuth2\Server\RequestTypes\AuthorizationRequest $authRequest + * @param \Psr\Http\Message\ResponseInterface $response * - * @return self + * @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface */ - public function setScopeDelimiter($scopeDelimiter = ' ') + public function completeAuthorizationRequest(AuthorizationRequest $authRequest, ResponseInterface $response) { - $this->scopeDelimiter = $scopeDelimiter; - - return $this; + return $this->enabledGrantTypes[$authRequest->getGrantTypeId()] + ->completeAuthorizationRequest($authRequest) + ->generateHttpResponse($response); } /** - * Get the TTL for an access token + * Return an access token response. * - * @return int The TTL - */ - public function getAccessTokenTTL() - { - return $this->accessTokenTTL; - } - - /** - * Set the TTL for an access token + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response * - * @param int $accessTokenTTL The new TTL + * @throws \League\OAuth2\Server\Exception\OAuthServerException * - * @return self + * @return \Psr\Http\Message\ResponseInterface */ - public function setAccessTokenTTL($accessTokenTTL = 3600) + public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response) { - $this->accessTokenTTL = $accessTokenTTL; - - return $this; - } - - /** - * Issue an access token - * - * @return array Authorise request parameters - * - * @throws - */ - public function issueAccessToken() - { - $grantType = $this->getRequest()->request->get('grant_type'); - if (is_null($grantType)) { - throw new Exception\InvalidRequestException('grant_type'); + $tokenResponse = null; + while ($tokenResponse === null && $grantType = array_shift($this->enabledGrantTypes)) { + /** @var \League\OAuth2\Server\Grant\GrantTypeInterface $grantType */ + if ($grantType->canRespondToAccessTokenRequest($request)) { + $tokenResponse = $grantType->respondToAccessTokenRequest( + $request, + $this->getResponseType(), + $this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] + ); + } } - // Ensure grant type is one that is recognised and is enabled - if (!in_array($grantType, array_keys($this->grantTypes))) { - throw new Exception\UnsupportedGrantTypeException($grantType); + if ($tokenResponse instanceof ResponseTypeInterface) { + return $tokenResponse->generateHttpResponse($response); } - // Complete the flow - return $this->getGrantType($grantType)->completeFlow(); + throw OAuthServerException::unsupportedGrantType(); } /** - * Return a grant type class - * - * @param string $grantType The grant type identifier + * Get the token type that grants will return in the HTTP response. * - * @return Grant\GrantTypeInterface - * - * @throws + * @return ResponseTypeInterface */ - public function getGrantType($grantType) + protected function getResponseType() { - if (isset($this->grantTypes[$grantType])) { - return $this->grantTypes[$grantType]; + if (!$this->responseType instanceof ResponseTypeInterface) { + $this->responseType = new BearerTokenResponse($this->accessTokenRepository); } - throw new Exception\InvalidGrantException($grantType); + $this->responseType->setPrivateKey($this->privateKey); + + return $this->responseType; } } diff --git a/src/AuthorizationValidators/AuthorizationValidatorInterface.php b/src/AuthorizationValidators/AuthorizationValidatorInterface.php new file mode 100644 index 000000000..7e49f8477 --- /dev/null +++ b/src/AuthorizationValidators/AuthorizationValidatorInterface.php @@ -0,0 +1,25 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\AuthorizationValidators; + +use Psr\Http\Message\ServerRequestInterface; + +interface AuthorizationValidatorInterface +{ + /** + * Determine the access token in the authorization header and append OAUth properties to the request + * as attributes. + * + * @param ServerRequestInterface $request + * + * @return ServerRequestInterface + */ + public function validateAuthorization(ServerRequestInterface $request); +} diff --git a/src/AuthorizationValidators/BearerTokenValidator.php b/src/AuthorizationValidators/BearerTokenValidator.php new file mode 100644 index 000000000..880347508 --- /dev/null +++ b/src/AuthorizationValidators/BearerTokenValidator.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\AuthorizationValidators; + +use Lcobucci\JWT\Parser; +use Lcobucci\JWT\Signer\Rsa\Sha256; +use Lcobucci\JWT\ValidationData; +use League\OAuth2\Server\CryptTrait; +use League\OAuth2\Server\Exception\OAuthServerException; +use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; +use Psr\Http\Message\ServerRequestInterface; + +class BearerTokenValidator implements AuthorizationValidatorInterface +{ + use CryptTrait; + + /** + * @var \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface + */ + private $accessTokenRepository; + + /** + * BearerTokenValidator constructor. + * + * @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository + */ + public function __construct(AccessTokenRepositoryInterface $accessTokenRepository) + { + $this->accessTokenRepository = $accessTokenRepository; + } + + /** + * {@inheritdoc} + */ + public function validateAuthorization(ServerRequestInterface $request) + { + if ($request->hasHeader('authorization') === false) { + throw OAuthServerException::accessDenied('Missing "Authorization" header'); + } + + $header = $request->getHeader('authorization'); + $jwt = trim(preg_replace('/^(?:\s+)?Bearer\s/', '', $header[0])); + + try { + // Attempt to parse and validate the JWT + $token = (new Parser())->parse($jwt); + if ($token->verify(new Sha256(), $this->publicKey->getKeyPath()) === false) { + throw OAuthServerException::accessDenied('Access token could not be verified'); + } + + // Ensure access token hasn't expired + $data = new ValidationData(); + $data->setCurrentTime(time()); + + if ($token->validate($data) === false) { + throw OAuthServerException::accessDenied('Access token is invalid'); + } + + // Check if token has been revoked + if ($this->accessTokenRepository->isAccessTokenRevoked($token->getClaim('jti'))) { + throw OAuthServerException::accessDenied('Access token has been revoked'); + } + + // Return the request with additional attributes + return $request + ->withAttribute('oauth_access_token_id', $token->getClaim('jti')) + ->withAttribute('oauth_client_id', $token->getClaim('aud')) + ->withAttribute('oauth_user_id', $token->getClaim('sub')) + ->withAttribute('oauth_scopes', $token->getClaim('scopes')); + } catch (\InvalidArgumentException $exception) { + // JWT couldn't be parsed so return the request as is + throw OAuthServerException::accessDenied($exception->getMessage()); + } + } +} diff --git a/src/CryptKey.php b/src/CryptKey.php new file mode 100644 index 000000000..4ea05264f --- /dev/null +++ b/src/CryptKey.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; + +class CryptKey +{ + /** + * @var string + */ + protected $keyPath; + + /** + * @var string + */ + protected $passPhrase; + + /** + * @param string $keyPath + * @param null|string $passPhrase + */ + public function __construct($keyPath, $passPhrase = null) + { + if (strpos($keyPath, 'file://') !== 0) { + $keyPath = 'file://' . $keyPath; + } + + if (!file_exists($keyPath) || !is_readable($keyPath)) { + throw new \LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath)); + } + + $this->keyPath = $keyPath; + $this->passPhrase = $passPhrase; + } + + /** + * Retrieve key path. + * + * @return string + */ + public function getKeyPath() + { + return $this->keyPath; + } + + /** + * Retrieve key pass phrase. + * + * @return null|string + */ + public function getPassPhrase() + { + return $this->passPhrase; + } +} diff --git a/src/CryptTrait.php b/src/CryptTrait.php new file mode 100644 index 000000000..1075ea120 --- /dev/null +++ b/src/CryptTrait.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; + +trait CryptTrait +{ + /** + * @var \League\OAuth2\Server\CryptKey + */ + protected $privateKey; + + /** + * @var \League\OAuth2\Server\CryptKey + */ + protected $publicKey; + + /** + * Set path to private key. + * + * @param \League\OAuth2\Server\CryptKey $privateKey + */ + public function setPrivateKey(CryptKey $privateKey) + { + $this->privateKey = $privateKey; + } + + /** + * Set path to public key. + * + * @param \League\OAuth2\Server\CryptKey $publicKey + */ + public function setPublicKey(CryptKey $publicKey) + { + $this->publicKey = $publicKey; + } + + /** + * Encrypt data with a private key. + * + * @param string $unencryptedData + * + * @return string + */ + protected function encrypt($unencryptedData) + { + $privateKey = openssl_pkey_get_private($this->privateKey->getKeyPath(), $this->privateKey->getPassPhrase()); + $privateKeyDetails = @openssl_pkey_get_details($privateKey); + if ($privateKeyDetails === null) { + throw new \LogicException( + sprintf('Could not get details of private key: %s', $this->privateKey->getKeyPath()) + ); + } + + $chunkSize = ceil($privateKeyDetails['bits'] / 8) - 11; + $output = ''; + + while ($unencryptedData) { + $chunk = substr($unencryptedData, 0, $chunkSize); + $unencryptedData = substr($unencryptedData, $chunkSize); + if (openssl_private_encrypt($chunk, $encrypted, $privateKey) === false) { + // @codeCoverageIgnoreStart + throw new \LogicException('Failed to encrypt data'); + // @codeCoverageIgnoreEnd + } + $output .= $encrypted; + } + openssl_pkey_free($privateKey); + + return base64_encode($output); + } + + /** + * Decrypt data with a public key. + * + * @param string $encryptedData + * + * @throws \LogicException + * + * @return string + */ + protected function decrypt($encryptedData) + { + $publicKey = openssl_pkey_get_public($this->publicKey->getKeyPath()); + $publicKeyDetails = @openssl_pkey_get_details($publicKey); + if ($publicKeyDetails === null) { + throw new \LogicException( + sprintf('Could not get details of public key: %s', $this->publicKey->getKeyPath()) + ); + } + + $chunkSize = ceil($publicKeyDetails['bits'] / 8); + $output = ''; + + $encryptedData = base64_decode($encryptedData); + + while ($encryptedData) { + $chunk = substr($encryptedData, 0, $chunkSize); + $encryptedData = substr($encryptedData, $chunkSize); + if (openssl_public_decrypt($chunk, $decrypted, $publicKey/*, OPENSSL_PKCS1_OAEP_PADDING*/) === false) { + // @codeCoverageIgnoreStart + throw new \LogicException('Failed to decrypt data'); + // @codeCoverageIgnoreEnd + } + $output .= $decrypted; + } + openssl_pkey_free($publicKey); + + return $output; + } +} diff --git a/src/Entities/AccessTokenEntityInterface.php b/src/Entities/AccessTokenEntityInterface.php new file mode 100644 index 000000000..642cc99f6 --- /dev/null +++ b/src/Entities/AccessTokenEntityInterface.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\Entities; + +use League\OAuth2\Server\CryptKey; + +interface AccessTokenEntityInterface extends TokenInterface +{ + /** + * Generate a JWT from the access token + * + * @param \League\OAuth2\Server\CryptKey $privateKey + * + * @return string + */ + public function convertToJWT(CryptKey $privateKey); +} diff --git a/src/Entities/AuthCodeEntityInterface.php b/src/Entities/AuthCodeEntityInterface.php new file mode 100644 index 000000000..e71aa2c8d --- /dev/null +++ b/src/Entities/AuthCodeEntityInterface.php @@ -0,0 +1,23 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entities; + +interface AuthCodeEntityInterface extends TokenInterface +{ + /** + * @return string + */ + public function getRedirectUri(); + + /** + * @param string $uri + */ + public function setRedirectUri($uri); +} diff --git a/src/Entities/ClientEntityInterface.php b/src/Entities/ClientEntityInterface.php new file mode 100644 index 000000000..80cc70c82 --- /dev/null +++ b/src/Entities/ClientEntityInterface.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\Entities; + +interface ClientEntityInterface +{ + /** + * Get the client's identifier. + * + * @return string + */ + public function getIdentifier(); + + /** + * Get the client's name. + * + * @return string + */ + public function getName(); + + /** + * Returns the registered redirect URI (as a string). + * + * Alternatively return an indexed array of redirect URIs. + * + * @return string|string[] + */ + public function getRedirectUri(); +} diff --git a/src/Entities/RefreshTokenEntityInterface.php b/src/Entities/RefreshTokenEntityInterface.php new file mode 100644 index 000000000..8b7d587d8 --- /dev/null +++ b/src/Entities/RefreshTokenEntityInterface.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\Entities; + +interface RefreshTokenEntityInterface +{ + /** + * Get the token's identifier. + * + * @return string + */ + public function getIdentifier(); + + /** + * Set the token's identifier. + * + * @param $identifier + */ + public function setIdentifier($identifier); + + /** + * Get the token's expiry date time. + * + * @return \DateTime + */ + public function getExpiryDateTime(); + + /** + * Set the date time when the token expires. + * + * @param \DateTime $dateTime + */ + public function setExpiryDateTime(\DateTime $dateTime); + + /** + * Set the access token that the refresh token was associated with. + * + * @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken + */ + public function setAccessToken(AccessTokenEntityInterface $accessToken); + + /** + * Get the access token that the refresh token was originally associated with. + * + * @return \League\OAuth2\Server\Entities\AccessTokenEntityInterface + */ + public function getAccessToken(); + + /** + * Has the token expired? + * + * @return bool + */ + public function isExpired(); +} diff --git a/src/Entities/ScopeEntityInterface.php b/src/Entities/ScopeEntityInterface.php new file mode 100644 index 000000000..34ef75f02 --- /dev/null +++ b/src/Entities/ScopeEntityInterface.php @@ -0,0 +1,20 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entities; + +interface ScopeEntityInterface extends \JsonSerializable +{ + /** + * Get the scope's identifier. + * + * @return string + */ + public function getIdentifier(); +} diff --git a/src/Entities/TokenInterface.php b/src/Entities/TokenInterface.php new file mode 100644 index 000000000..7d7c6d33e --- /dev/null +++ b/src/Entities/TokenInterface.php @@ -0,0 +1,90 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entities; + +interface TokenInterface +{ + /** + * Get the token's identifier. + * + * @return string + */ + public function getIdentifier(); + + /** + * Set the token's identifier. + * + * @param $identifier + */ + public function setIdentifier($identifier); + + /** + * Get the token's expiry date time. + * + * @return \DateTime + */ + public function getExpiryDateTime(); + + /** + * Set the date time when the token expires. + * + * @param \DateTime $dateTime + */ + public function setExpiryDateTime(\DateTime $dateTime); + + /** + * Set the identifier of the user associated with the token. + * + * @param string|int $identifier The identifier of the user + */ + public function setUserIdentifier($identifier); + + /** + * Get the token user's identifier. + * + * @return string|int + */ + public function getUserIdentifier(); + + /** + * Get the client that the token was issued to. + * + * @return ClientEntityInterface + */ + public function getClient(); + + /** + * Set the client that the token was issued to. + * + * @param \League\OAuth2\Server\Entities\ClientEntityInterface $client + */ + public function setClient(ClientEntityInterface $client); + + /** + * Associate a scope with the token. + * + * @param \League\OAuth2\Server\Entities\ScopeEntityInterface $scope + */ + public function addScope(ScopeEntityInterface $scope); + + /** + * Return an array of scopes associated with the token. + * + * @return ScopeEntityInterface[] + */ + public function getScopes(); + + /** + * Has the token expired? + * + * @return bool + */ + public function isExpired(); +} diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php new file mode 100644 index 000000000..f45b8b6ad --- /dev/null +++ b/src/Entities/Traits/AccessTokenTrait.php @@ -0,0 +1,39 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entities\Traits; + +use Lcobucci\JWT\Builder; +use Lcobucci\JWT\Signer\Key; +use Lcobucci\JWT\Signer\Rsa\Sha256; +use League\OAuth2\Server\CryptKey; + +trait AccessTokenTrait +{ + /** + * Generate a JWT from the access token + * + * @param \League\OAuth2\Server\CryptKey $privateKey + * + * @return string + */ + public function convertToJWT(CryptKey $privateKey) + { + return (new Builder()) + ->setAudience($this->getClient()->getIdentifier()) + ->setId($this->getIdentifier(), true) + ->setIssuedAt(time()) + ->setNotBefore(time()) + ->setExpiration($this->getExpiryDateTime()->getTimestamp()) + ->setSubject($this->getUserIdentifier()) + ->set('scopes', $this->getScopes()) + ->sign(new Sha256(), new Key($privateKey->getKeyPath(), $privateKey->getPassPhrase())) + ->getToken(); + } +} diff --git a/src/Entities/Traits/AuthCodeTrait.php b/src/Entities/Traits/AuthCodeTrait.php new file mode 100644 index 000000000..5bb9e3061 --- /dev/null +++ b/src/Entities/Traits/AuthCodeTrait.php @@ -0,0 +1,34 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entities\Traits; + +trait AuthCodeTrait +{ + /** + * @var null|string + */ + protected $redirectUri; + + /** + * @return string + */ + public function getRedirectUri() + { + return $this->redirectUri; + } + + /** + * @param string $uri + */ + public function setRedirectUri($uri) + { + $this->redirectUri = $uri; + } +} diff --git a/src/Entities/Traits/ClientTrait.php b/src/Entities/Traits/ClientTrait.php new file mode 100644 index 000000000..8e44ce09f --- /dev/null +++ b/src/Entities/Traits/ClientTrait.php @@ -0,0 +1,40 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entities\Traits; + +trait ClientTrait +{ + protected $name; + + protected $redirectUri; + + /** + * Get the client's name. + * + * @return string + * @codeCoverageIgnore + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the registered redirect URI (as a string). + * + * Alternatively return an indexed array of redirect URIs. + * + * @return string|string[] + */ + public function getRedirectUri() + { + return $this->redirectUri; + } +} diff --git a/src/Entities/Traits/EntityTrait.php b/src/Entities/Traits/EntityTrait.php new file mode 100644 index 000000000..20c86591f --- /dev/null +++ b/src/Entities/Traits/EntityTrait.php @@ -0,0 +1,34 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entities\Traits; + +trait EntityTrait +{ + /* + * @var string + */ + protected $identifier; + + /** + * @return mixed + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * @param mixed $identifier + */ + public function setIdentifier($identifier) + { + $this->identifier = $identifier; + } +} diff --git a/src/Entities/Traits/RefreshTokenTrait.php b/src/Entities/Traits/RefreshTokenTrait.php new file mode 100644 index 000000000..28d31a029 --- /dev/null +++ b/src/Entities/Traits/RefreshTokenTrait.php @@ -0,0 +1,72 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entities\Traits; + +use DateTime; +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; + +trait RefreshTokenTrait +{ + /** + * @var AccessTokenEntityInterface + */ + protected $accessToken; + + /** + * @var DateTime + */ + protected $expiryDateTime; + + /** + * {@inheritdoc} + */ + public function setAccessToken(AccessTokenEntityInterface $accessToken) + { + $this->accessToken = $accessToken; + } + + /** + * {@inheritdoc} + */ + public function getAccessToken() + { + return $this->accessToken; + } + + /** + * Get the token's expiry date time. + * + * @return DateTime + */ + public function getExpiryDateTime() + { + return $this->expiryDateTime; + } + + /** + * Set the date time when the token expires. + * + * @param DateTime $dateTime + */ + public function setExpiryDateTime(DateTime $dateTime) + { + $this->expiryDateTime = $dateTime; + } + + /** + * Has the token expired? + * + * @return bool + */ + public function isExpired() + { + return (new DateTime()) > $this->getExpiryDateTime(); + } +} diff --git a/src/Entities/Traits/TokenEntityTrait.php b/src/Entities/Traits/TokenEntityTrait.php new file mode 100644 index 000000000..87fdd2f46 --- /dev/null +++ b/src/Entities/Traits/TokenEntityTrait.php @@ -0,0 +1,127 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entities\Traits; + +use DateTime; +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Entities\ScopeEntityInterface; + +trait TokenEntityTrait +{ + /** + * @var ScopeEntityInterface[] + */ + protected $scopes = []; + + /** + * @var DateTime + */ + protected $expiryDateTime; + + /** + * @var string|int + */ + protected $userIdentifier; + + /** + * @var ClientEntityInterface + */ + protected $client; + + /** + * Associate a scope with the token. + * + * @param \League\OAuth2\Server\Entities\ScopeEntityInterface $scope + */ + public function addScope(ScopeEntityInterface $scope) + { + $this->scopes[$scope->getIdentifier()] = $scope; + } + + /** + * Return an array of scopes associated with the token. + * + * @return ScopeEntityInterface[] + */ + public function getScopes() + { + return array_values($this->scopes); + } + + /** + * Get the token's expiry date time. + * + * @return DateTime + */ + public function getExpiryDateTime() + { + return $this->expiryDateTime; + } + + /** + * Set the date time when the token expires. + * + * @param DateTime $dateTime + */ + public function setExpiryDateTime(DateTime $dateTime) + { + $this->expiryDateTime = $dateTime; + } + + /** + * Set the identifier of the user associated with the token. + * + * @param string|int $identifier The identifier of the user + */ + public function setUserIdentifier($identifier) + { + $this->userIdentifier = $identifier; + } + + /** + * Get the token user's identifier. + * + * @return string|int + */ + public function getUserIdentifier() + { + return $this->userIdentifier; + } + + /** + * Get the client that the token was issued to. + * + * @return ClientEntityInterface + */ + public function getClient() + { + return $this->client; + } + + /** + * Set the client that the token was issued to. + * + * @param \League\OAuth2\Server\Entities\ClientEntityInterface $client + */ + public function setClient(ClientEntityInterface $client) + { + $this->client = $client; + } + + /** + * Has the token expired? + * + * @return bool + */ + public function isExpired() + { + return (new DateTime()) > $this->getExpiryDateTime(); + } +} diff --git a/src/Entities/UserEntityInterface.php b/src/Entities/UserEntityInterface.php new file mode 100644 index 000000000..c71cb9c52 --- /dev/null +++ b/src/Entities/UserEntityInterface.php @@ -0,0 +1,20 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Entities; + +interface UserEntityInterface +{ + /** + * Return the user's identifier. + * + * @return mixed + */ + public function getIdentifier(); +} diff --git a/src/Entity/AbstractTokenEntity.php b/src/Entity/AbstractTokenEntity.php deleted file mode 100644 index 5f0465c3a..000000000 --- a/src/Entity/AbstractTokenEntity.php +++ /dev/null @@ -1,209 +0,0 @@ - - * @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\Util\SecureKey; - -/** - * Abstract token class - */ -abstract class AbstractTokenEntity -{ - /** - * Token identifier - * - * @var string - */ - protected $id; - - /** - * Associated session - * - * @var \League\OAuth2\Server\Entity\SessionEntity - */ - protected $session; - - /** - * Session scopes - * - * @var \League\OAuth2\Server\Entity\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\Entity\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; - } - - /** - * Is the token expired? - * - * @return bool - */ - public function isExpired() - { - return ((time() - $this->expireTime) > 0); - } - - /** - * Set token ID - * - * @param string $id 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 \League\OAuth2\Server\Entity\ScopeEntity[] - * - * @return array - */ - protected function formatScopes($unformatted = []) - { - if (is_null($unformatted)) { - return []; - } - - $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 deleted file mode 100644 index 7342b4986..000000000 --- a/src/Entity/AccessTokenEntity.php +++ /dev/null @@ -1,93 +0,0 @@ - - * @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\Entity\SessionEntity - */ - public function getSession() - { - if ($this->session instanceof SessionEntity) { - return $this->session; - } - - $this->session = $this->server->getSessionStorage()->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 access token - * - * @return \League\OAuth2\Server\Entity\ScopeEntity[] - */ - public function getScopes() - { - if ($this->scopes === null) { - $this->scopes = $this->formatScopes( - $this->server->getAccessTokenStorage()->getScopes($this) - ); - } - - return $this->scopes; - } - - /** - * {@inheritdoc} - */ - public function save() - { - $this->server->getAccessTokenStorage()->create( - $this->getId(), - $this->getExpireTime(), - $this->getSession()->getId() - ); - - // Associate the scope with the token - foreach ($this->getScopes() as $scope) { - $this->server->getAccessTokenStorage()->associateScope($this, $scope); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function expire() - { - $this->server->getAccessTokenStorage()->delete($this); - } -} diff --git a/src/Entity/AuthCodeEntity.php b/src/Entity/AuthCodeEntity.php deleted file mode 100644 index 5953aaf82..000000000 --- a/src/Entity/AuthCodeEntity.php +++ /dev/null @@ -1,128 +0,0 @@ - - * @copyright Copyright (c) Alex Bilbie - * @license http://mit-license.org/ - * @link https://github.com/thephpleague/oauth2-server - */ - -namespace League\OAuth2\Server\Entity; - -/** - * Auth Code 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, - ]); - } - - /** - * Get session - * - * @return \League\OAuth2\Server\Entity\SessionEntity - */ - public function getSession() - { - if ($this->session instanceof SessionEntity) { - return $this->session; - } - - $this->session = $this->server->getSessionStorage()->getByAuthCode($this); - - return $this->session; - } - - /** - * Return all scopes associated with the session - * - * @return \League\OAuth2\Server\Entity\ScopeEntity[] - */ - public function getScopes() - { - if ($this->scopes === null) { - $this->scopes = $this->formatScopes( - $this->server->getAuthCodeStorage()->getScopes($this) - ); - } - - return $this->scopes; - } - - /** - * {@inheritdoc} - */ - public function save() - { - $this->server->getAuthCodeStorage()->create( - $this->getId(), - $this->getExpireTime(), - $this->getSession()->getId(), - $this->getRedirectUri() - ); - - // Associate the scope with the token - foreach ($this->getScopes() as $scope) { - $this->server->getAuthCodeStorage()->associateScope($this, $scope); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function expire() - { - $this->server->getAuthCodeStorage()->delete($this); - } -} diff --git a/src/Entity/ClientEntity.php b/src/Entity/ClientEntity.php deleted file mode 100644 index b0e95285a..000000000 --- a/src/Entity/ClientEntity.php +++ /dev/null @@ -1,111 +0,0 @@ - - * @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 deleted file mode 100644 index 9424fdfd0..000000000 --- a/src/Entity/EntityTrait.php +++ /dev/null @@ -1,33 +0,0 @@ - - * @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 - * - * @return self - */ - 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 deleted file mode 100644 index f1ec89a53..000000000 --- a/src/Entity/RefreshTokenEntity.php +++ /dev/null @@ -1,94 +0,0 @@ - - * @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 $accessTokenId - * - * @return self - */ - public function setAccessTokenId($accessTokenId) - { - $this->accessTokenId = $accessTokenId; - - return $this; - } - - /** - * Associate an access token - * - * @param \League\OAuth2\Server\Entity\AccessTokenEntity $accessTokenEntity - * - * @return self - */ - public function setAccessToken(AccessTokenEntity $accessTokenEntity) - { - $this->accessTokenEntity = $accessTokenEntity; - - return $this; - } - - /** - * Return access token - * - * @return AccessTokenEntity - */ - public function getAccessToken() - { - if (! $this->accessTokenEntity instanceof AccessTokenEntity) { - $this->accessTokenEntity = $this->server->getAccessTokenStorage()->get($this->accessTokenId); - } - - return $this->accessTokenEntity; - } - - /** - * {@inheritdoc} - */ - public function save() - { - $this->server->getRefreshTokenStorage()->create( - $this->getId(), - $this->getExpireTime(), - $this->getAccessToken()->getId() - ); - } - - /** - * {@inheritdoc} - */ - public function expire() - { - $this->server->getRefreshTokenStorage()->delete($this); - } -} diff --git a/src/Entity/ScopeEntity.php b/src/Entity/ScopeEntity.php deleted file mode 100644 index 1d5c946fc..000000000 --- a/src/Entity/ScopeEntity.php +++ /dev/null @@ -1,90 +0,0 @@ - - * @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 deleted file mode 100644 index c78cead51..000000000 --- a/src/Entity/SessionEntity.php +++ /dev/null @@ -1,308 +0,0 @@ - - * @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\SessionOwnerEvent; - -/** - * Session entity grant - */ -class SessionEntity -{ - /** - * Session identifier - * - * @var string - */ - protected $id; - - /** - * Client identifier - * - * @var \League\OAuth2\Server\Entity\ClientEntity - */ - 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\AuthorizationServer|\League\OAuth2\Server\ResourceServer - */ - 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 \League\OAuth2\Server\Entity\ScopeEntity[] - */ - public function getScopes() - { - if ($this->scopes === null) { - $this->scopes = $this->formatScopes($this->server->getSessionStorage()->getScopes($this)); - } - - return $this->scopes; - } - - /** - * Format the local scopes array - * - * @param \League\OAuth2\Server\Entity\Scope[] - * - * @return array - */ - private function formatScopes($unformatted = []) - { - $scopes = []; - if (is_array($unformatted)) { - foreach ($unformatted 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\ClientEntity - */ - public function getClient() - { - if ($this->client instanceof ClientEntity) { - return $this->client; - } - - $this->client = $this->server->getClientStorage()->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 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->getSessionStorage()->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->getSessionStorage()->associateScope($this, $scope); - } - } -} diff --git a/src/Event/ClientAuthenticationFailedEvent.php b/src/Event/ClientAuthenticationFailedEvent.php deleted file mode 100644 index 4448bb4ee..000000000 --- a/src/Event/ClientAuthenticationFailedEvent.php +++ /dev/null @@ -1,55 +0,0 @@ - - * @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 Symfony\Component\HttpFoundation\Request; - -class ClientAuthenticationFailedEvent extends AbstractEvent -{ - /** - * Request - * - * @var \Symfony\Component\HttpFoundation\Request - */ - private $request; - - /** - * Init the event with a request - * - * @param \Symfony\Component\HttpFoundation\Request $request - */ - public function __construct(Request $request) - { - $this->request = $request; - } - - /** - * The name of the event - * - * @return string - */ - public function getName() - { - return 'error.auth.client'; - } - - /** - * Return request - * - * @return \Symfony\Component\HttpFoundation\Request - */ - public function getRequest() - { - return $this->request; - } -} diff --git a/src/Event/SessionOwnerEvent.php b/src/Event/SessionOwnerEvent.php deleted file mode 100644 index 83c4e7665..000000000 --- a/src/Event/SessionOwnerEvent.php +++ /dev/null @@ -1,55 +0,0 @@ - - * @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/Event/UserAuthenticationFailedEvent.php b/src/Event/UserAuthenticationFailedEvent.php deleted file mode 100644 index 4cd8c4cdc..000000000 --- a/src/Event/UserAuthenticationFailedEvent.php +++ /dev/null @@ -1,55 +0,0 @@ - - * @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 Symfony\Component\HttpFoundation\Request; - -class UserAuthenticationFailedEvent extends AbstractEvent -{ - /** - * Request - * - * @var \Symfony\Component\HttpFoundation\Request - */ - private $request; - - /** - * Init the event with a request - * - * @param \Symfony\Component\HttpFoundation\Request $request - */ - public function __construct(Request $request) - { - $this->request = $request; - } - - /** - * The name of the event - * - * @return string - */ - public function getName() - { - return 'error.auth.user'; - } - - /** - * Return request - * - * @return \Symfony\Component\HttpFoundation\Request - */ - public function getRequest() - { - return $this->request; - } -} diff --git a/src/Exception/AccessDeniedException.php b/src/Exception/AccessDeniedException.php deleted file mode 100644 index e5d84b617..000000000 --- a/src/Exception/AccessDeniedException.php +++ /dev/null @@ -1,36 +0,0 @@ - - * @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 deleted file mode 100644 index ba01d2776..000000000 --- a/src/Exception/InvalidClientException.php +++ /dev/null @@ -1,36 +0,0 @@ - - * @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 deleted file mode 100644 index 40c4ce7ef..000000000 --- a/src/Exception/InvalidCredentialsException.php +++ /dev/null @@ -1,36 +0,0 @@ - - * @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 deleted file mode 100644 index 4027a2136..000000000 --- a/src/Exception/InvalidGrantException.php +++ /dev/null @@ -1,43 +0,0 @@ - - * @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) - { - $this->parameter = $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 deleted file mode 100644 index 5ca3d9218..000000000 --- a/src/Exception/InvalidRefreshException.php +++ /dev/null @@ -1,36 +0,0 @@ - - * @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 deleted file mode 100644 index 3a6829303..000000000 --- a/src/Exception/InvalidRequestException.php +++ /dev/null @@ -1,45 +0,0 @@ - - * @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, $redirectUri = null) - { - $this->parameter = $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 - ) - ); - - $this->redirectUri = $redirectUri; - } -} diff --git a/src/Exception/InvalidScopeException.php b/src/Exception/InvalidScopeException.php deleted file mode 100644 index e70cd6923..000000000 --- a/src/Exception/InvalidScopeException.php +++ /dev/null @@ -1,45 +0,0 @@ - - * @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, $redirectUri = null) - { - $this->parameter = $parameter; - parent::__construct( - sprintf( - 'The requested scope is invalid, unknown, or malformed. Check the "%s" scope.', - $parameter - ) - ); - - $this->redirectUri = $redirectUri; - } -} diff --git a/src/Exception/OAuthException.php b/src/Exception/OAuthException.php deleted file mode 100644 index 3678984b2..000000000 --- a/src/Exception/OAuthException.php +++ /dev/null @@ -1,147 +0,0 @@ - - * @copyright Copyright (c) Alex Bilbie - * @license http://mit-license.org/ - * @link https://github.com/thephpleague/oauth2-server - */ - -namespace League\OAuth2\Server\Exception; - -use League\OAuth2\Server\Util\RedirectUri; -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; - - /** - * Redirect URI if the server should redirect back to the client - * - * @var string|null - */ - public $redirectUri = null; - - /** - * The exception type - */ - public $errorType = ''; - - /** - * Parameter eventually passed to Exception - */ - public $parameter = ''; - - /** - * Throw a new exception - * - * @param string $msg Exception Message - */ - public function __construct($msg = 'An error occured') - { - parent::__construct($msg); - } - - /** - * Should the server redirect back to the client? - * - * @return bool - */ - public function shouldRedirect() - { - return is_null($this->redirectUri) ? false : true; - } - - /** - * Return redirect URI if set - * - * @return string|null - */ - public function getRedirectUri() - { - return RedirectUri::make( - $this->redirectUri, - [ - 'error' => $this->errorType, - 'message' => $this->getMessage(), - ] - ); - } - - /** - * Return parameter if set - * - * @return string - */ - public function getParameter() - { - return $this->parameter; - } - - /** - * 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 = Request::createFromGlobals(); - 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'; - } elseif (strpos($authHeader, 'MAC') === 0) { - $authScheme = 'MAC'; - } - } - } - if ($authScheme !== null) { - $headers[] = 'WWW-Authenticate: '.$authScheme.' realm=""'; - } - } - // @codeCoverageIgnoreEnd - return $headers; - } -} diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php new file mode 100644 index 000000000..2f6983416 --- /dev/null +++ b/src/Exception/OAuthServerException.php @@ -0,0 +1,274 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Exception; + +use Psr\Http\Message\ResponseInterface; + +class OAuthServerException extends \Exception +{ + /** + * @var int + */ + private $httpStatusCode; + + /** + * @var string + */ + private $errorType; + + /** + * @var null|string + */ + private $hint; + + /** + * @var null|string + */ + private $redirectUri; + + /** + * Throw a new exception. + * + * @param string $message Error message + * @param int $code Error code + * @param string $errorType Error type + * @param int $httpStatusCode HTTP status code to send (default = 400) + * @param null|string $hint A helper hint + * @param null|string $redirectUri A HTTP URI to redirect the user back to + */ + public function __construct($message, $code, $errorType, $httpStatusCode = 400, $hint = null, $redirectUri = null) + { + parent::__construct($message, $code); + $this->httpStatusCode = $httpStatusCode; + $this->errorType = $errorType; + $this->hint = $hint; + $this->redirectUri = $redirectUri; + } + + /** + * Unsupported grant type error. + * + * @return static + */ + public static function unsupportedGrantType() + { + $errorMessage = 'The authorization grant type is not supported by the authorization server.'; + $hint = 'Check the `grant_type` parameter'; + + return new static($errorMessage, 2, 'unsupported_grant_type', 400, $hint); + } + + /** + * Invalid request error. + * + * @param string $parameter The invalid parameter + * @param string|null $hint + * + * @return static + */ + public static function invalidRequest($parameter, $hint = null) + { + $errorMessage = 'The request is missing a required parameter, includes an invalid parameter value, ' . + 'includes a parameter more than once, or is otherwise malformed.'; + $hint = ($hint === null) ? sprintf('Check the `%s` parameter', $parameter) : $hint; + + return new static($errorMessage, 3, 'invalid_request', 400, $hint); + } + + /** + * Invalid client error. + * + * @return static + */ + public static function invalidClient() + { + $errorMessage = 'Client authentication failed'; + + return new static($errorMessage, 4, 'invalid_client', 401); + } + + /** + * Invalid scope error. + * + * @param string $scope The bad scope + * @param null|string $redirectUri A HTTP URI to redirect the user back to + * + * @return static + */ + public static function invalidScope($scope, $redirectUri = null) + { + $errorMessage = 'The requested scope is invalid, unknown, or malformed'; + $hint = sprintf('Check the `%s` scope', $scope); + + return new static($errorMessage, 5, 'invalid_scope', 400, $hint, $redirectUri); + } + + /** + * Invalid credentials error. + * + * @return static + */ + public static function invalidCredentials() + { + return new static('The user credentials were incorrect.', 6, 'invalid_credentials', 401); + } + + /** + * Server error. + * + * @param $hint + * + * @return static + * + * @codeCoverageIgnore + */ + public static function serverError($hint) + { + return new static( + 'The authorization server encountered an unexpected condition which prevented it from fulfilling' + . ' the request: ' . $hint, + 7, + 'server_error', + 500 + ); + } + + /** + * Invalid refresh token. + * + * @param string|null $hint + * + * @return static + */ + public static function invalidRefreshToken($hint = null) + { + return new static('The refresh token is invalid.', 8, 'invalid_request', 400, $hint); + } + + /** + * Access denied. + * + * @param string|null $hint + * @param string|null $redirectUri + * + * @return static + */ + public static function accessDenied($hint = null, $redirectUri = null) + { + return new static( + 'The resource owner or authorization server denied the request.', + 9, + 'access_denied', + 401, + $hint, + $redirectUri + ); + } + + /** + * @return string + */ + public function getErrorType() + { + return $this->errorType; + } + + /** + * Generate a HTTP response. + * + * @param \Psr\Http\Message\ResponseInterface $response + * @param bool $useFragment True if errors should be in the URI fragment instead of + * query string + * + * @return \Psr\Http\Message\ResponseInterface + */ + public function generateHttpResponse(ResponseInterface $response, $useFragment = false) + { + $headers = $this->getHttpHeaders(); + + $payload = [ + 'error' => $this->getErrorType(), + 'message' => $this->getMessage(), + ]; + + if ($this->hint !== null) { + $payload['hint'] = $this->hint; + } + + if ($this->redirectUri !== null) { + if ($useFragment === true) { + $this->redirectUri .= (strstr($this->redirectUri, '#') === false) ? '#' : '&'; + } else { + $this->redirectUri .= (strstr($this->redirectUri, '?') === false) ? '?' : '&'; + } + + return $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload)); + } + + foreach ($headers as $header => $content) { + $response = $response->withHeader($header, $content); + } + + $response->getBody()->write(json_encode($payload)); + + return $response->withStatus($this->getHttpStatusCode()); + } + + /** + * Get all headers that have to be send with the error response. + * + * @return array Array with header values + */ + public function getHttpHeaders() + { + $headers = [ + 'Content-type' => 'application/json', + ]; + + // 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 = 'Basic'; + if (array_key_exists('HTTP_AUTHORIZATION', $_SERVER) !== false + && strpos($_SERVER['HTTP_AUTHORIZATION'], 'Bearer') === 0 + ) { + $authScheme = 'Bearer'; + } + $headers[] = 'WWW-Authenticate: ' . $authScheme . ' realm="OAuth"'; + } + // @codeCoverageIgnoreEnd + return $headers; + } + + /** + * Returns the HTTP status code to send when the exceptions is output. + * + * @return int + */ + public function getHttpStatusCode() + { + return $this->httpStatusCode; + } + + /** + * @return null|string + */ + public function getHint() + { + return $this->hint; + } +} diff --git a/src/Exception/ServerErrorException.php b/src/Exception/ServerErrorException.php deleted file mode 100644 index 70e9532c1..000000000 --- a/src/Exception/ServerErrorException.php +++ /dev/null @@ -1,39 +0,0 @@ - - * @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) - { - $this->parameter = $parameter; - $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 deleted file mode 100644 index fd1f18c36..000000000 --- a/src/Exception/UnauthorizedClientException.php +++ /dev/null @@ -1,36 +0,0 @@ - - * @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 deleted file mode 100644 index b0a33ce32..000000000 --- a/src/Exception/UnsupportedGrantTypeException.php +++ /dev/null @@ -1,43 +0,0 @@ - - * @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) - { - $this->parameter = $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 deleted file mode 100644 index 663326d1e..000000000 --- a/src/Exception/UnsupportedResponseTypeException.php +++ /dev/null @@ -1,38 +0,0 @@ - - * @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, $redirectUri = null) - { - $this->parameter = $parameter; - parent::__construct('The authorization server does not support obtaining an access token using this method.'); - $this->redirectUri = $redirectUri; - } -} diff --git a/src/Grant/AbstractAuthorizeGrant.php b/src/Grant/AbstractAuthorizeGrant.php new file mode 100644 index 000000000..7f05100ce --- /dev/null +++ b/src/Grant/AbstractAuthorizeGrant.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Grant; + +abstract class AbstractAuthorizeGrant extends AbstractGrant +{ + /** + * @param string $uri + * @param array $params + * @param string $queryDelimiter + * + * @return string + */ + public function makeRedirectUri($uri, $params = [], $queryDelimiter = '?') + { + $uri .= (strstr($uri, $queryDelimiter) === false) ? $queryDelimiter : '&'; + + return $uri . http_build_query($params); + } +} diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index fd5cb52f9..219097125 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -1,197 +1,429 @@ * @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\ClientEntity; -use League\OAuth2\Server\Entity\ScopeEntity; -use League\OAuth2\Server\Exception; +use League\Event\EmitterAwareTrait; +use League\OAuth2\Server\CryptTrait; +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Entities\ScopeEntityInterface; +use League\OAuth2\Server\Exception\OAuthServerException; +use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; +use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface; +use League\OAuth2\Server\Repositories\ClientRepositoryInterface; +use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface; +use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; +use League\OAuth2\Server\Repositories\UserRepositoryInterface; +use League\OAuth2\Server\RequestEvent; +use League\OAuth2\Server\RequestTypes\AuthorizationRequest; +use Psr\Http\Message\ServerRequestInterface; /** - * Abstract grant class + * Abstract grant class. */ abstract class AbstractGrant implements GrantTypeInterface { + use EmitterAwareTrait, CryptTrait; + + const SCOPE_DELIMITER_STRING = ' '; + /** - * Grant identifier - * - * @var string + * @var ServerRequestInterface */ - protected $identifier = ''; + protected $request; /** - * Response type - * - * @var string + * @var ClientRepositoryInterface */ - protected $responseType; + protected $clientRepository; /** - * Callback to authenticate a user's name and password - * - * @var callable + * @var AccessTokenRepositoryInterface */ - protected $callback; + protected $accessTokenRepository; /** - * AuthServer instance - * - * @var \League\OAuth2\Server\AuthorizationServer + * @var ScopeRepositoryInterface */ - protected $server; + protected $scopeRepository; /** - * Access token expires in override - * - * @var int + * @var \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface */ - protected $accessTokenTTL; + protected $authCodeRepository; /** - * {@inheritdoc} + * @var \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface + */ + protected $refreshTokenRepository; + + /** + * @var \League\OAuth2\Server\Repositories\UserRepositoryInterface */ - public function getIdentifier() + protected $userRepository; + + /** + * @var \DateInterval + */ + protected $refreshTokenTTL; + + /** + * @param ClientRepositoryInterface $clientRepository + */ + public function setClientRepository(ClientRepositoryInterface $clientRepository) { - return $this->identifier; + $this->clientRepository = $clientRepository; } /** - * {@inheritdoc} + * @param AccessTokenRepositoryInterface $accessTokenRepository + */ + public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository) + { + $this->accessTokenRepository = $accessTokenRepository; + } + + /** + * @param ScopeRepositoryInterface $scopeRepository */ - public function setIdentifier($identifier) + public function setScopeRepository(ScopeRepositoryInterface $scopeRepository) { - $this->identifier = $identifier; + $this->scopeRepository = $scopeRepository; + } - return $this; + /** + * @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository + */ + public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository) + { + $this->refreshTokenRepository = $refreshTokenRepository; + } + + /** + * @param \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface $authCodeRepository + */ + public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository) + { + $this->authCodeRepository = $authCodeRepository; + } + + /** + * @param \League\OAuth2\Server\Repositories\UserRepositoryInterface $userRepository + */ + public function setUserRepository(UserRepositoryInterface $userRepository) + { + $this->userRepository = $userRepository; } /** * {@inheritdoc} */ - public function getResponseType() + public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL) { - return $this->responseType; + $this->refreshTokenTTL = $refreshTokenTTL; } /** - * Get the TTL for an access token + * Validate the client. + * + * @param \Psr\Http\Message\ServerRequestInterface $request + * + * @throws \League\OAuth2\Server\Exception\OAuthServerException * - * @return int The TTL + * @return \League\OAuth2\Server\Entities\ClientEntityInterface */ - public function getAccessTokenTTL() + protected function validateClient(ServerRequestInterface $request) { - if ($this->accessTokenTTL) { - return $this->accessTokenTTL; + $clientId = $this->getRequestParameter( + 'client_id', + $request, + $this->getServerParameter('PHP_AUTH_USER', $request) + ); + if (is_null($clientId)) { + throw OAuthServerException::invalidRequest('client_id'); } - return $this->server->getAccessTokenTTL(); + // If the client is confidential require the client secret + $clientSecret = $this->getRequestParameter( + 'client_secret', + $request, + $this->getServerParameter('PHP_AUTH_PW', $request) + ); + + $client = $this->clientRepository->getClientEntity( + $clientId, + $this->getIdentifier(), + $clientSecret + ); + + if (!$client instanceof ClientEntityInterface) { + $this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); + throw OAuthServerException::invalidClient(); + } + + // If a redirect URI is provided ensure it matches what is pre-registered + $redirectUri = $this->getRequestParameter('redirect_uri', $request, null); + if ($redirectUri !== null) { + if ( + is_string($client->getRedirectUri()) + && (strcmp($client->getRedirectUri(), $redirectUri) !== 0) + ) { + $this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); + throw OAuthServerException::invalidClient(); + } elseif ( + is_array($client->getRedirectUri()) + && in_array($redirectUri, $client->getRedirectUri()) === false + ) { + $this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); + throw OAuthServerException::invalidClient(); + } + } + + return $client; } /** - * Override the default access token expire time + * Validate scopes in the request. + * + * @param string $scopes + * @param string $redirectUri * - * @param int $accessTokenTTL + * @throws \League\OAuth2\Server\Exception\OAuthServerException * - * @return self + * @return \League\OAuth2\Server\Entities\ScopeEntityInterface[] */ - public function setAccessTokenTTL($accessTokenTTL) - { - $this->accessTokenTTL = $accessTokenTTL; + public function validateScopes( + $scopes, + $redirectUri = null + ) { + $scopesList = array_filter( + explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), + function ($scope) { + return !empty($scope); + } + ); + + $scopes = []; + foreach ($scopesList as $scopeItem) { + $scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem); + + if (!$scope instanceof ScopeEntityInterface) { + throw OAuthServerException::invalidScope($scopeItem, $redirectUri); + } + + $scopes[] = $scope; + } - return $this; + return $scopes; } /** - * {@inheritdoc} + * Retrieve request parameter. + * + * @param string $parameter + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param mixed $default + * + * @return null|string */ - public function setAuthorizationServer(AuthorizationServer $server) + protected function getRequestParameter($parameter, ServerRequestInterface $request, $default = null) { - $this->server = $server; + $requestParameters = (array) $request->getParsedBody(); + + return isset($requestParameters[$parameter]) ? $requestParameters[$parameter] : $default; + } - return $this; + /** + * Retrieve query string parameter. + * + * @param string $parameter + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param mixed $default + * + * @return null|string + */ + protected function getQueryStringParameter($parameter, ServerRequestInterface $request, $default = null) + { + return isset($request->getQueryParams()[$parameter]) ? $request->getQueryParams()[$parameter] : $default; } /** - * Given a list of scopes, validate them and return an array of Scope entities + * Retrieve cookie parameter. * - * @param string $scopeParam A string of scopes (e.g. "profile email birthday") - * @param \League\OAuth2\Server\Entity\ClientEntity $client Client entity - * @param string|null $redirectUri The redirect URI to return the user to + * @param string $parameter + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param mixed $default * - * @return \League\OAuth2\Server\Entity\ScopeEntity[] + * @return null|string + */ + protected function getCookieParameter($parameter, ServerRequestInterface $request, $default = null) + { + return isset($request->getCookieParams()[$parameter]) ? $request->getCookieParams()[$parameter] : $default; + } + + /** + * Retrieve server parameter. * - * @throws \League\OAuth2\Server\Exception\InvalidScopeException If scope is invalid, or no scopes passed when required - * @throws + * @param string $parameter + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param mixed $default + * + * @return null|string */ - public function validateScopes($scopeParam = '', ClientEntity $client, $redirectUri = null) + protected function getServerParameter($parameter, ServerRequestInterface $request, $default = null) { - $scopesList = explode($this->server->getScopeDelimiter(), $scopeParam); + return isset($request->getServerParams()[$parameter]) ? $request->getServerParams()[$parameter] : $default; + } - for ($i = 0; $i < count($scopesList); $i++) { - $scopesList[$i] = trim($scopesList[$i]); - if ($scopesList[$i] === '') { - unset($scopesList[$i]); // Remove any junk scopes - } - } + /** + * Issue an access token. + * + * @param \DateInterval $accessTokenTTL + * @param \League\OAuth2\Server\Entities\ClientEntityInterface $client + * @param string $userIdentifier + * @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes + * + * @return \League\OAuth2\Server\Entities\AccessTokenEntityInterface + */ + protected function issueAccessToken( + \DateInterval $accessTokenTTL, + ClientEntityInterface $client, + $userIdentifier, + array $scopes = [] + ) { + $accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier); + $accessToken->setClient($client); + $accessToken->setUserIdentifier($userIdentifier); + $accessToken->setIdentifier($this->generateUniqueIdentifier()); + $accessToken->setExpiryDateTime((new \DateTime())->add($accessTokenTTL)); - 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()]; - } + foreach ($scopes as $scope) { + $accessToken->addScope($scope); } - $scopes = []; + $this->accessTokenRepository->persistNewAccessToken($accessToken); - foreach ($scopesList as $scopeItem) { - $scope = $this->server->getScopeStorage()->get( - $scopeItem, - $this->getIdentifier(), - $client->getId() - ); - - if (($scope instanceof ScopeEntity) === false) { - throw new Exception\InvalidScopeException($scopeItem, $redirectUri); - } + return $accessToken; + } + + /** + * Issue an auth code. + * + * @param \DateInterval $authCodeTTL + * @param \League\OAuth2\Server\Entities\ClientEntityInterface $client + * @param string $userIdentifier + * @param string $redirectUri + * @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes + * + * @return \League\OAuth2\Server\Entities\AuthCodeEntityInterface + */ + protected function issueAuthCode( + \DateInterval $authCodeTTL, + ClientEntityInterface $client, + $userIdentifier, + $redirectUri, + array $scopes = [] + ) { + $authCode = $this->authCodeRepository->getNewAuthCode(); + $authCode->setIdentifier($this->generateUniqueIdentifier()); + $authCode->setExpiryDateTime((new \DateTime())->add($authCodeTTL)); + $authCode->setClient($client); + $authCode->setUserIdentifier($userIdentifier); + $authCode->setRedirectUri($redirectUri); - $scopes[$scope->getId()] = $scope; + foreach ($scopes as $scope) { + $authCode->addScope($scope); } - return $scopes; + $this->authCodeRepository->persistNewAuthCode($authCode); + + return $authCode; } /** - * Format the local scopes array + * @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken * - * @param \League\OAuth2\Server\Entity\ScopeEntity[] + * @return \League\OAuth2\Server\Entities\RefreshTokenEntityInterface + */ + protected function issueRefreshToken(AccessTokenEntityInterface $accessToken) + { + $refreshToken = $this->refreshTokenRepository->getNewRefreshToken(); + $refreshToken->setIdentifier($this->generateUniqueIdentifier()); + $refreshToken->setExpiryDateTime((new \DateTime())->add($this->refreshTokenTTL)); + $refreshToken->setAccessToken($accessToken); + + $this->refreshTokenRepository->persistNewRefreshToken($refreshToken); + + return $refreshToken; + } + + /** + * Generate a new unique identifier. + * + * @param int $length * - * @return array + * @throws \League\OAuth2\Server\Exception\OAuthServerException + * + * @return string */ - protected function formatScopes($unformated = []) + protected function generateUniqueIdentifier($length = 40) { - $scopes = []; - foreach ($unformated as $scope) { - if ($scope instanceof ScopeEntity) { - $scopes[$scope->getId()] = $scope; - } + try { + return bin2hex(random_bytes($length)); + // @codeCoverageIgnoreStart + } catch (\TypeError $e) { + throw OAuthServerException::serverError('An unexpected error has occurred'); + } catch (\Error $e) { + throw OAuthServerException::serverError('An unexpected error has occurred'); + } catch (\Exception $e) { + // If you get this message, the CSPRNG failed hard. + throw OAuthServerException::serverError('Could not generate a random string'); } + // @codeCoverageIgnoreEnd + } - return $scopes; + /** + * {@inheritdoc} + */ + public function canRespondToAccessTokenRequest(ServerRequestInterface $request) + { + $requestParameters = (array) $request->getParsedBody(); + + return ( + array_key_exists('grant_type', $requestParameters) + && $requestParameters['grant_type'] === $this->getIdentifier() + ); + } + + /** + * {@inheritdoc} + */ + public function canRespondToAuthorizationRequest(ServerRequestInterface $request) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function validateAuthorizationRequest(ServerRequestInterface $request) + { + throw new \LogicException('This grant cannot validate an authorization request'); + } + + /** + * {@inheritdoc} + */ + public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest) + { + throw new \LogicException('This grant cannot complete an authorization request'); } } diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 943a3b5da..64ae2dcc4 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -1,303 +1,266 @@ * @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\AuthCodeEntity; -use League\OAuth2\Server\Entity\ClientEntity; -use League\OAuth2\Server\Entity\RefreshTokenEntity; -use League\OAuth2\Server\Entity\SessionEntity; -use League\OAuth2\Server\Event; -use League\OAuth2\Server\Exception; -use League\OAuth2\Server\Util\SecureKey; - -/** - * Auth code grant class - */ -class AuthCodeGrant extends AbstractGrant +use DateInterval; +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Entities\ScopeEntityInterface; +use League\OAuth2\Server\Entities\UserEntityInterface; +use League\OAuth2\Server\Exception\OAuthServerException; +use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface; +use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface; +use League\OAuth2\Server\RequestEvent; +use League\OAuth2\Server\RequestTypes\AuthorizationRequest; +use League\OAuth2\Server\ResponseTypes\RedirectResponse; +use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface; +use Psr\Http\Message\ServerRequestInterface; + +class AuthCodeGrant extends AbstractAuthorizeGrant { /** - * Grant identifier - * - * @var string + * @var \DateInterval */ - protected $identifier = 'authorization_code'; + private $authCodeTTL; /** - * Response type - * - * @var string + * @param \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface $authCodeRepository + * @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository + * @param \DateInterval $authCodeTTL */ - protected $responseType = 'code'; + public function __construct( + AuthCodeRepositoryInterface $authCodeRepository, + RefreshTokenRepositoryInterface $refreshTokenRepository, + \DateInterval $authCodeTTL + ) { + $this->setAuthCodeRepository($authCodeRepository); + $this->setRefreshTokenRepository($refreshTokenRepository); + $this->authCodeTTL = $authCodeTTL; + $this->refreshTokenTTL = new \DateInterval('P1M'); + } /** - * AuthServer instance + * Respond to an access token request. * - * @var \League\OAuth2\Server\AuthorizationServer - */ - protected $server = null; - - /** - * Access token expires in override + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType + * @param \DateInterval $accessTokenTTL * - * @var int - */ - protected $accessTokenTTL = null; - - /** - * The TTL of the auth token + * @throws \League\OAuth2\Server\Exception\OAuthServerException * - * @var integer + * @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface */ - protected $authTokenTTL = 600; + public function respondToAccessTokenRequest( + ServerRequestInterface $request, + ResponseTypeInterface $responseType, + DateInterval $accessTokenTTL + ) { + // Validate request + $client = $this->validateClient($request); + $encryptedAuthCode = $this->getRequestParameter('code', $request, null); + + if ($encryptedAuthCode === null) { + throw OAuthServerException::invalidRequest('code'); + } - /** - * Whether to require the client secret when - * completing the flow. - * - * @var boolean - */ - protected $requireClientSecret = true; + // Validate the authorization code + try { + $authCodePayload = json_decode($this->decrypt($encryptedAuthCode)); + if (time() > $authCodePayload->expire_time) { + throw OAuthServerException::invalidRequest('code', 'Authorization code has expired'); + } + + if ($this->authCodeRepository->isAuthCodeRevoked($authCodePayload->auth_code_id) === true) { + throw OAuthServerException::invalidRequest('code', 'Authorization code has been revoked'); + } + + if ($authCodePayload->client_id !== $client->getIdentifier()) { + throw OAuthServerException::invalidRequest('code', 'Authorization code was not issued to this client'); + } + + // The redirect URI is required in this request + $redirectUri = $this->getRequestParameter('redirect_uri', $request, null); + if (empty($authCodePayload->redirect_uri) === false && $redirectUri === null) { + throw OAuthServerException::invalidRequest('redirect_uri'); + } + + if ($authCodePayload->redirect_uri !== $redirectUri) { + throw OAuthServerException::invalidRequest('redirect_uri', 'Invalid redirect URI'); + } + + $scopes = []; + foreach ($authCodePayload->scopes as $scopeId) { + $scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId); + + if (!$scope instanceof ScopeEntityInterface) { + // @codeCoverageIgnoreStart + throw OAuthServerException::invalidScope($scopeId); + // @codeCoverageIgnoreEnd + } + + $scopes[] = $scope; + } + + // Finalize the requested scopes + $scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $authCodePayload->user_id); + } catch (\LogicException $e) { + throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code'); + } - /** - * Override the default access token expire time - * - * @param int $authTokenTTL - * - * @return void - */ - public function setAuthTokenTTL($authTokenTTL) - { - $this->authTokenTTL = $authTokenTTL; + // Issue and persist access + refresh tokens + $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $authCodePayload->user_id, $scopes); + $refreshToken = $this->issueRefreshToken($accessToken); + + // Inject tokens into response type + $responseType->setAccessToken($accessToken); + $responseType->setRefreshToken($refreshToken); + + // Revoke used auth code + $this->authCodeRepository->revokeAuthCode($authCodePayload->auth_code_id); + + return $responseType; } /** + * Return the grant identifier that can be used in matching up requests. * - * @param bool $required True to require client secret during access - * token request. False if not. Default = true + * @return string */ - public function setRequireClientSecret($required) + public function getIdentifier() { - $this->requireClientSecret = $required; + return 'authorization_code'; } /** - * True if client secret is required during - * access token request. False if it isn't. - * - * @return bool + * {@inheritdoc} */ - public function shouldRequireClientSecret() + public function canRespondToAuthorizationRequest(ServerRequestInterface $request) { - return $this->requireClientSecret; + return ( + array_key_exists('response_type', $request->getQueryParams()) + && $request->getQueryParams()['response_type'] === 'code' + && isset($request->getQueryParams()['client_id']) + ); } /** - * Check authorize parameters - * - * @return array Authorize request parameters - * - * @throws + * {@inheritdoc} */ - public function checkAuthorizeParams() + public function validateAuthorizationRequest(ServerRequestInterface $request) { - // Get required params - $clientId = $this->server->getRequest()->query->get('client_id', null); + $clientId = $this->getQueryStringParameter( + 'client_id', + $request, + $this->getServerParameter('PHP_AUTH_USER', $request) + ); if (is_null($clientId)) { - throw new Exception\InvalidRequestException('client_id'); + throw OAuthServerException::invalidRequest('client_id'); } - $redirectUri = $this->server->getRequest()->query->get('redirect_uri', null); - if (is_null($redirectUri)) { - throw new Exception\InvalidRequestException('redirect_uri'); - } - - // Validate client ID and redirect URI - $client = $this->server->getClientStorage()->get( + $client = $this->clientRepository->getClientEntity( $clientId, - null, - $redirectUri, $this->getIdentifier() ); - if (($client instanceof ClientEntity) === false) { - $this->server->getEventEmitter()->emit(new Event\ClientAuthenticationFailedEvent($this->server->getRequest())); - throw new Exception\InvalidClientException(); + if ($client instanceof ClientEntityInterface === false) { + $this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); + throw OAuthServerException::invalidClient(); } - $state = $this->server->getRequest()->query->get('state', null); - if ($this->server->stateParamRequired() === true && is_null($state)) { - throw new Exception\InvalidRequestException('state', $redirectUri); + $redirectUri = $this->getQueryStringParameter('redirect_uri', $request); + if ($redirectUri !== null) { + if ( + is_string($client->getRedirectUri()) + && (strcmp($client->getRedirectUri(), $redirectUri) !== 0) + ) { + $this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); + throw OAuthServerException::invalidClient(); + } elseif ( + is_array($client->getRedirectUri()) + && in_array($redirectUri, $client->getRedirectUri()) === false + ) { + $this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); + throw OAuthServerException::invalidClient(); + } } - $responseType = $this->server->getRequest()->query->get('response_type', null); - if (is_null($responseType)) { - throw new Exception\InvalidRequestException('response_type', $redirectUri); - } - - // Ensure response type is one that is recognised - if (!in_array($responseType, $this->server->getResponseTypes())) { - throw new Exception\UnsupportedResponseTypeException($responseType, $redirectUri); - } - - // Validate any scopes that are in the request - $scopeParam = $this->server->getRequest()->query->get('scope', ''); - $scopes = $this->validateScopes($scopeParam, $client, $redirectUri); - - return [ - 'client' => $client, - 'redirect_uri' => $redirectUri, - 'state' => $state, - 'response_type' => $responseType, - 'scopes' => $scopes - ]; - } + $scopes = $this->validateScopes( + $this->getQueryStringParameter('scope', $request), + $client->getRedirectUri() + ); - /** - * 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']); - - // 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); - $session->associateScope($scope); - } + $stateParameter = $this->getQueryStringParameter('state', $request); - $session->save(); - $authCode->setSession($session); - $authCode->save(); + $authorizationRequest = new AuthorizationRequest(); + $authorizationRequest->setGrantTypeId($this->getIdentifier()); + $authorizationRequest->setClient($client); + $authorizationRequest->setRedirectUri($redirectUri); + $authorizationRequest->setState($stateParameter); + $authorizationRequest->setScopes($scopes); - return $authCode->generateRedirectUri($authParams['state']); + return $authorizationRequest; } /** - * Complete the auth code grant - * - * @return array - * - * @throws + * {@inheritdoc} */ - public function completeFlow() + public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest) { - // Get the required params - $clientId = $this->server->getRequest()->request->get('client_id', $this->server->getRequest()->getUser()); - if (is_null($clientId)) { - throw new Exception\InvalidRequestException('client_id'); + if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) { + throw new \LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest'); } - $clientSecret = $this->server->getRequest()->request->get('client_secret', - $this->server->getRequest()->getPassword()); - if ($this->shouldRequireClientSecret() && is_null($clientSecret)) { - throw new Exception\InvalidRequestException('client_secret'); + $finalRedirectUri = ($authorizationRequest->getRedirectUri() === null) + ? is_array($authorizationRequest->getClient()->getRedirectUri()) + ? $authorizationRequest->getClient()->getRedirectUri()[0] + : $authorizationRequest->getClient()->getRedirectUri() + : $authorizationRequest->getRedirectUri(); + + // The user approved the client, redirect them back with an auth code + if ($authorizationRequest->isAuthorizationApproved() === true) { + $authCode = $this->issueAuthCode( + $this->authCodeTTL, + $authorizationRequest->getClient(), + $authorizationRequest->getUser()->getIdentifier(), + $authorizationRequest->getRedirectUri(), + $authorizationRequest->getScopes() + ); + + $redirectPayload['code'] = $this->encrypt( + json_encode( + [ + 'client_id' => $authCode->getClient()->getIdentifier(), + 'redirect_uri' => $authCode->getRedirectUri(), + 'auth_code_id' => $authCode->getIdentifier(), + 'scopes' => $authCode->getScopes(), + 'user_id' => $authCode->getUserIdentifier(), + 'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'), + ] + ) + ); + $redirectPayload['state'] = $authorizationRequest->getState(); + + $response = new RedirectResponse(); + $response->setRedirectUri( + $this->makeRedirectUri( + $finalRedirectUri, + $redirectPayload + ) + ); + + return $response; } - $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->getClientStorage()->get( - $clientId, - $clientSecret, - $redirectUri, - $this->getIdentifier() + // The user denied the client, redirect them back with an error + throw OAuthServerException::accessDenied( + 'The user denied the request', + $finalRedirectUri ); - - if (($client instanceof ClientEntity) === false) { - $this->server->getEventEmitter()->emit(new Event\ClientAuthenticationFailedEvent($this->server->getRequest())); - 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->getAuthCodeStorage()->get($authCode); - if (($code instanceof AuthCodeEntity) === false) { - throw new Exception\InvalidRequestException('code'); - } - - // Ensure the auth code hasn't expired - if ($code->isExpired() === true) { - 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(); - $session->associateClient($client); - - $authCodeScopes = $code->getScopes(); - - // Generate the access token - $accessToken = new AccessTokenEntity($this->server); - $accessToken->setId(SecureKey::generate()); - $accessToken->setExpireTime($this->getAccessTokenTTL() + time()); - - foreach ($authCodeScopes as $authCodeScope) { - $session->associateScope($authCodeScope); - } - - foreach ($session->getScopes() as $scope) { - $accessToken->associateScope($scope); - } - - $this->server->getTokenType()->setSession($session); - $this->server->getTokenType()->setParam('access_token', $accessToken->getId()); - $this->server->getTokenType()->setParam('expires_in', $this->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()->setParam('refresh_token', $refreshToken->getId()); - } - - // Expire the auth code - $code->expire(); - - // Save all the things - $accessToken->setSession($session); - $accessToken->save(); - - if (isset($refreshToken) && $this->server->hasGrantType('refresh_token')) { - $refreshToken->setAccessToken($accessToken); - $refreshToken->save(); - } - - return $this->server->getTokenType()->generateResponse(); } -} \ No newline at end of file +} diff --git a/src/Grant/ClientCredentialsGrant.php b/src/Grant/ClientCredentialsGrant.php index e219c6335..c8ff2d52f 100644 --- a/src/Grant/ClientCredentialsGrant.php +++ b/src/Grant/ClientCredentialsGrant.php @@ -1,122 +1,52 @@ * @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\Event; -use League\OAuth2\Server\Exception; -use League\OAuth2\Server\Util\SecureKey; +use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface; +use Psr\Http\Message\ServerRequestInterface; /** - * Client credentials grant class + * Client credentials grant class. */ class ClientCredentialsGrant extends AbstractGrant { /** - * Grant identifier - * - * @var string + * {@inheritdoc} */ - protected $identifier = 'client_credentials'; + public function respondToAccessTokenRequest( + ServerRequestInterface $request, + ResponseTypeInterface $responseType, + \DateInterval $accessTokenTTL + ) { + // Validate request + $client = $this->validateClient($request); + $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); - /** - * Response type - * - * @var string - */ - protected $responseType = null; + // Finalize the requested scopes + $scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client); - /** - * AuthServer instance - * - * @var \League\OAuth2\Server\AuthorizationServer - */ - protected $server = null; + // Issue and persist access token + $accessToken = $this->issueAccessToken($accessTokenTTL, $client, null, $scopes); - /** - * Access token expires in override - * - * @var int - */ - protected $accessTokenTTL = null; + // Inject access token into response type + $responseType->setAccessToken($accessToken); + + return $responseType; + } /** - * Complete the client credentials grant - * - * @return array - * - * @throws + * {@inheritdoc} */ - public function completeFlow() + public function getIdentifier() { - // Get the required params - $clientId = $this->server->getRequest()->request->get('client_id', $this->server->getRequest()->getUser()); - if (is_null($clientId)) { - throw new Exception\InvalidRequestException('client_id'); - } - - $clientSecret = $this->server->getRequest()->request->get('client_secret', - $this->server->getRequest()->getPassword()); - if (is_null($clientSecret)) { - throw new Exception\InvalidRequestException('client_secret'); - } - - // Validate client ID and client secret - $client = $this->server->getClientStorage()->get( - $clientId, - $clientSecret, - null, - $this->getIdentifier() - ); - - if (($client instanceof ClientEntity) === false) { - $this->server->getEventEmitter()->emit(new Event\ClientAuthenticationFailedEvent($this->server->getRequest())); - throw new Exception\InvalidClientException(); - } - - // Validate any scopes that are in the request - $scopeParam = $this->server->getRequest()->request->get('scope', ''); - $scopes = $this->validateScopes($scopeParam, $client); - - // 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->getAccessTokenTTL() + time()); - - // Associate scopes with the session and access token - foreach ($scopes as $scope) { - $session->associateScope($scope); - } - - foreach ($session->getScopes() as $scope) { - $accessToken->associateScope($scope); - } - - // Save everything - $session->save(); - $accessToken->setSession($session); - $accessToken->save(); - - $this->server->getTokenType()->setSession($session); - $this->server->getTokenType()->setParam('access_token', $accessToken->getId()); - $this->server->getTokenType()->setParam('expires_in', $this->getAccessTokenTTL()); - - return $this->server->getTokenType()->generateResponse(); + return 'client_credentials'; } } diff --git a/src/Grant/GrantTypeInterface.php b/src/Grant/GrantTypeInterface.php index a9468655c..c0ffdd9c5 100644 --- a/src/Grant/GrantTypeInterface.php +++ b/src/Grant/GrantTypeInterface.php @@ -1,59 +1,134 @@ * @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\Event\EmitterAwareInterface; +use League\OAuth2\Server\CryptKey; +use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; +use League\OAuth2\Server\Repositories\ClientRepositoryInterface; +use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; +use League\OAuth2\Server\RequestTypes\AuthorizationRequest; +use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface; +use Psr\Http\Message\ServerRequestInterface; /** - * Grant type interface + * Grant type interface. */ -interface GrantTypeInterface +interface GrantTypeInterface extends EmitterAwareInterface { /** - * Return the identifier + * Set refresh token TTL. + * + * @param \DateInterval $refreshTokenTTL + */ + public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL); + + /** + * Return the grant identifier that can be used in matching up requests. * * @return string */ public function getIdentifier(); /** - * Return the identifier + * Respond to an incoming request. * - * @param string $identifier + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType + * @param \DateInterval $accessTokenTTL * - * @return self + * @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface */ - public function setIdentifier($identifier); + public function respondToAccessTokenRequest( + ServerRequestInterface $request, + ResponseTypeInterface $responseType, + \DateInterval $accessTokenTTL + ); /** - * Return the response type + * The grant type should return true if it is able to response to an authorization request * - * @return string + * @param \Psr\Http\Message\ServerRequestInterface $request + * + * @return bool + */ + public function canRespondToAuthorizationRequest(ServerRequestInterface $request); + + /** + * If the grant can respond to an authorization request this method should be called to validate the parameters of + * the request. + * + * If the validation is successful an AuthorizationRequest object will be returned. This object can be safely + * serialized in a user's session, and can be used during user authentication and authorization. + * + * @param \Psr\Http\Message\ServerRequestInterface $request + * + * @return AuthorizationRequest + */ + public function validateAuthorizationRequest(ServerRequestInterface $request); + + /** + * Once a user has authenticated and authorized the client the grant can complete the authorization request. + * The AuthorizationRequest object's $userId property must be set to the authenticated user and the + * $authorizationApproved property must reflect their desire to authorize or deny the client. + * + * @param \League\OAuth2\Server\RequestTypes\AuthorizationRequest $authorizationRequest + * + * @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface + */ + public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest); + + /** + * The grant type should return true if it is able to respond to this request. + * + * For example most grant types will check that the $_POST['grant_type'] property matches it's identifier property. + * + * @param \Psr\Http\Message\ServerRequestInterface $request + * + * @return bool + */ + public function canRespondToAccessTokenRequest(ServerRequestInterface $request); + + /** + * Set the client repository. + * + * @param \League\OAuth2\Server\Repositories\ClientRepositoryInterface $clientRepository */ - public function getResponseType(); + public function setClientRepository(ClientRepositoryInterface $clientRepository); /** - * Inject the authorization server into the grant + * Set the access token repository. * - * @param \League\OAuth2\Server\AuthorizationServer $server The authorization server instance + * @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository + */ + public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository); + + /** + * Set the scope repository. + * + * @param \League\OAuth2\Server\Repositories\ScopeRepositoryInterface $scopeRepository + */ + public function setScopeRepository(ScopeRepositoryInterface $scopeRepository); + + /** + * Set the path to the private key. * - * @return self + * @param \League\OAuth2\Server\CryptKey $privateKey */ - public function setAuthorizationServer(AuthorizationServer $server); + public function setPrivateKey(CryptKey $privateKey); /** - * Complete the grant flow + * Set the path to the public key. * - * @return array + * @param \League\OAuth2\Server\CryptKey $publicKey */ - public function completeFlow(); + public function setPublicKey(CryptKey $publicKey); } diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php new file mode 100644 index 000000000..e7ac0d15d --- /dev/null +++ b/src/Grant/ImplicitGrant.php @@ -0,0 +1,208 @@ + + * @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\Entities\ClientEntityInterface; +use League\OAuth2\Server\Entities\UserEntityInterface; +use League\OAuth2\Server\Exception\OAuthServerException; +use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface; +use League\OAuth2\Server\RequestEvent; +use League\OAuth2\Server\RequestTypes\AuthorizationRequest; +use League\OAuth2\Server\ResponseTypes\RedirectResponse; +use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface; +use Psr\Http\Message\ServerRequestInterface; + +class ImplicitGrant extends AbstractAuthorizeGrant +{ + /** + * @var \DateInterval + */ + private $accessTokenTTL; + + /** + * @param \DateInterval $accessTokenTTL + */ + public function __construct(\DateInterval $accessTokenTTL) + { + $this->accessTokenTTL = $accessTokenTTL; + } + + /** + * @param \DateInterval $refreshTokenTTL + * + * @throw \LogicException + */ + public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL) + { + throw new \LogicException('The Implicit Grant does nto return refresh tokens'); + } + + /** + * @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository + * + * @throw \LogicException + */ + public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository) + { + throw new \LogicException('The Implicit Grant does nto return refresh tokens'); + } + + /** + * {@inheritdoc} + */ + public function canRespondToAccessTokenRequest(ServerRequestInterface $request) + { + return false; + } + + /** + * Return the grant identifier that can be used in matching up requests. + * + * @return string + */ + public function getIdentifier() + { + return 'implicit'; + } + + /** + * Respond to an incoming request. + * + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType + * @param \DateInterval $accessTokenTTL + * + * @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface + */ + public function respondToAccessTokenRequest( + ServerRequestInterface $request, + ResponseTypeInterface $responseType, + \DateInterval $accessTokenTTL + ) { + throw new \LogicException('This grant does not used this method'); + } + + /** + * {@inheritdoc} + */ + public function canRespondToAuthorizationRequest(ServerRequestInterface $request) + { + return ( + array_key_exists('response_type', $request->getQueryParams()) + && $request->getQueryParams()['response_type'] === 'token' + && isset($request->getQueryParams()['client_id']) + ); + } + + /** + * {@inheritdoc} + */ + public function validateAuthorizationRequest(ServerRequestInterface $request) + { + $clientId = $this->getQueryStringParameter( + 'client_id', + $request, + $this->getServerParameter('PHP_AUTH_USER', $request) + ); + if (is_null($clientId)) { + throw OAuthServerException::invalidRequest('client_id'); + } + + $client = $this->clientRepository->getClientEntity( + $clientId, + $this->getIdentifier() + ); + + if ($client instanceof ClientEntityInterface === false) { + $this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); + throw OAuthServerException::invalidClient(); + } + + $redirectUri = $this->getQueryStringParameter('redirect_uri', $request); + if ($redirectUri !== null) { + if ( + is_string($client->getRedirectUri()) + && (strcmp($client->getRedirectUri(), $redirectUri) !== 0) + ) { + $this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); + throw OAuthServerException::invalidClient(); + } elseif ( + is_array($client->getRedirectUri()) + && in_array($redirectUri, $client->getRedirectUri()) === false + ) { + $this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); + throw OAuthServerException::invalidClient(); + } + } + + $scopes = $this->validateScopes( + $this->getQueryStringParameter('scope', $request), + $client->getRedirectUri() + ); + + $stateParameter = $this->getQueryStringParameter('state', $request); + + $authorizationRequest = new AuthorizationRequest(); + $authorizationRequest->setGrantTypeId($this->getIdentifier()); + $authorizationRequest->setClient($client); + $authorizationRequest->setRedirectUri($redirectUri); + $authorizationRequest->setState($stateParameter); + $authorizationRequest->setScopes($scopes); + + return $authorizationRequest; + } + + /** + * {@inheritdoc} + */ + public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest) + { + if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) { + throw new \LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest'); + } + + $finalRedirectUri = ($authorizationRequest->getRedirectUri() === null) + ? is_array($authorizationRequest->getClient()->getRedirectUri()) + ? $authorizationRequest->getClient()->getRedirectUri()[0] + : $authorizationRequest->getClient()->getRedirectUri() + : $authorizationRequest->getRedirectUri(); + + // The user approved the client, redirect them back with an access token + if ($authorizationRequest->isAuthorizationApproved() === true) { + $accessToken = $this->issueAccessToken( + $this->accessTokenTTL, + $authorizationRequest->getClient(), + $authorizationRequest->getUser()->getIdentifier(), + $authorizationRequest->getScopes() + ); + + $redirectPayload['access_token'] = (string) $accessToken->convertToJWT($this->privateKey); + $redirectPayload['token_type'] = 'bearer'; + $redirectPayload['expires_in'] = $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(); + + $response = new RedirectResponse(); + $response->setRedirectUri( + $this->makeRedirectUri( + $finalRedirectUri, + $redirectPayload, + '#' + ) + ); + + return $response; + } + + // The user denied the client, redirect them back with an error + throw OAuthServerException::accessDenied( + 'The user denied the request', + $finalRedirectUri + ); + } +} diff --git a/src/Grant/PasswordGrant.php b/src/Grant/PasswordGrant.php index 71921085a..6d98115d3 100644 --- a/src/Grant/PasswordGrant.php +++ b/src/Grant/PasswordGrant.php @@ -1,182 +1,110 @@ * @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\RefreshTokenEntity; -use League\OAuth2\Server\Entity\SessionEntity; -use League\OAuth2\Server\Event; -use League\OAuth2\Server\Exception; -use League\OAuth2\Server\Util\SecureKey; +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Entities\UserEntityInterface; +use League\OAuth2\Server\Exception\OAuthServerException; +use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface; +use League\OAuth2\Server\Repositories\UserRepositoryInterface; +use League\OAuth2\Server\RequestEvent; +use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface; +use Psr\Http\Message\ServerRequestInterface; /** - * Password grant class + * 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 callable - */ - 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 + * @param \League\OAuth2\Server\Repositories\UserRepositoryInterface $userRepository + * @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository */ - public function setVerifyCredentialsCallback(callable $callback) - { - $this->callback = $callback; + public function __construct( + UserRepositoryInterface $userRepository, + RefreshTokenRepositoryInterface $refreshTokenRepository + ) { + $this->setUserRepository($userRepository); + $this->setRefreshTokenRepository($refreshTokenRepository); + + $this->refreshTokenTTL = new \DateInterval('P1M'); } /** - * Return the callback function - * - * @return callable - * - * @throws + * {@inheritdoc} */ - 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; + public function respondToAccessTokenRequest( + ServerRequestInterface $request, + ResponseTypeInterface $responseType, + \DateInterval $accessTokenTTL + ) { + // Validate request + $client = $this->validateClient($request); + $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); + $user = $this->validateUser($request, $client); + + // Finalize the requested scopes + $scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier()); + + // Issue and persist new tokens + $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $scopes); + $refreshToken = $this->issueRefreshToken($accessToken); + + // Inject tokens into response + $responseType->setAccessToken($accessToken); + $responseType->setRefreshToken($refreshToken); + + return $responseType; } /** - * Complete the password grant + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \League\OAuth2\Server\Entities\ClientEntityInterface $client * - * @return array + * @throws \League\OAuth2\Server\Exception\OAuthServerException * - * @throws + * @return \League\OAuth2\Server\Entities\UserEntityInterface */ - public function completeFlow() + protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client) { - // Get the required params - $clientId = $this->server->getRequest()->request->get('client_id', $this->server->getRequest()->getUser()); - if (is_null($clientId)) { - throw new Exception\InvalidRequestException('client_id'); - } - - $clientSecret = $this->server->getRequest()->request->get('client_secret', - $this->server->getRequest()->getPassword()); - if (is_null($clientSecret)) { - throw new Exception\InvalidRequestException('client_secret'); - } - - // Validate client ID and client secret - $client = $this->server->getClientStorage()->get( - $clientId, - $clientSecret, - null, - $this->getIdentifier() - ); - - if (($client instanceof ClientEntity) === false) { - $this->server->getEventEmitter()->emit(new Event\ClientAuthenticationFailedEvent($this->server->getRequest())); - throw new Exception\InvalidClientException(); - } - - $username = $this->server->getRequest()->request->get('username', null); + $username = $this->getRequestParameter('username', $request); if (is_null($username)) { - throw new Exception\InvalidRequestException('username'); + throw OAuthServerException::invalidRequest('username', '`%s` parameter is missing'); } - $password = $this->server->getRequest()->request->get('password', null); + $password = $this->getRequestParameter('password', $request); 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) { - $this->server->getEventEmitter()->emit(new Event\UserAuthenticationFailedEvent($this->server->getRequest())); - throw new Exception\InvalidCredentialsException(); + throw OAuthServerException::invalidRequest('password', '`%s` parameter is missing'); } - // Validate any scopes that are in the request - $scopeParam = $this->server->getRequest()->request->get('scope', ''); - $scopes = $this->validateScopes($scopeParam, $client); - - // 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->getAccessTokenTTL() + time()); - - // Associate scopes with the session and access token - foreach ($scopes as $scope) { - $session->associateScope($scope); - } - - foreach ($session->getScopes() as $scope) { - $accessToken->associateScope($scope); - } - - $this->server->getTokenType()->setSession($session); - $this->server->getTokenType()->setParam('access_token', $accessToken->getId()); - $this->server->getTokenType()->setParam('expires_in', $this->getAccessTokenTTL()); + $user = $this->userRepository->getUserEntityByUserCredentials( + $username, + $password, + $this->getIdentifier(), + $client + ); + if (!$user instanceof UserEntityInterface) { + $this->getEmitter()->emit(new RequestEvent('user.authentication.failed', $request)); - // 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()->setParam('refresh_token', $refreshToken->getId()); + throw OAuthServerException::invalidCredentials(); } - // Save everything - $session->save(); - $accessToken->setSession($session); - $accessToken->save(); - - if ($this->server->hasGrantType('refresh_token')) { - $refreshToken->setAccessToken($accessToken); - $refreshToken->save(); - } + return $user; + } - return $this->server->getTokenType()->generateResponse(); + /** + * {@inheritdoc} + */ + public function getIdentifier() + { + return 'password'; } } diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php index d7fd88152..0d65fae6e 100644 --- a/src/Grant/RefreshTokenGrant.php +++ b/src/Grant/RefreshTokenGrant.php @@ -1,223 +1,132 @@ * @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\RefreshTokenEntity; -use League\OAuth2\Server\Event; -use League\OAuth2\Server\Exception; -use League\OAuth2\Server\Util\SecureKey; +use League\OAuth2\Server\Entities\ScopeEntityInterface; +use League\OAuth2\Server\Exception\OAuthServerException; +use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface; +use League\OAuth2\Server\RequestEvent; +use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface; +use Psr\Http\Message\ServerRequestInterface; /** - * Refresh token grant + * Refresh token grant. */ class RefreshTokenGrant extends AbstractGrant { /** - * {@inheritdoc} + * @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository */ - protected $identifier = 'refresh_token'; + public function __construct(RefreshTokenRepositoryInterface $refreshTokenRepository) + { + $this->setRefreshTokenRepository($refreshTokenRepository); - /** - * Refresh token TTL (default = 604800 | 1 week) - * - * @var integer - */ - protected $refreshTokenTTL = 604800; + $this->refreshTokenTTL = new \DateInterval('P1M'); + } /** - * Rotate token (default = true) - * - * @var integer + * {@inheritdoc} */ - protected $refreshTokenRotate = true; + public function respondToAccessTokenRequest( + ServerRequestInterface $request, + ResponseTypeInterface $responseType, + \DateInterval $accessTokenTTL + ) { + // Validate request + $client = $this->validateClient($request); + $oldRefreshToken = $this->validateOldRefreshToken($request, $client->getIdentifier()); + $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); - /** - * Whether to require the client secret when - * completing the flow. - * - * @var boolean - */ - protected $requireClientSecret = true; + // If no new scopes are requested then give the access token the original session scopes + if (count($scopes) === 0) { + $scopes = array_map(function ($scopeId) use ($client) { + $scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId); + + if (!$scope instanceof ScopeEntityInterface) { + // @codeCoverageIgnoreStart + throw OAuthServerException::invalidScope($scopeId); + // @codeCoverageIgnoreEnd + } - /** - * Set the TTL of the refresh token - * - * @param int $refreshTokenTTL - * - * @return void - */ - public function setRefreshTokenTTL($refreshTokenTTL) - { - $this->refreshTokenTTL = $refreshTokenTTL; - } + return $scope; + }, $oldRefreshToken['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 ($scopes as $scope) { + if (in_array($scope->getIdentifier(), $oldRefreshToken['scopes']) === false) { + throw OAuthServerException::invalidScope($scope->getIdentifier()); + } + } + } - /** - * Get the TTL of the refresh token - * - * @return int - */ - public function getRefreshTokenTTL() - { - return $this->refreshTokenTTL; - } + // Expire old tokens + $this->accessTokenRepository->revokeAccessToken($oldRefreshToken['access_token_id']); + $this->refreshTokenRepository->revokeRefreshToken($oldRefreshToken['refresh_token_id']); - /** - * Set the rotation boolean of the refresh token - * @param bool $refreshTokenRotate - */ - public function setRefreshTokenRotation($refreshTokenRotate = true) - { - $this->refreshTokenRotate = $refreshTokenRotate; - } + // Issue and persist new tokens + $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $oldRefreshToken['user_id'], $scopes); + $refreshToken = $this->issueRefreshToken($accessToken); - /** - * Get rotation boolean of the refresh token - * - * @return bool - */ - public function shouldRotateRefreshTokens() - { - return $this->refreshTokenRotate; - } + // Inject tokens into response + $responseType->setAccessToken($accessToken); + $responseType->setRefreshToken($refreshToken); - /** - * - * @param bool $required True to require client secret during access - * token request. False if not. Default = true - */ - public function setRequireClientSecret($required) - { - $this->requireClientSecret = $required; + return $responseType; } /** - * True if client secret is required during - * access token request. False if it isn't. + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param string $clientId * - * @return bool - */ - public function shouldRequireClientSecret() - { - return $this->requireClientSecret; - } - - - /** - * {@inheritdoc} + * @throws \League\OAuth2\Server\Exception\OAuthServerException + * + * @return array */ - public function completeFlow() + protected function validateOldRefreshToken(ServerRequestInterface $request, $clientId) { - $clientId = $this->server->getRequest()->request->get('client_id', $this->server->getRequest()->getUser()); - if (is_null($clientId)) { - throw new Exception\InvalidRequestException('client_id'); - } - - $clientSecret = $this->server->getRequest()->request->get('client_secret', - $this->server->getRequest()->getPassword()); - if ($this->shouldRequireClientSecret() && is_null($clientSecret)) { - throw new Exception\InvalidRequestException('client_secret'); - } - - // Validate client ID and client secret - $client = $this->server->getClientStorage()->get( - $clientId, - $clientSecret, - null, - $this->getIdentifier() - ); - - if (($client instanceof ClientEntity) === false) { - $this->server->getEventEmitter()->emit(new Event\ClientAuthenticationFailedEvent($this->server->getRequest())); - throw new Exception\InvalidClientException(); - } - - $oldRefreshTokenParam = $this->server->getRequest()->request->get('refresh_token', null); - if ($oldRefreshTokenParam === null) { - throw new Exception\InvalidRequestException('refresh_token'); + $encryptedRefreshToken = $this->getRequestParameter('refresh_token', $request); + if (is_null($encryptedRefreshToken)) { + throw OAuthServerException::invalidRequest('refresh_token'); } // Validate refresh token - $oldRefreshToken = $this->server->getRefreshTokenStorage()->get($oldRefreshTokenParam); - - if (($oldRefreshToken instanceof RefreshTokenEntity) === false) { - throw new Exception\InvalidRefreshException(); + try { + $refreshToken = $this->decrypt($encryptedRefreshToken); + } catch (\LogicException $e) { + throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token'); } - // Ensure the old refresh token hasn't expired - if ($oldRefreshToken->isExpired() === true) { - throw new Exception\InvalidRefreshException(); + $refreshTokenData = json_decode($refreshToken, true); + if ($refreshTokenData['client_id'] !== $clientId) { + $this->getEmitter()->emit(new RequestEvent('refresh_token.client.failed', $request)); + throw OAuthServerException::invalidRefreshToken('Token is not linked to client'); } - $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, $client); - - // 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; + if ($refreshTokenData['expire_time'] < time()) { + throw OAuthServerException::invalidRefreshToken('Token has expired'); } - // Generate a new access token and assign it the correct sessions - $newAccessToken = new AccessTokenEntity($this->server); - $newAccessToken->setId(SecureKey::generate()); - $newAccessToken->setExpireTime($this->getAccessTokenTTL() + time()); - $newAccessToken->setSession($session); - - foreach ($newScopes as $newScope) { - $newAccessToken->associateScope($newScope); + if ($this->refreshTokenRepository->isRefreshTokenRevoked($refreshTokenData['refresh_token_id']) === true) { + throw OAuthServerException::invalidRefreshToken('Token has been revoked'); } - // Expire the old token and save the new one - $oldAccessToken->expire(); - $newAccessToken->save(); - - $this->server->getTokenType()->setSession($session); - $this->server->getTokenType()->setParam('access_token', $newAccessToken->getId()); - $this->server->getTokenType()->setParam('expires_in', $this->getAccessTokenTTL()); - - if ($this->shouldRotateRefreshTokens()) { - // Expire the old refresh token - $oldRefreshToken->expire(); - - // 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->getTokenType()->setParam('refresh_token', $newRefreshToken->getId()); - } else { - $this->server->getTokenType()->setParam('refresh_token', $oldRefreshToken->getId()); - } + return $refreshTokenData; + } - return $this->server->getTokenType()->generateResponse(); + /** + * {@inheritdoc} + */ + public function getIdentifier() + { + return 'refresh_token'; } -} \ No newline at end of file +} diff --git a/src/Middleware/AuthorizationServerMiddleware.php b/src/Middleware/AuthorizationServerMiddleware.php new file mode 100644 index 000000000..d33cbd315 --- /dev/null +++ b/src/Middleware/AuthorizationServerMiddleware.php @@ -0,0 +1,58 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Middleware; + +use League\OAuth2\Server\AuthorizationServer; +use League\OAuth2\Server\Exception\OAuthServerException; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; + +class AuthorizationServerMiddleware +{ + /** + * @var \League\OAuth2\Server\AuthorizationServer + */ + private $server; + + /** + * AuthorizationServerMiddleware constructor. + * + * @param \League\OAuth2\Server\AuthorizationServer $server + */ + public function __construct(AuthorizationServer $server) + { + $this->server = $server; + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response + * @param callable $next + * + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) + { + try { + $response = $this->server->respondToAccessTokenRequest($request, $response); + } catch (OAuthServerException $exception) { + return $exception->generateHttpResponse($response); + // @codeCoverageIgnoreStart + } catch (\Exception $exception) { + $response->getBody()->write($exception->getMessage()); + + return $response->withStatus(500); + // @codeCoverageIgnoreEnd + } + + // Pass the request and response on to the next responder in the chain + return $next($request, $response); + } +} diff --git a/src/Middleware/ResourceServerMiddleware.php b/src/Middleware/ResourceServerMiddleware.php new file mode 100644 index 000000000..6d67cb68f --- /dev/null +++ b/src/Middleware/ResourceServerMiddleware.php @@ -0,0 +1,58 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Middleware; + +use League\OAuth2\Server\Exception\OAuthServerException; +use League\OAuth2\Server\ResourceServer; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; + +class ResourceServerMiddleware +{ + /** + * @var \League\OAuth2\Server\ResourceServer + */ + private $server; + + /** + * ResourceServerMiddleware constructor. + * + * @param \League\OAuth2\Server\ResourceServer $server + */ + public function __construct(ResourceServer $server) + { + $this->server = $server; + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response + * @param callable $next + * + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) + { + try { + $request = $this->server->validateAuthenticatedRequest($request); + } catch (OAuthServerException $exception) { + return $exception->generateHttpResponse($response); + // @codeCoverageIgnoreStart + } catch (\Exception $exception) { + $response->getBody()->write($exception->getMessage()); + + return $response->withStatus(500); + // @codeCoverageIgnoreEnd + } + + // Pass the request and response on to the next responder in the chain + return $next($request, $response); + } +} diff --git a/src/Repositories/AccessTokenRepositoryInterface.php b/src/Repositories/AccessTokenRepositoryInterface.php new file mode 100644 index 000000000..aab8eab81 --- /dev/null +++ b/src/Repositories/AccessTokenRepositoryInterface.php @@ -0,0 +1,53 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Repositories; + +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\ClientEntityInterface; + +/** + * Access token interface. + */ +interface AccessTokenRepositoryInterface extends RepositoryInterface +{ + /** + * Create a new access token + * + * @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity + * @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes + * @param mixed $userIdentifier + * + * @return AccessTokenEntityInterface + */ + public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null); + + /** + * Persists a new access token to permanent storage. + * + * @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessTokenEntity + */ + public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity); + + /** + * Revoke an access token. + * + * @param string $tokenId + */ + public function revokeAccessToken($tokenId); + + /** + * Check if the access token has been revoked. + * + * @param string $tokenId + * + * @return bool Return true if this token has been revoked + */ + public function isAccessTokenRevoked($tokenId); +} diff --git a/src/Repositories/AuthCodeRepositoryInterface.php b/src/Repositories/AuthCodeRepositoryInterface.php new file mode 100644 index 000000000..cfe3b4d82 --- /dev/null +++ b/src/Repositories/AuthCodeRepositoryInterface.php @@ -0,0 +1,47 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ +namespace League\OAuth2\Server\Repositories; + +use League\OAuth2\Server\Entities\AuthCodeEntityInterface; + +/** + * Auth code storage interface. + */ +interface AuthCodeRepositoryInterface extends RepositoryInterface +{ + /** + * Creates a new AuthCode + * + * @return \League\OAuth2\Server\Entities\AuthCodeEntityInterface + */ + public function getNewAuthCode(); + + /** + * Persists a new auth code to permanent storage. + * + * @param \League\OAuth2\Server\Entities\AuthCodeEntityInterface $authCodeEntity + */ + public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity); + + /** + * Revoke an auth code. + * + * @param string $codeId + */ + public function revokeAuthCode($codeId); + + /** + * Check if the auth code has been revoked. + * + * @param string $codeId + * + * @return bool Return true if this code has been revoked + */ + public function isAuthCodeRevoked($codeId); +} diff --git a/src/Repositories/ClientRepositoryInterface.php b/src/Repositories/ClientRepositoryInterface.php new file mode 100644 index 000000000..a5d4c32dd --- /dev/null +++ b/src/Repositories/ClientRepositoryInterface.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\Repositories; + +/** + * Client storage interface. + */ +interface ClientRepositoryInterface extends RepositoryInterface +{ + /** + * Get a client. + * + * @param string $clientIdentifier The client's identifier + * @param string $grantType The grant type used + * @param null|string $clientSecret The client's secret (if sent) + * + * @return \League\OAuth2\Server\Entities\ClientEntityInterface + */ + public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null); +} diff --git a/src/Repositories/RefreshTokenRepositoryInterface.php b/src/Repositories/RefreshTokenRepositoryInterface.php new file mode 100644 index 000000000..49d3ea4ae --- /dev/null +++ b/src/Repositories/RefreshTokenRepositoryInterface.php @@ -0,0 +1,47 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ +namespace League\OAuth2\Server\Repositories; + +use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; + +/** + * Refresh token interface. + */ +interface RefreshTokenRepositoryInterface extends RepositoryInterface +{ + /** + * Creates a new refresh token + * + * @return RefreshTokenEntityInterface + */ + public function getNewRefreshToken(); + + /** + * Create a new refresh token_name. + * + * @param \League\OAuth2\Server\Entities\RefreshTokenEntityInterface $refreshTokenEntity + */ + public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity); + + /** + * Revoke the refresh token. + * + * @param string $tokenId + */ + public function revokeRefreshToken($tokenId); + + /** + * Check if the refresh token has been revoked. + * + * @param string $tokenId + * + * @return bool Return true if this token has been revoked + */ + public function isRefreshTokenRevoked($tokenId); +} diff --git a/src/Repositories/RepositoryInterface.php b/src/Repositories/RepositoryInterface.php new file mode 100644 index 000000000..3e50d4de3 --- /dev/null +++ b/src/Repositories/RepositoryInterface.php @@ -0,0 +1,16 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ +namespace League\OAuth2\Server\Repositories; + +/** + * Repository interface. + */ +interface RepositoryInterface +{ +} diff --git a/src/Repositories/ScopeRepositoryInterface.php b/src/Repositories/ScopeRepositoryInterface.php new file mode 100644 index 000000000..568f0e573 --- /dev/null +++ b/src/Repositories/ScopeRepositoryInterface.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\Repositories; + +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Entities\ScopeEntityInterface; + +/** + * Scope interface. + */ +interface ScopeRepositoryInterface extends RepositoryInterface +{ + /** + * Return information about a scope. + * + * @param string $identifier The scope identifier + * + * @return \League\OAuth2\Server\Entities\ScopeEntityInterface + */ + public function getScopeEntityByIdentifier($identifier); + + /** + * Given a client, grant type and optional user identifier validate the set of scopes requested are valid and optionally + * append additional scopes or remove requested scopes. + * + * @param ScopeEntityInterface[] $scopes + * @param string $grantType + * @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity + * @param null|string $userIdentifier + * + * @return \League\OAuth2\Server\Entities\ScopeEntityInterface[] + */ + public function finalizeScopes( + array $scopes, + $grantType, + ClientEntityInterface $clientEntity, + $userIdentifier = null + ); +} diff --git a/src/Repositories/UserRepositoryInterface.php b/src/Repositories/UserRepositoryInterface.php new file mode 100644 index 000000000..b2de24d6f --- /dev/null +++ b/src/Repositories/UserRepositoryInterface.php @@ -0,0 +1,32 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\Repositories; + +use League\OAuth2\Server\Entities\ClientEntityInterface; + +interface UserRepositoryInterface extends RepositoryInterface +{ + /** + * Get a user entity. + * + * @param string $username + * @param string $password + * @param string $grantType The grant type used + * @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity + * + * @return \League\OAuth2\Server\Entities\UserEntityInterface + */ + public function getUserEntityByUserCredentials( + $username, + $password, + $grantType, + ClientEntityInterface $clientEntity + ); +} diff --git a/src/RequestEvent.php b/src/RequestEvent.php new file mode 100644 index 000000000..f6cf4d928 --- /dev/null +++ b/src/RequestEvent.php @@ -0,0 +1,41 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ +namespace League\OAuth2\Server; + +use League\Event\Event; +use Psr\Http\Message\ServerRequestInterface; + +class RequestEvent extends Event +{ + /** + * @var \Psr\Http\Message\ServerRequestInterface + */ + private $request; + + /** + * RequestEvent constructor. + * + * @param string $name + * @param \Psr\Http\Message\ServerRequestInterface $request + */ + public function __construct($name, ServerRequestInterface $request) + { + parent::__construct($name); + $this->request = $request; + } + + /** + * @return ServerRequestInterface + * @codeCoverageIgnore + */ + public function getRequest() + { + return $this->request; + } +} diff --git a/src/RequestTypes/AuthorizationRequest.php b/src/RequestTypes/AuthorizationRequest.php new file mode 100644 index 000000000..eaf78e39f --- /dev/null +++ b/src/RequestTypes/AuthorizationRequest.php @@ -0,0 +1,178 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\RequestTypes; + +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Entities\ScopeEntityInterface; +use League\OAuth2\Server\Entities\UserEntityInterface; + +class AuthorizationRequest +{ + /** + * The grant type identifier + * + * @var string + */ + protected $grantTypeId; + + /** + * The client identifier + * + * @var ClientEntityInterface + */ + protected $client; + + /** + * The user identifier + * + * @var UserEntityInterface + */ + protected $user; + + /** + * An array of scope identifiers + * + * @var ScopeEntityInterface[] + */ + protected $scopes = []; + + /** + * Has the user authorized the authorization request + * + * @var bool + */ + protected $authorizationApproved = false; + + /** + * The redirect URI used in the request + * + * @var string + */ + protected $redirectUri; + + /** + * The state parameter on the authorization request + * + * @var string + */ + protected $state; + + /** + * @return string + */ + public function getGrantTypeId() + { + return $this->grantTypeId; + } + + /** + * @param string $grantTypeId + */ + public function setGrantTypeId($grantTypeId) + { + $this->grantTypeId = $grantTypeId; + } + + /** + * @return ClientEntityInterface + */ + public function getClient() + { + return $this->client; + } + + /** + * @param ClientEntityInterface $client + */ + public function setClient(ClientEntityInterface $client) + { + $this->client = $client; + } + + /** + * @return UserEntityInterface + */ + public function getUser() + { + return $this->user; + } + + /** + * @param UserEntityInterface $user + */ + public function setUser(UserEntityInterface $user) + { + $this->user = $user; + } + + /** + * @return \League\OAuth2\Server\Entities\ScopeEntityInterface[] + */ + public function getScopes() + { + return $this->scopes; + } + + /** + * @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes + */ + public function setScopes($scopes) + { + $this->scopes = $scopes; + } + + /** + * @return bool + */ + public function isAuthorizationApproved() + { + return $this->authorizationApproved; + } + + /** + * @param bool $authorizationApproved + */ + public function setAuthorizationApproved($authorizationApproved) + { + $this->authorizationApproved = $authorizationApproved; + } + + /** + * @return string + */ + public function getRedirectUri() + { + return $this->redirectUri; + } + + /** + * @param string $redirectUri + */ + public function setRedirectUri($redirectUri) + { + $this->redirectUri = $redirectUri; + } + + /** + * @return string + */ + public function getState() + { + return $this->state; + } + + /** + * @param string $state + */ + public function setState($state) + { + $this->state = $state; + } +} diff --git a/src/ResourceServer.php b/src/ResourceServer.php index 245cb4e20..6fe8b1fc5 100644 --- a/src/ResourceServer.php +++ b/src/ResourceServer.php @@ -1,155 +1,80 @@ * @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\Entity\AccessTokenEntity; -use League\OAuth2\Server\Exception\AccessDeniedException; -use League\OAuth2\Server\Exception\InvalidRequestException; -use League\OAuth2\Server\Storage\AccessTokenInterface; -use League\OAuth2\Server\Storage\ClientInterface; -use League\OAuth2\Server\Storage\ScopeInterface; -use League\OAuth2\Server\Storage\SessionInterface; -use League\OAuth2\Server\TokenType\Bearer; -use League\OAuth2\Server\TokenType\MAC; +use League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface; +use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator; +use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; +use Psr\Http\Message\ServerRequestInterface; -/** - * OAuth 2.0 Resource Server - */ -class ResourceServer extends AbstractServer +class ResourceServer { /** - * The access token - * - * @var \League\OAuth2\Server\Entity\AccessTokenEntity + * @var \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface */ - protected $accessToken; - + private $accessTokenRepository; /** - * The query string key which is used by clients to present the access token (default: access_token) - * - * @var string + * @var \League\OAuth2\Server\CryptKey|string */ - protected $tokenKey = 'access_token'; - + private $publicKey; /** - * Initialise the resource server - * - * @param \League\OAuth2\Server\Storage\SessionInterface $sessionStorage - * @param \League\OAuth2\Server\Storage\AccessTokenInterface $accessTokenStorage - * @param \League\OAuth2\Server\Storage\ClientInterface $clientStorage - * @param \League\OAuth2\Server\Storage\ScopeInterface $scopeStorage - * - * @return self + * @var \League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface|null */ - public function __construct( - SessionInterface $sessionStorage, - AccessTokenInterface $accessTokenStorage, - ClientInterface $clientStorage, - ScopeInterface $scopeStorage - ) { - $this->setSessionStorage($sessionStorage); - $this->setAccessTokenStorage($accessTokenStorage); - $this->setClientStorage($clientStorage); - $this->setScopeStorage($scopeStorage); - - // Set Bearer as the default token type - $this->setTokenType(new Bearer()); - - parent::__construct(); - - return $this; - } + private $authorizationValidator; /** - * Sets the query string key for the access token. + * New server instance. * - * @param string $key The new query string key - * - * @return self + * @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository + * @param \League\OAuth2\Server\CryptKey|string $publicKey + * @param null|\League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface $authorizationValidator */ - public function setIdKey($key) - { - $this->tokenKey = $key; + public function __construct( + AccessTokenRepositoryInterface $accessTokenRepository, + $publicKey, + AuthorizationValidatorInterface $authorizationValidator = null + ) { + $this->accessTokenRepository = $accessTokenRepository; - return $this; - } + if (!$publicKey instanceof CryptKey) { + $publicKey = new CryptKey($publicKey); + } + $this->publicKey = $publicKey; - /** - * Gets the access token - * - * @return \League\OAuth2\Server\Entity\AccessTokenEntity - */ - public function getAccessToken() - { - return $this->accessToken; + $this->authorizationValidator = $authorizationValidator; } /** - * Checks if the access token is valid or not - * - * @param bool $headerOnly Limit Access Token to Authorization header - * @param \League\OAuth2\Server\Entity\AccessTokenEntity|null $accessToken Access Token - * - * @throws \League\OAuth2\Server\Exception\AccessDeniedException - * @throws \League\OAuth2\Server\Exception\InvalidRequestException - * - * @return bool + * @return \League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface */ - public function isValidRequest($headerOnly = true, $accessToken = null) + protected function getAuthorizationValidator() { - $accessTokenString = ($accessToken !== null) - ? $accessToken - : $this->determineAccessToken($headerOnly); - - // Set the access token - $this->accessToken = $this->getAccessTokenStorage()->get($accessTokenString); - - // Ensure the access token exists - if (!$this->accessToken instanceof AccessTokenEntity) { - throw new AccessDeniedException(); + if (!$this->authorizationValidator instanceof AuthorizationValidatorInterface) { + $this->authorizationValidator = new BearerTokenValidator($this->accessTokenRepository); } - // Check the access token hasn't expired - // Ensure the auth code hasn't expired - if ($this->accessToken->isExpired() === true) { - throw new AccessDeniedException(); - } + $this->authorizationValidator->setPublicKey($this->publicKey); - return true; + return $this->authorizationValidator; } /** - * Reads in the access token from the headers + * Determine the access token validity. * - * @param bool $headerOnly Limit Access Token to Authorization header + * @param \Psr\Http\Message\ServerRequestInterface $request * - * @throws \League\OAuth2\Server\Exception\InvalidRequestException Thrown if there is no access token presented + * @throws \League\OAuth2\Server\Exception\OAuthServerException * - * @return string + * @return \Psr\Http\Message\ServerRequestInterface */ - public function determineAccessToken($headerOnly = false) + public function validateAuthenticatedRequest(ServerRequestInterface $request) { - if ($this->getRequest()->headers->get('Authorization') !== null) { - $accessToken = $this->getTokenType()->determineAccessTokenInHeader($this->getRequest()); - } elseif ($headerOnly === false && (! $this->getTokenType() instanceof MAC)) { - $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 InvalidRequestException('access token'); - } - - return $accessToken; + return $this->getAuthorizationValidator()->validateAuthorization($request); } } diff --git a/src/ResponseTypes/AbstractResponseType.php b/src/ResponseTypes/AbstractResponseType.php new file mode 100644 index 000000000..02ce842b2 --- /dev/null +++ b/src/ResponseTypes/AbstractResponseType.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\ResponseTypes; + +use League\OAuth2\Server\CryptTrait; +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; + +abstract class AbstractResponseType implements ResponseTypeInterface +{ + use CryptTrait; + + /** + * @var \League\OAuth2\Server\Entities\AccessTokenEntityInterface + */ + protected $accessToken; + + /** + * @var \League\OAuth2\Server\Entities\RefreshTokenEntityInterface + */ + protected $refreshToken; + + /** + * {@inheritdoc} + */ + public function setAccessToken(AccessTokenEntityInterface $accessToken) + { + $this->accessToken = $accessToken; + } + + /** + * {@inheritdoc} + */ + public function setRefreshToken(RefreshTokenEntityInterface $refreshToken) + { + $this->refreshToken = $refreshToken; + } +} diff --git a/src/ResponseTypes/BearerTokenResponse.php b/src/ResponseTypes/BearerTokenResponse.php new file mode 100644 index 000000000..a65ecaae2 --- /dev/null +++ b/src/ResponseTypes/BearerTokenResponse.php @@ -0,0 +1,60 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ +namespace League\OAuth2\Server\ResponseTypes; + +use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; +use Psr\Http\Message\ResponseInterface; + +class BearerTokenResponse extends AbstractResponseType +{ + /** + * {@inheritdoc} + */ + public function generateHttpResponse(ResponseInterface $response) + { + $expireDateTime = $this->accessToken->getExpiryDateTime()->getTimestamp(); + + $jwtAccessToken = $this->accessToken->convertToJWT($this->privateKey); + + $responseParams = [ + 'token_type' => 'Bearer', + 'expires_in' => $expireDateTime - (new \DateTime())->getTimestamp(), + 'access_token' => (string) $jwtAccessToken, + ]; + + if ($this->refreshToken instanceof RefreshTokenEntityInterface) { + $refreshToken = $this->encrypt( + json_encode( + [ + 'client_id' => $this->accessToken->getClient()->getIdentifier(), + 'refresh_token_id' => $this->refreshToken->getIdentifier(), + 'access_token_id' => $this->accessToken->getIdentifier(), + 'scopes' => $this->accessToken->getScopes(), + 'user_id' => $this->accessToken->getUserIdentifier(), + 'expire_time' => $this->refreshToken->getExpiryDateTime()->getTimestamp(), + ] + ) + ); + + $responseParams['refresh_token'] = $refreshToken; + } + + $response = $response + ->withStatus(200) + ->withHeader('pragma', 'no-cache') + ->withHeader('cache-control', 'no-store') + ->withHeader('content-type', 'application/json; charset=UTF-8'); + + $response->getBody()->write(json_encode($responseParams)); + + return $response; + } +} diff --git a/src/ResponseTypes/RedirectResponse.php b/src/ResponseTypes/RedirectResponse.php new file mode 100644 index 000000000..575a49e79 --- /dev/null +++ b/src/ResponseTypes/RedirectResponse.php @@ -0,0 +1,31 @@ +redirectUri = $redirectUri; + } + + /** + * @param ResponseInterface $response + * + * @return ResponseInterface + */ + public function generateHttpResponse(ResponseInterface $response) + { + return $response->withStatus(302)->withHeader('Location', $this->redirectUri); + } +} diff --git a/src/ResponseTypes/ResponseTypeInterface.php b/src/ResponseTypes/ResponseTypeInterface.php new file mode 100644 index 000000000..a26466a7d --- /dev/null +++ b/src/ResponseTypes/ResponseTypeInterface.php @@ -0,0 +1,35 @@ + + * @copyright Copyright (c) Alex Bilbie + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ +namespace League\OAuth2\Server\ResponseTypes; + +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; +use Psr\Http\Message\ResponseInterface; + +interface ResponseTypeInterface +{ + /** + * @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken + */ + public function setAccessToken(AccessTokenEntityInterface $accessToken); + + /** + * @param \League\OAuth2\Server\Entities\RefreshTokenEntityInterface $refreshToken + */ + public function setRefreshToken(RefreshTokenEntityInterface $refreshToken); + + /** + * @param ResponseInterface $response + * + * @return ResponseInterface + */ + public function generateHttpResponse(ResponseInterface $response); +} diff --git a/src/Storage/AbstractStorage.php b/src/Storage/AbstractStorage.php deleted file mode 100644 index 2bfb5601b..000000000 --- a/src/Storage/AbstractStorage.php +++ /dev/null @@ -1,51 +0,0 @@ - - * @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; - -/** - * Abstract storage class - */ -abstract class AbstractStorage implements StorageInterface -{ - /** - * Server - * - * @var \League\OAuth2\Server\AbstractServer $server - */ - protected $server; - - /** - * Set the server - * - * @param \League\OAuth2\Server\AbstractServer $server - * - * @return self - */ - 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/AccessTokenInterface.php b/src/Storage/AccessTokenInterface.php deleted file mode 100644 index 129679dda..000000000 --- a/src/Storage/AccessTokenInterface.php +++ /dev/null @@ -1,69 +0,0 @@ - - * @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\ScopeEntity; - -/** - * Access token interface - */ -interface AccessTokenInterface extends StorageInterface -{ - /** - * Get an instance of Entity\AccessTokenEntity - * - * @param string $token The access token - * - * @return \League\OAuth2\Server\Entity\AccessTokenEntity | null - */ - public function get($token); - - /** - * Get the scopes for an access token - * - * @param \League\OAuth2\Server\Entity\AccessTokenEntity $token The access token - * - * @return \League\OAuth2\Server\Entity\ScopeEntity[] Array of \League\OAuth2\Server\Entity\ScopeEntity - */ - public function getScopes(AccessTokenEntity $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 void - */ - public function create($token, $expireTime, $sessionId); - - /** - * Associate a scope with an acess token - * - * @param \League\OAuth2\Server\Entity\AccessTokenEntity $token The access token - * @param \League\OAuth2\Server\Entity\ScopeEntity $scope The scope - * - * @return void - */ - public function associateScope(AccessTokenEntity $token, ScopeEntity $scope); - - /** - * Delete an access token - * - * @param \League\OAuth2\Server\Entity\AccessTokenEntity $token The access token to delete - * - * @return void - */ - public function delete(AccessTokenEntity $token); -} diff --git a/src/Storage/AuthCodeInterface.php b/src/Storage/AuthCodeInterface.php deleted file mode 100644 index d451d2ef1..000000000 --- a/src/Storage/AuthCodeInterface.php +++ /dev/null @@ -1,70 +0,0 @@ - - * @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 extends StorageInterface -{ - /** - * Get the auth code - * - * @param string $code - * - * @return \League\OAuth2\Server\Entity\AuthCodeEntity | null - */ - 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 \League\OAuth2\Server\Entity\ScopeEntity[] 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 deleted file mode 100644 index e89683164..000000000 --- a/src/Storage/ClientInterface.php +++ /dev/null @@ -1,41 +0,0 @@ - - * @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 extends StorageInterface -{ - /** - * 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 | null - */ - 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 | null - */ - public function getBySession(SessionEntity $session); -} diff --git a/src/Storage/MacTokenInterface.php b/src/Storage/MacTokenInterface.php deleted file mode 100644 index ed5a0633d..000000000 --- a/src/Storage/MacTokenInterface.php +++ /dev/null @@ -1,34 +0,0 @@ - - * @copyright Copyright (c) Alex Bilbie - * @license http://mit-license.org/ - * @link https://github.com/thephpleague/oauth2-server - */ - -namespace League\OAuth2\Server\Storage; - - -/** - * MacTokenInterface - */ -interface MacTokenInterface extends StorageInterface -{ - /** - * Create a MAC key linked to an access token - * @param string $macKey - * @param string $accessToken - * @return void - */ - public function create($macKey, $accessToken); - - /** - * Get a MAC key by access token - * @param string $accessToken - * @return string - */ - public function getByAccessToken($accessToken); -} diff --git a/src/Storage/RefreshTokenInterface.php b/src/Storage/RefreshTokenInterface.php deleted file mode 100644 index e2f199598..000000000 --- a/src/Storage/RefreshTokenInterface.php +++ /dev/null @@ -1,49 +0,0 @@ - - * @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 extends StorageInterface -{ - /** - * Return a new instance of \League\OAuth2\Server\Entity\RefreshTokenEntity - * - * @param string $token - * - * @return \League\OAuth2\Server\Entity\RefreshTokenEntity | null - */ - 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 deleted file mode 100644 index 1257a819a..000000000 --- a/src/Storage/ScopeInterface.php +++ /dev/null @@ -1,29 +0,0 @@ - - * @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 extends StorageInterface -{ - /** - * Return information about a scope - * - * @param string $scope The scope - * @param string $grantType The grant type used in the request (default = "null") - * @param string $clientId The client sending the request (default = "null") - * - * @return \League\OAuth2\Server\Entity\ScopeEntity | null - */ - public function get($scope, $grantType = null, $clientId = null); -} diff --git a/src/Storage/SessionInterface.php b/src/Storage/SessionInterface.php deleted file mode 100644 index bc70e8f6f..000000000 --- a/src/Storage/SessionInterface.php +++ /dev/null @@ -1,72 +0,0 @@ - - * @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\ScopeEntity; -use League\OAuth2\Server\Entity\SessionEntity; - -/** - * Session storage interface - */ -interface SessionInterface extends StorageInterface -{ - /** - * Get a session from an access token - * - * @param \League\OAuth2\Server\Entity\AccessTokenEntity $accessToken The access token - * - * @return \League\OAuth2\Server\Entity\SessionEntity | null - */ - 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 | null - */ - public function getByAuthCode(AuthCodeEntity $authCode); - - /** - * Get a session's scopes - * - * @param \League\OAuth2\Server\Entity\SessionEntity - * - * @return \League\OAuth2\Server\Entity\ScopeEntity[] 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 $session The session - * @param \League\OAuth2\Server\Entity\ScopeEntity $scope The scope - * - * @return void - */ - public function associateScope(SessionEntity $session, ScopeEntity $scope); -} diff --git a/src/Storage/StorageInterface.php b/src/Storage/StorageInterface.php deleted file mode 100644 index ff9614b5d..000000000 --- a/src/Storage/StorageInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - - * @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 interface - */ -interface StorageInterface -{ - /** - * Set the server - * - * @param \League\OAuth2\Server\AbstractServer $server - */ - public function setServer(AbstractServer $server); -} diff --git a/src/TokenType/AbstractTokenType.php b/src/TokenType/AbstractTokenType.php deleted file mode 100644 index adb8837b7..000000000 --- a/src/TokenType/AbstractTokenType.php +++ /dev/null @@ -1,75 +0,0 @@ - - * @copyright Copyright (c) Alex Bilbie - * @license http://mit-license.org/ - * @link https://github.com/thephpleague/oauth2-server - */ - -namespace League\OAuth2\Server\TokenType; - -use League\OAuth2\Server\AbstractServer; -use League\OAuth2\Server\Entity\SessionEntity; - -abstract class AbstractTokenType -{ - /** - * Response array - * - * @var array - */ - protected $response = []; - - /** - * Server - * - * @var \League\OAuth2\Server\AbstractServer $server - */ - protected $server; - - /** - * Server - * - * @var \League\OAuth2\Server\Entity\SessionEntity $session - */ - protected $session; - - /** - * {@inheritdoc} - */ - public function setServer(AbstractServer $server) - { - $this->server = $server; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setSession(SessionEntity $session) - { - $this->session = $session; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setParam($key, $value) - { - $this->response[$key] = $value; - } - - /** - * {@inheritdoc} - */ - public function getParam($key) - { - return isset($this->response[$key]) ? $this->response[$key] : null; - } -} diff --git a/src/TokenType/Bearer.php b/src/TokenType/Bearer.php deleted file mode 100644 index 950fce1a9..000000000 --- a/src/TokenType/Bearer.php +++ /dev/null @@ -1,53 +0,0 @@ - - * @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->getParam('access_token'), - 'token_type' => 'Bearer', - 'expires_in' => $this->getParam('expires_in'), - ]; - - if (!is_null($this->getParam('refresh_token'))) { - $return['refresh_token'] = $this->getParam('refresh_token'); - } - - return $return; - } - - /** - * {@inheritdoc} - */ - public function determineAccessTokenInHeader(Request $request) - { - if ($request->headers->has('Authorization') === false) { - return; - } - - $header = $request->headers->get('Authorization'); - - if (substr($header, 0, 7) !== 'Bearer ') { - return; - } - - return trim(substr($header, 7)); - } -} diff --git a/src/TokenType/MAC.php b/src/TokenType/MAC.php deleted file mode 100644 index 886b73707..000000000 --- a/src/TokenType/MAC.php +++ /dev/null @@ -1,156 +0,0 @@ - - * @copyright Copyright (c) Alex Bilbie - * @license http://mit-license.org/ - * @link https://github.com/thephpleague/oauth2-server - */ - -namespace League\OAuth2\Server\TokenType; - -use League\OAuth2\Server\Util\SecureKey; -use Symfony\Component\HttpFoundation\ParameterBag; -use Symfony\Component\HttpFoundation\Request; - -/** - * MAC Token Type - */ -class MAC extends AbstractTokenType implements TokenTypeInterface -{ - /** - * {@inheritdoc} - */ - public function generateResponse() - { - $macKey = SecureKey::generate(); - $this->server->getMacStorage()->create($macKey, $this->getParam('access_token')); - - $response = [ - 'access_token' => $this->getParam('access_token'), - 'token_type' => 'mac', - 'expires_in' => $this->getParam('expires_in'), - 'mac_key' => $macKey, - 'mac_algorithm' => 'hmac-sha-256', - ]; - - if (!is_null($this->getParam('refresh_token'))) { - $response['refresh_token'] = $this->getParam('refresh_token'); - } - - return $response; - } - - /** - * {@inheritdoc} - */ - public function determineAccessTokenInHeader(Request $request) - { - if ($request->headers->has('Authorization') === false) { - return; - } - - $header = $request->headers->get('Authorization'); - - if (substr($header, 0, 4) !== 'MAC ') { - return; - } - - // Find all the parameters expressed in the header - $paramsRaw = explode(',', substr($header, 4)); - $params = new ParameterBag(); - - array_map(function ($param) use (&$params) { - $param = trim($param); - - preg_match_all('/([a-zA-Z]*)="([\w=\/+]*)"/', $param, $matches); - - // @codeCoverageIgnoreStart - if (count($matches) !== 3) { - return; - } - // @codeCoverageIgnoreEnd - - $key = reset($matches[1]); - $value = trim(reset($matches[2])); - - if (empty($value)) { - return; - } - - $params->set($key, $value); - }, $paramsRaw); - - // Validate parameters - if ($params->has('id') === false || $params->has('ts') === false || $params->has('nonce') === false || $params->has('mac') === false) { - return; - } - - if (abs($params->get('ts') - time()) > 300) { - return; - } - - $accessToken = $params->get('id'); - $timestamp = (int) $params->get('ts'); - $nonce = $params->get('nonce'); - $signature = $params->get('mac'); - - // Try to find the MAC key for the access token - $macKey = $this->server->getMacStorage()->getByAccessToken($accessToken); - - if ($macKey === null) { - return; - } - - // Calculate and compare the signature - $calculatedSignatureParts = [ - $timestamp, - $nonce, - strtoupper($request->getMethod()), - $request->getRequestUri(), - $request->getHost(), - $request->getPort(), - ]; - - if ($params->has('ext')) { - $calculatedSignatureParts[] = $params->get('ext'); - } - - $calculatedSignature = base64_encode( - hash_hmac( - 'sha256', - implode("\n", $calculatedSignatureParts), - $macKey, - true // raw_output: outputs raw binary data - ) - ); - - // Return the access token if the signature matches - return ($this->hash_equals($calculatedSignature, $signature)) ? $accessToken : null; - } - - /** - * Prevent timing attack - * @param string $knownString - * @param string $userString - * @return bool - */ - private function hash_equals($knownString, $userString) - { - if (function_exists('\hash_equals')) { - return \hash_equals($knownString, $userString); - } - if (strlen($knownString) !== strlen($userString)) { - return false; - } - $len = strlen($knownString); - $result = 0; - for ($i = 0; $i < $len; $i++) { - $result |= (ord($knownString[$i]) ^ ord($userString[$i])); - } - // They are only identical strings if $result is exactly 0... - return 0 === $result; - } -} diff --git a/src/TokenType/TokenTypeInterface.php b/src/TokenType/TokenTypeInterface.php deleted file mode 100644 index 17c2c1cf5..000000000 --- a/src/TokenType/TokenTypeInterface.php +++ /dev/null @@ -1,68 +0,0 @@ - - * @copyright Copyright (c) Alex Bilbie - * @license http://mit-license.org/ - * @link https://github.com/thephpleague/oauth2-server - */ - -namespace League\OAuth2\Server\TokenType; - -use League\OAuth2\Server\AbstractServer; -use League\OAuth2\Server\Entity\SessionEntity; -use Symfony\Component\HttpFoundation\Request; - -interface TokenTypeInterface -{ - /** - * Generate a response - * - * @return array - */ - public function generateResponse(); - - /** - * Set the server - * - * @param \League\OAuth2\Server\AbstractServer $server - * - * @return self - */ - public function setServer(AbstractServer $server); - - /** - * Set a key/value response pair - * - * @param string $key - * @param mixed $value - */ - public function setParam($key, $value); - - /** - * Get a key from the response array - * - * @param string $key - * - * @return mixed - */ - public function getParam($key); - - /** - * @param \League\OAuth2\Server\Entity\SessionEntity $session - * - * @return self - */ - public function setSession(SessionEntity $session); - - /** - * Determine the access token in the authorization header - * - * @param \Symfony\Component\HttpFoundation\Request $request - * - * @return string - */ - public function determineAccessTokenInHeader(Request $request); -} diff --git a/src/Util/KeyAlgorithm/DefaultAlgorithm.php b/src/Util/KeyAlgorithm/DefaultAlgorithm.php deleted file mode 100644 index 70fecb146..000000000 --- a/src/Util/KeyAlgorithm/DefaultAlgorithm.php +++ /dev/null @@ -1,36 +0,0 @@ - - * @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 -{ - /** - * {@inheritdoc} - */ - public function generate($len = 40) - { - $stripped = ''; - do { - $bytes = openssl_random_pseudo_bytes($len, $strong); - - // We want to stop execution if the key fails because, well, that is bad. - if ($bytes === false || $strong === false) { - // @codeCoverageIgnoreStart - throw new \Exception('Error Generating Key'); - // @codeCoverageIgnoreEnd - } - $stripped .= str_replace(['/', '+', '='], '', base64_encode($bytes)); - } while (strlen($stripped) < $len); - - return substr($stripped, 0, $len); - } -} diff --git a/src/Util/KeyAlgorithm/KeyAlgorithmInterface.php b/src/Util/KeyAlgorithm/KeyAlgorithmInterface.php deleted file mode 100644 index c1237f917..000000000 --- a/src/Util/KeyAlgorithm/KeyAlgorithmInterface.php +++ /dev/null @@ -1,24 +0,0 @@ - - * @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); -} diff --git a/src/Util/RedirectUri.php b/src/Util/RedirectUri.php deleted file mode 100644 index 848e854a3..000000000 --- a/src/Util/RedirectUri.php +++ /dev/null @@ -1,34 +0,0 @@ - - * @copyright Copyright (c) Alex Bilbie - * @license http://mit-license.org/ - * @link https://github.com/thephpleague/oauth2-server - */ - -namespace League\OAuth2\Server\Util; - -/** - * RedirectUri class - */ -class RedirectUri -{ - /** - * Generate a new redirect uri - * - * @param string $uri The base URI - * @param array $params The query string parameters - * @param string $queryDelimeter The query string delimeter (default: "?") - * - * @return string The updated URI - */ - public static function make($uri, $params = [], $queryDelimeter = '?') - { - $uri .= (strstr($uri, $queryDelimeter) === false) ? $queryDelimeter : '&'; - - return $uri.http_build_query($params); - } -} diff --git a/src/Util/SecureKey.php b/src/Util/SecureKey.php deleted file mode 100644 index 95ca56b00..000000000 --- a/src/Util/SecureKey.php +++ /dev/null @@ -1,55 +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; - -use League\OAuth2\Server\Util\KeyAlgorithm\DefaultAlgorithm; -use League\OAuth2\Server\Util\KeyAlgorithm\KeyAlgorithmInterface; - -/** - * SecureKey class - */ -class SecureKey -{ - protected static $algorithm; - - /** - * Generate a new unique code - * - * @param integer $len Length of the generated code - * - * @return string - */ - public static function generate($len = 40) - { - return self::getAlgorithm()->generate($len); - } - - /** - * @param KeyAlgorithmInterface $algorithm - */ - public static function setAlgorithm(KeyAlgorithmInterface $algorithm) - { - self::$algorithm = $algorithm; - } - - /** - * @return KeyAlgorithmInterface - */ - public static function getAlgorithm() - { - if (is_null(self::$algorithm)) { - self::$algorithm = new DefaultAlgorithm(); - } - - return self::$algorithm; - } -} diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php new file mode 100644 index 000000000..2303b7132 --- /dev/null +++ b/tests/AuthorizationServerTest.php @@ -0,0 +1,204 @@ +getMock(ClientRepositoryInterface::class), + $this->getMock(AccessTokenRepositoryInterface::class), + $this->getMock(ScopeRepositoryInterface::class), + 'file://' . __DIR__ . '/Stubs/private.key', + 'file://' . __DIR__ . '/Stubs/public.key', + new StubResponseType() + ); + + $server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M')); + + try { + $server->respondToAccessTokenRequest(ServerRequestFactory::fromGlobals(), new Response); + } catch (OAuthServerException $e) { + $this->assertEquals('unsupported_grant_type', $e->getErrorType()); + $this->assertEquals(400, $e->getHttpStatusCode()); + } + } + + public function testRespondToRequest() + { + $clientRepository = $this->getMock(ClientRepositoryInterface::class); + $clientRepository->method('getClientEntity')->willReturn(new ClientEntity()); + + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); + + $accessTokenRepositoryMock = $this->getMock(AccessTokenRepositoryInterface::class); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + + $server = new AuthorizationServer( + $clientRepository, + $accessTokenRepositoryMock, + $scopeRepositoryMock, + 'file://' . __DIR__ . '/Stubs/private.key', + 'file://' . __DIR__ . '/Stubs/public.key', + new StubResponseType() + ); + + $server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M')); + + $_POST['grant_type'] = 'client_credentials'; + $_POST['client_id'] = 'foo'; + $_POST['client_secret'] = 'bar'; + $response = $server->respondToAccessTokenRequest(ServerRequestFactory::fromGlobals(), new Response); + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testGetResponseType() + { + $clientRepository = $this->getMock(ClientRepositoryInterface::class); + + $server = new AuthorizationServer( + $clientRepository, + $this->getMock(AccessTokenRepositoryInterface::class), + $this->getMock(ScopeRepositoryInterface::class), + 'file://' . __DIR__ . '/Stubs/private.key', + 'file://' . __DIR__ . '/Stubs/public.key' + ); + + $abstractGrantReflection = new \ReflectionClass($server); + $method = $abstractGrantReflection->getMethod('getResponseType'); + $method->setAccessible(true); + + $this->assertTrue($method->invoke($server) instanceof BearerTokenResponse); + } + + public function testCompleteAuthorizationRequest() + { + $clientRepository = $this->getMock(ClientRepositoryInterface::class); + + $server = new AuthorizationServer( + $clientRepository, + $this->getMock(AccessTokenRepositoryInterface::class), + $this->getMock(ScopeRepositoryInterface::class), + 'file://' . __DIR__ . '/Stubs/private.key', + 'file://' . __DIR__ . '/Stubs/public.key' + ); + + $authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(); + $authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity()); + + $grant = new AuthCodeGrant( + $authCodeRepository, + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/Stubs/private.key')); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/Stubs/public.key')); + + $server->enableGrantType($grant); + + $authRequest = new AuthorizationRequest(); + $authRequest->setAuthorizationApproved(true); + $authRequest->setClient(new ClientEntity()); + $authRequest->setGrantTypeId('authorization_code'); + $authRequest->setUser(new UserEntity()); + + $this->assertTrue( + $server->completeAuthorizationRequest($authRequest, new Response) instanceof ResponseInterface + ); + } + + public function testValidateAuthorizationRequest() + { + $client = new ClientEntity(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + + $server = new AuthorizationServer( + $clientRepositoryMock, + $this->getMock(AccessTokenRepositoryInterface::class), + $this->getMock(ScopeRepositoryInterface::class), + 'file://' . __DIR__ . '/Stubs/private.key', + 'file://' . __DIR__ . '/Stubs/public.key' + ); + $server->enableGrantType($grant); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + ] + ); + + $this->assertTrue($server->validateAuthorizationRequest($request) instanceof AuthorizationRequest); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 2 + */ + public function testValidateAuthorizationRequestUnregistered() + { + $server = new AuthorizationServer( + $this->getMock(ClientRepositoryInterface::class), + $this->getMock(AccessTokenRepositoryInterface::class), + $this->getMock(ScopeRepositoryInterface::class), + 'file://' . __DIR__ . '/Stubs/private.key', + 'file://' . __DIR__ . '/Stubs/public.key' + ); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + ] + ); + + $server->validateAuthorizationRequest($request); + } +} diff --git a/tests/Bootstrap.php b/tests/Bootstrap.php new file mode 100644 index 000000000..b02cb7be4 --- /dev/null +++ b/tests/Bootstrap.php @@ -0,0 +1,11 @@ + wget http://getcomposer.org/composer.phar +> php composer.phar install +MSG; + + exit($message); +} diff --git a/tests/CryptTraitTest.php b/tests/CryptTraitTest.php new file mode 100644 index 000000000..d0ada49e2 --- /dev/null +++ b/tests/CryptTraitTest.php @@ -0,0 +1,55 @@ +cryptStub = new CryptTraitStub; + } + + public function testEncryptDecrypt() + { + $payload = 'alex loves whisky'; + $encrypted = $this->cryptStub->doEncrypt($payload); + $plainText = $this->cryptStub->doDecrypt($encrypted); + + $this->assertNotEquals($payload, $encrypted); + $this->assertEquals($payload, $plainText); + } + + /** + * @expectedException \LogicException + */ + public function testBadPrivateKey() + { + $this->cryptStub->setPrivateKey(new CryptKey(__DIR__ . '/Stubs/public.key')); + $this->cryptStub->doEncrypt(''); + } + + /** + * @expectedException \LogicException + */ + public function testBadPublicKey() + { + $this->cryptStub->setPublicKey(new CryptKey(__DIR__ . '/Stubs/private.key')); + $this->cryptStub->doDecrypt(''); + } + + /** + * @expectedException \LogicException + */ + public function testNonExistentKey() + { + new CryptKey('foo/bar'); + } +} diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php new file mode 100644 index 000000000..0c4f6ae70 --- /dev/null +++ b/tests/Grant/AbstractGrantTest.php @@ -0,0 +1,430 @@ +getMockForAbstractClass(AbstractGrant::class); + $grantMock->setPrivateKey(new CryptKey(__DIR__ . '/../Stubs/private.key')); + $grantMock->setPublicKey(new CryptKey(__DIR__ . '/../Stubs/public.key')); + $grantMock->setEmitter(new Emitter()); + } + + public function testValidateClientPublic() + { + $client = new ClientEntity(); + + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setClientRepository($clientRepositoryMock); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + ] + ); + $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); + $validateClientMethod->setAccessible(true); + + $result = $validateClientMethod->invoke($grantMock, $serverRequest, true, true); + $this->assertEquals($client, $result); + } + + public function testValidateClientConfidential() + { + $client = new ClientEntity(); + + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setClientRepository($clientRepositoryMock); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'redirect_uri' => 'http://foo/bar', + ] + ); + $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); + $validateClientMethod->setAccessible(true); + + $result = $validateClientMethod->invoke($grantMock, $serverRequest, true, true); + $this->assertEquals($client, $result); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testValidateClientMissingClientId() + { + $client = new ClientEntity(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setClientRepository($clientRepositoryMock); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + + $serverRequest = new ServerRequest(); + $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); + $validateClientMethod->setAccessible(true); + + $validateClientMethod->invoke($grantMock, $serverRequest, true, true); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testValidateClientMissingClientSecret() + { + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn(null); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setClientRepository($clientRepositoryMock); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody([ + 'client_id' => 'foo', + ]); + + $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); + $validateClientMethod->setAccessible(true); + + $validateClientMethod->invoke($grantMock, $serverRequest, true, true); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testValidateClientInvalidClientSecret() + { + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn(null); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setClientRepository($clientRepositoryMock); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'foo', + ]); + + $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); + $validateClientMethod->setAccessible(true); + + $validateClientMethod->invoke($grantMock, $serverRequest, true, true); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testValidateClientInvalidRedirectUri() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setClientRepository($clientRepositoryMock); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody([ + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar/foo', + ]); + + $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); + $validateClientMethod->setAccessible(true); + + $validateClientMethod->invoke($grantMock, $serverRequest, true, true); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testValidateClientInvalidRedirectUriArray() + { + $client = new ClientEntity(); + $client->setRedirectUri(['http://foo/bar']); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setClientRepository($clientRepositoryMock); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody([ + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar/foo', + ]); + + $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); + $validateClientMethod->setAccessible(true); + + $validateClientMethod->invoke($grantMock, $serverRequest, true, true); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testValidateClientBadClient() + { + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn(null); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setClientRepository($clientRepositoryMock); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + ]); + + $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); + $validateClientMethod->setAccessible(true); + + $validateClientMethod->invoke($grantMock, $serverRequest, true); + } + + public function testCanRespondToRequest() + { + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->method('getIdentifier')->willReturn('foobar'); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody([ + 'grant_type' => 'foobar', + ]); + + $this->assertTrue($grantMock->canRespondToAccessTokenRequest($serverRequest)); + } + + public function testIssueRefreshToken() + { + $refreshTokenRepoMock = $this->getMock(RefreshTokenRepositoryInterface::class); + $refreshTokenRepoMock + ->expects($this->once()) + ->method('getNewRefreshToken') + ->willReturn(new RefreshTokenEntity()); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setRefreshTokenTTL(new \DateInterval('PT1M')); + $grantMock->setRefreshTokenRepository($refreshTokenRepoMock); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + $issueRefreshTokenMethod = $abstractGrantReflection->getMethod('issueRefreshToken'); + $issueRefreshTokenMethod->setAccessible(true); + + $accessToken = new AccessTokenEntity(); + /** @var RefreshTokenEntityInterface $refreshToken */ + $refreshToken = $issueRefreshTokenMethod->invoke($grantMock, $accessToken); + $this->assertTrue($refreshToken instanceof RefreshTokenEntityInterface); + $this->assertFalse($refreshToken->isExpired()); + $this->assertEquals($accessToken, $refreshToken->getAccessToken()); + } + + public function testIssueAccessToken() + { + $accessTokenRepoMock = $this->getMock(AccessTokenRepositoryInterface::class); + $accessTokenRepoMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setAccessTokenRepository($accessTokenRepoMock); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + $issueAccessTokenMethod = $abstractGrantReflection->getMethod('issueAccessToken'); + $issueAccessTokenMethod->setAccessible(true); + + /** @var AccessTokenEntityInterface $accessToken */ + $accessToken = $issueAccessTokenMethod->invoke( + $grantMock, + new \DateInterval('PT1H'), + new ClientEntity(), + 123, + [new ScopeEntity()] + ); + $this->assertTrue($accessToken instanceof AccessTokenEntityInterface); + $this->assertFalse($accessToken->isExpired()); + } + + public function testIssueAuthCode() + { + $authCodeRepoMock = $this->getMock(AuthCodeRepositoryInterface::class); + $authCodeRepoMock->expects($this->once())->method('getNewAuthCode')->willReturn(new AuthCodeEntity()); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setAuthCodeRepository($authCodeRepoMock); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + $issueAuthCodeMethod = $abstractGrantReflection->getMethod('issueAuthCode'); + $issueAuthCodeMethod->setAccessible(true); + + $this->assertTrue( + $issueAuthCodeMethod->invoke( + $grantMock, + new \DateInterval('PT1H'), + new ClientEntity(), + 123, + 'http://foo/bar', + [new ScopeEntity()] + ) instanceof AuthCodeEntityInterface + ); + } + + public function testGetCookieParameter() + { + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->method('getIdentifier')->willReturn('foobar'); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + $method = $abstractGrantReflection->getMethod('getCookieParameter'); + $method->setAccessible(true); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withCookieParams([ + 'foo' => 'bar', + ]); + + $this->assertEquals('bar', $method->invoke($grantMock, 'foo', $serverRequest)); + $this->assertEquals('foo', $method->invoke($grantMock, 'bar', $serverRequest, 'foo')); + } + + public function testGetQueryStringParameter() + { + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->method('getIdentifier')->willReturn('foobar'); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + $method = $abstractGrantReflection->getMethod('getQueryStringParameter'); + $method->setAccessible(true); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withQueryParams([ + 'foo' => 'bar', + ]); + + $this->assertEquals('bar', $method->invoke($grantMock, 'foo', $serverRequest)); + $this->assertEquals('foo', $method->invoke($grantMock, 'bar', $serverRequest, 'foo')); + } + + public function testValidateScopes() + { + $scope = new ScopeEntity(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setScopeRepository($scopeRepositoryMock); + + $this->assertEquals([$scope], $grantMock->validateScopes('basic ')); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testValidateScopesBadScope() + { + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn(null); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setScopeRepository($scopeRepositoryMock); + + $grantMock->validateScopes('basic '); + } + + public function testGenerateUniqueIdentifier() + { + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + $method = $abstractGrantReflection->getMethod('generateUniqueIdentifier'); + $method->setAccessible(true); + + $this->assertTrue(is_string($method->invoke($grantMock))); + } + + public function testCanRespondToAuthorizationRequest() + { + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $this->assertFalse($grantMock->canRespondToAuthorizationRequest(new ServerRequest())); + } + + /** + * @expectedException \LogicException + */ + public function testValidateAuthorizationRequest() + { + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->validateAuthorizationRequest(new ServerRequest()); + } + + /** + * @expectedException \LogicException + */ + public function testCompleteAuthorizationRequest() + { + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->completeAuthorizationRequest(new AuthorizationRequest()); + } +} diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php new file mode 100644 index 000000000..3783d349f --- /dev/null +++ b/tests/Grant/AuthCodeGrantTest.php @@ -0,0 +1,713 @@ +cryptStub = new CryptTraitStub; + } + + public function testGetIdentifier() + { + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + + $this->assertEquals('authorization_code', $grant->getIdentifier()); + } + + public function testCanRespondToAuthorizationRequest() + { + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + ] + ); + + $this->assertTrue($grant->canRespondToAuthorizationRequest($request)); + } + + public function testValidateAuthorizationRequest() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + ] + ); + + $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); + } + + public function testValidateAuthorizationRequestRedirectUriArray() + { + $client = new ClientEntity(); + $client->setRedirectUri(['http://foo/bar']); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + ] + ); + + $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 3 + */ + public function testValidateAuthorizationRequestMissingClientId() + { + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + ] + ); + + $grant->validateAuthorizationRequest($request); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 4 + */ + public function testValidateAuthorizationRequestInvalidClientId() + { + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn(null); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + ] + ); + + $grant->validateAuthorizationRequest($request); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 4 + */ + public function testValidateAuthorizationRequestBadRedirectUriString() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar', + ] + ); + + $grant->validateAuthorizationRequest($request); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 4 + */ + public function testValidateAuthorizationRequestBadRedirectUriArray() + { + $client = new ClientEntity(); + $client->setRedirectUri(['http://foo/bar']); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar', + ] + ); + + $grant->validateAuthorizationRequest($request); + } + + public function testCompleteAuthorizationRequest() + { + $authRequest = new AuthorizationRequest(); + $authRequest->setAuthorizationApproved(true); + $authRequest->setClient(new ClientEntity()); + $authRequest->setGrantTypeId('authorization_code'); + $authRequest->setUser(new UserEntity()); + + $authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(); + $authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity()); + + $grant = new AuthCodeGrant( + $authCodeRepository, + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 9 + */ + public function testCompleteAuthorizationRequestDenied() + { + $authRequest = new AuthorizationRequest(); + $authRequest->setAuthorizationApproved(false); + $authRequest->setClient(new ClientEntity()); + $authRequest->setGrantTypeId('authorization_code'); + $authRequest->setUser(new UserEntity()); + + $authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(); + $authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity()); + + $grant = new AuthCodeGrant( + $authCodeRepository, + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $grant->completeAuthorizationRequest($authRequest); + } + + public function testRespondToAccessTokenRequest() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeEntity = new ScopeEntity(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); + $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $request = new ServerRequest( + [], + [], + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code' => $this->cryptStub->doEncrypt( + json_encode( + [ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => 'http://foo/bar', + ] + ) + ), + ] + ); + + /** @var StubResponseType $response */ + $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + + $this->assertTrue($response->getAccessToken() instanceof AccessTokenEntityInterface); + $this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 3 + */ + public function testRespondToAccessTokenRequestMissingRedirectUri() + { + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $request = new ServerRequest( + [], + [], + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + ] + ); + + /* @var StubResponseType $response */ + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 3 + */ + public function testRespondToAccessTokenRequestMissingCode() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $request = new ServerRequest( + [], + [], + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'redirect_uri' => 'http://foo/bar', + ] + ); + + /* @var StubResponseType $response */ + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } + + public function testRespondToAccessTokenRequestExpiredCode() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $request = new ServerRequest( + [], + [], + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code' => $this->cryptStub->doEncrypt( + json_encode( + [ + 'auth_code_id' => uniqid(), + 'expire_time' => time() - 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => 'http://foo/bar', + ] + ) + ), + ] + ); + + try { + /* @var StubResponseType $response */ + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } catch (OAuthServerException $e) { + $this->assertEquals($e->getHint(), 'Authorization code has expired'); + } + } + + public function testRespondToAccessTokenRequestRevokedCode() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + + $authCodeRepositoryMock = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(); + $authCodeRepositoryMock->method('isAuthCodeRevoked')->willReturn(true); + + $grant = new AuthCodeGrant( + $authCodeRepositoryMock, + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $request = new ServerRequest( + [], + [], + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code' => $this->cryptStub->doEncrypt( + json_encode( + [ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => 'http://foo/bar', + ] + ) + ), + ] + ); + + try { + /* @var StubResponseType $response */ + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } catch (OAuthServerException $e) { + $this->assertEquals($e->getHint(), 'Authorization code has been revoked'); + } + } + + public function testRespondToAccessTokenRequestClientMismatch() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $request = new ServerRequest( + [], + [], + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code' => $this->cryptStub->doEncrypt( + json_encode( + [ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'bar', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => 'http://foo/bar', + ] + ) + ), + ] + ); + + try { + /* @var StubResponseType $response */ + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } catch (OAuthServerException $e) { + $this->assertEquals($e->getHint(), 'Authorization code was not issued to this client'); + } + } + + public function testRespondToAccessTokenRequestBadCodeEncryption() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $request = new ServerRequest( + [], + [], + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code' => 'sdfsfsd', + ] + ); + + try { + /* @var StubResponseType $response */ + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } catch (OAuthServerException $e) { + $this->assertEquals($e->getHint(), 'Cannot decrypt the authorization code'); + } + } +} diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php new file mode 100644 index 000000000..a1665831d --- /dev/null +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -0,0 +1,54 @@ +assertEquals('client_credentials', $grant->getIdentifier()); + } + + public function testRespondToRequest() + { + $client = new ClientEntity(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); + + $grant = new ClientCredentialsGrant(); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + + $this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface); + } +} diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php new file mode 100644 index 000000000..fbf60b8c8 --- /dev/null +++ b/tests/Grant/ImplicitGrantTest.php @@ -0,0 +1,298 @@ +cryptStub = new CryptTraitStub(); + } + + public function testGetIdentifier() + { + $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $this->assertEquals('implicit', $grant->getIdentifier()); + } + + public function testCanRespondToAccessTokenRequest() + { + $grant = new ImplicitGrant(new \DateInterval('PT10M')); + + $this->assertFalse( + $grant->canRespondToAccessTokenRequest(new ServerRequest()) + ); + } + + /** + * @expectedException \LogicException + */ + public function testRespondToAccessTokenRequest() + { + $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant->respondToAccessTokenRequest( + new ServerRequest(), + new StubResponseType(), + new \DateInterval('PT10M') + ); + } + + public function testCanRespondToAuthorizationRequest() + { + $grant = new ImplicitGrant(new \DateInterval('PT10M')); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'token', + 'client_id' => 'foo', + ] + ); + + $this->assertTrue($grant->canRespondToAuthorizationRequest($request)); + } + + public function testValidateAuthorizationRequest() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + ] + ); + + $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); + } + + public function testValidateAuthorizationRequestRedirectUriArray() + { + $client = new ClientEntity(); + $client->setRedirectUri(['http://foo/bar']); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + ] + ); + + $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 3 + */ + public function testValidateAuthorizationRequestMissingClientId() + { + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + + $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + ] + ); + + $grant->validateAuthorizationRequest($request); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 4 + */ + public function testValidateAuthorizationRequestInvalidClientId() + { + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn(null); + + $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + ] + ); + + $grant->validateAuthorizationRequest($request); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 4 + */ + public function testValidateAuthorizationRequestBadRedirectUriString() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar', + ] + ); + + $grant->validateAuthorizationRequest($request); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 4 + */ + public function testValidateAuthorizationRequestBadRedirectUriArray() + { + $client = new ClientEntity(); + $client->setRedirectUri(['http://foo/bar']); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar', + ] + ); + + $grant->validateAuthorizationRequest($request); + } + + public function testCompleteAuthorizationRequest() + { + $authRequest = new AuthorizationRequest(); + $authRequest->setAuthorizationApproved(true); + $authRequest->setClient(new ClientEntity()); + $authRequest->setGrantTypeId('authorization_code'); + $authRequest->setUser(new UserEntity()); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + + $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 9 + */ + public function testCompleteAuthorizationRequestDenied() + { + $authRequest = new AuthorizationRequest(); + $authRequest->setAuthorizationApproved(false); + $authRequest->setClient(new ClientEntity()); + $authRequest->setGrantTypeId('authorization_code'); + $authRequest->setUser(new UserEntity()); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + + $grant->completeAuthorizationRequest($authRequest); + } +} diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php new file mode 100644 index 000000000..20f2ce2b1 --- /dev/null +++ b/tests/Grant/PasswordGrantTest.php @@ -0,0 +1,170 @@ +getMock(UserRepositoryInterface::class); + $refreshTokenRepositoryMock = $this->getMock(RefreshTokenRepositoryInterface::class); + + $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); + $this->assertEquals('password', $grant->getIdentifier()); + } + + public function testRespondToRequest() + { + $client = new ClientEntity(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + $userEntity = new UserEntity(); + $userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); + + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); + + $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'username' => 'foo', + 'password' => 'bar', + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + + $this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface); + $this->assertTrue($responseType->getRefreshToken() instanceof RefreshTokenEntityInterface); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testRespondToRequestMissingUsername() + { + $client = new ClientEntity(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + + $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testRespondToRequestMissingPassword() + { + $client = new ClientEntity(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + + $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'username' => 'alex', + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testRespondToRequestBadCredentials() + { + $client = new ClientEntity(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + $userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn(null); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + + $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'username' => 'alex', + 'password' => 'whisky', + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + } +} diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php new file mode 100644 index 000000000..af3d477f3 --- /dev/null +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -0,0 +1,422 @@ +cryptStub = new CryptTraitStub(); + } + + public function testGetIdentifier() + { + $refreshTokenRepositoryMock = $this->getMock(RefreshTokenRepositoryInterface::class); + + $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); + $this->assertEquals('refresh_token', $grant->getIdentifier()); + } + + public function testRespondToRequest() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeEntity = new ScopeEntity(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock + ->expects($this->once()) + ->method('persistNewAccessToken')->willReturnSelf(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); + $refreshTokenRepositoryMock + ->expects($this->once()) + ->method('persistNewRefreshToken')->willReturnSelf(); + + $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $oldRefreshToken = $this->cryptStub->doEncrypt( + json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ) + ); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + + $this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface); + $this->assertTrue($responseType->getRefreshToken() instanceof RefreshTokenEntityInterface); + } + + public function testRespondToReducedScopes() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); + + $scope = new ScopeEntity(); + $scope->setIdentifier('foo'); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + + $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $oldRefreshToken = $this->cryptStub->doEncrypt( + json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo', 'bar'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ) + ); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + 'scope' => 'foo', + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + + $this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface); + $this->assertTrue($responseType->getRefreshToken() instanceof RefreshTokenEntityInterface); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 5 + */ + public function testRespondToUnexpectedScope() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + + $scope = new ScopeEntity(); + $scope->setIdentifier('foobar'); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + + $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $oldRefreshToken = $this->cryptStub->doEncrypt( + json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo', 'bar'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ) + ); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + 'scope' => 'foobar', + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 3 + */ + public function testRespondToRequestMissingOldToken() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + + $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 8 + */ + public function testRespondToRequestInvalidOldToken() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + + $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $oldRefreshToken = 'foobar'; + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 8 + */ + public function testRespondToRequestClientMismatch() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + + $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $oldRefreshToken = $this->cryptStub->doEncrypt( + json_encode( + [ + 'client_id' => 'bar', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ) + ); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 8 + */ + public function testRespondToRequestExpiredToken() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + + $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $oldRefreshToken = $this->cryptStub->doEncrypt( + json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => time() - 3600, + ] + ) + ); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 8 + */ + public function testRespondToRequestRevokedToken() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('isRefreshTokenRevoked')->willReturn(true); + + $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $oldRefreshToken = $this->cryptStub->doEncrypt( + json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ) + ); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + } +} diff --git a/tests/Middleware/AuthorizationServerMiddlewareTest.php b/tests/Middleware/AuthorizationServerMiddlewareTest.php new file mode 100644 index 000000000..bb068179c --- /dev/null +++ b/tests/Middleware/AuthorizationServerMiddlewareTest.php @@ -0,0 +1,111 @@ +getMock(ClientRepositoryInterface::class); + $clientRepository->method('getClientEntity')->willReturn(new ClientEntity()); + + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); + + $accessRepositoryMock = $this->getMock(AccessTokenRepositoryInterface::class); + $accessRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + + $server = new AuthorizationServer( + $clientRepository, + $accessRepositoryMock, + $scopeRepositoryMock, + 'file://' . __DIR__ . '/../Stubs/private.key', + 'file://' . __DIR__ . '/../Stubs/public.key', + new StubResponseType() + ); + + $server->enableGrantType(new ClientCredentialsGrant()); + + $_POST['grant_type'] = 'client_credentials'; + $_POST['client_id'] = 'foo'; + $_POST['client_secret'] = 'bar'; + + $request = ServerRequestFactory::fromGlobals(); + + $middleware = new AuthorizationServerMiddleware($server); + $response = $middleware->__invoke( + $request, + new Response(), + function () { + return func_get_args()[1]; + } + ); + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testOAuthErrorResponse() + { + $clientRepository = $this->getMock(ClientRepositoryInterface::class); + $clientRepository->method('getClientEntity')->willReturn(null); + + $server = new AuthorizationServer( + $clientRepository, + $this->getMock(AccessTokenRepositoryInterface::class), + $this->getMock(ScopeRepositoryInterface::class), + 'file://' . __DIR__ . '/../Stubs/private.key', + 'file://' . __DIR__ . '/../Stubs/public.key', + new StubResponseType() + ); + + $server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M')); + + $_POST['grant_type'] = 'client_credentials'; + $_POST['client_id'] = 'foo'; + $_POST['client_secret'] = 'bar'; + + $request = ServerRequestFactory::fromGlobals(); + + $middleware = new AuthorizationServerMiddleware($server); + + $response = $middleware->__invoke( + $request, + new Response(), + function () { + return func_get_args()[1]; + } + ); + + $this->assertEquals(401, $response->getStatusCode()); + } + + public function testOAuthErrorResponseRedirectUri() + { + $exception = OAuthServerException::invalidScope('test', 'http://foo/bar'); + $response = $exception->generateHttpResponse(new Response()); + + $this->assertEquals(302, $response->getStatusCode()); + $this->assertEquals('http://foo/bar?error=invalid_scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope', $response->getHeader('location')[0]); + } + + public function testOAuthErrorResponseRedirectUriFragment() + { + $exception = OAuthServerException::invalidScope('test', 'http://foo/bar'); + $response = $exception->generateHttpResponse(new Response(), true); + + $this->assertEquals(302, $response->getStatusCode()); + $this->assertEquals('http://foo/bar#error=invalid_scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope', $response->getHeader('location')[0]); + } +} diff --git a/tests/Middleware/ResourceServerMiddlewareTest.php b/tests/Middleware/ResourceServerMiddlewareTest.php new file mode 100644 index 000000000..e91e9b9bc --- /dev/null +++ b/tests/Middleware/ResourceServerMiddlewareTest.php @@ -0,0 +1,107 @@ +getMock(AccessTokenRepositoryInterface::class), + 'file://' . __DIR__ . '/../Stubs/public.key' + ); + + $client = new ClientEntity(); + $client->setIdentifier('clientName'); + + $accessToken = new AccessTokenEntity(); + $accessToken->setIdentifier('test'); + $accessToken->setUserIdentifier(123); + $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $accessToken->setClient($client); + + $token = $accessToken->convertToJWT(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $request = new ServerRequest(); + $request = $request->withHeader('authorization', sprintf('Bearer %s', $token)); + + $middleware = new ResourceServerMiddleware($server); + $response = $middleware->__invoke( + $request, + new Response(), + function () { + $this->assertEquals('test', func_get_args()[0]->getAttribute('oauth_access_token_id')); + + return func_get_args()[1]; + } + ); + + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testValidResponseExpiredToken() + { + $server = new ResourceServer( + $this->getMock(AccessTokenRepositoryInterface::class), + 'file://' . __DIR__ . '/../Stubs/public.key' + ); + + $client = new ClientEntity(); + $client->setIdentifier('clientName'); + + $accessToken = new AccessTokenEntity(); + $accessToken->setIdentifier('test'); + $accessToken->setUserIdentifier(123); + $accessToken->setExpiryDateTime((new \DateTime())->sub(new \DateInterval('PT1H'))); + $accessToken->setClient($client); + + $token = $accessToken->convertToJWT(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $request = new ServerRequest(); + $request = $request->withHeader('authorization', sprintf('Bearer %s', $token)); + + $middleware = new ResourceServerMiddleware($server); + $response = $middleware->__invoke( + $request, + new Response(), + function () { + $this->assertEquals('test', func_get_args()[0]->getAttribute('oauth_access_token_id')); + + return func_get_args()[1]; + } + ); + + $this->assertEquals(401, $response->getStatusCode()); + } + + public function testErrorResponse() + { + $server = new ResourceServer( + $this->getMock(AccessTokenRepositoryInterface::class), + 'file://' . __DIR__ . '/../Stubs/public.key' + ); + + $request = new ServerRequest(); + $request = $request->withHeader('authorization', ''); + + $middleware = new ResourceServerMiddleware($server); + $response = $middleware->__invoke( + $request, + new Response(), + function () { + return func_get_args()[1]; + } + ); + + $this->assertEquals(401, $response->getStatusCode()); + } +} diff --git a/tests/ResourceServerTest.php b/tests/ResourceServerTest.php new file mode 100644 index 000000000..c1dafe272 --- /dev/null +++ b/tests/ResourceServerTest.php @@ -0,0 +1,26 @@ +getMock(AccessTokenRepositoryInterface::class), + 'file://' . __DIR__ . '/Stubs/public.key' + ); + + try { + $server->validateAuthenticatedRequest(ServerRequestFactory::fromGlobals()); + } catch (OAuthServerException $e) { + $this->assertEquals('Missing "Authorization" header', $e->getHint()); + } + } +} diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php new file mode 100644 index 000000000..6c84e148b --- /dev/null +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -0,0 +1,229 @@ +getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + + $responseType = new BearerTokenResponse($accessTokenRepositoryMock); + $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $client = new ClientEntity(); + $client->setIdentifier('clientName'); + + $scope = new ScopeEntity(); + $scope->setIdentifier('basic'); + + $accessToken = new AccessTokenEntity(); + $accessToken->setIdentifier('abcdef'); + $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $accessToken->setClient($client); + $accessToken->addScope($scope); + + $refreshToken = new RefreshTokenEntity(); + $refreshToken->setIdentifier('abcdef'); + $refreshToken->setAccessToken($accessToken); + $refreshToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + + $responseType->setAccessToken($accessToken); + $responseType->setRefreshToken($refreshToken); + + $response = $responseType->generateHttpResponse(new Response()); + + $this->assertTrue($response instanceof ResponseInterface); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('no-cache', $response->getHeader('pragma')[0]); + $this->assertEquals('no-store', $response->getHeader('cache-control')[0]); + $this->assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]); + + $response->getBody()->rewind(); + $json = json_decode($response->getBody()->getContents()); + $this->assertEquals('Bearer', $json->token_type); + $this->assertTrue(isset($json->expires_in)); + $this->assertTrue(isset($json->access_token)); + $this->assertTrue(isset($json->refresh_token)); + } + + public function testDetermineAccessTokenInHeaderValidToken() + { + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false); + + $responseType = new BearerTokenResponse($accessTokenRepositoryMock); + $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $client = new ClientEntity(); + $client->setIdentifier('clientName'); + + $accessToken = new AccessTokenEntity(); + $accessToken->setIdentifier('abcdef'); + $accessToken->setUserIdentifier(123); + $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $accessToken->setClient($client); + + $refreshToken = new RefreshTokenEntity(); + $refreshToken->setIdentifier('abcdef'); + $refreshToken->setAccessToken($accessToken); + $refreshToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + + $responseType->setAccessToken($accessToken); + $responseType->setRefreshToken($refreshToken); + + $response = $responseType->generateHttpResponse(new Response()); + $json = json_decode((string) $response->getBody()); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false); + + $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); + $authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $request = new ServerRequest(); + $request = $request->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); + + $request = $authorizationValidator->validateAuthorization($request); + + $this->assertEquals('abcdef', $request->getAttribute('oauth_access_token_id')); + $this->assertEquals('clientName', $request->getAttribute('oauth_client_id')); + $this->assertEquals('123', $request->getAttribute('oauth_user_id')); + $this->assertEquals([], $request->getAttribute('oauth_scopes')); + } + + public function testDetermineAccessTokenInHeaderInvalidJWT() + { + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false); + + $responseType = new BearerTokenResponse($accessTokenRepositoryMock); + $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $client = new ClientEntity(); + $client->setIdentifier('clientName'); + + $accessToken = new AccessTokenEntity(); + $accessToken->setIdentifier('abcdef'); + $accessToken->setUserIdentifier(123); + $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $accessToken->setClient($client); + + $refreshToken = new RefreshTokenEntity(); + $refreshToken->setIdentifier('abcdef'); + $refreshToken->setAccessToken($accessToken); + $refreshToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + + $responseType->setAccessToken($accessToken); + $responseType->setRefreshToken($refreshToken); + + $response = $responseType->generateHttpResponse(new Response()); + $json = json_decode((string) $response->getBody()); + + $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); + $authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $request = new ServerRequest(); + $request = $request->withHeader('authorization', sprintf('Bearer %s', $json->access_token . 'foo')); + + try { + $authorizationValidator->validateAuthorization($request); + } catch (OAuthServerException $e) { + $this->assertEquals( + 'Access token could not be verified', + $e->getHint() + ); + } + } + + public function testDetermineAccessTokenInHeaderRevokedToken() + { + $responseType = new BearerTokenResponse(); + $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $client = new ClientEntity(); + $client->setIdentifier('clientName'); + + $accessToken = new AccessTokenEntity(); + $accessToken->setIdentifier('abcdef'); + $accessToken->setUserIdentifier(123); + $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $accessToken->setClient($client); + + $refreshToken = new RefreshTokenEntity(); + $refreshToken->setIdentifier('abcdef'); + $refreshToken->setAccessToken($accessToken); + $refreshToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + + $responseType->setAccessToken($accessToken); + $responseType->setRefreshToken($refreshToken); + + $response = $responseType->generateHttpResponse(new Response()); + $json = json_decode((string) $response->getBody()); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(true); + + $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); + $authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $request = new ServerRequest(); + $request = $request->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); + + try { + $authorizationValidator->validateAuthorization($request); + } catch (OAuthServerException $e) { + $this->assertEquals( + 'Access token has been revoked', + $e->getHint() + ); + } + } + + public function testDetermineAccessTokenInHeaderInvalidToken() + { + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + + $responseType = new BearerTokenResponse($accessTokenRepositoryMock); + $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + + $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); + $authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $request = new ServerRequest(); + $request = $request->withHeader('authorization', 'Bearer blah'); + + try { + $authorizationValidator->validateAuthorization($request); + } catch (OAuthServerException $e) { + $this->assertEquals( + 'The JWT string must have two dots', + $e->getHint() + ); + } + } +} diff --git a/tests/Stubs/AccessTokenEntity.php b/tests/Stubs/AccessTokenEntity.php new file mode 100644 index 000000000..77a4d2231 --- /dev/null +++ b/tests/Stubs/AccessTokenEntity.php @@ -0,0 +1,13 @@ +redirectUri = $uri; + } + + public function setName($name) + { + $this->name = $name; + } +} diff --git a/tests/Stubs/CryptTraitStub.php b/tests/Stubs/CryptTraitStub.php new file mode 100644 index 000000000..2414c199a --- /dev/null +++ b/tests/Stubs/CryptTraitStub.php @@ -0,0 +1,27 @@ +setPrivateKey(new CryptKey('file://' . __DIR__ . '/private.key')); + $this->setPublicKey(new CryptKey('file://' . __DIR__ . '/public.key')); + } + + public function doEncrypt($unencryptedData) + { + return $this->encrypt($unencryptedData); + } + + public function doDecrypt($encryptedData) + { + return $this->decrypt($encryptedData); + } +} diff --git a/tests/Stubs/RefreshTokenEntity.php b/tests/Stubs/RefreshTokenEntity.php new file mode 100644 index 000000000..f145b7065 --- /dev/null +++ b/tests/Stubs/RefreshTokenEntity.php @@ -0,0 +1,12 @@ +getIdentifier(); + } +} diff --git a/tests/Stubs/StubResponseType.php b/tests/Stubs/StubResponseType.php new file mode 100644 index 000000000..ac8679d22 --- /dev/null +++ b/tests/Stubs/StubResponseType.php @@ -0,0 +1,70 @@ +accessToken; + } + + public function getRefreshToken() + { + return $this->refreshToken; + } + + /** + * @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken + */ + public function setAccessToken(AccessTokenEntityInterface $accessToken) + { + $this->accessToken = $accessToken; + } + + /** + * @param \League\OAuth2\Server\Entities\RefreshTokenEntityInterface $refreshToken + */ + public function setRefreshToken(RefreshTokenEntityInterface $refreshToken) + { + $this->refreshToken = $refreshToken; + } + + /** + * @param ServerRequestInterface $request + * + * @throws \League\OAuth2\Server\Exception\OAuthServerException + * + * @return \Psr\Http\Message\ServerRequestInterface + */ + public function validateAccessToken(ServerRequestInterface $request) + { + if ($request->getHeader('authorization')[0] === 'Basic test') { + return $request->withAttribute('oauth_access_token_id', 'test'); + } + + throw OAuthServerException::accessDenied(); + } + + /** + * @param ResponseInterface $response + * + * @return ResponseInterface + */ + public function generateHttpResponse(ResponseInterface $response) + { + return new Response(); + } +} diff --git a/tests/Stubs/UserEntity.php b/tests/Stubs/UserEntity.php new file mode 100644 index 000000000..6bbdc8c41 --- /dev/null +++ b/tests/Stubs/UserEntity.php @@ -0,0 +1,16 @@ +setIdentifier(123); + } +} diff --git a/tests/Stubs/private.key b/tests/Stubs/private.key new file mode 100644 index 000000000..1d6a3bf60 --- /dev/null +++ b/tests/Stubs/private.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDOBcFjGUlo3BJ9zjwQLgAHn6Oy5Si0uB7MublTiPob8rWTiCE4 +weAFqzPoAB07vB0t0f8c1R8rmwHMD5ljWPBgJ8FewtwAUzprOBcau6DWukd/TKxX +WeVLAl/NZxijI+jR5QDBYLNBtj1G4LBVHMmINd3ryCycbf9ac3rcC8zhrQIDAQAB +AoGADfOJ0wIlXHp6rhZHLvlOezWuSjEGfqZxP3/cMvH1rerTrPfs+AD5AKlFTJKl +aCQm/bFYy0ULZVKL3pu30Wh2bo1nh/wLuLSI9Nz3O8jqAP3z0i07SoRoQmb8fRnn +dwoDFqnk3uGqcOenheSqheIgl9vdW/3avhD6nkMKZGxPYwECQQDoSj/xHogEzMqB +1Z2E5H/exeE9GQ7+dGITRR2MSgo9WvcKdRhGaQ44dsnTmqiZWAfqAPJjTQIIA/Cn +YRRTeBbNAkEA4w0iEvCIygGQOAnWuvVzlh+pxIB+BTeGkbiBG7nkYYc9b6B/Tw1B +GWGRddBr/FIfPvy1X2ip/TBpH+9bHnE2YQJBAIbZw/EYhmIy+UUSW9WwSUNsoOu1 +Rm0V53HEZ/jvaq5fxpa9j5AgoO7KlzROzp3m6wE/93cKV6mLkAO7ae9jAekCQQCf +B6DZIS6+RrAMACAt3SOzf8P6BYG/B7Ayusd7cw2ang4S9JiW9xKkw2kN2wj3t1F5 +XalwBTAjTdgj7ROmU+ehAkEAkOyXKONGBoVfaixRHgBP6jIBSSPbB2Aosi0QAURX +6GOY7wOS1pCSntTOBQxV7wVjqFwYAR10MSxFSNfpJ7RkzA== +-----END RSA PRIVATE KEY----- diff --git a/tests/Stubs/public.key b/tests/Stubs/public.key new file mode 100644 index 000000000..250101088 --- /dev/null +++ b/tests/Stubs/public.key @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOBcFjGUlo3BJ9zjwQLgAHn6Oy +5Si0uB7MublTiPob8rWTiCE4weAFqzPoAB07vB0t0f8c1R8rmwHMD5ljWPBgJ8Fe +wtwAUzprOBcau6DWukd/TKxXWeVLAl/NZxijI+jR5QDBYLNBtj1G4LBVHMmINd3r +yCycbf9ac3rcC8zhrQIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/fuzz/grant-authcode.yml b/tests/fuzz/grant-authcode.yml deleted file mode 100644 index e4df2d687..000000000 --- a/tests/fuzz/grant-authcode.yml +++ /dev/null @@ -1,9 +0,0 @@ -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]*)/ diff --git a/tests/fuzz/grant-client-credentials.yml b/tests/fuzz/grant-client-credentials.yml deleted file mode 100644 index 47b9f566b..000000000 --- a/tests/fuzz/grant-client-credentials.yml +++ /dev/null @@ -1,67 +0,0 @@ -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 diff --git a/tests/fuzz/grant-password.yml b/tests/fuzz/grant-password.yml deleted file mode 100644 index e0f958279..000000000 --- a/tests/fuzz/grant-password.yml +++ /dev/null @@ -1,88 +0,0 @@ -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 diff --git a/tests/fuzz/tokeninfo-no-access-token.yml b/tests/fuzz/tokeninfo-no-access-token.yml deleted file mode 100644 index 253d29e94..000000000 --- a/tests/fuzz/tokeninfo-no-access-token.yml +++ /dev/null @@ -1,16 +0,0 @@ -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." diff --git a/tests/fuzz/tokeninfo-no-invalid-token-query-string.yml b/tests/fuzz/tokeninfo-no-invalid-token-query-string.yml deleted file mode 100644 index 2606eb05e..000000000 --- a/tests/fuzz/tokeninfo-no-invalid-token-query-string.yml +++ /dev/null @@ -1,16 +0,0 @@ -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." diff --git a/tests/fuzz/tokeninfo-no-invalid-token.yml b/tests/fuzz/tokeninfo-no-invalid-token.yml deleted file mode 100644 index eab58240c..000000000 --- a/tests/fuzz/tokeninfo-no-invalid-token.yml +++ /dev/null @@ -1,20 +0,0 @@ -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." diff --git a/tests/fuzz/tokeninfo-valid-token-header.yml b/tests/fuzz/tokeninfo-valid-token-header.yml deleted file mode 100644 index 67f74d69f..000000000 --- a/tests/fuzz/tokeninfo-valid-token-header.yml +++ /dev/null @@ -1,26 +0,0 @@ -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 diff --git a/tests/fuzz/tokeninfo-valid-token.yml b/tests/fuzz/tokeninfo-valid-token.yml deleted file mode 100644 index d76def84a..000000000 --- a/tests/fuzz/tokeninfo-valid-token.yml +++ /dev/null @@ -1,22 +0,0 @@ -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 diff --git a/tests/fuzz/users-token-iamalex.yml b/tests/fuzz/users-token-iamalex.yml deleted file mode 100644 index 43086f3ee..000000000 --- a/tests/fuzz/users-token-iamalex.yml +++ /dev/null @@ -1,32 +0,0 @@ -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 diff --git a/tests/fuzz/users-token-iamphil.yml b/tests/fuzz/users-token-iamphil.yml deleted file mode 100644 index 98d8e9824..000000000 --- a/tests/fuzz/users-token-iamphil.yml +++ /dev/null @@ -1,32 +0,0 @@ -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 diff --git a/tests/unit/AbstractServerTest.php b/tests/unit/AbstractServerTest.php deleted file mode 100644 index 074f363cc..000000000 --- a/tests/unit/AbstractServerTest.php +++ /dev/null @@ -1,26 +0,0 @@ -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); - } -} diff --git a/tests/unit/AuthorizationServerTest.php b/tests/unit/AuthorizationServerTest.php deleted file mode 100644 index 10a216864..000000000 --- a/tests/unit/AuthorizationServerTest.php +++ /dev/null @@ -1,82 +0,0 @@ -requireScopeParam(true); - $server->requireStateParam(true); - $server->setDefaultScope('foobar'); - $server->setScopeDelimiter(','); - $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->getScopeStorage() instanceof ScopeInterface); - $this->assertEquals('foobar', $server->getDefaultScope()); - $this->assertEquals(',', $server->getScopeDelimiter()); - $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/unit/Bootstrap.php b/tests/unit/Bootstrap.php deleted file mode 100644 index 4c9c8086d..000000000 --- a/tests/unit/Bootstrap.php +++ /dev/null @@ -1,5 +0,0 @@ - wget http://getcomposer.org/composer.phar\n> php composer.phar install\n"); -} diff --git a/tests/unit/Entity/AbstractTokenEntityTest.php b/tests/unit/Entity/AbstractTokenEntityTest.php deleted file mode 100644 index 244280853..000000000 --- a/tests/unit/Entity/AbstractTokenEntityTest.php +++ /dev/null @@ -1,116 +0,0 @@ -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 deleted file mode 100644 index 6f2a617e2..000000000 --- a/tests/unit/Entity/AccessTokenEntityTest.php +++ /dev/null @@ -1,59 +0,0 @@ -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('getSessionStorage')->andReturn($sessionStorage); - $server->shouldReceive('getAccessTokenStorage')->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('getAccessTokenStorage')->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 deleted file mode 100644 index 5b7fc0893..000000000 --- a/tests/unit/Entity/AuthCodeEntityTest.php +++ /dev/null @@ -1,73 +0,0 @@ -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('getAuthCodeStorage')->andReturn($authCodeStorage); - - $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); - $sessionStorage->shouldReceive('getByAuthCode')->andReturn( - (new SessionEntity($server)) - ); - $sessionStorage->shouldReceive('setServer'); - - $server->shouldReceive('getSessionStorage')->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 deleted file mode 100644 index 9bb13c7d8..000000000 --- a/tests/unit/Entity/ClientEntityTest.php +++ /dev/null @@ -1,25 +0,0 @@ -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 deleted file mode 100644 index f2b05cf7d..000000000 --- a/tests/unit/Entity/RefreshTokenEntityTest.php +++ /dev/null @@ -1,94 +0,0 @@ -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('getRefreshTokenStorage')->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('getAccessTokenStorage')->andReturn($accessTokenStorage); - - $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); - $sessionStorage->shouldReceive('getByAccessToken')->andReturn( - (new SessionEntity($server)) - ); - $sessionStorage->shouldReceive('setServer'); - - $server->shouldReceive('getSessionStorage')->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('getRefreshTokenStorage')->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 deleted file mode 100644 index 3fec4fe11..000000000 --- a/tests/unit/Entity/ScopeEntityTest.php +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index 5ca26a75e..000000000 --- a/tests/unit/Entity/SessionEntityTest.php +++ /dev/null @@ -1,154 +0,0 @@ -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('getAccessTokenStorage')->andReturn($accessTokenStorage); - - $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); - $sessionStorage->shouldReceive('getScopes')->andReturn( - [] - ); - $sessionStorage->shouldReceive('setServer'); - $server->setSessionStorage($sessionStorage); - - $server->shouldReceive('getSessionStorage')->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('getAccessTokenStorage')->andReturn($accessTokenStorage); - - $sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface'); - $sessionStorage->shouldReceive('getScopes')->andReturn( - [] - ); - $sessionStorage->shouldReceive('setServer'); - $server->setSessionStorage($sessionStorage); - - $server->shouldReceive('getSessionStorage')->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('getSessionStorage')->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('getClientStorage')->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 deleted file mode 100644 index 273ed374c..000000000 --- a/tests/unit/Exception/OAuthExceptionTest.php +++ /dev/null @@ -1,34 +0,0 @@ -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']); - } - - public function testShouldRedirect() - { - $exception = new OAuthException(); - $exception->redirectUri = 'http://example.com/'; - $exception->errorType = 'Error'; - $this->assertTrue($exception->shouldRedirect()); - $this->assertEquals('http://example.com/?error=Error&message=An+error+occured', $exception->getRedirectUri()); - } -} diff --git a/tests/unit/Grant/AbstractGrantTest.php b/tests/unit/Grant/AbstractGrantTest.php deleted file mode 100644 index 8a57b61b9..000000000 --- a/tests/unit/Grant/AbstractGrantTest.php +++ /dev/null @@ -1,160 +0,0 @@ -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); - - $client = (new ClientEntity($server))->hydrate(['id' => 'testapp']); - - $this->assertEquals( - [ - 'foo' => (new ScopeEntity($server))->hydrate(['id' => 'foo']), - ], - $grant->validateScopes('foo', $client) - ); - } - - 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); - - $client = (new ClientEntity($server))->hydrate(['id' => 'testapp']); - - $grant->validateScopes(null, $client); - } - - 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); - - $client = (new ClientEntity($server))->hydrate(['id' => 'testapp']); - - $grant->validateScopes('blah', $client); - } - - 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); - - $client = (new ClientEntity($server))->hydrate(['id' => 'testapp']); - - $grant->validateScopes(null, $client); - } - - 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); - - $client = (new ClientEntity($server))->hydrate(['id' => 'testapp']); - - $grant->validateScopes(null, $client); - } -} diff --git a/tests/unit/Grant/AuthCodeGrantTest.php b/tests/unit/Grant/AuthCodeGrantTest.php deleted file mode 100644 index 72a564d44..000000000 --- a/tests/unit/Grant/AuthCodeGrantTest.php +++ /dev/null @@ -1,696 +0,0 @@ -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 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 testCheckAuthoriseParamsMissingStateParam() - { - $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRequestException'); - - $_GET = [ - 'client_id' => 'testapp', - 'redirect_uri' => 'http://foo/bar', - ]; - $server = new AuthorizationServer(); - - $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); - $clientStorage->shouldReceive('setServer'); - $clientStorage->shouldReceive('get')->andReturn( - (new ClientEntity($server))->hydrate(['id' => 'testapp']) - ); - $server->setClientStorage($clientStorage); - - $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(); - - $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); - $clientStorage->shouldReceive('setServer'); - $clientStorage->shouldReceive('get')->andReturn( - (new ClientEntity($server))->hydrate(['id' => 'testapp']) - ); - $server->setClientStorage($clientStorage); - - $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(); - - $clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface'); - $clientStorage->shouldReceive('setServer'); - $clientStorage->shouldReceive('get')->andReturn( - (new ClientEntity($server))->hydrate(['id' => 'testapp']) - ); - $server->setClientStorage($clientStorage); - - $grant = new AuthCodeGrant(); - - $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 testCompleteFlowExpiredCode() - { - $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')->setExpireTime(time() - 300)->setRedirectUri('http://foo/bar') - ); - - $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')->setExpireTime(time() + 300)->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('getByAccessToken')->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')->setExpireTime(time() + 300) - ); - $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('getByAccessToken')->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')->setExpireTime(time() + 300) - ); - $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 deleted file mode 100644 index 8e5643f5e..000000000 --- a/tests/unit/Grant/ClientCredentialsGrantTest.php +++ /dev/null @@ -1,251 +0,0 @@ -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('getByAccessToken')->andReturn( - (new SessionEntity($server))->setId('foobar') - ); - $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('getByAccessToken')->andReturn( - (new SessionEntity($server))->setId('foobar') - ); - $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'); - $sessionStorage->shouldReceive('getByAccessToken')->andReturn( - (new SessionEntity($server))->setId('foobar') - ); - - $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 deleted file mode 100644 index e54ca479b..000000000 --- a/tests/unit/Grant/PasswordGrantTest.php +++ /dev/null @@ -1,479 +0,0 @@ -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('getByAccessToken')->andReturn( - (new SessionEntity($server))->setId('foobar') - ); - $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('getByAccessToken')->andReturn( - (new SessionEntity($server))->setId('foobar') - ); - $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 deleted file mode 100644 index f0ed0d7e9..000000000 --- a/tests/unit/Grant/RefreshTokenGrantTest.php +++ /dev/null @@ -1,570 +0,0 @@ -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))->setExpireTime(time() + 86400) - ); - - $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))->setExpireTime(time() + 86400) - ); - - $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 testCompleteFlowExpiredRefreshToken() - { - $this->setExpectedException('League\OAuth2\Server\Exception\InvalidRefreshException'); - - $_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); - $server->issueAccessToken(); - } - - 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))->setExpireTime(time() + 86400) - ); - - $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(); - } - - public function testCompleteFlowRotateRefreshToken() - { - $_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))->setId('refresh_token')->setExpireTime(time() + 86400) - ); - - $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)); - $this->assertNotEquals($response['refresh_token'], $_POST['refresh_token']); - - $grant->setRefreshTokenRotation(false); - $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)); - $this->assertEquals($response['refresh_token'], $_POST['refresh_token']); - } - - public function testCompleteFlowShouldRequireClientSecret() - { - $_POST = [ - 'grant_type' => 'refresh_token', - 'client_id' => 'testapp', - 'refresh_token' => 'refresh_token', - ]; - - $server = new AuthorizationServer(); - $grant = new RefreshTokenGrant(); - $grant->setRequireClientSecret(false); - - $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))->setId('refresh_token')->setExpireTime(time() + 86400) - ); - - $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)); - - } -} diff --git a/tests/unit/ResourceServerTest.php b/tests/unit/ResourceServerTest.php deleted file mode 100644 index 8855abad2..000000000 --- a/tests/unit/ResourceServerTest.php +++ /dev/null @@ -1,226 +0,0 @@ -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')->setExpireTime(time() + 300) - ); - - $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('abcdef', $server->getAccessToken()); - } - - /** - * @expectedException League\OAuth2\Server\Exception\AccessDeniedException - */ - public function testIsValidExpiredToken() - { - $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')->setExpireTime(time() - 300) - ); - - $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); - - $server->isValidRequest(); - } -} diff --git a/tests/unit/Storage/AbstractStorageTest.php b/tests/unit/Storage/AbstractStorageTest.php deleted file mode 100644 index 30312cb76..000000000 --- a/tests/unit/Storage/AbstractStorageTest.php +++ /dev/null @@ -1,23 +0,0 @@ -getMethod('setServer'); - $setMethod->setAccessible(true); - $setMethod->invokeArgs($storage, [new StubAbstractServer()]); - $getMethod = $reflector->getMethod('getServer'); - $getMethod->setAccessible(true); - - $this->assertTrue($getMethod->invoke($storage) instanceof StubAbstractServer); - } -} diff --git a/tests/unit/Stubs/StubAbstractGrant.php b/tests/unit/Stubs/StubAbstractGrant.php deleted file mode 100644 index d58fb56e3..000000000 --- a/tests/unit/Stubs/StubAbstractGrant.php +++ /dev/null @@ -1,18 +0,0 @@ -server; - } -} diff --git a/tests/unit/Stubs/StubAbstractServer.php b/tests/unit/Stubs/StubAbstractServer.php deleted file mode 100644 index 9254d80d6..000000000 --- a/tests/unit/Stubs/StubAbstractServer.php +++ /dev/null @@ -1,8 +0,0 @@ -shouldReceive('create'); - - $server = new AuthorizationServer(); - $server->setMacStorage($macStorage); - - $tokenType = new MAC(); - $tokenType->setServer($server); - - $accessToken = new AccessTokenEntity($server); - $accessToken->setId(uniqid()); - $accessToken->setExpireTime(time()); - - $tokenType->setParam('access_token', $accessToken->getId()); - $tokenType->setParam('expires_in', 3600); - - $response = $tokenType->generateResponse(); - - $this->assertEquals($accessToken->getId(), $response['access_token']); - $this->assertEquals('mac', $response['token_type']); - $this->assertEquals(3600, $response['expires_in']); - $this->assertEquals('hmac-sha-256', $response['mac_algorithm']); - $this->assertArrayHasKey('mac_key', $response); - } - - public function testDetermineAccessTokenInHeaderValid() - { - $macStorage = M::mock('\League\OAuth2\Server\Storage\MacTokenInterface'); - $macStorage->shouldReceive('getByAccessToken')->andReturn('abcdef'); - - $server = new AuthorizationServer(); - $server->setMacStorage($macStorage); - - $ts = time(); - - $request = Request::createFromGlobals(); - $calculatedSignatureParts = [ - $ts, - 'foo', - strtoupper($request->getMethod()), - $request->getRequestUri(), - $request->getHost(), - $request->getPort(), - 'ext' - ]; - $calculatedSignature = base64_encode(hash_hmac('sha256', implode("\n", $calculatedSignatureParts), 'abcdef', true)); - - $request->headers->set('Authorization', sprintf('MAC id="foo", nonce="foo", ts="%s", mac="%s", ext="ext"', $ts, $calculatedSignature)); - - $tokenType = new MAC(); - $tokenType->setServer($server); - - $response = $tokenType->determineAccessTokenInHeader($request); - $this->assertEquals('foo', $response); - } - - public function testDetermineAccessTokenInHeaderMissingHeader() - { - $macStorage = M::mock('\League\OAuth2\Server\Storage\MacTokenInterface'); - $macStorage->shouldReceive('getByAccessToken')->andReturn('abcdef'); - - $server = new AuthorizationServer(); - $server->setMacStorage($macStorage); - - $request = Request::createFromGlobals(); - $tokenType = new MAC(); - $tokenType->setServer($server); - - $response = $tokenType->determineAccessTokenInHeader($request); - - $this->assertEquals(null, $response); - } - - public function testDetermineAccessTokenInHeaderMissingAuthMac() - { - $macStorage = M::mock('\League\OAuth2\Server\Storage\MacTokenInterface'); - $macStorage->shouldReceive('getByAccessToken')->andReturn('abcdef'); - - $server = new AuthorizationServer(); - $server->setMacStorage($macStorage); - - $request = Request::createFromGlobals(); - $request->headers->set('Authorization', ''); - - $tokenType = new MAC(); - $tokenType->setServer($server); - - $response = $tokenType->determineAccessTokenInHeader($request); - - $this->assertEquals(null, $response); - } - - public function testDetermineAccessTokenInHeaderInvalidParam() - { - $macStorage = M::mock('\League\OAuth2\Server\Storage\MacTokenInterface'); - $macStorage->shouldReceive('getByAccessToken')->andReturn('abcdef'); - - $server = new AuthorizationServer(); - $server->setMacStorage($macStorage); - - $request = Request::createFromGlobals(); - $request->headers->set('Authorization', 'MAC '); - - $tokenType = new MAC(); - $tokenType->setServer($server); - - $response = $tokenType->determineAccessTokenInHeader($request); - - $this->assertEquals(null, $response); - } - - public function testDetermineAccessTokenInHeaderMismatchTimestamp() - { - $macStorage = M::mock('\League\OAuth2\Server\Storage\MacTokenInterface'); - $macStorage->shouldReceive('getByAccessToken')->andReturn('abcdef'); - - $server = new AuthorizationServer(); - $server->setMacStorage($macStorage); - - $ts = time() - 100; - - $request = Request::createFromGlobals(); - $request->headers->set('Authorization', sprintf('MAC id="foo", nonce="foo", ts="%s", mac="%s", ext="ext"', $ts, 'foo')); - - $tokenType = new MAC(); - $tokenType->setServer($server); - - $response = $tokenType->determineAccessTokenInHeader($request); - $this->assertEquals(null, $response); - } - - public function testDetermineAccessTokenInHeaderMissingMacKey() - { - $macStorage = M::mock('\League\OAuth2\Server\Storage\MacTokenInterface'); - $macStorage->shouldReceive('getByAccessToken')->andReturn(null); - - $server = new AuthorizationServer(); - $server->setMacStorage($macStorage); - - $ts = time(); - - $request = Request::createFromGlobals(); - $request->headers->set('Authorization', sprintf('MAC id="foo", nonce="foo", ts="%s", mac="%s", ext="ext"', $ts, 'foo')); - - $tokenType = new MAC(); - $tokenType->setServer($server); - - $response = $tokenType->determineAccessTokenInHeader($request); - $this->assertEquals(null, $response); - } -} diff --git a/tests/unit/util/RedirectUriTest.php b/tests/unit/util/RedirectUriTest.php deleted file mode 100644 index 6b6774042..000000000 --- a/tests/unit/util/RedirectUriTest.php +++ /dev/null @@ -1,19 +0,0 @@ - 'bar']); - $v2 = RedirectUri::make('https://foobar/', ['foo' => 'bar'], '#'); - $v3 = RedirectUri::make('https://foobar/', ['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 deleted file mode 100644 index 394226f58..000000000 --- a/tests/unit/util/SecureKeyTest.php +++ /dev/null @@ -1,35 +0,0 @@ -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)); - } -}