mirror of
https://github.com/elyby/oauth2-server.git
synced 2025-05-31 14:12:07 +05:30
Compare commits
78 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
f2cd3646ff | ||
|
7839a61170 | ||
|
443d7c485a | ||
|
a61c6a318a | ||
|
94e75ba6f3 | ||
|
efa8ef6fce | ||
|
7982275757 | ||
|
f6c1070ccc | ||
|
d64fb3f526 | ||
|
95a9f4649d | ||
|
4bb5b747c1 | ||
|
5868996961 | ||
|
9542af627e | ||
|
3b983ad0b4 | ||
|
34ec35019b | ||
|
ac818bd921 | ||
|
73698e28d9 | ||
|
c87be9477c | ||
|
d288a2ad8a | ||
|
a34f5dd7db | ||
|
c0efdf0dd0 | ||
|
f96fca3b48 | ||
|
20b355b025 | ||
|
793f65d3a3 | ||
|
322b55eddf | ||
|
50ab9dd8ac | ||
|
b624124d5a | ||
|
dbf2b55bc5 | ||
|
b11d628e8b | ||
|
0515129c9c | ||
|
50566cdc87 | ||
|
b4d88995de | ||
|
398029be56 | ||
|
30ed221481 | ||
|
939c0619d0 | ||
|
4042a31159 | ||
|
0bdd02cdb4 | ||
|
7bf7700645 | ||
|
d76025d613 | ||
|
d6792c1662 | ||
|
9882f6716c | ||
|
71c605117a | ||
|
6bc6ac09d2 | ||
|
b7b7dda28c | ||
|
ef864b5cba | ||
|
27b956c149 | ||
|
6949a007e5 | ||
|
acf16e924a | ||
|
a479b5762e | ||
|
8184f771d4 | ||
|
51b3b415b4 | ||
|
e3ad09d4a2 | ||
|
aeb1fe48d3 | ||
|
f54980da25 | ||
|
105834af96 | ||
|
ffffc4bfeb | ||
|
a77732e97c | ||
|
614bba2c11 | ||
|
224763cda6 | ||
|
a31bc7d4cc | ||
|
0d20c755d4 | ||
|
e36ff17ad9 | ||
|
a339d99135 | ||
|
a7a1065e38 | ||
|
09bf988922 | ||
|
a571e2262b | ||
|
519543e925 | ||
|
3614f8bd7c | ||
|
e4a7fea834 | ||
|
68c9fbd83c | ||
|
466e1a639d | ||
|
d7ab153500 | ||
|
72ead2e3ce | ||
|
ae4ab26aaf | ||
|
2a7f671a95 | ||
|
02609c37cc | ||
|
9941a96feb | ||
|
d8ece093d5 |
@@ -4,6 +4,7 @@ enabled:
|
||||
- binary_operator_spaces
|
||||
- blank_line_before_return
|
||||
- concat_with_spaces
|
||||
- fully_qualified_strict_types
|
||||
- function_typehint_space
|
||||
- hash_to_slash_comment
|
||||
- include
|
||||
|
45
CHANGELOG.md
45
CHANGELOG.md
@@ -6,6 +6,40 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [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
|
||||
|
||||
### Changed
|
||||
- Added new`validateRedirectUri` method AbstractGrant to remove three instances of code duplication (PR #912)
|
||||
- Allow 640 as a crypt key file permission (PR #917)
|
||||
|
||||
### Added
|
||||
- Function `hasRedirect()` added to `OAuthServerException` (PR #703)
|
||||
|
||||
### Fixed
|
||||
- Catch and handle `BadMethodCallException` from the `verify()` method of the JWT token in the `validateAuthorization` method (PR #904)
|
||||
|
||||
## [4.1.7] - released 2018-06-23
|
||||
|
||||
### Fixed
|
||||
- Ensure `empty()` function call only contains variable to be compatible with PHP 5.4 (PR #918)
|
||||
|
||||
## [7.1.1] - released 2018-05-21
|
||||
|
||||
### Fixed
|
||||
@@ -97,6 +131,10 @@ To address feedback from the security release the following change has been made
|
||||
|
||||
- Fixed `finalizeScopes` call (Issue #650)
|
||||
|
||||
## [4.1.6] - 2016-09-13
|
||||
|
||||
- Less restrictive on Authorization header check (Issue #652)
|
||||
|
||||
## [5.1.1] - 2016-07-26
|
||||
|
||||
- Improved test suite (Issue #614)
|
||||
@@ -389,7 +427,10 @@ Version 5 is a complete code rewrite.
|
||||
|
||||
- First major release
|
||||
|
||||
[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...HEAD
|
||||
[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/7.3.1...HEAD
|
||||
[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.2.0]: https://github.com/thephpleague/oauth2-server/compare/7.1.1...7.2.0
|
||||
[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.0.0]: https://github.com/thephpleague/oauth2-server/compare/6.1.1...7.0.0
|
||||
@@ -409,6 +450,8 @@ Version 5 is a complete code rewrite.
|
||||
[5.0.0]: https://github.com/thephpleague/oauth2-server/compare/5.0.0-RC2...5.0.0
|
||||
[5.0.0-RC2]: https://github.com/thephpleague/oauth2-server/compare/5.0.0-RC1...5.0.0-RC2
|
||||
[5.0.0-RC1]: https://github.com/thephpleague/oauth2-server/compare/4.1.5...5.0.0-RC1
|
||||
[4.1.7]: https://github.com/thephpleague/oauth2-server/compare/4.1.6...4.1.7
|
||||
[4.1.6]: https://github.com/thephpleague/oauth2-server/compare/4.1.5...4.1.6
|
||||
[4.1.5]: https://github.com/thephpleague/oauth2-server/compare/4.1.4...4.1.5
|
||||
[4.1.4]: https://github.com/thephpleague/oauth2-server/compare/4.1.3...4.1.4
|
||||
[4.1.3]: https://github.com/thephpleague/oauth2-server/compare/4.1.2...4.1.3
|
||||
|
@@ -37,7 +37,7 @@ The following versions of PHP are supported:
|
||||
|
||||
The `openssl` extension is 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 between 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.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -68,6 +68,7 @@ We use [Travis CI](https://travis-ci.org/), [Scrutinizer](https://scrutinizer-ci
|
||||
* [Drupal](https://www.drupal.org/project/simple_oauth)
|
||||
* [Laravel Passport](https://github.com/laravel/passport)
|
||||
* [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)
|
||||
|
||||
## Changelog
|
||||
|
||||
|
@@ -16,7 +16,8 @@
|
||||
"zendframework/zend-diactoros": "^1.3.2",
|
||||
"phpstan/phpstan": "^0.9.2",
|
||||
"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": [
|
||||
{
|
||||
@@ -46,6 +47,12 @@
|
||||
"email": "hello@alexbilbie.com",
|
||||
"homepage": "http://www.alexbilbie.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Andy Millington",
|
||||
"email": "andrew@noexceptions.io",
|
||||
"homepage": "https://www.noexceptions.io",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"replace": {
|
||||
|
@@ -1,14 +1,13 @@
|
||||
{
|
||||
"require": {
|
||||
"slim/slim": "3.0.*"
|
||||
"slim/slim": "^3.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"league/event": "^2.1",
|
||||
"lcobucci/jwt": "^3.1",
|
||||
"paragonie/random_compat": "^2.0",
|
||||
"lcobucci/jwt": "^3.2",
|
||||
"psr/http-message": "^1.0",
|
||||
"defuse/php-encryption": "^2.1",
|
||||
"zendframework/zend-diactoros": "^1.0"
|
||||
"defuse/php-encryption": "^2.2",
|
||||
"zendframework/zend-diactoros": "^2.0.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
219
examples/composer.lock
generated
219
examples/composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "9813ed7c3b6dcf107f44df9392935b8f",
|
||||
"content-hash": "97f2878428e37d1d8e5418cc85cbfa3d",
|
||||
"packages": [
|
||||
{
|
||||
"name": "container-interop/container-interop",
|
||||
@@ -39,21 +39,24 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/fast-route",
|
||||
"version": "v0.6.0",
|
||||
"version": "v1.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/FastRoute.git",
|
||||
"reference": "31fa86924556b80735f98b294a7ffdfb26789f22"
|
||||
"reference": "181d480e08d9476e61381e04a71b34dc0432e812"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/31fa86924556b80735f98b294a7ffdfb26789f22",
|
||||
"reference": "31fa86924556b80735f98b294a7ffdfb26789f22",
|
||||
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812",
|
||||
"reference": "181d480e08d9476e61381e04a71b34dc0432e812",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35|~5.7"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -78,29 +81,33 @@
|
||||
"router",
|
||||
"routing"
|
||||
],
|
||||
"time": "2015-06-18T19:15:47+00:00"
|
||||
"time": "2018-02-13T20:26:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pimple/pimple",
|
||||
"version": "v3.0.2",
|
||||
"version": "v3.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/silexphp/Pimple.git",
|
||||
"reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a"
|
||||
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/a30f7d6e57565a2e1a316e1baf2a483f788b258a",
|
||||
"reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a",
|
||||
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
|
||||
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
"php": ">=5.3.0",
|
||||
"psr/container": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "^3.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0.x-dev"
|
||||
"dev-master": "3.2.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -124,7 +131,7 @@
|
||||
"container",
|
||||
"dependency injection"
|
||||
],
|
||||
"time": "2015-09-11T15:10:35+00:00"
|
||||
"time": "2018-01-21T07:42:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
@@ -227,27 +234,32 @@
|
||||
},
|
||||
{
|
||||
"name": "slim/slim",
|
||||
"version": "3.0.0",
|
||||
"version": "3.11.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/slimphp/Slim.git",
|
||||
"reference": "3b06f0f2d84dabbe81b6cea46ace46a3e883253e"
|
||||
"reference": "d378e70431e78ee92ee32ddde61ecc72edf5dc0a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/slimphp/Slim/zipball/3b06f0f2d84dabbe81b6cea46ace46a3e883253e",
|
||||
"reference": "3b06f0f2d84dabbe81b6cea46ace46a3e883253e",
|
||||
"url": "https://api.github.com/repos/slimphp/Slim/zipball/d378e70431e78ee92ee32ddde61ecc72edf5dc0a",
|
||||
"reference": "d378e70431e78ee92ee32ddde61ecc72edf5dc0a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"container-interop/container-interop": "^1.1",
|
||||
"nikic/fast-route": "^0.6",
|
||||
"container-interop/container-interop": "^1.2",
|
||||
"nikic/fast-route": "^1.0",
|
||||
"php": ">=5.5.0",
|
||||
"pimple/pimple": "^3.0",
|
||||
"psr/container": "^1.0",
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.0"
|
||||
"phpunit/phpunit": "^4.0",
|
||||
"squizlabs/php_codesniffer": "^2.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@@ -282,38 +294,38 @@
|
||||
}
|
||||
],
|
||||
"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": [
|
||||
"api",
|
||||
"framework",
|
||||
"micro",
|
||||
"router"
|
||||
],
|
||||
"time": "2015-12-07T14:11:09+00:00"
|
||||
"time": "2018-09-16T10:54:21+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "defuse/php-encryption",
|
||||
"version": "v2.1.0",
|
||||
"version": "v2.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/defuse/php-encryption.git",
|
||||
"reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689"
|
||||
"reference": "0f407c43b953d571421e0020ba92082ed5fb7620"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/defuse/php-encryption/zipball/5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689",
|
||||
"reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689",
|
||||
"url": "https://api.github.com/repos/defuse/php-encryption/zipball/0f407c43b953d571421e0020ba92082ed5fb7620",
|
||||
"reference": "0f407c43b953d571421e0020ba92082ed5fb7620",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-openssl": "*",
|
||||
"paragonie/random_compat": "~2.0",
|
||||
"paragonie/random_compat": ">= 2",
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nikic/php-parser": "^2.0|^3.0",
|
||||
"nikic/php-parser": "^2.0|^3.0|^4.0",
|
||||
"phpunit/phpunit": "^4|^5"
|
||||
},
|
||||
"bin": [
|
||||
@@ -354,20 +366,20 @@
|
||||
"security",
|
||||
"symmetric key cryptography"
|
||||
],
|
||||
"time": "2017-05-18T21:28:48+00:00"
|
||||
"time": "2018-07-24T23:27:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "lcobucci/jwt",
|
||||
"version": "3.2.1",
|
||||
"version": "3.2.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lcobucci/jwt.git",
|
||||
"reference": "ddce703826f9c5229781933b1a39069e38e6a0f3"
|
||||
"reference": "c9704b751315d21735dc98d78d4f37bd73596da7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/ddce703826f9c5229781933b1a39069e38e6a0f3",
|
||||
"reference": "ddce703826f9c5229781933b1a39069e38e6a0f3",
|
||||
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/c9704b751315d21735dc98d78d4f37bd73596da7",
|
||||
"reference": "c9704b751315d21735dc98d78d4f37bd73596da7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -412,7 +424,7 @@
|
||||
"JWS",
|
||||
"jwt"
|
||||
],
|
||||
"time": "2016-10-31T20:09:32+00:00"
|
||||
"time": "2018-08-03T11:23:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/event",
|
||||
@@ -466,33 +478,29 @@
|
||||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
"version": "v2.0.10",
|
||||
"version": "v9.99.99",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/random_compat.git",
|
||||
"reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d"
|
||||
"reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d",
|
||||
"reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
|
||||
"reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.2.0"
|
||||
"php": "^7"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.*|5.*"
|
||||
"phpunit/phpunit": "4.*|5.*",
|
||||
"vimeo/psalm": "^1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"lib/random.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
@@ -507,10 +515,129 @@
|
||||
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
|
||||
"keywords": [
|
||||
"csprng",
|
||||
"polyfill",
|
||||
"pseudorandom",
|
||||
"random"
|
||||
],
|
||||
"time": "2017-03-13T16:27:32+00:00"
|
||||
"time": "2018-07-02T15:55:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-factory",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-factory.git",
|
||||
"reference": "378bfe27931ecc54ff824a20d6f6bfc303bbd04c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/378bfe27931ecc54ff824a20d6f6bfc303bbd04c",
|
||||
"reference": "378bfe27931ecc54ff824a20d6f6bfc303bbd04c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"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"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-factory-implementation": "1.0",
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"http-interop/http-factory-tests": "^0.5.0",
|
||||
"php-http/psr7-integration-tests": "dev-master",
|
||||
"phpunit/phpunit": "^7.0.2",
|
||||
"zendframework/zend-coding-standard": "~1.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev",
|
||||
"dev-develop": "2.1.x-dev",
|
||||
"dev-release-1.8": "1.8.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions/create_uploaded_file.php",
|
||||
"src/functions/marshal_headers_from_sapi.php",
|
||||
"src/functions/marshal_method_from_sapi.php",
|
||||
"src/functions/marshal_protocol_version_from_sapi.php",
|
||||
"src/functions/marshal_uri_from_sapi.php",
|
||||
"src/functions/normalize_server.php",
|
||||
"src/functions/normalize_uploaded_files.php",
|
||||
"src/functions/parse_cookie_header.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Zend\\Diactoros\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"description": "PSR HTTP Message implementations",
|
||||
"keywords": [
|
||||
"http",
|
||||
"psr",
|
||||
"psr-7"
|
||||
],
|
||||
"time": "2018-09-27T19:49:04+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
@@ -11,13 +11,9 @@ namespace OAuth2ServerExamples\Entities;
|
||||
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\ScopeTrait;
|
||||
|
||||
class ScopeEntity implements ScopeEntityInterface
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->getIdentifier();
|
||||
}
|
||||
use EntityTrait, ScopeTrait;
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ class RefreshTokenRepository implements RefreshTokenRepositoryInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntityInterface)
|
||||
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity)
|
||||
{
|
||||
// Some logic to persist the refresh token in a database
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace League\OAuth2\Server;
|
||||
|
||||
use DateInterval;
|
||||
use Defuse\Crypto\Key;
|
||||
use League\Event\EmitterAwareInterface;
|
||||
use League\Event\EmitterAwareTrait;
|
||||
@@ -34,7 +35,7 @@ class AuthorizationServer implements EmitterAwareInterface
|
||||
protected $enabledGrantTypes = [];
|
||||
|
||||
/**
|
||||
* @var \DateInterval[]
|
||||
* @var DateInterval[]
|
||||
*/
|
||||
protected $grantTypeAccessTokenTTL = [];
|
||||
|
||||
@@ -49,7 +50,7 @@ class AuthorizationServer implements EmitterAwareInterface
|
||||
protected $publicKey;
|
||||
|
||||
/**
|
||||
* @var null|ResponseTypeInterface
|
||||
* @var ResponseTypeInterface
|
||||
*/
|
||||
protected $responseType;
|
||||
|
||||
@@ -103,8 +104,22 @@ class AuthorizationServer implements EmitterAwareInterface
|
||||
if ($privateKey instanceof CryptKey === false) {
|
||||
$privateKey = new CryptKey($privateKey);
|
||||
}
|
||||
|
||||
$this->privateKey = $privateKey;
|
||||
$this->encryptionKey = $encryptionKey;
|
||||
|
||||
if ($responseType === null) {
|
||||
$responseType = new BearerTokenResponse();
|
||||
} else {
|
||||
$responseType = clone $responseType;
|
||||
}
|
||||
|
||||
if ($responseType instanceof AbstractResponseType) {
|
||||
$responseType->setPrivateKey($this->privateKey);
|
||||
}
|
||||
|
||||
$responseType->setEncryptionKey($this->encryptionKey);
|
||||
|
||||
$this->responseType = $responseType;
|
||||
}
|
||||
|
||||
@@ -112,12 +127,12 @@ class AuthorizationServer implements EmitterAwareInterface
|
||||
* Enable a grant type on the server.
|
||||
*
|
||||
* @param GrantTypeInterface $grantType
|
||||
* @param null|\DateInterval $accessTokenTTL
|
||||
* @param null|DateInterval $accessTokenTTL
|
||||
*/
|
||||
public function enableGrantType(GrantTypeInterface $grantType, \DateInterval $accessTokenTTL = null)
|
||||
public function enableGrantType(GrantTypeInterface $grantType, DateInterval $accessTokenTTL = null)
|
||||
{
|
||||
if ($accessTokenTTL instanceof \DateInterval === false) {
|
||||
$accessTokenTTL = new \DateInterval('PT1H');
|
||||
if ($accessTokenTTL instanceof DateInterval === false) {
|
||||
$accessTokenTTL = new DateInterval('PT1H');
|
||||
}
|
||||
|
||||
$grantType->setAccessTokenRepository($this->accessTokenRepository);
|
||||
@@ -204,16 +219,7 @@ class AuthorizationServer implements EmitterAwareInterface
|
||||
*/
|
||||
protected function getResponseType()
|
||||
{
|
||||
if ($this->responseType instanceof ResponseTypeInterface === false) {
|
||||
$this->responseType = new BearerTokenResponse();
|
||||
}
|
||||
|
||||
if ($this->responseType instanceof AbstractResponseType === true) {
|
||||
$this->responseType->setPrivateKey($this->privateKey);
|
||||
}
|
||||
$this->responseType->setEncryptionKey($this->encryptionKey);
|
||||
|
||||
return $this->responseType;
|
||||
return clone $this->responseType;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -9,6 +9,8 @@
|
||||
|
||||
namespace League\OAuth2\Server\AuthorizationValidators;
|
||||
|
||||
use BadMethodCallException;
|
||||
use InvalidArgumentException;
|
||||
use Lcobucci\JWT\Parser;
|
||||
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||
use Lcobucci\JWT\ValidationData;
|
||||
@@ -17,6 +19,7 @@ use League\OAuth2\Server\CryptTrait;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use RuntimeException;
|
||||
|
||||
class BearerTokenValidator implements AuthorizationValidatorInterface
|
||||
{
|
||||
@@ -28,7 +31,7 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
|
||||
private $accessTokenRepository;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\CryptKey
|
||||
* @var CryptKey
|
||||
*/
|
||||
protected $publicKey;
|
||||
|
||||
@@ -43,7 +46,7 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
|
||||
/**
|
||||
* Set the public key
|
||||
*
|
||||
* @param \League\OAuth2\Server\CryptKey $key
|
||||
* @param CryptKey $key
|
||||
*/
|
||||
public function setPublicKey(CryptKey $key)
|
||||
{
|
||||
@@ -65,8 +68,12 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
|
||||
try {
|
||||
// Attempt to parse and validate the JWT
|
||||
$token = (new Parser())->parse($jwt);
|
||||
if ($token->verify(new Sha256(), $this->publicKey->getKeyPath()) === false) {
|
||||
throw OAuthServerException::accessDenied('Access token could not be verified');
|
||||
try {
|
||||
if ($token->verify(new Sha256(), $this->publicKey->getKeyPath()) === false) {
|
||||
throw OAuthServerException::accessDenied('Access token could not be verified');
|
||||
}
|
||||
} catch (BadMethodCallException $exception) {
|
||||
throw OAuthServerException::accessDenied('Access token is not signed', null, $exception);
|
||||
}
|
||||
|
||||
// Ensure access token hasn't expired
|
||||
@@ -88,12 +95,12 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
|
||||
->withAttribute('oauth_client_id', $token->getClaim('aud'))
|
||||
->withAttribute('oauth_user_id', $token->getClaim('sub'))
|
||||
->withAttribute('oauth_scopes', $token->getClaim('scopes'));
|
||||
} catch (\InvalidArgumentException $exception) {
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
// JWT couldn't be parsed so return the request as is
|
||||
throw OAuthServerException::accessDenied($exception->getMessage());
|
||||
} catch (\RuntimeException $exception) {
|
||||
throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception);
|
||||
} catch (RuntimeException $exception) {
|
||||
//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;
|
||||
|
||||
use LogicException;
|
||||
use RuntimeException;
|
||||
|
||||
class CryptKey
|
||||
{
|
||||
const RSA_KEY_PATTERN =
|
||||
@@ -42,13 +45,13 @@ class CryptKey
|
||||
}
|
||||
|
||||
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) {
|
||||
// Verify the permissions of the key
|
||||
$keyPathPerms = decoct(fileperms($keyPath) & 0777);
|
||||
if (in_array($keyPathPerms, ['400', '440', '600', '660'], true) === false) {
|
||||
if (in_array($keyPathPerms, ['400', '440', '600', '640', '660'], true) === false) {
|
||||
trigger_error(sprintf(
|
||||
'Key file "%s" permissions are not correct, recommend changing to 600 or 660 instead of %s',
|
||||
$keyPath,
|
||||
@@ -64,7 +67,7 @@ class CryptKey
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @throws RuntimeException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -79,19 +82,19 @@ class CryptKey
|
||||
|
||||
if (!touch($keyPath)) {
|
||||
// @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
|
||||
}
|
||||
|
||||
if (file_put_contents($keyPath, $key) === false) {
|
||||
// @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
|
||||
}
|
||||
|
||||
if (chmod($keyPath, 0600) === false) {
|
||||
// @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
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Public/private key encryption.
|
||||
* Encrypt/decrypt with encryptionKey.
|
||||
*
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
@@ -13,6 +13,8 @@ namespace League\OAuth2\Server;
|
||||
|
||||
use Defuse\Crypto\Crypto;
|
||||
use Defuse\Crypto\Key;
|
||||
use Exception;
|
||||
use LogicException;
|
||||
|
||||
trait CryptTrait
|
||||
{
|
||||
@@ -22,11 +24,11 @@ trait CryptTrait
|
||||
protected $encryptionKey;
|
||||
|
||||
/**
|
||||
* Encrypt data with a private key.
|
||||
* Encrypt data with encryptionKey.
|
||||
*
|
||||
* @param string $unencryptedData
|
||||
*
|
||||
* @throws \LogicException
|
||||
* @throws LogicException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -38,17 +40,17 @@ trait CryptTrait
|
||||
}
|
||||
|
||||
return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey);
|
||||
} catch (\Exception $e) {
|
||||
throw new \LogicException($e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
throw new LogicException($e->getMessage(), null, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt data with a public key.
|
||||
* Decrypt data with encryptionKey.
|
||||
*
|
||||
* @param string $encryptedData
|
||||
*
|
||||
* @throws \LogicException
|
||||
* @throws LogicException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -60,8 +62,8 @@ trait CryptTrait
|
||||
}
|
||||
|
||||
return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey);
|
||||
} catch (\Exception $e) {
|
||||
throw new \LogicException($e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
throw new LogicException($e->getMessage(), null, $e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,8 @@
|
||||
|
||||
namespace League\OAuth2\Server\Entities;
|
||||
|
||||
use DateTime;
|
||||
|
||||
interface RefreshTokenEntityInterface
|
||||
{
|
||||
/**
|
||||
@@ -28,16 +30,16 @@ interface RefreshTokenEntityInterface
|
||||
/**
|
||||
* Get the token's expiry date time.
|
||||
*
|
||||
* @return \DateTime
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getExpiryDateTime();
|
||||
|
||||
/**
|
||||
* Set the date time when the token expires.
|
||||
*
|
||||
* @param \DateTime $dateTime
|
||||
* @param DateTime $dateTime
|
||||
*/
|
||||
public function setExpiryDateTime(\DateTime $dateTime);
|
||||
public function setExpiryDateTime(DateTime $dateTime);
|
||||
|
||||
/**
|
||||
* Set the access token that the refresh token was associated with.
|
||||
|
@@ -9,7 +9,9 @@
|
||||
|
||||
namespace League\OAuth2\Server\Entities;
|
||||
|
||||
interface ScopeEntityInterface extends \JsonSerializable
|
||||
use JsonSerializable;
|
||||
|
||||
interface ScopeEntityInterface extends JsonSerializable
|
||||
{
|
||||
/**
|
||||
* Get the scope's identifier.
|
||||
|
@@ -9,6 +9,8 @@
|
||||
|
||||
namespace League\OAuth2\Server\Entities;
|
||||
|
||||
use DateTime;
|
||||
|
||||
interface TokenInterface
|
||||
{
|
||||
/**
|
||||
@@ -28,16 +30,16 @@ interface TokenInterface
|
||||
/**
|
||||
* Get the token's expiry date time.
|
||||
*
|
||||
* @return \DateTime
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getExpiryDateTime();
|
||||
|
||||
/**
|
||||
* Set the date time when the token expires.
|
||||
*
|
||||
* @param \DateTime $dateTime
|
||||
* @param DateTime $dateTime
|
||||
*/
|
||||
public function setExpiryDateTime(\DateTime $dateTime);
|
||||
public function setExpiryDateTime(DateTime $dateTime);
|
||||
|
||||
/**
|
||||
* Set the identifier of the user associated with the token.
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace League\OAuth2\Server\Entities\Traits;
|
||||
|
||||
use DateTime;
|
||||
use Lcobucci\JWT\Builder;
|
||||
use Lcobucci\JWT\Signer\Key;
|
||||
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||
@@ -46,7 +47,7 @@ trait AccessTokenTrait
|
||||
abstract public function getClient();
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
* @return DateTime
|
||||
*/
|
||||
abstract public function getExpiryDateTime();
|
||||
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace League\OAuth2\Server\Entities\Traits;
|
||||
|
||||
use DateTime;
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
|
||||
trait RefreshTokenTrait
|
||||
@@ -19,7 +20,7 @@ trait RefreshTokenTrait
|
||||
protected $accessToken;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
* @var DateTime
|
||||
*/
|
||||
protected $expiryDateTime;
|
||||
|
||||
@@ -42,7 +43,7 @@ trait RefreshTokenTrait
|
||||
/**
|
||||
* Get the token's expiry date time.
|
||||
*
|
||||
* @return \DateTime
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getExpiryDateTime()
|
||||
{
|
||||
@@ -52,9 +53,9 @@ trait RefreshTokenTrait
|
||||
/**
|
||||
* Set the date time when the token expires.
|
||||
*
|
||||
* @param \DateTime $dateTime
|
||||
* @param DateTime $dateTime
|
||||
*/
|
||||
public function setExpiryDateTime(\DateTime $dateTime)
|
||||
public function setExpiryDateTime(DateTime $dateTime)
|
||||
{
|
||||
$this->expiryDateTime = $dateTime;
|
||||
}
|
||||
|
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,6 +9,7 @@
|
||||
|
||||
namespace League\OAuth2\Server\Entities\Traits;
|
||||
|
||||
use DateTime;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
|
||||
@@ -20,7 +21,7 @@ trait TokenEntityTrait
|
||||
protected $scopes = [];
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
* @var DateTime
|
||||
*/
|
||||
protected $expiryDateTime;
|
||||
|
||||
@@ -57,7 +58,7 @@ trait TokenEntityTrait
|
||||
/**
|
||||
* Get the token's expiry date time.
|
||||
*
|
||||
* @return \DateTime
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getExpiryDateTime()
|
||||
{
|
||||
@@ -67,9 +68,9 @@ trait TokenEntityTrait
|
||||
/**
|
||||
* Set the date time when the token expires.
|
||||
*
|
||||
* @param \DateTime $dateTime
|
||||
* @param DateTime $dateTime
|
||||
*/
|
||||
public function setExpiryDateTime(\DateTime $dateTime)
|
||||
public function setExpiryDateTime(DateTime $dateTime)
|
||||
{
|
||||
$this->expiryDateTime = $dateTime;
|
||||
}
|
||||
|
@@ -9,9 +9,11 @@
|
||||
|
||||
namespace League\OAuth2\Server\Exception;
|
||||
|
||||
use Exception;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Throwable;
|
||||
|
||||
class OAuthServerException extends \Exception
|
||||
class OAuthServerException extends Exception
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
@@ -47,10 +49,11 @@ class OAuthServerException extends \Exception
|
||||
* @param int $httpStatusCode HTTP status code to send (default = 400)
|
||||
* @param null|string $hint A helper hint
|
||||
* @param null|string $redirectUri A HTTP URI to redirect the user back to
|
||||
* @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->errorType = $errorType;
|
||||
$this->hint = $hint;
|
||||
@@ -102,16 +105,17 @@ class OAuthServerException extends \Exception
|
||||
*
|
||||
* @param string $parameter The invalid parameter
|
||||
* @param null|string $hint
|
||||
* @param Throwable $previous Previous exception
|
||||
*
|
||||
* @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, ' .
|
||||
'includes a parameter more than once, or is otherwise malformed.';
|
||||
$hint = ($hint === null) ? sprintf('Check the `%s` parameter', $parameter) : $hint;
|
||||
|
||||
return new static($errorMessage, 3, 'invalid_request', 400, $hint);
|
||||
return new static($errorMessage, 3, 'invalid_request', 400, $hint, null, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,20 +167,24 @@ class OAuthServerException extends \Exception
|
||||
/**
|
||||
* Server error.
|
||||
*
|
||||
* @param string $hint
|
||||
* @param string $hint
|
||||
* @param Throwable $previous
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function serverError($hint)
|
||||
public static function serverError($hint, Throwable $previous = null)
|
||||
{
|
||||
return new static(
|
||||
'The authorization server encountered an unexpected condition which prevented it from fulfilling'
|
||||
. ' the request: ' . $hint,
|
||||
7,
|
||||
'server_error',
|
||||
500
|
||||
500,
|
||||
null,
|
||||
null,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
|
||||
@@ -184,12 +192,13 @@ class OAuthServerException extends \Exception
|
||||
* Invalid refresh token.
|
||||
*
|
||||
* @param null|string $hint
|
||||
* @param Throwable $previous
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,10 +206,11 @@ class OAuthServerException extends \Exception
|
||||
*
|
||||
* @param null|string $hint
|
||||
* @param null|string $redirectUri
|
||||
* @param Throwable $previous
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function accessDenied($hint = null, $redirectUri = null)
|
||||
public static function accessDenied($hint = null, $redirectUri = null, Throwable $previous = null)
|
||||
{
|
||||
return new static(
|
||||
'The resource owner or authorization server denied the request.',
|
||||
@@ -208,7 +218,8 @@ class OAuthServerException extends \Exception
|
||||
'access_denied',
|
||||
401,
|
||||
$hint,
|
||||
$redirectUri
|
||||
$redirectUri,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
|
||||
@@ -303,6 +314,21 @@ class OAuthServerException extends \Exception
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the exception has an associated redirect URI.
|
||||
*
|
||||
* Returns whether the exception includes a redirect, since
|
||||
* getHttpStatusCode() doesn't return a 302 when there's a
|
||||
* redirect enabled. This helps when you want to override local
|
||||
* error pages but want to let redirects through.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRedirect()
|
||||
{
|
||||
return $this->redirectUri !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP status code to send when the exceptions is output.
|
||||
*
|
||||
|
@@ -11,6 +11,9 @@ namespace League\OAuth2\Server\Exception;
|
||||
|
||||
class UniqueTokenIdentifierConstraintViolationException extends OAuthServerException
|
||||
{
|
||||
/**
|
||||
* @return UniqueTokenIdentifierConstraintViolationException
|
||||
*/
|
||||
public static function create()
|
||||
{
|
||||
$errorMessage = 'Could not create unique access token identifier';
|
||||
|
@@ -10,6 +10,10 @@
|
||||
*/
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
use Error;
|
||||
use Exception;
|
||||
use League\Event\EmitterAwareTrait;
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
use League\OAuth2\Server\CryptTrait;
|
||||
@@ -28,7 +32,9 @@ use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
|
||||
use League\OAuth2\Server\RequestEvent;
|
||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||
use LogicException;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TypeError;
|
||||
|
||||
/**
|
||||
* Abstract grant class.
|
||||
@@ -72,12 +78,12 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
protected $userRepository;
|
||||
|
||||
/**
|
||||
* @var \DateInterval
|
||||
* @var DateInterval
|
||||
*/
|
||||
protected $refreshTokenTTL;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\CryptKey
|
||||
* @var CryptKey
|
||||
*/
|
||||
protected $privateKey;
|
||||
|
||||
@@ -137,7 +143,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL)
|
||||
public function setRefreshTokenTTL(DateInterval $refreshTokenTTL)
|
||||
{
|
||||
$this->refreshTokenTTL = $refreshTokenTTL;
|
||||
}
|
||||
@@ -145,7 +151,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
/**
|
||||
* Set the private key
|
||||
*
|
||||
* @param \League\OAuth2\Server\CryptKey $key
|
||||
* @param CryptKey $key
|
||||
*/
|
||||
public function setPrivateKey(CryptKey $key)
|
||||
{
|
||||
@@ -174,7 +180,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
list($basicAuthUser, $basicAuthPassword) = $this->getBasicAuthCredentials($request);
|
||||
|
||||
$clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser);
|
||||
if (is_null($clientId)) {
|
||||
if ($clientId === null) {
|
||||
throw OAuthServerException::invalidRequest('client_id');
|
||||
}
|
||||
|
||||
@@ -193,32 +199,48 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
|
||||
// If a redirect URI is provided ensure it matches what is pre-registered
|
||||
$redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
|
||||
|
||||
if ($redirectUri !== null) {
|
||||
if (
|
||||
is_string($client->getRedirectUri())
|
||||
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
} elseif (
|
||||
is_array($client->getRedirectUri())
|
||||
&& in_array($redirectUri, $client->getRedirectUri(), true) === false
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
$this->validateRedirectUri($redirectUri, $client, $request);
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate redirectUri from the request.
|
||||
* If a redirect URI is provided ensure it matches what is pre-registered
|
||||
*
|
||||
* @param string $redirectUri
|
||||
* @param ClientEntityInterface $client
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*/
|
||||
protected function validateRedirectUri(
|
||||
string $redirectUri,
|
||||
ClientEntityInterface $client,
|
||||
ServerRequestInterface $request
|
||||
) {
|
||||
if (\is_string($client->getRedirectUri())
|
||||
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
} elseif (\is_array($client->getRedirectUri())
|
||||
&& \in_array($redirectUri, $client->getRedirectUri(), true) === false
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate scopes in the request.
|
||||
*
|
||||
* @param string $scopes
|
||||
* @param string $redirectUri
|
||||
* @param string|array $scopes
|
||||
* @param string $redirectUri
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
@@ -226,13 +248,13 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
*/
|
||||
public function validateScopes($scopes, $redirectUri = null)
|
||||
{
|
||||
$scopesList = array_filter(explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), function ($scope) {
|
||||
return !empty($scope);
|
||||
});
|
||||
if (!\is_array($scopes)) {
|
||||
$scopes = $this->convertScopesQueryStringToArray($scopes);
|
||||
}
|
||||
|
||||
$validScopes = [];
|
||||
|
||||
foreach ($scopesList as $scopeItem) {
|
||||
foreach ($scopes as $scopeItem) {
|
||||
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem);
|
||||
|
||||
if ($scope instanceof ScopeEntityInterface === false) {
|
||||
@@ -245,6 +267,20 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
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.
|
||||
*
|
||||
@@ -258,7 +294,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
{
|
||||
$requestParameters = (array) $request->getParsedBody();
|
||||
|
||||
return isset($requestParameters[$parameter]) ? $requestParameters[$parameter] : $default;
|
||||
return $requestParameters[$parameter] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -339,7 +375,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
/**
|
||||
* Issue an access token.
|
||||
*
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
* @param DateInterval $accessTokenTTL
|
||||
* @param ClientEntityInterface $client
|
||||
* @param string|null $userIdentifier
|
||||
* @param ScopeEntityInterface[] $scopes
|
||||
@@ -350,7 +386,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
* @return AccessTokenEntityInterface
|
||||
*/
|
||||
protected function issueAccessToken(
|
||||
\DateInterval $accessTokenTTL,
|
||||
DateInterval $accessTokenTTL,
|
||||
ClientEntityInterface $client,
|
||||
$userIdentifier,
|
||||
array $scopes = []
|
||||
@@ -360,7 +396,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
$accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier);
|
||||
$accessToken->setClient($client);
|
||||
$accessToken->setUserIdentifier($userIdentifier);
|
||||
$accessToken->setExpiryDateTime((new \DateTime())->add($accessTokenTTL));
|
||||
$accessToken->setExpiryDateTime((new DateTime())->add($accessTokenTTL));
|
||||
|
||||
foreach ($scopes as $scope) {
|
||||
$accessToken->addScope($scope);
|
||||
@@ -383,7 +419,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
/**
|
||||
* Issue an auth code.
|
||||
*
|
||||
* @param \DateInterval $authCodeTTL
|
||||
* @param DateInterval $authCodeTTL
|
||||
* @param ClientEntityInterface $client
|
||||
* @param string $userIdentifier
|
||||
* @param string|null $redirectUri
|
||||
@@ -395,7 +431,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
* @return AuthCodeEntityInterface
|
||||
*/
|
||||
protected function issueAuthCode(
|
||||
\DateInterval $authCodeTTL,
|
||||
DateInterval $authCodeTTL,
|
||||
ClientEntityInterface $client,
|
||||
$userIdentifier,
|
||||
$redirectUri,
|
||||
@@ -404,7 +440,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
||||
|
||||
$authCode = $this->authCodeRepository->getNewAuthCode();
|
||||
$authCode->setExpiryDateTime((new \DateTime())->add($authCodeTTL));
|
||||
$authCode->setExpiryDateTime((new DateTime())->add($authCodeTTL));
|
||||
$authCode->setClient($client);
|
||||
$authCode->setUserIdentifier($userIdentifier);
|
||||
|
||||
@@ -443,7 +479,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
||||
|
||||
$refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
|
||||
$refreshToken->setExpiryDateTime((new \DateTime())->add($this->refreshTokenTTL));
|
||||
$refreshToken->setExpiryDateTime((new DateTime())->add($this->refreshTokenTTL));
|
||||
$refreshToken->setAccessToken($accessToken);
|
||||
|
||||
while ($maxGenerationAttempts-- > 0) {
|
||||
@@ -474,13 +510,13 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
try {
|
||||
return bin2hex(random_bytes($length));
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (\TypeError $e) {
|
||||
throw OAuthServerException::serverError('An unexpected error has occurred');
|
||||
} catch (\Error $e) {
|
||||
throw OAuthServerException::serverError('An unexpected error has occurred');
|
||||
} catch (\Exception $e) {
|
||||
} catch (TypeError $e) {
|
||||
throw OAuthServerException::serverError('An unexpected error has occurred', $e);
|
||||
} catch (Error $e) {
|
||||
throw OAuthServerException::serverError('An unexpected error has occurred', $e);
|
||||
} catch (Exception $e) {
|
||||
// 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
|
||||
}
|
||||
@@ -511,7 +547,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -519,6 +555,6 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
*/
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
@@ -9,8 +9,10 @@
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
|
||||
@@ -19,12 +21,14 @@ use League\OAuth2\Server\RequestEvent;
|
||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use LogicException;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use stdClass;
|
||||
|
||||
class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
{
|
||||
/**
|
||||
* @var \DateInterval
|
||||
* @var DateInterval
|
||||
*/
|
||||
private $authCodeTTL;
|
||||
|
||||
@@ -36,17 +40,19 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
/**
|
||||
* @param AuthCodeRepositoryInterface $authCodeRepository
|
||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
* @param \DateInterval $authCodeTTL
|
||||
* @param DateInterval $authCodeTTL
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct(
|
||||
AuthCodeRepositoryInterface $authCodeRepository,
|
||||
RefreshTokenRepositoryInterface $refreshTokenRepository,
|
||||
\DateInterval $authCodeTTL
|
||||
DateInterval $authCodeTTL
|
||||
) {
|
||||
$this->setAuthCodeRepository($authCodeRepository);
|
||||
$this->setRefreshTokenRepository($refreshTokenRepository);
|
||||
$this->authCodeTTL = $authCodeTTL;
|
||||
$this->refreshTokenTTL = new \DateInterval('P1M');
|
||||
$this->refreshTokenTTL = new DateInterval('P1M');
|
||||
}
|
||||
|
||||
public function enableCodeExchangeProof()
|
||||
@@ -59,7 +65,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseTypeInterface $responseType
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
* @param DateInterval $accessTokenTTL
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
@@ -68,7 +74,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
\DateInterval $accessTokenTTL
|
||||
DateInterval $accessTokenTTL
|
||||
) {
|
||||
// Validate request
|
||||
$client = $this->validateClient($request);
|
||||
@@ -78,58 +84,25 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
throw OAuthServerException::invalidRequest('code');
|
||||
}
|
||||
|
||||
// Validate the authorization code
|
||||
try {
|
||||
$authCodePayload = json_decode($this->decrypt($encryptedAuthCode));
|
||||
if (time() > $authCodePayload->expire_time) {
|
||||
throw OAuthServerException::invalidRequest('code', 'Authorization code has expired');
|
||||
}
|
||||
|
||||
if ($this->authCodeRepository->isAuthCodeRevoked($authCodePayload->auth_code_id) === true) {
|
||||
throw OAuthServerException::invalidRequest('code', 'Authorization code has been revoked');
|
||||
}
|
||||
$this->validateAuthorizationCode($authCodePayload, $client, $request);
|
||||
|
||||
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->validateScopes($authCodePayload->scopes),
|
||||
$this->getIdentifier(),
|
||||
$client,
|
||||
$authCodePayload->user_id
|
||||
);
|
||||
} catch (\LogicException $e) {
|
||||
throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code');
|
||||
} catch (LogicException $e) {
|
||||
throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code', $e);
|
||||
}
|
||||
|
||||
// Validate code challenge
|
||||
if ($this->enableCodeExchangeProof === true) {
|
||||
$codeVerifier = $this->getRequestParameter('code_verifier', $request, null);
|
||||
|
||||
if ($codeVerifier === null) {
|
||||
throw OAuthServerException::invalidRequest('code_verifier');
|
||||
}
|
||||
@@ -190,6 +163,41 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
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.
|
||||
*
|
||||
@@ -223,7 +231,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
$this->getServerParameter('PHP_AUTH_USER', $request)
|
||||
);
|
||||
|
||||
if (is_null($clientId)) {
|
||||
if ($clientId === null) {
|
||||
throw OAuthServerException::invalidRequest('client_id');
|
||||
}
|
||||
|
||||
@@ -242,25 +250,13 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
|
||||
|
||||
if ($redirectUri !== null) {
|
||||
if (
|
||||
is_string($client->getRedirectUri())
|
||||
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
} elseif (
|
||||
is_array($client->getRedirectUri())
|
||||
&& in_array($redirectUri, $client->getRedirectUri(), true) === false
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
} elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1
|
||||
|| empty($client->getRedirectUri())) {
|
||||
$this->validateRedirectUri($redirectUri, $client, $request);
|
||||
} elseif (empty($client->getRedirectUri()) ||
|
||||
(\is_array($client->getRedirectUri()) && \count($client->getRedirectUri()) !== 1)) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
} else {
|
||||
$redirectUri = is_array($client->getRedirectUri())
|
||||
$redirectUri = \is_array($client->getRedirectUri())
|
||||
? $client->getRedirectUri()[0]
|
||||
: $client->getRedirectUri();
|
||||
}
|
||||
@@ -291,7 +287,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
|
||||
$codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain');
|
||||
|
||||
if (in_array($codeChallengeMethod, ['plain', 'S256'], true) === false) {
|
||||
if (\in_array($codeChallengeMethod, ['plain', 'S256'], true) === false) {
|
||||
throw OAuthServerException::invalidRequest(
|
||||
'code_challenge_method',
|
||||
'Code challenge method must be `plain` or `S256`'
|
||||
@@ -320,14 +316,11 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
||||
{
|
||||
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)
|
||||
? is_array($authorizationRequest->getClient()->getRedirectUri())
|
||||
? $authorizationRequest->getClient()->getRedirectUri()[0]
|
||||
: $authorizationRequest->getClient()->getRedirectUri()
|
||||
: $authorizationRequest->getRedirectUri();
|
||||
$finalRedirectUri = $authorizationRequest->getRedirectUri()
|
||||
?? $this->getClientRedirectUri($authorizationRequest);
|
||||
|
||||
// The user approved the client, redirect them back with an auth code
|
||||
if ($authorizationRequest->isAuthorizationApproved() === true) {
|
||||
@@ -345,7 +338,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
'auth_code_id' => $authCode->getIdentifier(),
|
||||
'scopes' => $authCode->getScopes(),
|
||||
'user_id' => $authCode->getUserIdentifier(),
|
||||
'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'),
|
||||
'expire_time' => (new DateTime())->add($this->authCodeTTL)->format('U'),
|
||||
'code_challenge' => $authorizationRequest->getCodeChallenge(),
|
||||
'code_challenge_method' => $authorizationRequest->getCodeChallengeMethod(),
|
||||
];
|
||||
@@ -379,4 +372,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();
|
||||
}
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use League\OAuth2\Server\RequestEvent;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
@@ -26,7 +27,7 @@ class ClientCredentialsGrant extends AbstractGrant
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
\DateInterval $accessTokenTTL
|
||||
DateInterval $accessTokenTTL
|
||||
) {
|
||||
// Validate request
|
||||
$client = $this->validateClient($request);
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use Defuse\Crypto\Key;
|
||||
use League\Event\EmitterAwareInterface;
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
@@ -29,9 +30,9 @@ interface GrantTypeInterface extends EmitterAwareInterface
|
||||
/**
|
||||
* Set refresh token TTL.
|
||||
*
|
||||
* @param \DateInterval $refreshTokenTTL
|
||||
* @param DateInterval $refreshTokenTTL
|
||||
*/
|
||||
public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL);
|
||||
public function setRefreshTokenTTL(DateInterval $refreshTokenTTL);
|
||||
|
||||
/**
|
||||
* Return the grant identifier that can be used in matching up requests.
|
||||
@@ -45,14 +46,14 @@ interface GrantTypeInterface extends EmitterAwareInterface
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseTypeInterface $responseType
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
* @param DateInterval $accessTokenTTL
|
||||
*
|
||||
* @return ResponseTypeInterface
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
\DateInterval $accessTokenTTL
|
||||
DateInterval $accessTokenTTL
|
||||
);
|
||||
|
||||
/**
|
||||
|
@@ -9,6 +9,8 @@
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
@@ -17,12 +19,13 @@ use League\OAuth2\Server\RequestEvent;
|
||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use LogicException;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
{
|
||||
/**
|
||||
* @var \DateInterval
|
||||
* @var DateInterval
|
||||
*/
|
||||
private $accessTokenTTL;
|
||||
|
||||
@@ -32,33 +35,33 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
private $queryDelimiter;
|
||||
|
||||
/**
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
* @param string $queryDelimiter
|
||||
* @param DateInterval $accessTokenTTL
|
||||
* @param string $queryDelimiter
|
||||
*/
|
||||
public function __construct(\DateInterval $accessTokenTTL, $queryDelimiter = '#')
|
||||
public function __construct(DateInterval $accessTokenTTL, $queryDelimiter = '#')
|
||||
{
|
||||
$this->accessTokenTTL = $accessTokenTTL;
|
||||
$this->queryDelimiter = $queryDelimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
* @throw \LogicException
|
||||
* @throw LogicException
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,16 +87,16 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseTypeInterface $responseType
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
* @param DateInterval $accessTokenTTL
|
||||
*
|
||||
* @return ResponseTypeInterface
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,6 +121,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
$request,
|
||||
$this->getServerParameter('PHP_AUTH_USER', $request)
|
||||
);
|
||||
|
||||
if (is_null($clientId)) {
|
||||
throw OAuthServerException::invalidRequest('client_id');
|
||||
}
|
||||
@@ -135,20 +139,9 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
}
|
||||
|
||||
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
|
||||
|
||||
if ($redirectUri !== null) {
|
||||
if (
|
||||
is_string($client->getRedirectUri())
|
||||
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
} elseif (
|
||||
is_array($client->getRedirectUri())
|
||||
&& in_array($redirectUri, $client->getRedirectUri(), true) === false
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
$this->validateRedirectUri($redirectUri, $client, $request);
|
||||
} elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1
|
||||
|| empty($client->getRedirectUri())) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
@@ -164,13 +157,6 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
$redirectUri
|
||||
);
|
||||
|
||||
// Finalize the requested scopes
|
||||
$finalizedScopes = $this->scopeRepository->finalizeScopes(
|
||||
$scopes,
|
||||
$this->getIdentifier(),
|
||||
$client
|
||||
);
|
||||
|
||||
$stateParameter = $this->getQueryStringParameter('state', $request);
|
||||
|
||||
$authorizationRequest = new AuthorizationRequest();
|
||||
@@ -182,7 +168,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
$authorizationRequest->setState($stateParameter);
|
||||
}
|
||||
|
||||
$authorizationRequest->setScopes($finalizedScopes);
|
||||
$authorizationRequest->setScopes($scopes);
|
||||
|
||||
return $authorizationRequest;
|
||||
}
|
||||
@@ -193,7 +179,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
||||
{
|
||||
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)
|
||||
@@ -204,11 +190,19 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
|
||||
// The user approved the client, redirect them back with an access token
|
||||
if ($authorizationRequest->isAuthorizationApproved() === true) {
|
||||
// Finalize the requested scopes
|
||||
$finalizedScopes = $this->scopeRepository->finalizeScopes(
|
||||
$authorizationRequest->getScopes(),
|
||||
$this->getIdentifier(),
|
||||
$authorizationRequest->getClient(),
|
||||
$authorizationRequest->getUser()->getIdentifier()
|
||||
);
|
||||
|
||||
$accessToken = $this->issueAccessToken(
|
||||
$this->accessTokenTTL,
|
||||
$authorizationRequest->getClient(),
|
||||
$authorizationRequest->getUser()->getIdentifier(),
|
||||
$authorizationRequest->getScopes()
|
||||
$finalizedScopes
|
||||
);
|
||||
|
||||
$response = new RedirectResponse();
|
||||
@@ -218,7 +212,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
[
|
||||
'access_token' => (string) $accessToken->convertToJWT($this->privateKey),
|
||||
'token_type' => 'Bearer',
|
||||
'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(),
|
||||
'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new DateTime())->getTimestamp(),
|
||||
'state' => $authorizationRequest->getState(),
|
||||
],
|
||||
$this->queryDelimiter
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
@@ -36,7 +37,7 @@ class PasswordGrant extends AbstractGrant
|
||||
$this->setUserRepository($userRepository);
|
||||
$this->setRefreshTokenRepository($refreshTokenRepository);
|
||||
|
||||
$this->refreshTokenTTL = new \DateInterval('P1M');
|
||||
$this->refreshTokenTTL = new DateInterval('P1M');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,7 +46,7 @@ class PasswordGrant extends AbstractGrant
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
\DateInterval $accessTokenTTL
|
||||
DateInterval $accessTokenTTL
|
||||
) {
|
||||
// Validate request
|
||||
$client = $this->validateClient($request);
|
||||
|
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use Exception;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||
use League\OAuth2\Server\RequestEvent;
|
||||
@@ -29,7 +31,7 @@ class RefreshTokenGrant extends AbstractGrant
|
||||
{
|
||||
$this->setRefreshTokenRepository($refreshTokenRepository);
|
||||
|
||||
$this->refreshTokenTTL = new \DateInterval('P1M');
|
||||
$this->refreshTokenTTL = new DateInterval('P1M');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,7 +40,7 @@ class RefreshTokenGrant extends AbstractGrant
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
\DateInterval $accessTokenTTL
|
||||
DateInterval $accessTokenTTL
|
||||
) {
|
||||
// Validate request
|
||||
$client = $this->validateClient($request);
|
||||
@@ -94,8 +96,8 @@ class RefreshTokenGrant extends AbstractGrant
|
||||
// Validate refresh token
|
||||
try {
|
||||
$refreshToken = $this->decrypt($encryptedRefreshToken);
|
||||
} catch (\Exception $e) {
|
||||
throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token');
|
||||
} catch (Exception $e) {
|
||||
throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token', $e);
|
||||
}
|
||||
|
||||
$refreshTokenData = json_decode($refreshToken, true);
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace League\OAuth2\Server\Middleware;
|
||||
|
||||
use Exception;
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
@@ -43,7 +44,7 @@ class AuthorizationServerMiddleware
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (\Exception $exception) {
|
||||
} catch (Exception $exception) {
|
||||
return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
|
||||
->generateHttpResponse($response);
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace League\OAuth2\Server\Middleware;
|
||||
|
||||
use Exception;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\ResourceServer;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
@@ -34,7 +35,7 @@ class ResourceServerMiddleware
|
||||
* @param ResponseInterface $response
|
||||
* @param callable $next
|
||||
*
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
|
||||
{
|
||||
@@ -43,7 +44,7 @@ class ResourceServerMiddleware
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (\Exception $exception) {
|
||||
} catch (Exception $exception) {
|
||||
return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
|
||||
->generateHttpResponse($response);
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
@@ -54,7 +54,7 @@ abstract class AbstractResponseType implements ResponseTypeInterface
|
||||
/**
|
||||
* Set the private key
|
||||
*
|
||||
* @param \League\OAuth2\Server\CryptKey $key
|
||||
* @param CryptKey $key
|
||||
*/
|
||||
public function setPrivateKey(CryptKey $key)
|
||||
{
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace League\OAuth2\Server\ResponseTypes;
|
||||
|
||||
use DateTime;
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
@@ -28,7 +29,7 @@ class BearerTokenResponse extends AbstractResponseType
|
||||
|
||||
$responseParams = [
|
||||
'token_type' => 'Bearer',
|
||||
'expires_in' => $expireDateTime - (new \DateTime())->getTimestamp(),
|
||||
'expires_in' => $expireDateTime - (new DateTime())->getTimestamp(),
|
||||
'access_token' => (string) $jwtAccessToken,
|
||||
];
|
||||
|
||||
|
@@ -3,6 +3,7 @@
|
||||
namespace LeagueTests;
|
||||
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Grant\AuthCodeGrant;
|
||||
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
|
||||
@@ -109,6 +110,58 @@ class AuthorizationServerTest extends TestCase
|
||||
$this->assertInstanceOf(BearerTokenResponse::class, $method->invoke($server));
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||
|
40
tests/AuthorizationValidators/BearerTokenValidatorTest.php
Normal file
40
tests/AuthorizationValidators/BearerTokenValidatorTest.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace LeagueTests\AuthorizationValidators;
|
||||
|
||||
use Lcobucci\JWT\Builder;
|
||||
use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator;
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Zend\Diactoros\ServerRequest;
|
||||
|
||||
class BearerTokenValidatorTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedException League\OAuth2\Server\Exception\OAuthServerException
|
||||
* @expectedExceptionCode 9
|
||||
*/
|
||||
public function testThrowExceptionWhenAccessTokenIsNotSigned()
|
||||
{
|
||||
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||
|
||||
$bearerTokenValidator = new BearerTokenValidator($accessTokenRepositoryMock);
|
||||
$bearerTokenValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
|
||||
|
||||
$unsignedJwt = (new Builder())
|
||||
->setAudience('client-id')
|
||||
->setId('token-id', true)
|
||||
->setIssuedAt(time())
|
||||
->setNotBefore(time())
|
||||
->setExpiration(time())
|
||||
->setSubject('user-id')
|
||||
->set('scopes', 'scope1 scope2 scope3 scope4')
|
||||
->getToken();
|
||||
|
||||
$request = new ServerRequest();
|
||||
$request = $request->withHeader('authorization', sprintf('Bearer %s', $unsignedJwt));
|
||||
|
||||
$bearerTokenValidator->validateAuthorization($request);
|
||||
}
|
||||
}
|
39
tests/Exception/OAuthServerExceptionTest.php
Normal file
39
tests/Exception/OAuthServerExceptionTest.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace LeagueTests\Exception;
|
||||
|
||||
use Exception;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class OAuthServerExceptionTest extends TestCase
|
||||
{
|
||||
public function testHasRedirect()
|
||||
{
|
||||
$exceptionWithRedirect = OAuthServerException::accessDenied('some hint', 'https://example.com/error');
|
||||
|
||||
$this->assertTrue($exceptionWithRedirect->hasRedirect());
|
||||
}
|
||||
|
||||
public function testDoesNotHaveRedirect()
|
||||
{
|
||||
$exceptionWithoutRedirect = OAuthServerException::accessDenied('Some hint');
|
||||
|
||||
$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());
|
||||
}
|
||||
}
|
@@ -94,7 +94,6 @@ class ImplicitGrantTest extends TestCase
|
||||
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||
$scopeEntity = new ScopeEntity();
|
||||
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
|
||||
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||
|
||||
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||
$grant->setClientRepository($clientRepositoryMock);
|
||||
@@ -129,7 +128,6 @@ class ImplicitGrantTest extends TestCase
|
||||
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||
$scopeEntity = new ScopeEntity();
|
||||
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
|
||||
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||
|
||||
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||
$grant->setClientRepository($clientRepositoryMock);
|
||||
@@ -286,9 +284,14 @@ class ImplicitGrantTest extends TestCase
|
||||
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
||||
|
||||
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||
$scopeEntity = new ScopeEntity();
|
||||
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||
|
||||
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||
$grant->setScopeRepository($scopeRepositoryMock);
|
||||
|
||||
$this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
|
||||
}
|
||||
@@ -309,9 +312,14 @@ class ImplicitGrantTest extends TestCase
|
||||
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
||||
|
||||
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||
$scopeEntity = new ScopeEntity();
|
||||
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||
|
||||
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||
$grant->setScopeRepository($scopeRepositoryMock);
|
||||
|
||||
$grant->completeAuthorizationRequest($authRequest);
|
||||
}
|
||||
@@ -330,9 +338,14 @@ class ImplicitGrantTest extends TestCase
|
||||
$accessTokenRepositoryMock->expects($this->at(0))->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
|
||||
$accessTokenRepositoryMock->expects($this->at(1))->method('persistNewAccessToken')->willReturnSelf();
|
||||
|
||||
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||
$scopeEntity = new ScopeEntity();
|
||||
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||
|
||||
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||
$grant->setScopeRepository($scopeRepositoryMock);
|
||||
|
||||
$this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
|
||||
}
|
||||
@@ -354,9 +367,14 @@ class ImplicitGrantTest extends TestCase
|
||||
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||
$accessTokenRepositoryMock->method('persistNewAccessToken')->willThrowException(OAuthServerException::serverError('something bad happened'));
|
||||
|
||||
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||
$scopeEntity = new ScopeEntity();
|
||||
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||
|
||||
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||
$grant->setScopeRepository($scopeRepositoryMock);
|
||||
|
||||
$grant->completeAuthorizationRequest($authRequest);
|
||||
}
|
||||
@@ -378,9 +396,14 @@ class ImplicitGrantTest extends TestCase
|
||||
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||
$accessTokenRepositoryMock->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
|
||||
|
||||
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||
$scopeEntity = new ScopeEntity();
|
||||
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||
|
||||
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||
$grant->setScopeRepository($scopeRepositoryMock);
|
||||
|
||||
$grant->completeAuthorizationRequest($authRequest);
|
||||
}
|
||||
|
Reference in New Issue
Block a user