mirror of
https://github.com/elyby/oauth2-server.git
synced 2024-12-22 13:09:44 +05:30
Merge remote-tracking branch 'upstream/8.0.0' into protect-client-entity-gets
This commit is contained in:
commit
e1324b88b2
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ phpunit.xml
|
|||||||
examples/public.key
|
examples/public.key
|
||||||
examples/private.key
|
examples/private.key
|
||||||
build
|
build
|
||||||
|
*.orig
|
||||||
|
@ -4,6 +4,7 @@ enabled:
|
|||||||
- binary_operator_spaces
|
- binary_operator_spaces
|
||||||
- blank_line_before_return
|
- blank_line_before_return
|
||||||
- concat_with_spaces
|
- concat_with_spaces
|
||||||
|
- fully_qualified_strict_types
|
||||||
- function_typehint_space
|
- function_typehint_space
|
||||||
- hash_to_slash_comment
|
- hash_to_slash_comment
|
||||||
- include
|
- include
|
||||||
@ -40,7 +41,6 @@ enabled:
|
|||||||
- print_to_echo
|
- print_to_echo
|
||||||
- short_array_syntax
|
- short_array_syntax
|
||||||
- short_scalar_cast
|
- short_scalar_cast
|
||||||
- simplified_null_return
|
|
||||||
- single_quote
|
- single_quote
|
||||||
- spaces_cast
|
- spaces_cast
|
||||||
- standardize_not_equal
|
- standardize_not_equal
|
||||||
|
@ -12,9 +12,9 @@ env:
|
|||||||
- DEPENDENCIES="--prefer-lowest --prefer-stable"
|
- DEPENDENCIES="--prefer-lowest --prefer-stable"
|
||||||
|
|
||||||
php:
|
php:
|
||||||
- 7.0
|
|
||||||
- 7.1
|
- 7.1
|
||||||
- 7.2
|
- 7.2
|
||||||
|
- 7.3
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- composer update --no-interaction --prefer-dist $DEPENDENCIES
|
- composer update --no-interaction --prefer-dist $DEPENDENCIES
|
||||||
|
49
CHANGELOG.md
49
CHANGELOG.md
@ -7,13 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Flag, `requireCodeChallengeForPublicClients`, used to reject public clients that do not provide a code challenge for the Auth Code Grant (PR #938)
|
- Flag, `requireCodeChallengeForPublicClients`, used to reject public clients that do not provide a code challenge for the Auth Code Grant; use AuthCodeGrant::disableRequireCodeCallengeForPublicClients() to turn off this requirement (PR #938)
|
||||||
- Public clients can now use the Auth Code Grant (PR #938)
|
- Public clients can now use the Auth Code Grant (PR #938)
|
||||||
- `isConfidential` property added to `ClientEntity` to identify type of client (PR #938)
|
- `isConfidential` getter added to `ClientEntity` to identify type of client (PR #938)
|
||||||
- Function `validateClient()` added to validate clients which was previously performed by the `getClientEntity()` function (PR #938)
|
- Function `validateClient()` added to validate clients which was previously performed by the `getClientEntity()` function (PR #938)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Replace `convertToJWT()` interface with a more generic `__toString()` to improve extensibility (PR #874)
|
- Replace `convertToJWT()` interface with a more generic `__toString()` to improve extensibility; AccessTokenEntityInterface now requires `setPrivateKey(CryptKey $privateKey)` so `__toString()` has everything it needs to work (PR #874)
|
||||||
- The `invalidClient()` function accepts a PSR-7 compliant `$serverRequest` argument to avoid accessing the `$_SERVER` global variable and improve testing (PR #899)
|
- The `invalidClient()` function accepts a PSR-7 compliant `$serverRequest` argument to avoid accessing the `$_SERVER` global variable and improve testing (PR #899)
|
||||||
- `issueAccessToken()` in the Abstract Grant no longer sets access token client, user ID or scopes. These values should already have been set when calling `getNewToken()` (PR #919)
|
- `issueAccessToken()` in the Abstract Grant no longer sets access token client, user ID or scopes. These values should already have been set when calling `getNewToken()` (PR #919)
|
||||||
- No longer need to enable PKCE with `enableCodeExchangeProof` flag. Any client sending a code challenge will initiate PKCE checks. (PR #938)
|
- No longer need to enable PKCE with `enableCodeExchangeProof` flag. Any client sending a code challenge will initiate PKCE checks. (PR #938)
|
||||||
@ -23,6 +23,42 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- `enableCodeExchangeProof` flag (PR #938)
|
- `enableCodeExchangeProof` flag (PR #938)
|
||||||
|
- Support for PHP 7.0 (PR #1014)
|
||||||
|
|
||||||
|
## [7.4.0] - released 2019-05-05
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- RefreshTokenRepository can now return null, allowing refresh tokens to be optional. (PR #649)
|
||||||
|
|
||||||
|
## [7.3.3] - released 2019-03-29
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Added `error_description` to the error payload to improve standards compliance. The contents of this are copied from the existing `message` value. (PR #1006)
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
- Error payload will not issue `message` value in the next major release (PR #1006)
|
||||||
|
|
||||||
|
## [7.3.2] - released 2018-11-21
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Revert setting keys on response type to be inside `getResponseType()` function instead of AuthorizationServer constructor (PR #969)
|
||||||
|
|
||||||
|
## [7.3.1] - released 2018-11-15
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix issue with previous release where interface had changed for the AuthorizationServer. Reverted to the previous interface while maintaining functionality changes (PR #970)
|
||||||
|
|
||||||
|
## [7.3.0] - released 2018-11-13
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Moved the `finalizeScopes()` call from `validateAuthorizationRequest` method to the `completeAuthorizationRequest` method so it is called just before the access token is issued (PR #923)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Added a ScopeTrait to provide an implementation for jsonSerialize (PR #952)
|
||||||
|
- Ability to nest exceptions (PR #965)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix issue where AuthorizationServer is not stateless as ResponseType could store state of a previous request (PR #960)
|
||||||
|
|
||||||
## [7.2.0] - released 2018-06-23
|
## [7.2.0] - released 2018-06-23
|
||||||
|
|
||||||
@ -424,7 +460,12 @@ Version 5 is a complete code rewrite.
|
|||||||
|
|
||||||
- First major release
|
- First major release
|
||||||
|
|
||||||
[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...HEAD
|
[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/7.4.0...HEAD
|
||||||
|
[7.4.0]: https://github.com/thephpleague/oauth2-server/compare/7.3.3...7.4.0
|
||||||
|
[7.3.3]: https://github.com/thephpleague/oauth2-server/compare/7.3.2...7.3.3
|
||||||
|
[7.3.2]: https://github.com/thephpleague/oauth2-server/compare/7.3.1...7.3.2
|
||||||
|
[7.3.1]: https://github.com/thephpleague/oauth2-server/compare/7.3.0...7.3.1
|
||||||
|
[7.3.0]: https://github.com/thephpleague/oauth2-server/compare/7.2.0...7.3.0
|
||||||
[7.1.1]: https://github.com/thephpleague/oauth2-server/compare/7.1.0...7.1.1
|
[7.1.1]: https://github.com/thephpleague/oauth2-server/compare/7.1.0...7.1.1
|
||||||
[7.1.0]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...7.1.0
|
[7.1.0]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...7.1.0
|
||||||
[7.0.0]: https://github.com/thephpleague/oauth2-server/compare/6.1.1...7.0.0
|
[7.0.0]: https://github.com/thephpleague/oauth2-server/compare/6.1.1...7.0.0
|
||||||
|
12
README.md
12
README.md
@ -31,11 +31,11 @@ This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](ht
|
|||||||
|
|
||||||
The following versions of PHP are supported:
|
The following versions of PHP are supported:
|
||||||
|
|
||||||
* PHP 7.0
|
|
||||||
* PHP 7.1
|
* PHP 7.1
|
||||||
* PHP 7.2
|
* PHP 7.2
|
||||||
|
* PHP 7.3
|
||||||
|
|
||||||
The `openssl` extension is also required.
|
The `openssl` and `json` extensions are also required.
|
||||||
|
|
||||||
All HTTP messages passed to the server should be [PSR-7 compliant](https://www.php-fig.org/psr/psr-7/). This ensures interoperability with other packages and frameworks.
|
All HTTP messages passed to the server should be [PSR-7 compliant](https://www.php-fig.org/psr/psr-7/). This ensures interoperability with other packages and frameworks.
|
||||||
|
|
||||||
@ -68,6 +68,8 @@ We use [Travis CI](https://travis-ci.org/), [Scrutinizer](https://scrutinizer-ci
|
|||||||
* [Drupal](https://www.drupal.org/project/simple_oauth)
|
* [Drupal](https://www.drupal.org/project/simple_oauth)
|
||||||
* [Laravel Passport](https://github.com/laravel/passport)
|
* [Laravel Passport](https://github.com/laravel/passport)
|
||||||
* [OAuth 2 Server for CakePHP 3](https://github.com/uafrica/oauth-server)
|
* [OAuth 2 Server for CakePHP 3](https://github.com/uafrica/oauth-server)
|
||||||
|
* [OAuth 2 Server for Expressive](https://github.com/zendframework/zend-expressive-authentication-oauth2)
|
||||||
|
* [Trikoder OAuth 2 Bundle (Symfony)](https://github.com/trikoder/oauth2-bundle)
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
@ -83,13 +85,9 @@ Bugs and feature request are tracked on [GitHub](https://github.com/thephpleague
|
|||||||
|
|
||||||
If you have any questions about OAuth _please_ open a ticket here; please **don't** email the address below.
|
If you have any questions about OAuth _please_ open a ticket here; please **don't** email the address below.
|
||||||
|
|
||||||
## Commercial Support
|
|
||||||
|
|
||||||
If you would like help implementing this library into your existing platform, or would be interested in OAuth advice or training for you and your team please get in touch with [Glynde Labs](https://glyndelabs.com).
|
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
If you discover any security related issues, please email `hello@alexbilbie.com` instead of using the issue tracker.
|
If you discover any security related issues, please email `andrew@noexceptions.io` instead of using the issue tracker.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -4,19 +4,21 @@
|
|||||||
"homepage": "https://oauth2.thephpleague.com/",
|
"homepage": "https://oauth2.thephpleague.com/",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.0.0",
|
"php": ">=7.1.0",
|
||||||
"ext-openssl": "*",
|
"ext-openssl": "*",
|
||||||
"league/event": "^2.1",
|
"league/event": "^2.1",
|
||||||
"lcobucci/jwt": "^3.2.2",
|
"lcobucci/jwt": "^3.2.2",
|
||||||
"psr/http-message": "^1.0.1",
|
"psr/http-message": "^1.0.1",
|
||||||
"defuse/php-encryption": "^2.1"
|
"defuse/php-encryption": "^2.1",
|
||||||
|
"ext-json": "*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^6.3 || ^7.0",
|
"phpunit/phpunit": "^6.3 || ^7.0",
|
||||||
"zendframework/zend-diactoros": "^1.3.2",
|
"zendframework/zend-diactoros": "^1.3.2",
|
||||||
"phpstan/phpstan": "^0.9.2",
|
"phpstan/phpstan": "^0.9.2",
|
||||||
"phpstan/phpstan-phpunit": "^0.9.4",
|
"phpstan/phpstan-phpunit": "^0.9.4",
|
||||||
"phpstan/phpstan-strict-rules": "^0.9.0"
|
"phpstan/phpstan-strict-rules": "^0.9.0",
|
||||||
|
"roave/security-advisories": "dev-master"
|
||||||
},
|
},
|
||||||
"repositories": [
|
"repositories": [
|
||||||
{
|
{
|
||||||
@ -46,6 +48,12 @@
|
|||||||
"email": "hello@alexbilbie.com",
|
"email": "hello@alexbilbie.com",
|
||||||
"homepage": "http://www.alexbilbie.com",
|
"homepage": "http://www.alexbilbie.com",
|
||||||
"role": "Developer"
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Andy Millington",
|
||||||
|
"email": "andrew@noexceptions.io",
|
||||||
|
"homepage": "https://www.noexceptions.io",
|
||||||
|
"role": "Developer"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"replace": {
|
"replace": {
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
{
|
{
|
||||||
"require": {
|
"require": {
|
||||||
"slim/slim": "3.0.*"
|
"slim/slim": "^3.0.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"league/event": "^2.1",
|
"league/event": "^2.1",
|
||||||
"lcobucci/jwt": "^3.1",
|
"lcobucci/jwt": "^3.2",
|
||||||
"paragonie/random_compat": "^2.0",
|
|
||||||
"psr/http-message": "^1.0",
|
"psr/http-message": "^1.0",
|
||||||
"defuse/php-encryption": "^2.1",
|
"defuse/php-encryption": "^2.2",
|
||||||
"zendframework/zend-diactoros": "^1.0"
|
"zendframework/zend-diactoros": "^2.0.0"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
143
examples/composer.lock
generated
143
examples/composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "6701e0eaa09f74e1ebb19c3d61f39068",
|
"content-hash": "97f2878428e37d1d8e5418cc85cbfa3d",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "container-interop/container-interop",
|
"name": "container-interop/container-interop",
|
||||||
@ -39,21 +39,24 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nikic/fast-route",
|
"name": "nikic/fast-route",
|
||||||
"version": "v0.6.0",
|
"version": "v1.3.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/nikic/FastRoute.git",
|
"url": "https://github.com/nikic/FastRoute.git",
|
||||||
"reference": "31fa86924556b80735f98b294a7ffdfb26789f22"
|
"reference": "181d480e08d9476e61381e04a71b34dc0432e812"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/31fa86924556b80735f98b294a7ffdfb26789f22",
|
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812",
|
||||||
"reference": "31fa86924556b80735f98b294a7ffdfb26789f22",
|
"reference": "181d480e08d9476e61381e04a71b34dc0432e812",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.4.0"
|
"php": ">=5.4.0"
|
||||||
},
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^4.8.35|~5.7"
|
||||||
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
@ -78,7 +81,7 @@
|
|||||||
"router",
|
"router",
|
||||||
"routing"
|
"routing"
|
||||||
],
|
],
|
||||||
"time": "2015-06-18T19:15:47+00:00"
|
"time": "2018-02-13T20:26:39+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pimple/pimple",
|
"name": "pimple/pimple",
|
||||||
@ -231,27 +234,32 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "slim/slim",
|
"name": "slim/slim",
|
||||||
"version": "3.0.0",
|
"version": "3.11.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/slimphp/Slim.git",
|
"url": "https://github.com/slimphp/Slim.git",
|
||||||
"reference": "3b06f0f2d84dabbe81b6cea46ace46a3e883253e"
|
"reference": "d378e70431e78ee92ee32ddde61ecc72edf5dc0a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/slimphp/Slim/zipball/3b06f0f2d84dabbe81b6cea46ace46a3e883253e",
|
"url": "https://api.github.com/repos/slimphp/Slim/zipball/d378e70431e78ee92ee32ddde61ecc72edf5dc0a",
|
||||||
"reference": "3b06f0f2d84dabbe81b6cea46ace46a3e883253e",
|
"reference": "d378e70431e78ee92ee32ddde61ecc72edf5dc0a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"container-interop/container-interop": "^1.1",
|
"container-interop/container-interop": "^1.2",
|
||||||
"nikic/fast-route": "^0.6",
|
"nikic/fast-route": "^1.0",
|
||||||
"php": ">=5.5.0",
|
"php": ">=5.5.0",
|
||||||
"pimple/pimple": "^3.0",
|
"pimple/pimple": "^3.0",
|
||||||
|
"psr/container": "^1.0",
|
||||||
"psr/http-message": "^1.0"
|
"psr/http-message": "^1.0"
|
||||||
},
|
},
|
||||||
|
"provide": {
|
||||||
|
"psr/http-message-implementation": "1.0"
|
||||||
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^4.0"
|
"phpunit/phpunit": "^4.0",
|
||||||
|
"squizlabs/php_codesniffer": "^2.5"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@ -286,14 +294,14 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs",
|
"description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs",
|
||||||
"homepage": "http://slimframework.com",
|
"homepage": "https://slimframework.com",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"api",
|
"api",
|
||||||
"framework",
|
"framework",
|
||||||
"micro",
|
"micro",
|
||||||
"router"
|
"router"
|
||||||
],
|
],
|
||||||
"time": "2015-12-07T14:11:09+00:00"
|
"time": "2018-09-16T10:54:21+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
@ -470,33 +478,29 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "paragonie/random_compat",
|
"name": "paragonie/random_compat",
|
||||||
"version": "v2.0.17",
|
"version": "v9.99.99",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/paragonie/random_compat.git",
|
"url": "https://github.com/paragonie/random_compat.git",
|
||||||
"reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d"
|
"reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d",
|
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
|
||||||
"reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d",
|
"reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.2.0"
|
"php": "^7"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "4.*|5.*"
|
"phpunit/phpunit": "4.*|5.*",
|
||||||
|
"vimeo/psalm": "^1"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
|
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
|
||||||
"files": [
|
|
||||||
"lib/random.php"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
"license": [
|
"license": [
|
||||||
"MIT"
|
"MIT"
|
||||||
@ -515,41 +519,97 @@
|
|||||||
"pseudorandom",
|
"pseudorandom",
|
||||||
"random"
|
"random"
|
||||||
],
|
],
|
||||||
"time": "2018-07-04T16:31:37+00:00"
|
"time": "2018-07-02T15:55:56+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "zendframework/zend-diactoros",
|
"name": "psr/http-factory",
|
||||||
"version": "1.8.4",
|
"version": "1.0.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/zendframework/zend-diactoros.git",
|
"url": "https://github.com/php-fig/http-factory.git",
|
||||||
"reference": "736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba"
|
"reference": "378bfe27931ecc54ff824a20d6f6bfc303bbd04c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba",
|
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/378bfe27931ecc54ff824a20d6f6bfc303bbd04c",
|
||||||
"reference": "736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba",
|
"reference": "378bfe27931ecc54ff824a20d6f6bfc303bbd04c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^5.6 || ^7.0",
|
"php": ">=7.0.0",
|
||||||
|
"psr/http-message": "^1.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 interfaces for PSR-7 HTTP message factories",
|
||||||
|
"keywords": [
|
||||||
|
"factory",
|
||||||
|
"http",
|
||||||
|
"message",
|
||||||
|
"psr",
|
||||||
|
"psr-17",
|
||||||
|
"psr-7",
|
||||||
|
"request",
|
||||||
|
"response"
|
||||||
|
],
|
||||||
|
"time": "2018-07-30T21:54:04+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "zendframework/zend-diactoros",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/zendframework/zend-diactoros.git",
|
||||||
|
"reference": "0bae78192e634774b5584f0210c1232da82cb1ff"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/0bae78192e634774b5584f0210c1232da82cb1ff",
|
||||||
|
"reference": "0bae78192e634774b5584f0210c1232da82cb1ff",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.1",
|
||||||
|
"psr/http-factory": "^1.0",
|
||||||
"psr/http-message": "^1.0"
|
"psr/http-message": "^1.0"
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
|
"psr/http-factory-implementation": "1.0",
|
||||||
"psr/http-message-implementation": "1.0"
|
"psr/http-message-implementation": "1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
"ext-libxml": "*",
|
"ext-libxml": "*",
|
||||||
"phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7",
|
"http-interop/http-factory-tests": "^0.5.0",
|
||||||
"zendframework/zend-coding-standard": "~1.0"
|
"php-http/psr7-integration-tests": "dev-master",
|
||||||
|
"phpunit/phpunit": "^7.0.2",
|
||||||
|
"zendframework/zend-coding-standard": "~1.0.0"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "1.8.x-dev",
|
"dev-master": "2.0.x-dev",
|
||||||
"dev-develop": "1.9.x-dev",
|
"dev-develop": "2.1.x-dev",
|
||||||
"dev-release-2.0": "2.0.x-dev"
|
"dev-release-1.8": "1.8.x-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@ -569,16 +629,15 @@
|
|||||||
},
|
},
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
"license": [
|
"license": [
|
||||||
"BSD-2-Clause"
|
"BSD-3-Clause"
|
||||||
],
|
],
|
||||||
"description": "PSR HTTP Message implementations",
|
"description": "PSR HTTP Message implementations",
|
||||||
"homepage": "https://github.com/zendframework/zend-diactoros",
|
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"http",
|
"http",
|
||||||
"psr",
|
"psr",
|
||||||
"psr-7"
|
"psr-7"
|
||||||
],
|
],
|
||||||
"time": "2018-08-01T13:47:49+00:00"
|
"time": "2018-09-27T19:49:04+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
|
@ -11,13 +11,9 @@ namespace OAuth2ServerExamples\Entities;
|
|||||||
|
|
||||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\ScopeTrait;
|
||||||
|
|
||||||
class ScopeEntity implements ScopeEntityInterface
|
class ScopeEntity implements ScopeEntityInterface
|
||||||
{
|
{
|
||||||
use EntityTrait;
|
use EntityTrait, ScopeTrait;
|
||||||
|
|
||||||
public function jsonSerialize()
|
|
||||||
{
|
|
||||||
return $this->getIdentifier();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ class RefreshTokenRepository implements RefreshTokenRepositoryInterface
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntityInterface)
|
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity)
|
||||||
{
|
{
|
||||||
// Some logic to persist the refresh token in a database
|
// Some logic to persist the refresh token in a database
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ class AuthorizationServer implements EmitterAwareInterface
|
|||||||
protected $publicKey;
|
protected $publicKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var null|ResponseTypeInterface
|
* @var ResponseTypeInterface
|
||||||
*/
|
*/
|
||||||
protected $responseType;
|
protected $responseType;
|
||||||
|
|
||||||
@ -104,8 +104,16 @@ class AuthorizationServer implements EmitterAwareInterface
|
|||||||
if ($privateKey instanceof CryptKey === false) {
|
if ($privateKey instanceof CryptKey === false) {
|
||||||
$privateKey = new CryptKey($privateKey);
|
$privateKey = new CryptKey($privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->privateKey = $privateKey;
|
$this->privateKey = $privateKey;
|
||||||
$this->encryptionKey = $encryptionKey;
|
$this->encryptionKey = $encryptionKey;
|
||||||
|
|
||||||
|
if ($responseType === null) {
|
||||||
|
$responseType = new BearerTokenResponse();
|
||||||
|
} else {
|
||||||
|
$responseType = clone $responseType;
|
||||||
|
}
|
||||||
|
|
||||||
$this->responseType = $responseType;
|
$this->responseType = $responseType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,16 +213,15 @@ class AuthorizationServer implements EmitterAwareInterface
|
|||||||
*/
|
*/
|
||||||
protected function getResponseType()
|
protected function getResponseType()
|
||||||
{
|
{
|
||||||
if ($this->responseType instanceof ResponseTypeInterface === false) {
|
$responseType = clone $this->responseType;
|
||||||
$this->responseType = new BearerTokenResponse();
|
|
||||||
|
if ($responseType instanceof AbstractResponseType) {
|
||||||
|
$responseType->setPrivateKey($this->privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->responseType instanceof AbstractResponseType === true) {
|
$responseType->setEncryptionKey($this->encryptionKey);
|
||||||
$this->responseType->setPrivateKey($this->privateKey);
|
|
||||||
}
|
|
||||||
$this->responseType->setEncryptionKey($this->encryptionKey);
|
|
||||||
|
|
||||||
return $this->responseType;
|
return $responseType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
namespace League\OAuth2\Server\AuthorizationValidators;
|
namespace League\OAuth2\Server\AuthorizationValidators;
|
||||||
|
|
||||||
|
use BadMethodCallException;
|
||||||
|
use InvalidArgumentException;
|
||||||
use Lcobucci\JWT\Parser;
|
use Lcobucci\JWT\Parser;
|
||||||
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||||
use Lcobucci\JWT\ValidationData;
|
use Lcobucci\JWT\ValidationData;
|
||||||
@ -17,6 +19,7 @@ use League\OAuth2\Server\CryptTrait;
|
|||||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
class BearerTokenValidator implements AuthorizationValidatorInterface
|
class BearerTokenValidator implements AuthorizationValidatorInterface
|
||||||
{
|
{
|
||||||
@ -28,7 +31,7 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
|
|||||||
private $accessTokenRepository;
|
private $accessTokenRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \League\OAuth2\Server\CryptKey
|
* @var CryptKey
|
||||||
*/
|
*/
|
||||||
protected $publicKey;
|
protected $publicKey;
|
||||||
|
|
||||||
@ -43,7 +46,7 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
|
|||||||
/**
|
/**
|
||||||
* Set the public key
|
* Set the public key
|
||||||
*
|
*
|
||||||
* @param \League\OAuth2\Server\CryptKey $key
|
* @param CryptKey $key
|
||||||
*/
|
*/
|
||||||
public function setPublicKey(CryptKey $key)
|
public function setPublicKey(CryptKey $key)
|
||||||
{
|
{
|
||||||
@ -69,8 +72,8 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
|
|||||||
if ($token->verify(new Sha256(), $this->publicKey->getKeyPath()) === false) {
|
if ($token->verify(new Sha256(), $this->publicKey->getKeyPath()) === false) {
|
||||||
throw OAuthServerException::accessDenied('Access token could not be verified');
|
throw OAuthServerException::accessDenied('Access token could not be verified');
|
||||||
}
|
}
|
||||||
} catch (\BadMethodCallException $exception) {
|
} catch (BadMethodCallException $exception) {
|
||||||
throw OAuthServerException::accessDenied('Access token is not signed');
|
throw OAuthServerException::accessDenied('Access token is not signed', null, $exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure access token hasn't expired
|
// Ensure access token hasn't expired
|
||||||
@ -92,12 +95,12 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
|
|||||||
->withAttribute('oauth_client_id', $token->getClaim('aud'))
|
->withAttribute('oauth_client_id', $token->getClaim('aud'))
|
||||||
->withAttribute('oauth_user_id', $token->getClaim('sub'))
|
->withAttribute('oauth_user_id', $token->getClaim('sub'))
|
||||||
->withAttribute('oauth_scopes', $token->getClaim('scopes'));
|
->withAttribute('oauth_scopes', $token->getClaim('scopes'));
|
||||||
} catch (\InvalidArgumentException $exception) {
|
} catch (InvalidArgumentException $exception) {
|
||||||
// JWT couldn't be parsed so return the request as is
|
// JWT couldn't be parsed so return the request as is
|
||||||
throw OAuthServerException::accessDenied($exception->getMessage());
|
throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception);
|
||||||
} catch (\RuntimeException $exception) {
|
} catch (RuntimeException $exception) {
|
||||||
//JWR couldn't be parsed so return the request as is
|
//JWR couldn't be parsed so return the request as is
|
||||||
throw OAuthServerException::accessDenied('Error while decoding to JSON');
|
throw OAuthServerException::accessDenied('Error while decoding to JSON', null, $exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,9 @@
|
|||||||
|
|
||||||
namespace League\OAuth2\Server;
|
namespace League\OAuth2\Server;
|
||||||
|
|
||||||
|
use LogicException;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
class CryptKey
|
class CryptKey
|
||||||
{
|
{
|
||||||
const RSA_KEY_PATTERN =
|
const RSA_KEY_PATTERN =
|
||||||
@ -42,7 +45,7 @@ class CryptKey
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!file_exists($keyPath) || !is_readable($keyPath)) {
|
if (!file_exists($keyPath) || !is_readable($keyPath)) {
|
||||||
throw new \LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
|
throw new LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($keyPermissionsCheck === true) {
|
if ($keyPermissionsCheck === true) {
|
||||||
@ -64,7 +67,7 @@ class CryptKey
|
|||||||
/**
|
/**
|
||||||
* @param string $key
|
* @param string $key
|
||||||
*
|
*
|
||||||
* @throws \RuntimeException
|
* @throws RuntimeException
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
@ -79,19 +82,19 @@ class CryptKey
|
|||||||
|
|
||||||
if (!touch($keyPath)) {
|
if (!touch($keyPath)) {
|
||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
throw new \RuntimeException(sprintf('"%s" key file could not be created', $keyPath));
|
throw new RuntimeException(sprintf('"%s" key file could not be created', $keyPath));
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_put_contents($keyPath, $key) === false) {
|
if (file_put_contents($keyPath, $key) === false) {
|
||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
throw new \RuntimeException(sprintf('Unable to write key file to temporary directory "%s"', $tmpDir));
|
throw new RuntimeException(sprintf('Unable to write key file to temporary directory "%s"', $tmpDir));
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chmod($keyPath, 0600) === false) {
|
if (chmod($keyPath, 0600) === false) {
|
||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
throw new \RuntimeException(sprintf('The key file "%s" file mode could not be changed with chmod to 600', $keyPath));
|
throw new RuntimeException(sprintf('The key file "%s" file mode could not be changed with chmod to 600', $keyPath));
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@ namespace League\OAuth2\Server;
|
|||||||
|
|
||||||
use Defuse\Crypto\Crypto;
|
use Defuse\Crypto\Crypto;
|
||||||
use Defuse\Crypto\Key;
|
use Defuse\Crypto\Key;
|
||||||
|
use Exception;
|
||||||
|
use LogicException;
|
||||||
|
|
||||||
trait CryptTrait
|
trait CryptTrait
|
||||||
{
|
{
|
||||||
@ -26,7 +28,7 @@ trait CryptTrait
|
|||||||
*
|
*
|
||||||
* @param string $unencryptedData
|
* @param string $unencryptedData
|
||||||
*
|
*
|
||||||
* @throws \LogicException
|
* @throws LogicException
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
@ -38,8 +40,8 @@ trait CryptTrait
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey);
|
return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey);
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new \LogicException($e->getMessage());
|
throw new LogicException($e->getMessage(), null, $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +50,7 @@ trait CryptTrait
|
|||||||
*
|
*
|
||||||
* @param string $encryptedData
|
* @param string $encryptedData
|
||||||
*
|
*
|
||||||
* @throws \LogicException
|
* @throws LogicException
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
@ -60,8 +62,8 @@ trait CryptTrait
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey);
|
return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey);
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new \LogicException($e->getMessage());
|
throw new LogicException($e->getMessage(), null, $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,9 @@
|
|||||||
|
|
||||||
namespace League\OAuth2\Server\Entities;
|
namespace League\OAuth2\Server\Entities;
|
||||||
|
|
||||||
interface ScopeEntityInterface extends \JsonSerializable
|
use JsonSerializable;
|
||||||
|
|
||||||
|
interface ScopeEntityInterface extends JsonSerializable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Get the scope's identifier.
|
* Get the scope's identifier.
|
||||||
|
28
src/Entities/Traits/ScopeTrait.php
Normal file
28
src/Entities/Traits/ScopeTrait.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Andrew Millington <andrew@noexceptions.io>
|
||||||
|
* @copyright Copyright (c) Andrew Millington
|
||||||
|
* @license http://mit-license.org
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace League\OAuth2\Server\Entities\Traits;
|
||||||
|
|
||||||
|
trait ScopeTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Serialize the object to the scopes string identifier when using json_encode().
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function jsonSerialize()
|
||||||
|
{
|
||||||
|
return $this->getIdentifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
abstract public function getIdentifier();
|
||||||
|
}
|
@ -9,10 +9,12 @@
|
|||||||
|
|
||||||
namespace League\OAuth2\Server\Exception;
|
namespace League\OAuth2\Server\Exception;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
class OAuthServerException extends \Exception
|
class OAuthServerException extends Exception
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
@ -53,17 +55,18 @@ class OAuthServerException extends \Exception
|
|||||||
* @param int $httpStatusCode HTTP status code to send (default = 400)
|
* @param int $httpStatusCode HTTP status code to send (default = 400)
|
||||||
* @param null|string $hint A helper hint
|
* @param null|string $hint A helper hint
|
||||||
* @param null|string $redirectUri A HTTP URI to redirect the user back to
|
* @param null|string $redirectUri A HTTP URI to redirect the user back to
|
||||||
|
* @param Throwable $previous Previous exception
|
||||||
*/
|
*/
|
||||||
public function __construct($message, $code, $errorType, $httpStatusCode = 400, $hint = null, $redirectUri = null)
|
public function __construct($message, $code, $errorType, $httpStatusCode = 400, $hint = null, $redirectUri = null, Throwable $previous = null)
|
||||||
{
|
{
|
||||||
parent::__construct($message, $code);
|
parent::__construct($message, $code, $previous);
|
||||||
$this->httpStatusCode = $httpStatusCode;
|
$this->httpStatusCode = $httpStatusCode;
|
||||||
$this->errorType = $errorType;
|
$this->errorType = $errorType;
|
||||||
$this->hint = $hint;
|
$this->hint = $hint;
|
||||||
$this->redirectUri = $redirectUri;
|
$this->redirectUri = $redirectUri;
|
||||||
$this->payload = [
|
$this->payload = [
|
||||||
'error' => $errorType,
|
'error' => $errorType,
|
||||||
'message' => $message,
|
'error_description' => $message,
|
||||||
];
|
];
|
||||||
if ($hint !== null) {
|
if ($hint !== null) {
|
||||||
$this->payload['hint'] = $hint;
|
$this->payload['hint'] = $hint;
|
||||||
@ -77,7 +80,15 @@ class OAuthServerException extends \Exception
|
|||||||
*/
|
*/
|
||||||
public function getPayload()
|
public function getPayload()
|
||||||
{
|
{
|
||||||
return $this->payload;
|
$payload = $this->payload;
|
||||||
|
|
||||||
|
// The "message" property is deprecated and replaced by "error_description"
|
||||||
|
// TODO: remove "message" property
|
||||||
|
if (isset($payload['error_description']) && !isset($payload['message'])) {
|
||||||
|
$payload['message'] = $payload['error_description'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,7 +106,7 @@ class OAuthServerException extends \Exception
|
|||||||
*
|
*
|
||||||
* @param ServerRequestInterface $serverRequest
|
* @param ServerRequestInterface $serverRequest
|
||||||
*/
|
*/
|
||||||
public function setServerRequest($serverRequest)
|
public function setServerRequest(ServerRequestInterface $serverRequest)
|
||||||
{
|
{
|
||||||
$this->serverRequest = $serverRequest;
|
$this->serverRequest = $serverRequest;
|
||||||
}
|
}
|
||||||
@ -118,16 +129,17 @@ class OAuthServerException extends \Exception
|
|||||||
*
|
*
|
||||||
* @param string $parameter The invalid parameter
|
* @param string $parameter The invalid parameter
|
||||||
* @param null|string $hint
|
* @param null|string $hint
|
||||||
|
* @param Throwable $previous Previous exception
|
||||||
*
|
*
|
||||||
* @return static
|
* @return static
|
||||||
*/
|
*/
|
||||||
public static function invalidRequest($parameter, $hint = null)
|
public static function invalidRequest($parameter, $hint = null, Throwable $previous = null)
|
||||||
{
|
{
|
||||||
$errorMessage = 'The request is missing a required parameter, includes an invalid parameter value, ' .
|
$errorMessage = 'The request is missing a required parameter, includes an invalid parameter value, ' .
|
||||||
'includes a parameter more than once, or is otherwise malformed.';
|
'includes a parameter more than once, or is otherwise malformed.';
|
||||||
$hint = ($hint === null) ? sprintf('Check the `%s` parameter', $parameter) : $hint;
|
$hint = ($hint === null) ? sprintf('Check the `%s` parameter', $parameter) : $hint;
|
||||||
|
|
||||||
return new static($errorMessage, 3, 'invalid_request', 400, $hint);
|
return new static($errorMessage, 3, 'invalid_request', 400, $hint, null, $previous);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,7 +149,7 @@ class OAuthServerException extends \Exception
|
|||||||
*
|
*
|
||||||
* @return static
|
* @return static
|
||||||
*/
|
*/
|
||||||
public static function invalidClient($serverRequest)
|
public static function invalidClient(ServerRequestInterface $serverRequest)
|
||||||
{
|
{
|
||||||
$exception = new static('Client authentication failed', 4, 'invalid_client', 401);
|
$exception = new static('Client authentication failed', 4, 'invalid_client', 401);
|
||||||
|
|
||||||
@ -184,19 +196,23 @@ class OAuthServerException extends \Exception
|
|||||||
* Server error.
|
* Server error.
|
||||||
*
|
*
|
||||||
* @param string $hint
|
* @param string $hint
|
||||||
|
* @param Throwable $previous
|
||||||
*
|
*
|
||||||
* @return static
|
* @return static
|
||||||
*
|
*
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
*/
|
*/
|
||||||
public static function serverError($hint)
|
public static function serverError($hint, Throwable $previous = null)
|
||||||
{
|
{
|
||||||
return new static(
|
return new static(
|
||||||
'The authorization server encountered an unexpected condition which prevented it from fulfilling'
|
'The authorization server encountered an unexpected condition which prevented it from fulfilling'
|
||||||
. ' the request: ' . $hint,
|
. ' the request: ' . $hint,
|
||||||
7,
|
7,
|
||||||
'server_error',
|
'server_error',
|
||||||
500
|
500,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
$previous
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,12 +220,13 @@ class OAuthServerException extends \Exception
|
|||||||
* Invalid refresh token.
|
* Invalid refresh token.
|
||||||
*
|
*
|
||||||
* @param null|string $hint
|
* @param null|string $hint
|
||||||
|
* @param Throwable $previous
|
||||||
*
|
*
|
||||||
* @return static
|
* @return static
|
||||||
*/
|
*/
|
||||||
public static function invalidRefreshToken($hint = null)
|
public static function invalidRefreshToken($hint = null, Throwable $previous = null)
|
||||||
{
|
{
|
||||||
return new static('The refresh token is invalid.', 8, 'invalid_request', 401, $hint);
|
return new static('The refresh token is invalid.', 8, 'invalid_request', 401, $hint, null, $previous);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -217,10 +234,11 @@ class OAuthServerException extends \Exception
|
|||||||
*
|
*
|
||||||
* @param null|string $hint
|
* @param null|string $hint
|
||||||
* @param null|string $redirectUri
|
* @param null|string $redirectUri
|
||||||
|
* @param Throwable $previous
|
||||||
*
|
*
|
||||||
* @return static
|
* @return static
|
||||||
*/
|
*/
|
||||||
public static function accessDenied($hint = null, $redirectUri = null)
|
public static function accessDenied($hint = null, $redirectUri = null, Throwable $previous = null)
|
||||||
{
|
{
|
||||||
return new static(
|
return new static(
|
||||||
'The resource owner or authorization server denied the request.',
|
'The resource owner or authorization server denied the request.',
|
||||||
@ -228,7 +246,8 @@ class OAuthServerException extends \Exception
|
|||||||
'access_denied',
|
'access_denied',
|
||||||
401,
|
401,
|
||||||
$hint,
|
$hint,
|
||||||
$redirectUri
|
$redirectUri,
|
||||||
|
$previous
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,9 @@ namespace League\OAuth2\Server\Exception;
|
|||||||
|
|
||||||
class UniqueTokenIdentifierConstraintViolationException extends OAuthServerException
|
class UniqueTokenIdentifierConstraintViolationException extends OAuthServerException
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return UniqueTokenIdentifierConstraintViolationException
|
||||||
|
*/
|
||||||
public static function create()
|
public static function create()
|
||||||
{
|
{
|
||||||
$errorMessage = 'Could not create unique access token identifier';
|
$errorMessage = 'Could not create unique access token identifier';
|
||||||
|
@ -12,6 +12,8 @@ namespace League\OAuth2\Server\Grant;
|
|||||||
|
|
||||||
use DateInterval;
|
use DateInterval;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
|
use Error;
|
||||||
|
use Exception;
|
||||||
use League\Event\EmitterAwareTrait;
|
use League\Event\EmitterAwareTrait;
|
||||||
use League\OAuth2\Server\CryptKey;
|
use League\OAuth2\Server\CryptKey;
|
||||||
use League\OAuth2\Server\CryptTrait;
|
use League\OAuth2\Server\CryptTrait;
|
||||||
@ -30,7 +32,9 @@ use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
|||||||
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
|
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
|
||||||
use League\OAuth2\Server\RequestEvent;
|
use League\OAuth2\Server\RequestEvent;
|
||||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||||
|
use LogicException;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use TypeError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract grant class.
|
* Abstract grant class.
|
||||||
@ -79,7 +83,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
|||||||
protected $refreshTokenTTL;
|
protected $refreshTokenTTL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \League\OAuth2\Server\CryptKey
|
* @var CryptKey
|
||||||
*/
|
*/
|
||||||
protected $privateKey;
|
protected $privateKey;
|
||||||
|
|
||||||
@ -147,7 +151,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
|||||||
/**
|
/**
|
||||||
* Set the private key
|
* Set the private key
|
||||||
*
|
*
|
||||||
* @param \League\OAuth2\Server\CryptKey $key
|
* @param CryptKey $key
|
||||||
*/
|
*/
|
||||||
public function setPrivateKey(CryptKey $key)
|
public function setPrivateKey(CryptKey $key)
|
||||||
{
|
{
|
||||||
@ -258,13 +262,13 @@ abstract class AbstractGrant implements GrantTypeInterface
|
|||||||
ClientEntityInterface $client,
|
ClientEntityInterface $client,
|
||||||
ServerRequestInterface $request
|
ServerRequestInterface $request
|
||||||
) {
|
) {
|
||||||
if (is_string($client->getRedirectUri())
|
if (\is_string($client->getRedirectUri())
|
||||||
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||||
) {
|
) {
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||||
throw OAuthServerException::invalidClient($request);
|
throw OAuthServerException::invalidClient($request);
|
||||||
} elseif (is_array($client->getRedirectUri())
|
} elseif (\is_array($client->getRedirectUri())
|
||||||
&& in_array($redirectUri, $client->getRedirectUri(), true) === false
|
&& \in_array($redirectUri, $client->getRedirectUri(), true) === false
|
||||||
) {
|
) {
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||||
throw OAuthServerException::invalidClient($request);
|
throw OAuthServerException::invalidClient($request);
|
||||||
@ -274,7 +278,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
|||||||
/**
|
/**
|
||||||
* Validate scopes in the request.
|
* Validate scopes in the request.
|
||||||
*
|
*
|
||||||
* @param string $scopes
|
* @param string|array $scopes
|
||||||
* @param string $redirectUri
|
* @param string $redirectUri
|
||||||
*
|
*
|
||||||
* @throws OAuthServerException
|
* @throws OAuthServerException
|
||||||
@ -283,13 +287,13 @@ abstract class AbstractGrant implements GrantTypeInterface
|
|||||||
*/
|
*/
|
||||||
public function validateScopes($scopes, $redirectUri = null)
|
public function validateScopes($scopes, $redirectUri = null)
|
||||||
{
|
{
|
||||||
$scopesList = array_filter(explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), function ($scope) {
|
if (!\is_array($scopes)) {
|
||||||
return !empty($scope);
|
$scopes = $this->convertScopesQueryStringToArray($scopes);
|
||||||
});
|
}
|
||||||
|
|
||||||
$validScopes = [];
|
$validScopes = [];
|
||||||
|
|
||||||
foreach ($scopesList as $scopeItem) {
|
foreach ($scopes as $scopeItem) {
|
||||||
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem);
|
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem);
|
||||||
|
|
||||||
if ($scope instanceof ScopeEntityInterface === false) {
|
if ($scope instanceof ScopeEntityInterface === false) {
|
||||||
@ -302,6 +306,20 @@ abstract class AbstractGrant implements GrantTypeInterface
|
|||||||
return $validScopes;
|
return $validScopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a scopes query string to an array to easily iterate for validation.
|
||||||
|
*
|
||||||
|
* @param string $scopes
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function convertScopesQueryStringToArray($scopes)
|
||||||
|
{
|
||||||
|
return array_filter(explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), function ($scope) {
|
||||||
|
return !empty($scope);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve request parameter.
|
* Retrieve request parameter.
|
||||||
*
|
*
|
||||||
@ -315,7 +333,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
|||||||
{
|
{
|
||||||
$requestParameters = (array) $request->getParsedBody();
|
$requestParameters = (array) $request->getParsedBody();
|
||||||
|
|
||||||
return isset($requestParameters[$parameter]) ? $requestParameters[$parameter] : $default;
|
return $requestParameters[$parameter] ?? $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -488,16 +506,21 @@ abstract class AbstractGrant implements GrantTypeInterface
|
|||||||
* @throws OAuthServerException
|
* @throws OAuthServerException
|
||||||
* @throws UniqueTokenIdentifierConstraintViolationException
|
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||||
*
|
*
|
||||||
* @return RefreshTokenEntityInterface
|
* @return RefreshTokenEntityInterface|null
|
||||||
*/
|
*/
|
||||||
protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
|
protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
|
||||||
{
|
{
|
||||||
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
|
||||||
|
|
||||||
$refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
|
$refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
|
||||||
|
|
||||||
|
if ($refreshToken === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$refreshToken->setExpiryDateTime((new DateTimeImmutable())->add($this->refreshTokenTTL));
|
$refreshToken->setExpiryDateTime((new DateTimeImmutable())->add($this->refreshTokenTTL));
|
||||||
$refreshToken->setAccessToken($accessToken);
|
$refreshToken->setAccessToken($accessToken);
|
||||||
|
|
||||||
|
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
||||||
|
|
||||||
while ($maxGenerationAttempts-- > 0) {
|
while ($maxGenerationAttempts-- > 0) {
|
||||||
$refreshToken->setIdentifier($this->generateUniqueIdentifier());
|
$refreshToken->setIdentifier($this->generateUniqueIdentifier());
|
||||||
try {
|
try {
|
||||||
@ -526,13 +549,13 @@ abstract class AbstractGrant implements GrantTypeInterface
|
|||||||
try {
|
try {
|
||||||
return bin2hex(random_bytes($length));
|
return bin2hex(random_bytes($length));
|
||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
} catch (\TypeError $e) {
|
} catch (TypeError $e) {
|
||||||
throw OAuthServerException::serverError('An unexpected error has occurred');
|
throw OAuthServerException::serverError('An unexpected error has occurred', $e);
|
||||||
} catch (\Error $e) {
|
} catch (Error $e) {
|
||||||
throw OAuthServerException::serverError('An unexpected error has occurred');
|
throw OAuthServerException::serverError('An unexpected error has occurred', $e);
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
// If you get this message, the CSPRNG failed hard.
|
// If you get this message, the CSPRNG failed hard.
|
||||||
throw OAuthServerException::serverError('Could not generate a random string');
|
throw OAuthServerException::serverError('Could not generate a random string', $e);
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
@ -563,7 +586,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
|||||||
*/
|
*/
|
||||||
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
||||||
{
|
{
|
||||||
throw new \LogicException('This grant cannot validate an authorization request');
|
throw new LogicException('This grant cannot validate an authorization request');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -571,6 +594,6 @@ abstract class AbstractGrant implements GrantTypeInterface
|
|||||||
*/
|
*/
|
||||||
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
||||||
{
|
{
|
||||||
throw new \LogicException('This grant cannot complete an authorization request');
|
throw new LogicException('This grant cannot complete an authorization request');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,10 @@ namespace League\OAuth2\Server\Grant;
|
|||||||
|
|
||||||
use DateInterval;
|
use DateInterval;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
|
use Exception;
|
||||||
use League\OAuth2\Server\CodeChallengeVerifiers\CodeChallengeVerifierInterface;
|
use League\OAuth2\Server\CodeChallengeVerifiers\CodeChallengeVerifierInterface;
|
||||||
use League\OAuth2\Server\CodeChallengeVerifiers\PlainVerifier;
|
use League\OAuth2\Server\CodeChallengeVerifiers\PlainVerifier;
|
||||||
use League\OAuth2\Server\CodeChallengeVerifiers\S256Verifier;
|
use League\OAuth2\Server\CodeChallengeVerifiers\S256Verifier;
|
||||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
|
||||||
use League\OAuth2\Server\Entities\UserEntityInterface;
|
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
|
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
|
||||||
@ -23,7 +23,9 @@ use League\OAuth2\Server\RequestEvent;
|
|||||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||||
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
|
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
|
||||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||||
|
use LogicException;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
class AuthCodeGrant extends AbstractAuthorizeGrant
|
class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||||
{
|
{
|
||||||
@ -46,6 +48,8 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
|||||||
* @param AuthCodeRepositoryInterface $authCodeRepository
|
* @param AuthCodeRepositoryInterface $authCodeRepository
|
||||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||||
* @param DateInterval $authCodeTTL
|
* @param DateInterval $authCodeTTL
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AuthCodeRepositoryInterface $authCodeRepository,
|
AuthCodeRepositoryInterface $authCodeRepository,
|
||||||
@ -57,14 +61,13 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
|||||||
$this->authCodeTTL = $authCodeTTL;
|
$this->authCodeTTL = $authCodeTTL;
|
||||||
$this->refreshTokenTTL = new DateInterval('P1M');
|
$this->refreshTokenTTL = new DateInterval('P1M');
|
||||||
|
|
||||||
// SHOULD ONLY DO THIS IS SHA256 is supported
|
if (in_array('sha256', hash_algos(), true)) {
|
||||||
$s256Verifier = new S256Verifier();
|
$s256Verifier = new S256Verifier();
|
||||||
$plainVerifier = new PlainVerifier();
|
$this->codeChallengeVerifiers[$s256Verifier->getMethod()] = $s256Verifier;
|
||||||
|
}
|
||||||
|
|
||||||
$this->codeChallengeVerifiers = [
|
$plainVerifier = new PlainVerifier();
|
||||||
$s256Verifier->getMethod() => $s256Verifier,
|
$this->codeChallengeVerifiers[$plainVerifier->getMethod()] = $plainVerifier;
|
||||||
$plainVerifier->getMethod() => $plainVerifier,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,56 +109,22 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
|||||||
throw OAuthServerException::invalidRequest('code');
|
throw OAuthServerException::invalidRequest('code');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the authorization code
|
|
||||||
try {
|
try {
|
||||||
$authCodePayload = json_decode($this->decrypt($encryptedAuthCode));
|
$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) {
|
$this->validateAuthorizationCode($authCodePayload, $client, $request);
|
||||||
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 === false) {
|
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
throw OAuthServerException::invalidScope($scopeId);
|
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
$scopes[] = $scope;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finalize the requested scopes
|
|
||||||
$scopes = $this->scopeRepository->finalizeScopes(
|
$scopes = $this->scopeRepository->finalizeScopes(
|
||||||
$scopes,
|
$this->validateScopes($authCodePayload->scopes),
|
||||||
$this->getIdentifier(),
|
$this->getIdentifier(),
|
||||||
$client,
|
$client,
|
||||||
$authCodePayload->user_id
|
$authCodePayload->user_id
|
||||||
);
|
);
|
||||||
} catch (\LogicException $e) {
|
} catch (LogicException $e) {
|
||||||
throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code');
|
throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code', $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate code challenge
|
// Validate code challenge
|
||||||
@ -191,17 +160,18 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue and persist access + refresh tokens
|
// Issue and persist new access token
|
||||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $authCodePayload->user_id, $scopes);
|
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $authCodePayload->user_id, $scopes);
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
||||||
|
$responseType->setAccessToken($accessToken);
|
||||||
|
|
||||||
|
// Issue and persist new refresh token if given
|
||||||
$refreshToken = $this->issueRefreshToken($accessToken);
|
$refreshToken = $this->issueRefreshToken($accessToken);
|
||||||
|
|
||||||
// Send events to emitter
|
if ($refreshToken !== null) {
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
||||||
|
|
||||||
// Inject tokens into response type
|
|
||||||
$responseType->setAccessToken($accessToken);
|
|
||||||
$responseType->setRefreshToken($refreshToken);
|
$responseType->setRefreshToken($refreshToken);
|
||||||
|
}
|
||||||
|
|
||||||
// Revoke used auth code
|
// Revoke used auth code
|
||||||
$this->authCodeRepository->revokeAuthCode($authCodePayload->auth_code_id);
|
$this->authCodeRepository->revokeAuthCode($authCodePayload->auth_code_id);
|
||||||
@ -209,6 +179,41 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
|||||||
return $responseType;
|
return $responseType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the authorization code.
|
||||||
|
*
|
||||||
|
* @param stdClass $authCodePayload
|
||||||
|
* @param ClientEntityInterface $client
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
*/
|
||||||
|
private function validateAuthorizationCode(
|
||||||
|
$authCodePayload,
|
||||||
|
ClientEntityInterface $client,
|
||||||
|
ServerRequestInterface $request
|
||||||
|
) {
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the grant identifier that can be used in matching up requests.
|
* Return the grant identifier that can be used in matching up requests.
|
||||||
*
|
*
|
||||||
@ -242,7 +247,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
|||||||
$this->getServerParameter('PHP_AUTH_USER', $request)
|
$this->getServerParameter('PHP_AUTH_USER', $request)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (is_null($clientId)) {
|
if ($clientId === null) {
|
||||||
throw OAuthServerException::invalidRequest('client_id');
|
throw OAuthServerException::invalidRequest('client_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,12 +257,12 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
|||||||
|
|
||||||
if ($redirectUri !== null) {
|
if ($redirectUri !== null) {
|
||||||
$this->validateRedirectUri($redirectUri, $client, $request);
|
$this->validateRedirectUri($redirectUri, $client, $request);
|
||||||
} elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1
|
} elseif (empty($client->getRedirectUri()) ||
|
||||||
|| empty($client->getRedirectUri())) {
|
(\is_array($client->getRedirectUri()) && \count($client->getRedirectUri()) !== 1)) {
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||||
throw OAuthServerException::invalidClient($request);
|
throw OAuthServerException::invalidClient($request);
|
||||||
} else {
|
} else {
|
||||||
$redirectUri = is_array($client->getRedirectUri())
|
$redirectUri = \is_array($client->getRedirectUri())
|
||||||
? $client->getRedirectUri()[0]
|
? $client->getRedirectUri()[0]
|
||||||
: $client->getRedirectUri();
|
: $client->getRedirectUri();
|
||||||
}
|
}
|
||||||
@ -321,14 +326,11 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
|||||||
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
||||||
{
|
{
|
||||||
if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) {
|
if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) {
|
||||||
throw new \LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest');
|
throw new LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest');
|
||||||
}
|
}
|
||||||
|
|
||||||
$finalRedirectUri = ($authorizationRequest->getRedirectUri() === null)
|
$finalRedirectUri = $authorizationRequest->getRedirectUri()
|
||||||
? is_array($authorizationRequest->getClient()->getRedirectUri())
|
?? $this->getClientRedirectUri($authorizationRequest);
|
||||||
? $authorizationRequest->getClient()->getRedirectUri()[0]
|
|
||||||
: $authorizationRequest->getClient()->getRedirectUri()
|
|
||||||
: $authorizationRequest->getRedirectUri();
|
|
||||||
|
|
||||||
// The user approved the client, redirect them back with an auth code
|
// The user approved the client, redirect them back with an auth code
|
||||||
if ($authorizationRequest->isAuthorizationApproved() === true) {
|
if ($authorizationRequest->isAuthorizationApproved() === true) {
|
||||||
@ -376,4 +378,18 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the client redirect URI if not set in the request.
|
||||||
|
*
|
||||||
|
* @param AuthorizationRequest $authorizationRequest
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getClientRedirectUri(AuthorizationRequest $authorizationRequest)
|
||||||
|
{
|
||||||
|
return \is_array($authorizationRequest->getClient()->getRedirectUri())
|
||||||
|
? $authorizationRequest->getClient()->getRedirectUri()[0]
|
||||||
|
: $authorizationRequest->getClient()->getRedirectUri();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ use League\OAuth2\Server\RequestEvent;
|
|||||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||||
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
|
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
|
||||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||||
|
use LogicException;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
class ImplicitGrant extends AbstractAuthorizeGrant
|
class ImplicitGrant extends AbstractAuthorizeGrant
|
||||||
@ -44,21 +45,21 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
|||||||
/**
|
/**
|
||||||
* @param DateInterval $refreshTokenTTL
|
* @param DateInterval $refreshTokenTTL
|
||||||
*
|
*
|
||||||
* @throw \LogicException
|
* @throw LogicException
|
||||||
*/
|
*/
|
||||||
public function setRefreshTokenTTL(DateInterval $refreshTokenTTL)
|
public function setRefreshTokenTTL(DateInterval $refreshTokenTTL)
|
||||||
{
|
{
|
||||||
throw new \LogicException('The Implicit Grant does not return refresh tokens');
|
throw new LogicException('The Implicit Grant does not return refresh tokens');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||||
*
|
*
|
||||||
* @throw \LogicException
|
* @throw LogicException
|
||||||
*/
|
*/
|
||||||
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
|
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
|
||||||
{
|
{
|
||||||
throw new \LogicException('The Implicit Grant does not return refresh tokens');
|
throw new LogicException('The Implicit Grant does not return refresh tokens');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,7 +94,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
|||||||
ResponseTypeInterface $responseType,
|
ResponseTypeInterface $responseType,
|
||||||
DateInterval $accessTokenTTL
|
DateInterval $accessTokenTTL
|
||||||
) {
|
) {
|
||||||
throw new \LogicException('This grant does not used this method');
|
throw new LogicException('This grant does not used this method');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,13 +145,6 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
|||||||
$redirectUri
|
$redirectUri
|
||||||
);
|
);
|
||||||
|
|
||||||
// Finalize the requested scopes
|
|
||||||
$finalizedScopes = $this->scopeRepository->finalizeScopes(
|
|
||||||
$scopes,
|
|
||||||
$this->getIdentifier(),
|
|
||||||
$client
|
|
||||||
);
|
|
||||||
|
|
||||||
$stateParameter = $this->getQueryStringParameter('state', $request);
|
$stateParameter = $this->getQueryStringParameter('state', $request);
|
||||||
|
|
||||||
$authorizationRequest = new AuthorizationRequest();
|
$authorizationRequest = new AuthorizationRequest();
|
||||||
@ -162,7 +156,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
|||||||
$authorizationRequest->setState($stateParameter);
|
$authorizationRequest->setState($stateParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
$authorizationRequest->setScopes($finalizedScopes);
|
$authorizationRequest->setScopes($scopes);
|
||||||
|
|
||||||
return $authorizationRequest;
|
return $authorizationRequest;
|
||||||
}
|
}
|
||||||
@ -173,7 +167,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
|||||||
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
||||||
{
|
{
|
||||||
if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) {
|
if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) {
|
||||||
throw new \LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest');
|
throw new LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest');
|
||||||
}
|
}
|
||||||
|
|
||||||
$finalRedirectUri = ($authorizationRequest->getRedirectUri() === null)
|
$finalRedirectUri = ($authorizationRequest->getRedirectUri() === null)
|
||||||
@ -184,11 +178,19 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
|||||||
|
|
||||||
// The user approved the client, redirect them back with an access token
|
// The user approved the client, redirect them back with an access token
|
||||||
if ($authorizationRequest->isAuthorizationApproved() === true) {
|
if ($authorizationRequest->isAuthorizationApproved() === true) {
|
||||||
|
// Finalize the requested scopes
|
||||||
|
$finalizedScopes = $this->scopeRepository->finalizeScopes(
|
||||||
|
$authorizationRequest->getScopes(),
|
||||||
|
$this->getIdentifier(),
|
||||||
|
$authorizationRequest->getClient(),
|
||||||
|
$authorizationRequest->getUser()->getIdentifier()
|
||||||
|
);
|
||||||
|
|
||||||
$accessToken = $this->issueAccessToken(
|
$accessToken = $this->issueAccessToken(
|
||||||
$this->accessTokenTTL,
|
$this->accessTokenTTL,
|
||||||
$authorizationRequest->getClient(),
|
$authorizationRequest->getClient(),
|
||||||
$authorizationRequest->getUser()->getIdentifier(),
|
$authorizationRequest->getUser()->getIdentifier(),
|
||||||
$authorizationRequest->getScopes()
|
$finalizedScopes
|
||||||
);
|
);
|
||||||
|
|
||||||
$response = new RedirectResponse();
|
$response = new RedirectResponse();
|
||||||
|
@ -56,17 +56,18 @@ class PasswordGrant extends AbstractGrant
|
|||||||
// Finalize the requested scopes
|
// Finalize the requested scopes
|
||||||
$finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier());
|
$finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier());
|
||||||
|
|
||||||
// Issue and persist new tokens
|
// Issue and persist new access token
|
||||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes);
|
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes);
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
||||||
|
$responseType->setAccessToken($accessToken);
|
||||||
|
|
||||||
|
// Issue and persist new refresh token if given
|
||||||
$refreshToken = $this->issueRefreshToken($accessToken);
|
$refreshToken = $this->issueRefreshToken($accessToken);
|
||||||
|
|
||||||
// Send events to emitter
|
if ($refreshToken !== null) {
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
||||||
|
|
||||||
// Inject tokens into response
|
|
||||||
$responseType->setAccessToken($accessToken);
|
|
||||||
$responseType->setRefreshToken($refreshToken);
|
$responseType->setRefreshToken($refreshToken);
|
||||||
|
}
|
||||||
|
|
||||||
return $responseType;
|
return $responseType;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace League\OAuth2\Server\Grant;
|
namespace League\OAuth2\Server\Grant;
|
||||||
|
|
||||||
use DateInterval;
|
use DateInterval;
|
||||||
|
use Exception;
|
||||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||||
use League\OAuth2\Server\RequestEvent;
|
use League\OAuth2\Server\RequestEvent;
|
||||||
@ -62,17 +63,18 @@ class RefreshTokenGrant extends AbstractGrant
|
|||||||
$this->accessTokenRepository->revokeAccessToken($oldRefreshToken['access_token_id']);
|
$this->accessTokenRepository->revokeAccessToken($oldRefreshToken['access_token_id']);
|
||||||
$this->refreshTokenRepository->revokeRefreshToken($oldRefreshToken['refresh_token_id']);
|
$this->refreshTokenRepository->revokeRefreshToken($oldRefreshToken['refresh_token_id']);
|
||||||
|
|
||||||
// Issue and persist new tokens
|
// Issue and persist new access token
|
||||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $oldRefreshToken['user_id'], $scopes);
|
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $oldRefreshToken['user_id'], $scopes);
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
||||||
|
$responseType->setAccessToken($accessToken);
|
||||||
|
|
||||||
|
// Issue and persist new refresh token if given
|
||||||
$refreshToken = $this->issueRefreshToken($accessToken);
|
$refreshToken = $this->issueRefreshToken($accessToken);
|
||||||
|
|
||||||
// Send events to emitter
|
if ($refreshToken !== null) {
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
||||||
|
|
||||||
// Inject tokens into response
|
|
||||||
$responseType->setAccessToken($accessToken);
|
|
||||||
$responseType->setRefreshToken($refreshToken);
|
$responseType->setRefreshToken($refreshToken);
|
||||||
|
}
|
||||||
|
|
||||||
return $responseType;
|
return $responseType;
|
||||||
}
|
}
|
||||||
@ -95,8 +97,8 @@ class RefreshTokenGrant extends AbstractGrant
|
|||||||
// Validate refresh token
|
// Validate refresh token
|
||||||
try {
|
try {
|
||||||
$refreshToken = $this->decrypt($encryptedRefreshToken);
|
$refreshToken = $this->decrypt($encryptedRefreshToken);
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token');
|
throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token', $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
$refreshTokenData = json_decode($refreshToken, true);
|
$refreshTokenData = json_decode($refreshToken, true);
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
namespace League\OAuth2\Server\Middleware;
|
namespace League\OAuth2\Server\Middleware;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
use League\OAuth2\Server\AuthorizationServer;
|
use League\OAuth2\Server\AuthorizationServer;
|
||||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
@ -43,7 +44,7 @@ class AuthorizationServerMiddleware
|
|||||||
} catch (OAuthServerException $exception) {
|
} catch (OAuthServerException $exception) {
|
||||||
return $exception->generateHttpResponse($response);
|
return $exception->generateHttpResponse($response);
|
||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
} catch (\Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
|
return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
|
||||||
->generateHttpResponse($response);
|
->generateHttpResponse($response);
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
namespace League\OAuth2\Server\Middleware;
|
namespace League\OAuth2\Server\Middleware;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
use League\OAuth2\Server\ResourceServer;
|
use League\OAuth2\Server\ResourceServer;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
@ -34,7 +35,7 @@ class ResourceServerMiddleware
|
|||||||
* @param ResponseInterface $response
|
* @param ResponseInterface $response
|
||||||
* @param callable $next
|
* @param callable $next
|
||||||
*
|
*
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
* @return ResponseInterface
|
||||||
*/
|
*/
|
||||||
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
|
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
|
||||||
{
|
{
|
||||||
@ -43,7 +44,7 @@ class ResourceServerMiddleware
|
|||||||
} catch (OAuthServerException $exception) {
|
} catch (OAuthServerException $exception) {
|
||||||
return $exception->generateHttpResponse($response);
|
return $exception->generateHttpResponse($response);
|
||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
} catch (\Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
|
return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
|
||||||
->generateHttpResponse($response);
|
->generateHttpResponse($response);
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
|
@ -20,7 +20,7 @@ interface RefreshTokenRepositoryInterface extends RepositoryInterface
|
|||||||
/**
|
/**
|
||||||
* Creates a new refresh token
|
* Creates a new refresh token
|
||||||
*
|
*
|
||||||
* @return RefreshTokenEntityInterface
|
* @return RefreshTokenEntityInterface|null
|
||||||
*/
|
*/
|
||||||
public function getNewRefreshToken();
|
public function getNewRefreshToken();
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ abstract class AbstractResponseType implements ResponseTypeInterface
|
|||||||
/**
|
/**
|
||||||
* Set the private key
|
* Set the private key
|
||||||
*
|
*
|
||||||
* @param \League\OAuth2\Server\CryptKey $key
|
* @param CryptKey $key
|
||||||
*/
|
*/
|
||||||
public function setPrivateKey(CryptKey $key)
|
public function setPrivateKey(CryptKey $key)
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,7 @@ namespace LeagueTests;
|
|||||||
|
|
||||||
use DateInterval;
|
use DateInterval;
|
||||||
use League\OAuth2\Server\AuthorizationServer;
|
use League\OAuth2\Server\AuthorizationServer;
|
||||||
|
use League\OAuth2\Server\CryptKey;
|
||||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
use League\OAuth2\Server\Grant\AuthCodeGrant;
|
use League\OAuth2\Server\Grant\AuthCodeGrant;
|
||||||
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
|
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
|
||||||
@ -110,6 +111,95 @@ class AuthorizationServerTest extends TestCase
|
|||||||
$this->assertInstanceOf(BearerTokenResponse::class, $method->invoke($server));
|
$this->assertInstanceOf(BearerTokenResponse::class, $method->invoke($server));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testGetResponseTypeExtended()
|
||||||
|
{
|
||||||
|
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$privateKey = 'file://' . __DIR__ . '/Stubs/private.key';
|
||||||
|
$encryptionKey = 'file://' . __DIR__ . '/Stubs/public.key';
|
||||||
|
|
||||||
|
$server = new class($clientRepository, $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), $privateKey, $encryptionKey) extends AuthorizationServer {
|
||||||
|
protected function getResponseType()
|
||||||
|
{
|
||||||
|
$this->responseType = new class extends BearerTokenResponse {
|
||||||
|
/* @return null|CryptKey */
|
||||||
|
public function getPrivateKey()
|
||||||
|
{
|
||||||
|
return $this->privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEncryptionKey()
|
||||||
|
{
|
||||||
|
return $this->encryptionKey;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parent::getResponseType();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($server);
|
||||||
|
$method = $abstractGrantReflection->getMethod('getResponseType');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
$responseType = $method->invoke($server);
|
||||||
|
|
||||||
|
$this->assertInstanceOf(BearerTokenResponse::class, $responseType);
|
||||||
|
// generated instances should have keys setup
|
||||||
|
$this->assertSame($privateKey, $responseType->getPrivateKey()->getKeyPath());
|
||||||
|
$this->assertSame($encryptionKey, $responseType->getEncryptionKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMultipleRequestsGetDifferentResponseTypeInstances()
|
||||||
|
{
|
||||||
|
$privateKey = 'file://' . __DIR__ . '/Stubs/private.key';
|
||||||
|
$encryptionKey = 'file://' . __DIR__ . '/Stubs/public.key';
|
||||||
|
|
||||||
|
$responseTypePrototype = new class extends BearerTokenResponse {
|
||||||
|
/* @return null|CryptKey */
|
||||||
|
public function getPrivateKey()
|
||||||
|
{
|
||||||
|
return $this->privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEncryptionKey()
|
||||||
|
{
|
||||||
|
return $this->encryptionKey;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
$clientRepository,
|
||||||
|
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||||
|
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
|
||||||
|
$privateKey,
|
||||||
|
$encryptionKey,
|
||||||
|
$responseTypePrototype
|
||||||
|
);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($server);
|
||||||
|
$method = $abstractGrantReflection->getMethod('getResponseType');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$responseTypeA = $method->invoke($server);
|
||||||
|
$responseTypeB = $method->invoke($server);
|
||||||
|
|
||||||
|
// prototype should not get changed
|
||||||
|
$this->assertNull($responseTypePrototype->getPrivateKey());
|
||||||
|
$this->assertNull($responseTypePrototype->getEncryptionKey());
|
||||||
|
|
||||||
|
// generated instances should have keys setup
|
||||||
|
$this->assertSame($privateKey, $responseTypeA->getPrivateKey()->getKeyPath());
|
||||||
|
$this->assertSame($encryptionKey, $responseTypeA->getEncryptionKey());
|
||||||
|
|
||||||
|
// all instances should be different but based on the same prototype
|
||||||
|
$this->assertSame(get_class($responseTypePrototype), get_class($responseTypeA));
|
||||||
|
$this->assertSame(get_class($responseTypePrototype), get_class($responseTypeB));
|
||||||
|
$this->assertNotSame($responseTypePrototype, $responseTypeA);
|
||||||
|
$this->assertNotSame($responseTypePrototype, $responseTypeB);
|
||||||
|
$this->assertNotSame($responseTypeA, $responseTypeB);
|
||||||
|
}
|
||||||
|
|
||||||
public function testCompleteAuthorizationRequest()
|
public function testCompleteAuthorizationRequest()
|
||||||
{
|
{
|
||||||
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace LeagueTests\Exception;
|
namespace LeagueTests\Exception;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
use League\OAuth2\Server\Grant\AbstractGrant;
|
use League\OAuth2\Server\Grant\AbstractGrant;
|
||||||
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||||
@ -78,4 +79,19 @@ class OAuthServerExceptionTest extends TestCase
|
|||||||
|
|
||||||
$this->assertFalse($exceptionWithoutRedirect->hasRedirect());
|
$this->assertFalse($exceptionWithoutRedirect->hasRedirect());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHasPrevious()
|
||||||
|
{
|
||||||
|
$previous = new Exception('This is the previous');
|
||||||
|
$exceptionWithPrevious = OAuthServerException::accessDenied(null, null, $previous);
|
||||||
|
|
||||||
|
$this->assertSame('This is the previous', $exceptionWithPrevious->getPrevious()->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDoesNotHavePrevious()
|
||||||
|
{
|
||||||
|
$exceptionWithoutPrevious = OAuthServerException::accessDenied();
|
||||||
|
|
||||||
|
$this->assertNull($exceptionWithoutPrevious->getPrevious());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -340,6 +340,27 @@ class AbstractGrantTest extends TestCase
|
|||||||
$this->assertEquals($accessToken, $refreshToken->getAccessToken());
|
$this->assertEquals($accessToken, $refreshToken->getAccessToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testIssueNullRefreshToken()
|
||||||
|
{
|
||||||
|
$refreshTokenRepoMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepoMock
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getNewRefreshToken')
|
||||||
|
->willReturn(null);
|
||||||
|
|
||||||
|
/** @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();
|
||||||
|
$this->assertNull($issueRefreshTokenMethod->invoke($grantMock, $accessToken));
|
||||||
|
}
|
||||||
|
|
||||||
public function testIssueAccessToken()
|
public function testIssueAccessToken()
|
||||||
{
|
{
|
||||||
$accessTokenRepoMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
$accessTokenRepoMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
@ -740,6 +740,74 @@ class AuthCodeGrantTest extends TestCase
|
|||||||
$this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
|
$this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRespondToAccessTokenRequestNullRefreshToken()
|
||||||
|
{
|
||||||
|
$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(null);
|
||||||
|
|
||||||
|
$grant = new AuthCodeGrant(
|
||||||
|
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
|
||||||
|
$refreshTokenRepositoryMock,
|
||||||
|
new \DateInterval('PT10M')
|
||||||
|
);
|
||||||
|
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setEncryptionKey($this->cryptStub->getKey());
|
||||||
|
$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->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken());
|
||||||
|
$this->assertNull($response->getRefreshToken());
|
||||||
|
}
|
||||||
|
|
||||||
public function testRespondToAccessTokenRequestCodeChallengePlain()
|
public function testRespondToAccessTokenRequestCodeChallengePlain()
|
||||||
{
|
{
|
||||||
$client = new ClientEntity();
|
$client = new ClientEntity();
|
||||||
|
@ -95,7 +95,6 @@ class ImplicitGrantTest extends TestCase
|
|||||||
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
$scopeEntity = new ScopeEntity();
|
$scopeEntity = new ScopeEntity();
|
||||||
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
|
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
|
||||||
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
|
||||||
|
|
||||||
$grant = new ImplicitGrant(new DateInterval('PT10M'));
|
$grant = new ImplicitGrant(new DateInterval('PT10M'));
|
||||||
$grant->setClientRepository($clientRepositoryMock);
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
@ -130,7 +129,6 @@ class ImplicitGrantTest extends TestCase
|
|||||||
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
$scopeEntity = new ScopeEntity();
|
$scopeEntity = new ScopeEntity();
|
||||||
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
|
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
|
||||||
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
|
||||||
|
|
||||||
$grant = new ImplicitGrant(new DateInterval('PT10M'));
|
$grant = new ImplicitGrant(new DateInterval('PT10M'));
|
||||||
$grant->setClientRepository($clientRepositoryMock);
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
@ -293,9 +291,13 @@ class ImplicitGrantTest extends TestCase
|
|||||||
$accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken);
|
$accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken);
|
||||||
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
||||||
|
|
||||||
$grant = new ImplicitGrant(new DateInterval('PT10M'));
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||||
|
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
|
||||||
$this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
|
$this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
|
||||||
}
|
}
|
||||||
@ -316,9 +318,13 @@ class ImplicitGrantTest extends TestCase
|
|||||||
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
||||||
|
|
||||||
$grant = new ImplicitGrant(new DateInterval('PT10M'));
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||||
|
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
|
||||||
$grant->completeAuthorizationRequest($authRequest);
|
$grant->completeAuthorizationRequest($authRequest);
|
||||||
}
|
}
|
||||||
@ -343,9 +349,13 @@ class ImplicitGrantTest extends TestCase
|
|||||||
$accessTokenRepositoryMock->expects($this->at(0))->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
|
$accessTokenRepositoryMock->expects($this->at(0))->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
|
||||||
$accessTokenRepositoryMock->expects($this->at(1))->method('persistNewAccessToken')->willReturnSelf();
|
$accessTokenRepositoryMock->expects($this->at(1))->method('persistNewAccessToken')->willReturnSelf();
|
||||||
|
|
||||||
$grant = new ImplicitGrant(new DateInterval('PT10M'));
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||||
|
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
|
||||||
$this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
|
$this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
|
||||||
}
|
}
|
||||||
@ -367,9 +377,13 @@ class ImplicitGrantTest extends TestCase
|
|||||||
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
$accessTokenRepositoryMock->method('persistNewAccessToken')->willThrowException(OAuthServerException::serverError('something bad happened'));
|
$accessTokenRepositoryMock->method('persistNewAccessToken')->willThrowException(OAuthServerException::serverError('something bad happened'));
|
||||||
|
|
||||||
$grant = new ImplicitGrant(new DateInterval('PT10M'));
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||||
|
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
|
||||||
$grant->completeAuthorizationRequest($authRequest);
|
$grant->completeAuthorizationRequest($authRequest);
|
||||||
}
|
}
|
||||||
@ -391,9 +405,13 @@ class ImplicitGrantTest extends TestCase
|
|||||||
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
$accessTokenRepositoryMock->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
|
$accessTokenRepositoryMock->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
|
||||||
|
|
||||||
$grant = new ImplicitGrant(new DateInterval('PT10M'));
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||||
|
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
|
||||||
$grant->completeAuthorizationRequest($authRequest);
|
$grant->completeAuthorizationRequest($authRequest);
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,52 @@ class PasswordGrantTest extends TestCase
|
|||||||
$this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
|
$this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRespondToRequestNullRefreshToken()
|
||||||
|
{
|
||||||
|
$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);
|
||||||
|
|
||||||
|
$scope = new ScopeEntity();
|
||||||
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
|
||||||
|
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||||
|
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(null);
|
||||||
|
|
||||||
|
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
$grant->setDefaultScope(self::DEFAULT_SCOPE);
|
||||||
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
|
||||||
|
$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->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken());
|
||||||
|
$this->assertNull($responseType->getRefreshToken());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
*/
|
*/
|
||||||
|
@ -95,6 +95,63 @@ class RefreshTokenGrantTest extends TestCase
|
|||||||
$this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
|
$this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRespondToRequestNullRefreshToken()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('foo');
|
||||||
|
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$scopeEntity = new ScopeEntity();
|
||||||
|
$scopeEntity->setIdentifier('foo');
|
||||||
|
|
||||||
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$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(null);
|
||||||
|
$refreshTokenRepositoryMock->expects($this->never())->method('persistNewRefreshToken');
|
||||||
|
|
||||||
|
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setEncryptionKey($this->cryptStub->getKey());
|
||||||
|
$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,
|
||||||
|
'scopes' => ['foo'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
|
||||||
|
$this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken());
|
||||||
|
$this->assertNull($responseType->getRefreshToken());
|
||||||
|
}
|
||||||
|
|
||||||
public function testRespondToReducedScopes()
|
public function testRespondToReducedScopes()
|
||||||
{
|
{
|
||||||
$client = new ClientEntity();
|
$client = new ClientEntity();
|
||||||
|
@ -105,7 +105,7 @@ class AuthorizationServerMiddlewareTest extends TestCase
|
|||||||
$response = $exception->generateHttpResponse(new Response());
|
$response = $exception->generateHttpResponse(new Response());
|
||||||
|
|
||||||
$this->assertEquals(302, $response->getStatusCode());
|
$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',
|
$this->assertEquals('http://foo/bar?error=invalid_scope&error_description=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed',
|
||||||
$response->getHeader('location')[0]);
|
$response->getHeader('location')[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +115,7 @@ class AuthorizationServerMiddlewareTest extends TestCase
|
|||||||
$response = $exception->generateHttpResponse(new Response(), true);
|
$response = $exception->generateHttpResponse(new Response(), true);
|
||||||
|
|
||||||
$this->assertEquals(302, $response->getStatusCode());
|
$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',
|
$this->assertEquals('http://foo/bar#error=invalid_scope&error_description=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed',
|
||||||
$response->getHeader('location')[0]);
|
$response->getHeader('location')[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user