diff --git a/.github/workflows/backwards-compatibility.yml b/.github/workflows/backwards-compatibility.yml
index cc1971dc9..f9da8f578 100644
--- a/.github/workflows/backwards-compatibility.yml
+++ b/.github/workflows/backwards-compatibility.yml
@@ -11,7 +11,7 @@ jobs:
steps:
- name: "Checkout"
- uses: "actions/checkout@v2"
+ uses: "actions/checkout@v4"
with:
fetch-depth: 0
- name: Fix git safe.directory in container
@@ -19,4 +19,4 @@ jobs:
- name: "Backwards Compatibility Check"
uses: docker://nyholm/roave-bc-check-ga
with:
- args: --from=${{ github.event.pull_request.base.sha }}
\ No newline at end of file
+ args: --from=${{ github.event.pull_request.base.sha }}
diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml
new file mode 100644
index 000000000..e5c9be702
--- /dev/null
+++ b/.github/workflows/coding-standards.yml
@@ -0,0 +1,36 @@
+name: Coding Standards
+
+on:
+ pull_request:
+ push:
+
+jobs:
+ coding-standards:
+ name: Coding Standards
+
+ runs-on: ${{ matrix.operating-system }}
+
+ strategy:
+ matrix:
+ php-version:
+ - 8.3
+ operating-system:
+ - ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ coverage: none
+ php-version: ${{ matrix.php-version }}
+ ini-values: memory_limit=-1
+ tools: composer:v2, cs2pr
+
+ - name: Install Dependencies
+ run: composer update --prefer-stable --prefer-dist --no-interaction --no-progress
+
+ - name: Run Codesniffer
+ run: vendor/bin/phpcs
diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml
new file mode 100644
index 000000000..c6c01d4ea
--- /dev/null
+++ b/.github/workflows/static-analysis.yml
@@ -0,0 +1,37 @@
+name: Static Analysis
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ static-analysis:
+ name: Static Analysis
+
+ runs-on: ${{ matrix.operating-system }}
+
+ strategy:
+ matrix:
+ php-version: [8.1, 8.2, 8.3]
+ composer-stability: [prefer-lowest, prefer-stable]
+ operating-system:
+ - ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ coverage: none
+ php-version: ${{ matrix.php-version }}
+ ini-values: memory_limit=-1
+ tools: composer:v2, cs2pr
+
+ - name: Install Dependencies
+ run: composer update --${{ matrix.composer-stability }} --prefer-dist --no-interaction --no-progress
+
+ - name: Run Static Analysis
+ run: vendor/bin/phpstan analyse
+
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 6b82845c8..7090fce13 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -8,19 +8,20 @@ on:
jobs:
tests:
- runs-on: ubuntu-latest
-
strategy:
fail-fast: false
matrix:
- php: [8.0, 8.1, 8.2]
+ php: [8.1, 8.2, 8.3]
+ os: [ubuntu-22.04]
stability: [prefer-lowest, prefer-stable]
+ runs-on: ${{ matrix.os }}
+
name: PHP ${{ matrix.php }} - ${{ matrix.stability }}
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -40,7 +41,7 @@ jobs:
composer global require scrutinizer/ocular
- name: Execute tests
- run: vendor/bin/phpunit --verbose --coverage-clover=coverage.clover
+ run: vendor/bin/phpunit --coverage-clover=coverage.clover
- name: Code coverage
if: ${{ github.ref == 'refs/heads/master' && github.repository == 'thephpleague/oauth2-server' }}
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
index d0cb3f2e6..65631698c 100644
--- a/.scrutinizer.yml
+++ b/.scrutinizer.yml
@@ -1,7 +1,7 @@
build:
environment:
php:
- version: 7.4
+ version: 8.3.3
nodes:
analysis:
tests:
diff --git a/.styleci.yml b/.styleci.yml
index 1caa7b894..742b2ce1f 100644
--- a/.styleci.yml
+++ b/.styleci.yml
@@ -1,30 +1,21 @@
-preset: psr2
+preset: psr12
risky: true
enabled:
- - binary_operator_spaces
- blank_line_before_return
- - concat_with_spaces
- fully_qualified_strict_types
- - function_typehint_space
- hash_to_slash_comment
- include
- - lowercase_cast
- method_separation
- native_function_casing
- - native_function_invocation
- - no_blank_lines_after_class_opening
- no_blank_lines_between_uses
- no_duplicate_semicolons
- - no_leading_import_slash
- - no_leading_namespace_whitespace
- no_multiline_whitespace_before_semicolons
- no_php4_constructor
- no_short_bool_cast
- no_singleline_whitespace_before_semicolons
- no_trailing_comma_in_singleline_array
- - no_unreachable_default_argument_value
- no_unused_imports
- no_whitespace_before_comma_in_array
- ordered_imports
@@ -43,13 +34,9 @@ enabled:
- phpdoc_var_without_name
- print_to_echo
- short_array_syntax
- - short_scalar_cast
- single_quote
- spaces_cast
- standardize_not_equal
- - ternary_operator_spaces
- trailing_comma_in_multiline_array
- trim_array_spaces
- - unary_operator_spaces
- whitespace_after_comma_in_array
- - whitespacy_lines
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4d502a650..2bd3ea6c7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,41 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
+## [9.0.0-RC1] - released 2024-03-27
+### Added
+- Device Authorization Grant added (PR #1074)
+- GrantTypeInterface has a new function, `revokeRefreshTokens()` for enabling or disabling refresh tokens after use (PR #1375)
+- A CryptKeyInterface to allow developers to change the CryptKey implementation with greater ease (PR #1044)
+- The authorization server can now finalize scopes when a client uses a refresh token (PR #1094)
+- An AuthorizationRequestInterface to make it easier to extend the AuthorizationRequest (PR #1110)
+- Added function `getKeyContents()` to the `CryptKeyInterface` (PR #1375)
+
+### Fixed
+- If a refresh token has expired, been revoked, cannot be decrypted, or does not belong to the correct client, the server will now issue an `invalid_grant` error and a HTTP 400 response. In previous versions the server incorrectly issued an `invalid_request` and HTTP 401 response (PR #1042) (PR #1082)
+
+### Changed
+- Authorization Request objects are now created through the factory method, `createAuthorizationRequest()` (PR #1111)
+- Changed parameters for `finalizeScopes()` to allow a reference to an auth code ID (PR #1112)
+- AccessTokenEntityInterface now requires the implementation of `toString()` instead of the magic method `__toString()` (PR #1395)
+
+### Removed
+- Removed message property from OAuthException HTTP response. Now just use error_description as per the OAuth 2 spec (PR #1375)
+
+## [8.5.4] - released 2023-08-25
+### Added
+- Support for league/uri ^7.0 (PR #1367)
+
+## [8.5.3] - released 2023-07-06
+### Security
+- If a key string is provided to the CryptKey constructor with an invalid
+passphrase, the LogicException message generated will expose the given key.
+The key is no longer leaked via this exception (PR #1353)
+
+## [8.5.2] - released 2023-06-16
+### Changed
+- Bumped the versions for laminas/diactoros and psr/http-message to support
+PSR-7 v2.0 (PR #1339)
+
## [8.5.1] - released 2023-04-04
### Fixed
- Fixed PHP version constraints and lcobucci/clock version constraint to support PHP 8.1 (PR #1336)
@@ -104,7 +139,6 @@ a PKCE downgrade attack (PR #1326)
- If you provide a valid redirect_uri with the auth code grant and an invalid scope, the server will use the given
redirect_uri instead of the default client redirect uri (PR #1126)
-
## [8.1.0] - released 2020-04-29
### Added
@@ -591,7 +625,11 @@ Version 5 is a complete code rewrite.
- First major release
-[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.5.1...HEAD
+[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/9.0.0-RC1...HEAD
+[9.0.0-RC1]: https://github.com/thephpleague/oauth2-server/compare/8.5.4...9.0.0-RC1
+[8.5.4]: https://github.com/thephpleague/oauth2-server/compare/8.5.3...8.5.4
+[8.5.3]: https://github.com/thephpleague/oauth2-server/compare/8.5.2...8.5.3
+[8.5.2]: https://github.com/thephpleague/oauth2-server/compare/8.5.1...8.5.2
[8.5.1]: https://github.com/thephpleague/oauth2-server/compare/8.5.0...8.5.1
[8.5.0]: https://github.com/thephpleague/oauth2-server/compare/8.4.1...8.5.0
[8.4.1]: https://github.com/thephpleague/oauth2-server/compare/8.4.0...8.4.1
diff --git a/README.md b/README.md
index 84a8584a5..091c84f95 100644
--- a/README.md
+++ b/README.md
@@ -12,10 +12,11 @@
Out of the box it supports the following grants:
* Authorization code grant
-* Implicit grant
* Client credentials grant
-* Resource owner password credentials grant
+* Device authorization grant
+* Implicit grant
* Refresh grant
+* Resource owner password credentials grant
The following RFCs are implemented:
@@ -23,6 +24,7 @@ The following RFCs are implemented:
* [RFC6750 " The OAuth 2.0 Authorization Framework: Bearer Token Usage"](https://tools.ietf.org/html/rfc6750)
* [RFC7519 "JSON Web Token (JWT)"](https://tools.ietf.org/html/rfc7519)
* [RFC7636 "Proof Key for Code Exchange by OAuth Public Clients"](https://tools.ietf.org/html/rfc7636)
+* [RFC8628 "OAuth 2.0 Device Authorization Grant](https://tools.ietf.org/html/rfc8628)
This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](https://twitter.com/alexbilbie).
@@ -30,9 +32,9 @@ This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](ht
The latest version of this package supports the following versions of PHP:
-* PHP 8.0
* PHP 8.1
* PHP 8.2
+* PHP 8.3
The `openssl` and `json` extensions are also required.
diff --git a/composer.json b/composer.json
index 9699116ee..9376506a2 100644
--- a/composer.json
+++ b/composer.json
@@ -4,22 +4,29 @@
"homepage": "https://oauth2.thephpleague.com/",
"license": "MIT",
"require": {
- "php": "^8.0",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"ext-openssl": "*",
- "league/event": "^2.2",
- "league/uri": "^6.7",
- "lcobucci/jwt": "^4.3 || ^5.0",
- "psr/http-message": "^1.0.1",
- "defuse/php-encryption": "^2.3",
+ "league/event": "^3.0",
+ "league/uri": "^7.0",
+ "lcobucci/jwt": "^5.0",
+ "psr/http-message": "^2.0",
+ "defuse/php-encryption": "^2.4",
"ext-json": "*",
- "lcobucci/clock": "^2.2 || ^3.0"
+ "lcobucci/clock": "^2.3 || ^3.0",
+ "psr/http-server-middleware": "^1.0"
},
"require-dev": {
- "phpunit/phpunit": "^9.6.6",
- "laminas/laminas-diactoros": "^2.24.0",
- "phpstan/phpstan": "^0.12.57",
- "phpstan/phpstan-phpunit": "^0.12.16",
- "roave/security-advisories": "dev-master"
+ "phpunit/phpunit": "^9.6.15",
+ "laminas/laminas-diactoros": "^3.3.0",
+ "phpstan/phpstan": "^1.10.55",
+ "phpstan/phpstan-phpunit": "^1.3.15",
+ "roave/security-advisories": "dev-master",
+ "phpstan/extension-installer": "^1.3.1",
+ "phpstan/phpstan-deprecation-rules": "^1.1.4",
+ "phpstan/phpstan-strict-rules": "^1.5.2",
+ "slevomat/coding-standard": "^8.14.1",
+ "php-parallel-lint/php-parallel-lint": "^1.3.2",
+ "squizlabs/php_codesniffer": "^3.8"
},
"repositories": [
{
@@ -70,5 +77,12 @@
"psr-4": {
"LeagueTests\\": "tests/"
}
+ },
+ "config": {
+ "allow-plugins": {
+ "ocramius/package-versions": true,
+ "phpstan/extension-installer": true,
+ "dealerdirect/phpcodesniffer-composer-installer": false
+ }
}
}
diff --git a/examples/README.md b/examples/README.md
index d8898b46c..8213e61c0 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -51,3 +51,32 @@ curl -X "POST" "http://localhost:4444/refresh_token.php/access_token" \
--data-urlencode "client_secret=abc123" \
--data-urlencode "refresh_token={{REFRESH_TOKEN}}"
```
+
+## Testing the device authorization grant example
+
+Send the following cURL request. This will return a device code which can be exchanged for an access token.
+
+```
+curl -X "POST" "http://localhost:4444/device_code.php/device_authorization" \
+ -H "Content-Type: application/x-www-form-urlencoded" \
+ -H "Accept: 1.0" \
+ --data-urlencode "client_id=myawesomeapp" \
+ --data-urlencode "client_secret=abc123" \
+ --data-urlencode "scope=basic email"
+```
+
+We have set up the example so that a user ID is already associated with the device code. In a production application you
+would implement an authorization view to allow a user to authorize the device.
+
+Issue the following cURL request to exchange your device code for an access token. Replace `{{DEVICE_CODE}}` with the
+device code returned from your first cURL post:
+
+```
+curl -X "POST" "http://localhost:4444/device_code.php/access_token" \
+ -H "Content-Type: application/x-www-form-urlencoded" \
+ -H "Accept: 1.0" \
+ --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
+ --data-urlencode "device_code={{DEVICE_CODE}}" \
+ --data-urlencode "client_id=myawesomeapp" \
+ --data-urlencode "client_secret=abc123"
+```
\ No newline at end of file
diff --git a/examples/composer.json b/examples/composer.json
index 087caab72..7d2000be5 100644
--- a/examples/composer.json
+++ b/examples/composer.json
@@ -3,7 +3,7 @@
"slim/slim": "^3.12.3"
},
"require-dev": {
- "league/event": "^2.2",
+ "league/event": "^3.0",
"lcobucci/jwt": "^3.4.6 || ^4.0.4",
"psr/http-message": "^1.0.1",
"defuse/php-encryption": "^2.2.1",
diff --git a/examples/composer.lock b/examples/composer.lock
index b937aac99..58f1c60cb 100644
--- a/examples/composer.lock
+++ b/examples/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "37792b4ef2ef1a2cf73184749dd182d6",
+ "content-hash": "ac8c2c0c3717f72036b55ab34445a89d",
"packages": [
{
"name": "nikic/fast-route",
@@ -28,12 +28,12 @@
},
"type": "library",
"autoload": {
- "psr-4": {
- "FastRoute\\": "src/"
- },
"files": [
"src/functions.php"
- ]
+ ],
+ "psr-4": {
+ "FastRoute\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -50,33 +50,37 @@
"router",
"routing"
],
+ "support": {
+ "issues": "https://github.com/nikic/FastRoute/issues",
+ "source": "https://github.com/nikic/FastRoute/tree/master"
+ },
"time": "2018-02-13T20:26:39+00:00"
},
{
"name": "pimple/pimple",
- "version": "v3.3.1",
+ "version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/silexphp/Pimple.git",
- "reference": "21e45061c3429b1e06233475cc0e1f6fc774d5b0"
+ "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/silexphp/Pimple/zipball/21e45061c3429b1e06233475cc0e1f6fc774d5b0",
- "reference": "21e45061c3429b1e06233475cc0e1f6fc774d5b0",
+ "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a94b3a4db7fb774b3d78dad2315ddc07629e1bed",
+ "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
- "psr/container": "^1.0"
+ "psr/container": "^1.1 || ^2.0"
},
"require-dev": {
- "symfony/phpunit-bridge": "^5.0"
+ "symfony/phpunit-bridge": "^5.4@dev"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.3.x-dev"
+ "dev-master": "3.4.x-dev"
}
},
"autoload": {
@@ -100,31 +104,29 @@
"container",
"dependency injection"
],
- "time": "2020-11-24T20:35:42+00:00"
+ "support": {
+ "source": "https://github.com/silexphp/Pimple/tree/v3.5.0"
+ },
+ "time": "2021-10-28T11:13:42+00:00"
},
{
"name": "psr/container",
- "version": "1.0.0",
+ "version": "1.1.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
- "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
- "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
"shasum": ""
},
"require": {
- "php": ">=5.3.0"
+ "php": ">=7.4.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
- },
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
@@ -137,7 +139,7 @@
"authors": [
{
"name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "homepage": "https://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
@@ -149,29 +151,33 @@
"container-interop",
"psr"
],
- "time": "2017-02-14T16:28:37+00:00"
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/1.1.2"
+ },
+ "time": "2021-11-05T16:50:12+00:00"
},
{
"name": "psr/http-message",
- "version": "1.0.1",
+ "version": "1.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
- "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+ "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
- "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+ "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
"shasum": ""
},
"require": {
- "php": ">=5.3.0"
+ "php": "^7.2 || ^8.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "1.1.x-dev"
}
},
"autoload": {
@@ -200,22 +206,22 @@
"response"
],
"support": {
- "source": "https://github.com/php-fig/http-message/tree/master"
+ "source": "https://github.com/php-fig/http-message/tree/1.1"
},
- "time": "2016-08-06T14:39:51+00:00"
+ "time": "2023-04-04T09:50:52+00:00"
},
{
"name": "slim/slim",
- "version": "3.12.3",
+ "version": "3.12.5",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Slim.git",
- "reference": "1c9318a84ffb890900901136d620b4f03a59da38"
+ "reference": "565632b2d9b64ecedf89546edbbf4f3648089f0c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/slimphp/Slim/zipball/1c9318a84ffb890900901136d620b4f03a59da38",
- "reference": "1c9318a84ffb890900901136d620b4f03a59da38",
+ "url": "https://api.github.com/repos/slimphp/Slim/zipball/565632b2d9b64ecedf89546edbbf4f3648089f0c",
+ "reference": "565632b2d9b64ecedf89546edbbf4f3648089f0c",
"shasum": ""
},
"require": {
@@ -233,7 +239,7 @@
},
"require-dev": {
"phpunit/phpunit": "^4.0",
- "squizlabs/php_codesniffer": "^2.5"
+ "squizlabs/php_codesniffer": "^3.6.0"
},
"type": "library",
"autoload": {
@@ -275,32 +281,46 @@
"micro",
"router"
],
- "time": "2019-11-28T17:40:33+00:00"
+ "support": {
+ "issues": "https://github.com/slimphp/Slim/issues",
+ "source": "https://github.com/slimphp/Slim/tree/3.12.5"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/slimphp",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/slim/slim",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-07-23T04:32:51+00:00"
}
],
"packages-dev": [
{
"name": "defuse/php-encryption",
- "version": "v2.2.1",
+ "version": "v2.4.0",
"source": {
"type": "git",
"url": "https://github.com/defuse/php-encryption.git",
- "reference": "0f407c43b953d571421e0020ba92082ed5fb7620"
+ "reference": "f53396c2d34225064647a05ca76c1da9d99e5828"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/defuse/php-encryption/zipball/0f407c43b953d571421e0020ba92082ed5fb7620",
- "reference": "0f407c43b953d571421e0020ba92082ed5fb7620",
+ "url": "https://api.github.com/repos/defuse/php-encryption/zipball/f53396c2d34225064647a05ca76c1da9d99e5828",
+ "reference": "f53396c2d34225064647a05ca76c1da9d99e5828",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"paragonie/random_compat": ">= 2",
- "php": ">=5.4.0"
+ "php": ">=5.6.0"
},
"require-dev": {
- "nikic/php-parser": "^2.0|^3.0|^4.0",
- "phpunit/phpunit": "^4|^5"
+ "phpunit/phpunit": "^5|^6|^7|^8|^9|^10",
+ "yoast/phpunit-polyfills": "^2.0.0"
},
"bin": [
"bin/generate-defuse-key"
@@ -340,29 +360,32 @@
"security",
"symmetric key cryptography"
],
- "time": "2018-07-24T23:27:56+00:00"
+ "support": {
+ "issues": "https://github.com/defuse/php-encryption/issues",
+ "source": "https://github.com/defuse/php-encryption/tree/v2.4.0"
+ },
+ "time": "2023-06-19T06:10:36+00:00"
},
{
"name": "laminas/laminas-diactoros",
- "version": "2.11.1",
+ "version": "2.26.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-diactoros.git",
- "reference": "25b11d422c2e5dad868f68619888763b30f91e2d"
+ "reference": "6584d44eb8e477e89d453313b858daac6183cddc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/25b11d422c2e5dad868f68619888763b30f91e2d",
- "reference": "25b11d422c2e5dad868f68619888763b30f91e2d",
+ "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/6584d44eb8e477e89d453313b858daac6183cddc",
+ "reference": "6584d44eb8e477e89d453313b858daac6183cddc",
"shasum": ""
},
"require": {
- "php": "^7.3 || ~8.0.0 || ~8.1.0",
+ "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0",
"psr/http-factory": "^1.0",
- "psr/http-message": "^1.0"
+ "psr/http-message": "^1.1"
},
"conflict": {
- "phpspec/prophecy": "<1.9.0",
"zendframework/zend-diactoros": "*"
},
"provide": {
@@ -374,13 +397,12 @@
"ext-dom": "*",
"ext-gd": "*",
"ext-libxml": "*",
- "http-interop/http-factory-tests": "^0.8.0",
- "laminas/laminas-coding-standard": "~1.0.0",
- "php-http/psr7-integration-tests": "^1.1",
- "phpspec/prophecy-phpunit": "^2.0",
- "phpunit/phpunit": "^9.1",
- "psalm/plugin-phpunit": "^0.14.0",
- "vimeo/psalm": "^4.3"
+ "http-interop/http-factory-tests": "^0.9.0",
+ "laminas/laminas-coding-standard": "^2.5",
+ "php-http/psr7-integration-tests": "^1.2",
+ "phpunit/phpunit": "^9.5.28",
+ "psalm/plugin-phpunit": "^0.18.4",
+ "vimeo/psalm": "^5.6"
},
"type": "library",
"extra": {
@@ -439,35 +461,38 @@
"type": "community_bridge"
}
],
- "time": "2022-06-28T21:07:29+00:00"
+ "time": "2023-10-29T16:17:44+00:00"
},
{
"name": "lcobucci/clock",
- "version": "2.0.0",
+ "version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/clock.git",
- "reference": "353d83fe2e6ae95745b16b3d911813df6a05bfb3"
+ "reference": "039ef98c6b57b101d10bd11d8fdfda12cbd996dc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/lcobucci/clock/zipball/353d83fe2e6ae95745b16b3d911813df6a05bfb3",
- "reference": "353d83fe2e6ae95745b16b3d911813df6a05bfb3",
+ "url": "https://api.github.com/repos/lcobucci/clock/zipball/039ef98c6b57b101d10bd11d8fdfda12cbd996dc",
+ "reference": "039ef98c6b57b101d10bd11d8fdfda12cbd996dc",
"shasum": ""
},
"require": {
- "php": "^7.4 || ^8.0"
+ "php": "~8.1.0 || ~8.2.0",
+ "psr/clock": "^1.0"
+ },
+ "provide": {
+ "psr/clock-implementation": "1.0"
},
"require-dev": {
- "infection/infection": "^0.17",
- "lcobucci/coding-standard": "^6.0",
- "phpstan/extension-installer": "^1.0",
- "phpstan/phpstan": "^0.12",
- "phpstan/phpstan-deprecation-rules": "^0.12",
- "phpstan/phpstan-phpunit": "^0.12",
- "phpstan/phpstan-strict-rules": "^0.12",
- "phpunit/php-code-coverage": "9.1.4",
- "phpunit/phpunit": "9.3.7"
+ "infection/infection": "^0.26",
+ "lcobucci/coding-standard": "^9.0",
+ "phpstan/extension-installer": "^1.2",
+ "phpstan/phpstan": "^1.9.4",
+ "phpstan/phpstan-deprecation-rules": "^1.1.1",
+ "phpstan/phpstan-phpunit": "^1.3.2",
+ "phpstan/phpstan-strict-rules": "^1.4.4",
+ "phpunit/phpunit": "^9.5.27"
},
"type": "library",
"autoload": {
@@ -488,7 +513,7 @@
"description": "Yet another clock abstraction",
"support": {
"issues": "https://github.com/lcobucci/clock/issues",
- "source": "https://github.com/lcobucci/clock/tree/2.0.x"
+ "source": "https://github.com/lcobucci/clock/tree/3.0.0"
},
"funding": [
{
@@ -500,47 +525,45 @@
"type": "patreon"
}
],
- "time": "2020-08-27T18:56:02+00:00"
+ "time": "2022-12-19T15:00:24+00:00"
},
{
"name": "lcobucci/jwt",
- "version": "4.0.4",
+ "version": "4.3.0",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/jwt.git",
- "reference": "55564265fddf810504110bd68ca311932324b0e9"
+ "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/lcobucci/jwt/zipball/55564265fddf810504110bd68ca311932324b0e9",
- "reference": "55564265fddf810504110bd68ca311932324b0e9",
+ "url": "https://api.github.com/repos/lcobucci/jwt/zipball/4d7de2fe0d51a96418c0d04004986e410e87f6b4",
+ "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4",
"shasum": ""
},
"require": {
+ "ext-hash": "*",
+ "ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
- "lcobucci/clock": "^2.0",
+ "ext-sodium": "*",
+ "lcobucci/clock": "^2.0 || ^3.0",
"php": "^7.4 || ^8.0"
},
"require-dev": {
- "infection/infection": "^0.20",
+ "infection/infection": "^0.21",
"lcobucci/coding-standard": "^6.0",
- "mikey179/vfsstream": "^1.6",
- "phpbench/phpbench": "^0.17",
+ "mikey179/vfsstream": "^1.6.7",
+ "phpbench/phpbench": "^1.2",
"phpstan/extension-installer": "^1.0",
- "phpstan/phpstan": "^0.12",
- "phpstan/phpstan-deprecation-rules": "^0.12",
- "phpstan/phpstan-phpunit": "^0.12",
- "phpstan/phpstan-strict-rules": "^0.12",
+ "phpstan/phpstan": "^1.4",
+ "phpstan/phpstan-deprecation-rules": "^1.0",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.0",
"phpunit/php-invoker": "^3.1",
- "phpunit/phpunit": "^9.4"
+ "phpunit/phpunit": "^9.5"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.0-dev"
- }
- },
"autoload": {
"psr-4": {
"Lcobucci\\JWT\\": "src"
@@ -564,7 +587,7 @@
],
"support": {
"issues": "https://github.com/lcobucci/jwt/issues",
- "source": "https://github.com/lcobucci/jwt/tree/4.0.4"
+ "source": "https://github.com/lcobucci/jwt/tree/4.3.0"
},
"funding": [
{
@@ -576,33 +599,38 @@
"type": "patreon"
}
],
- "time": "2021-09-28T19:18:28+00:00"
+ "time": "2023-01-02T13:28:00+00:00"
},
{
"name": "league/event",
- "version": "2.2.0",
+ "version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/event.git",
- "reference": "d2cc124cf9a3fab2bb4ff963307f60361ce4d119"
+ "reference": "221867a61087ee265ca07bd39aa757879afca820"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/event/zipball/d2cc124cf9a3fab2bb4ff963307f60361ce4d119",
- "reference": "d2cc124cf9a3fab2bb4ff963307f60361ce4d119",
+ "url": "https://api.github.com/repos/thephpleague/event/zipball/221867a61087ee265ca07bd39aa757879afca820",
+ "reference": "221867a61087ee265ca07bd39aa757879afca820",
"shasum": ""
},
"require": {
- "php": ">=5.4.0"
+ "php": ">=7.2.0",
+ "psr/event-dispatcher": "^1.0"
+ },
+ "provide": {
+ "psr/event-dispatcher-implementation": "1.0"
},
"require-dev": {
- "henrikbjorn/phpspec-code-coverage": "~1.0.1",
- "phpspec/phpspec": "^2.2"
+ "friendsofphp/php-cs-fixer": "^2.16",
+ "phpstan/phpstan": "^0.12.45",
+ "phpunit/phpunit": "^8.5"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.2-dev"
+ "dev-master": "3.0-dev"
}
},
"autoload": {
@@ -626,7 +654,11 @@
"event",
"listener"
],
- "time": "2018-11-26T11:52:41+00:00"
+ "support": {
+ "issues": "https://github.com/thephpleague/event/issues",
+ "source": "https://github.com/thephpleague/event/tree/3.0.2"
+ },
+ "time": "2022-10-29T09:31:25+00:00"
},
{
"name": "paragonie/random_compat",
@@ -671,25 +703,128 @@
"pseudorandom",
"random"
],
+ "support": {
+ "email": "info@paragonie.com",
+ "issues": "https://github.com/paragonie/random_compat/issues",
+ "source": "https://github.com/paragonie/random_compat"
+ },
"time": "2020-10-15T08:29:30+00:00"
},
+ {
+ "name": "psr/clock",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/clock.git",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Clock\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for reading the clock.",
+ "homepage": "https://github.com/php-fig/clock",
+ "keywords": [
+ "clock",
+ "now",
+ "psr",
+ "psr-20",
+ "time"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/clock/issues",
+ "source": "https://github.com/php-fig/clock/tree/1.0.0"
+ },
+ "time": "2022-11-25T14:36:26+00:00"
+ },
+ {
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Standard interfaces for event handling.",
+ "keywords": [
+ "events",
+ "psr",
+ "psr-14"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+ },
+ "time": "2019-01-08T18:20:26+00:00"
+ },
{
"name": "psr/http-factory",
- "version": "1.0.1",
+ "version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
- "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
+ "reference": "e616d01114759c4c489f93b099585439f795fe35"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
- "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
+ "reference": "e616d01114759c4c489f93b099585439f795fe35",
"shasum": ""
},
"require": {
"php": ">=7.0.0",
- "psr/http-message": "^1.0"
+ "psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
@@ -709,7 +844,7 @@
"authors": [
{
"name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "homepage": "https://www.php-fig.org/"
}
],
"description": "Common interfaces for PSR-7 HTTP message factories",
@@ -724,9 +859,9 @@
"response"
],
"support": {
- "source": "https://github.com/php-fig/http-factory/tree/master"
+ "source": "https://github.com/php-fig/http-factory/tree/1.0.2"
},
- "time": "2019-04-30T12:38:16+00:00"
+ "time": "2023-04-10T20:10:41+00:00"
}
],
"aliases": [],
diff --git a/examples/public/api.php b/examples/public/api.php
index 1fa9d82dc..8bd5cb6d4 100644
--- a/examples/public/api.php
+++ b/examples/public/api.php
@@ -1,13 +1,16 @@
function () {
@@ -22,7 +25,7 @@
// Add the resource server middleware which will intercept and validate requests
$app->add(
- new \League\OAuth2\Server\Middleware\ResourceServerMiddleware(
+ new ResourceServerMiddleware(
$app->getContainer()->get(ResourceServer::class)
)
);
@@ -30,7 +33,7 @@
// An example endpoint secured with OAuth 2.0
$app->get(
'/users',
- function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
+ function (ServerRequestInterface $request, ResponseInterface $response) {
$users = [
[
'id' => 123,
@@ -49,23 +52,23 @@ function (ServerRequestInterface $request, ResponseInterface $response) use ($ap
],
];
- $totalUsers = \count($users);
+ $totalUsers = count($users);
// If the access token doesn't have the `basic` scope hide users' names
- if (\in_array('basic', $request->getAttribute('oauth_scopes')) === false) {
+ if (in_array('basic', $request->getAttribute('oauth_scopes')) === false) {
for ($i = 0; $i < $totalUsers; $i++) {
unset($users[$i]['name']);
}
}
// If the access token doesn't have the `email` scope hide users' email addresses
- if (\in_array('email', $request->getAttribute('oauth_scopes')) === false) {
+ if (in_array('email', $request->getAttribute('oauth_scopes')) === false) {
for ($i = 0; $i < $totalUsers; $i++) {
unset($users[$i]['email']);
}
}
- $response->getBody()->write(\json_encode($users));
+ $response->getBody()->write(json_encode($users));
return $response->withStatus(200);
}
diff --git a/examples/public/auth_code.php b/examples/public/auth_code.php
index c082e3b3f..815d86dee 100644
--- a/examples/public/auth_code.php
+++ b/examples/public/auth_code.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
use Laminas\Diactoros\Stream;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
@@ -51,9 +54,9 @@
new AuthCodeGrant(
$authCodeRepository,
$refreshTokenRepository,
- new \DateInterval('PT10M')
+ new DateInterval('PT10M')
),
- new \DateInterval('PT1H')
+ new DateInterval('PT1H')
);
return $server;
@@ -80,7 +83,7 @@
return $server->completeAuthorizationRequest($authRequest, $response);
} catch (OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
- } catch (\Exception $exception) {
+ } catch (Exception $exception) {
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
@@ -96,7 +99,7 @@
return $server->respondToAccessTokenRequest($request, $response);
} catch (OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
- } catch (\Exception $exception) {
+ } catch (Exception $exception) {
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
diff --git a/examples/public/client_credentials.php b/examples/public/client_credentials.php
index 1e5f090d7..080b06e07 100644
--- a/examples/public/client_credentials.php
+++ b/examples/public/client_credentials.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,9 +8,14 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
+include __DIR__ . '/../vendor/autoload.php';
+
use Laminas\Diactoros\Stream;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
+use League\OAuth2\Server\Grant\ClientCredentialsGrant;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\ClientRepository;
use OAuth2ServerExamples\Repositories\ScopeRepository;
@@ -17,8 +23,6 @@
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
-include __DIR__ . '/../vendor/autoload.php';
-
$app = new App([
'settings' => [
'displayErrorDetails' => true,
@@ -44,8 +48,8 @@
// Enable the client credentials grant on the server
$server->enableGrantType(
- new \League\OAuth2\Server\Grant\ClientCredentialsGrant(),
- new \DateInterval('PT1H') // access tokens will expire after 1 hour
+ new ClientCredentialsGrant(),
+ new DateInterval('PT1H') // access tokens will expire after 1 hour
);
return $server;
@@ -62,7 +66,7 @@
} catch (OAuthServerException $exception) {
// All instances of OAuthServerException can be formatted into a HTTP response
return $exception->generateHttpResponse($response);
- } catch (\Exception $exception) {
+ } catch (Exception $exception) {
// Unknown exception
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
diff --git a/examples/public/device_code.php b/examples/public/device_code.php
new file mode 100644
index 000000000..e63349e76
--- /dev/null
+++ b/examples/public/device_code.php
@@ -0,0 +1,108 @@
+
+ * @copyright Copyright (c) Alex Bilbie
+ * @license http://mit-license.org/
+ *
+ * @link https://github.com/thephpleague/oauth2-server
+ */
+
+declare(strict_types=1);
+
+include __DIR__ . '/../vendor/autoload.php';
+
+use League\OAuth2\Server\AuthorizationServer;
+use League\OAuth2\Server\Exception\OAuthServerException;
+use League\OAuth2\Server\Grant\DeviceCodeGrant;
+use OAuth2ServerExamples\Repositories\AccessTokenRepository;
+use OAuth2ServerExamples\Repositories\ClientRepository;
+use OAuth2ServerExamples\Repositories\DeviceCodeRepository;
+use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
+use OAuth2ServerExamples\Repositories\ScopeRepository;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Slim\App;
+use Zend\Diactoros\Stream;
+
+$app = new App([
+ 'settings' => [
+ 'displayErrorDetails' => true,
+ ],
+ AuthorizationServer::class => function () {
+ // Init our repositories
+ $clientRepository = new ClientRepository();
+ $scopeRepository = new ScopeRepository();
+ $accessTokenRepository = new AccessTokenRepository();
+ $refreshTokenRepository = new RefreshTokenRepository();
+ $deviceCodeRepository = new DeviceCodeRepository();
+
+ $privateKeyPath = 'file://' . __DIR__ . '/../private.key';
+
+ // Set up the authorization server
+ $server = new AuthorizationServer(
+ $clientRepository,
+ $accessTokenRepository,
+ $scopeRepository,
+ $privateKeyPath,
+ 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
+ );
+
+ // Enable the device code grant on the server with a token TTL of 1 hour
+ $server->enableGrantType(
+ new DeviceCodeGrant(
+ $deviceCodeRepository,
+ $refreshTokenRepository,
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ ),
+ new DateInterval('PT1H')
+ );
+
+ return $server;
+ },
+]);
+
+$app->post('/device_authorization', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
+ /* @var \League\OAuth2\Server\AuthorizationServer $server */
+ $server = $app->getContainer()->get(AuthorizationServer::class);
+
+ try {
+ $deviceCodeResponse = $server->respondToDeviceAuthorizationRequest($request, $response);
+
+ return $deviceCodeResponse;
+
+ // Extract the device code. Usually we would then assign the user ID to
+ // the device code but for the purposes of this example, we've hard
+ // coded it in the response above.
+ // $deviceCode = json_decode((string) $deviceCodeResponse->getBody());
+
+ // Once the user has logged in and approved the request, set the user on the device code
+ // $server->completeDeviceAuthorizationRequest($deviceCode->user_code, 1);
+ } catch (OAuthServerException $exception) {
+ return $exception->generateHttpResponse($response);
+ } catch (Exception $exception) {
+ $body = new Stream('php://temp', 'r+');
+ $body->write($exception->getMessage());
+
+ return $response->withStatus(500)->withBody($body);
+ }
+});
+
+$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
+ /* @var \League\OAuth2\Server\AuthorizationServer $server */
+ $server = $app->getContainer()->get(AuthorizationServer::class);
+
+ try {
+ return $server->respondToAccessTokenRequest($request, $response);
+ } catch (OAuthServerException $exception) {
+ return $exception->generateHttpResponse($response);
+ } catch (Exception $exception) {
+ $body = new Stream('php://temp', 'r+');
+ $body->write($exception->getMessage());
+
+ return $response->withStatus(500)->withBody($body);
+ }
+});
+
+$app->run();
diff --git a/examples/public/implicit.php b/examples/public/implicit.php
index ac43f5dd1..6c54b8f2c 100644
--- a/examples/public/implicit.php
+++ b/examples/public/implicit.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
use Laminas\Diactoros\Stream;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
@@ -43,7 +46,7 @@
);
// Enable the implicit grant on the server with a token TTL of 1 hour
- $server->enableGrantType(new ImplicitGrant(new \DateInterval('PT1H')));
+ $server->enableGrantType(new ImplicitGrant(new DateInterval('PT1H')));
return $server;
},
@@ -69,7 +72,7 @@
return $server->completeAuthorizationRequest($authRequest, $response);
} catch (OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
- } catch (\Exception $exception) {
+ } catch (Exception $exception) {
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
diff --git a/examples/public/middleware_use.php b/examples/public/middleware_use.php
index 9f958ed26..49bb5b5bb 100644
--- a/examples/public/middleware_use.php
+++ b/examples/public/middleware_use.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,10 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
+include __DIR__ . '/../vendor/autoload.php';
+
use Laminas\Diactoros\Stream;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Grant\AuthCodeGrant;
@@ -23,8 +28,6 @@
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
-include __DIR__ . '/../vendor/autoload.php';
-
$app = new App([
'settings' => [
'displayErrorDetails' => true,
@@ -53,15 +56,15 @@
new AuthCodeGrant(
$authCodeRepository,
$refreshTokenRepository,
- new \DateInterval('PT10M')
+ new DateInterval('PT10M')
),
- new \DateInterval('PT1H')
+ new DateInterval('PT1H')
);
// Enable the refresh token grant on the server with a token TTL of 1 month
$server->enableGrantType(
new RefreshTokenGrant($refreshTokenRepository),
- new \DateInterval('P1M')
+ new DateInterval('P1M')
);
return $server;
@@ -79,15 +82,15 @@
]);
// Access token issuer
-$app->post('/access_token', function () {
+$app->post('/access_token', function (): void {
})->add(new AuthorizationServerMiddleware($app->getContainer()->get(AuthorizationServer::class)));
// Secured API
-$app->group('/api', function () {
- $this->get('/user', function (ServerRequestInterface $request, ResponseInterface $response) {
+$app->group('/api', function (): void {
+ $app->get('/user', function (ServerRequestInterface $request, ResponseInterface $response) {
$params = [];
- if (\in_array('basic', $request->getAttribute('oauth_scopes', []))) {
+ if (in_array('basic', $request->getAttribute('oauth_scopes', []))) {
$params = [
'id' => 1,
'name' => 'Alex',
@@ -95,12 +98,12 @@
];
}
- if (\in_array('email', $request->getAttribute('oauth_scopes', []))) {
+ if (in_array('email', $request->getAttribute('oauth_scopes', []))) {
$params['email'] = 'alex@example.com';
}
$body = new Stream('php://temp', 'r+');
- $body->write(\json_encode($params));
+ $body->write(json_encode($params));
return $response->withBody($body);
});
diff --git a/examples/public/password.php b/examples/public/password.php
index db65d7840..61096226e 100644
--- a/examples/public/password.php
+++ b/examples/public/password.php
@@ -1,5 +1,7 @@
setRefreshTokenTTL(new \DateInterval('P1M')); // refresh tokens will expire after 1 month
+ $grant->setRefreshTokenTTL(new DateInterval('P1M')); // refresh tokens will expire after 1 month
// Enable the password grant on the server with a token TTL of 1 hour
$server->enableGrantType(
$grant,
- new \DateInterval('PT1H') // access tokens will expire after 1 hour
+ new DateInterval('PT1H') // access tokens will expire after 1 hour
);
return $server;
@@ -54,7 +56,7 @@ function (ServerRequestInterface $request, ResponseInterface $response) use ($ap
} catch (OAuthServerException $exception) {
// All instances of OAuthServerException can be converted to a PSR-7 response
return $exception->generateHttpResponse($response);
- } catch (\Exception $exception) {
+ } catch (Exception $exception) {
// Catch unexpected exceptions
$body = $response->getBody();
$body->write($exception->getMessage());
diff --git a/examples/public/refresh_token.php b/examples/public/refresh_token.php
index 39be08262..f534d7483 100644
--- a/examples/public/refresh_token.php
+++ b/examples/public/refresh_token.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\RefreshTokenGrant;
@@ -44,11 +47,11 @@
// Enable the refresh token grant on the server
$grant = new RefreshTokenGrant($refreshTokenRepository);
- $grant->setRefreshTokenTTL(new \DateInterval('P1M')); // The refresh token will expire in 1 month
+ $grant->setRefreshTokenTTL(new DateInterval('P1M')); // The refresh token will expire in 1 month
$server->enableGrantType(
$grant,
- new \DateInterval('PT1H') // The new access token will expire after 1 hour
+ new DateInterval('PT1H') // The new access token will expire after 1 hour
);
return $server;
@@ -63,7 +66,7 @@
return $server->respondToAccessTokenRequest($request, $response);
} catch (OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
- } catch (\Exception $exception) {
+ } catch (Exception $exception) {
$response->getBody()->write($exception->getMessage());
return $response->withStatus(500);
diff --git a/examples/src/Entities/AccessTokenEntity.php b/examples/src/Entities/AccessTokenEntity.php
index f55246b33..d08fee8df 100644
--- a/examples/src/Entities/AccessTokenEntity.php
+++ b/examples/src/Entities/AccessTokenEntity.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace OAuth2ServerExamples\Entities;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
@@ -16,5 +19,7 @@
class AccessTokenEntity implements AccessTokenEntityInterface
{
- use AccessTokenTrait, TokenEntityTrait, EntityTrait;
+ use AccessTokenTrait;
+ use TokenEntityTrait;
+ use EntityTrait;
}
diff --git a/examples/src/Entities/AuthCodeEntity.php b/examples/src/Entities/AuthCodeEntity.php
index acfbc3b60..ed118e019 100644
--- a/examples/src/Entities/AuthCodeEntity.php
+++ b/examples/src/Entities/AuthCodeEntity.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace OAuth2ServerExamples\Entities;
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
@@ -16,5 +19,7 @@
class AuthCodeEntity implements AuthCodeEntityInterface
{
- use EntityTrait, TokenEntityTrait, AuthCodeTrait;
+ use EntityTrait;
+ use TokenEntityTrait;
+ use AuthCodeTrait;
}
diff --git a/examples/src/Entities/ClientEntity.php b/examples/src/Entities/ClientEntity.php
index c25b70edc..69f8a7093 100644
--- a/examples/src/Entities/ClientEntity.php
+++ b/examples/src/Entities/ClientEntity.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace OAuth2ServerExamples\Entities;
use League\OAuth2\Server\Entities\ClientEntityInterface;
@@ -15,19 +18,20 @@
class ClientEntity implements ClientEntityInterface
{
- use EntityTrait, ClientTrait;
+ use EntityTrait;
+ use ClientTrait;
- public function setName($name)
+ public function setName(string $name): void
{
$this->name = $name;
}
- public function setRedirectUri($uri)
+ public function setRedirectUri(string $uri): void
{
$this->redirectUri = $uri;
}
- public function setConfidential()
+ public function setConfidential(): void
{
$this->isConfidential = true;
}
diff --git a/examples/src/Entities/DeviceCodeEntity.php b/examples/src/Entities/DeviceCodeEntity.php
new file mode 100644
index 000000000..aa82d73a0
--- /dev/null
+++ b/examples/src/Entities/DeviceCodeEntity.php
@@ -0,0 +1,25 @@
+
+ * @copyright Copyright (c) Alex Bilbie
+ * @license http://mit-license.org/
+ *
+ * @link https://github.com/thephpleague/oauth2-server
+ */
+
+declare(strict_types=1);
+
+namespace OAuth2ServerExamples\Entities;
+
+use League\OAuth2\Server\Entities\DeviceCodeEntityInterface;
+use League\OAuth2\Server\Entities\Traits\DeviceCodeTrait;
+use League\OAuth2\Server\Entities\Traits\EntityTrait;
+use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
+
+class DeviceCodeEntity implements DeviceCodeEntityInterface
+{
+ use EntityTrait;
+ use DeviceCodeTrait;
+ use TokenEntityTrait;
+}
diff --git a/examples/src/Entities/RefreshTokenEntity.php b/examples/src/Entities/RefreshTokenEntity.php
index 60109c029..9083feaa3 100644
--- a/examples/src/Entities/RefreshTokenEntity.php
+++ b/examples/src/Entities/RefreshTokenEntity.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace OAuth2ServerExamples\Entities;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
@@ -15,5 +18,6 @@
class RefreshTokenEntity implements RefreshTokenEntityInterface
{
- use RefreshTokenTrait, EntityTrait;
+ use RefreshTokenTrait;
+ use EntityTrait;
}
diff --git a/examples/src/Entities/ScopeEntity.php b/examples/src/Entities/ScopeEntity.php
index 913c0592e..5e07084f5 100644
--- a/examples/src/Entities/ScopeEntity.php
+++ b/examples/src/Entities/ScopeEntity.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace OAuth2ServerExamples\Entities;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
@@ -15,5 +18,6 @@
class ScopeEntity implements ScopeEntityInterface
{
- use EntityTrait, ScopeTrait;
+ use EntityTrait;
+ use ScopeTrait;
}
diff --git a/examples/src/Entities/UserEntity.php b/examples/src/Entities/UserEntity.php
index 22c1b4e55..00d038462 100644
--- a/examples/src/Entities/UserEntity.php
+++ b/examples/src/Entities/UserEntity.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace OAuth2ServerExamples\Entities;
use League\OAuth2\Server\Entities\UserEntityInterface;
@@ -15,10 +18,8 @@ class UserEntity implements UserEntityInterface
{
/**
* Return the user's identifier.
- *
- * @return mixed
*/
- public function getIdentifier()
+ public function getIdentifier(): mixed
{
return 1;
}
diff --git a/examples/src/Repositories/AccessTokenRepository.php b/examples/src/Repositories/AccessTokenRepository.php
index d7736c763..1eb3e5bdd 100644
--- a/examples/src/Repositories/AccessTokenRepository.php
+++ b/examples/src/Repositories/AccessTokenRepository.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace OAuth2ServerExamples\Repositories;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
@@ -19,7 +22,7 @@ class AccessTokenRepository implements AccessTokenRepositoryInterface
/**
* {@inheritdoc}
*/
- public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity)
+ public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity): void
{
// Some logic here to save the access token to a database
}
@@ -27,7 +30,7 @@ public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEnt
/**
* {@inheritdoc}
*/
- public function revokeAccessToken($tokenId)
+ public function revokeAccessToken($tokenId): void
{
// Some logic here to revoke the access token
}
@@ -35,7 +38,7 @@ public function revokeAccessToken($tokenId)
/**
* {@inheritdoc}
*/
- public function isAccessTokenRevoked($tokenId)
+ public function isAccessTokenRevoked($tokenId): bool
{
return false; // Access token hasn't been revoked
}
@@ -43,14 +46,19 @@ public function isAccessTokenRevoked($tokenId)
/**
* {@inheritdoc}
*/
- public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null)
+ public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null): AccessTokenEntityInterface
{
$accessToken = new AccessTokenEntity();
+
$accessToken->setClient($clientEntity);
+
foreach ($scopes as $scope) {
$accessToken->addScope($scope);
}
- $accessToken->setUserIdentifier($userIdentifier);
+
+ if ($userIdentifier !== null) {
+ $accessToken->setUserIdentifier((string) $userIdentifier);
+ }
return $accessToken;
}
diff --git a/examples/src/Repositories/AuthCodeRepository.php b/examples/src/Repositories/AuthCodeRepository.php
index d3ca4825c..962ed8da9 100644
--- a/examples/src/Repositories/AuthCodeRepository.php
+++ b/examples/src/Repositories/AuthCodeRepository.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace OAuth2ServerExamples\Repositories;
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
diff --git a/examples/src/Repositories/ClientRepository.php b/examples/src/Repositories/ClientRepository.php
index 3a398f4ed..0b19d57d7 100644
--- a/examples/src/Repositories/ClientRepository.php
+++ b/examples/src/Repositories/ClientRepository.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,20 +8,27 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace OAuth2ServerExamples\Repositories;
+use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use OAuth2ServerExamples\Entities\ClientEntity;
+use function array_key_exists;
+use function password_hash;
+use function password_verify;
+
class ClientRepository implements ClientRepositoryInterface
{
- const CLIENT_NAME = 'My Awesome App';
- const REDIRECT_URI = 'http://foo/bar';
+ private const CLIENT_NAME = 'My Awesome App';
+ private const REDIRECT_URI = 'http://foo/bar';
/**
* {@inheritdoc}
*/
- public function getClientEntity($clientIdentifier)
+ public function getClientEntity(string $clientIdentifier): ?ClientEntityInterface
{
$client = new ClientEntity();
@@ -35,11 +43,11 @@ public function getClientEntity($clientIdentifier)
/**
* {@inheritdoc}
*/
- public function validateClient($clientIdentifier, $clientSecret, $grantType)
+ public function validateClient($clientIdentifier, $clientSecret, $grantType): bool
{
$clients = [
'myawesomeapp' => [
- 'secret' => \password_hash('abc123', PASSWORD_BCRYPT),
+ 'secret' => password_hash('abc123', PASSWORD_BCRYPT),
'name' => self::CLIENT_NAME,
'redirect_uri' => self::REDIRECT_URI,
'is_confidential' => true,
@@ -47,14 +55,11 @@ public function validateClient($clientIdentifier, $clientSecret, $grantType)
];
// Check if client is registered
- if (\array_key_exists($clientIdentifier, $clients) === false) {
+ if (array_key_exists($clientIdentifier, $clients) === false) {
return false;
}
- if (
- $clients[$clientIdentifier]['is_confidential'] === true
- && \password_verify($clientSecret, $clients[$clientIdentifier]['secret']) === false
- ) {
+ if (password_verify($clientSecret, $clients[$clientIdentifier]['secret']) === false) {
return false;
}
diff --git a/examples/src/Repositories/DeviceCodeRepository.php b/examples/src/Repositories/DeviceCodeRepository.php
new file mode 100644
index 000000000..9495c9a15
--- /dev/null
+++ b/examples/src/Repositories/DeviceCodeRepository.php
@@ -0,0 +1,76 @@
+
+ * @copyright Copyright (c) Alex Bilbie
+ * @license http://mit-license.org/
+ *
+ * @link https://github.com/thephpleague/oauth2-server
+ */
+
+declare(strict_types=1);
+
+namespace OAuth2ServerExamples\Repositories;
+
+use DateTimeImmutable;
+use League\OAuth2\Server\Entities\DeviceCodeEntityInterface;
+use League\OAuth2\Server\Repositories\DeviceCodeRepositoryInterface;
+use OAuth2ServerExamples\Entities\ClientEntity;
+use OAuth2ServerExamples\Entities\DeviceCodeEntity;
+
+class DeviceCodeRepository implements DeviceCodeRepositoryInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getNewDeviceCode(): DeviceCodeEntityInterface
+ {
+ return new DeviceCodeEntity();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function persistDeviceCode(DeviceCodeEntityInterface $deviceCodeEntity): void
+ {
+ // Some logic to persist a new device code to a database
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDeviceCodeEntityByDeviceCode($deviceCode): ?DeviceCodeEntityInterface
+ {
+ $clientEntity = new ClientEntity();
+ $clientEntity->setIdentifier('myawesomeapp');
+
+ $deviceCodeEntity = new DeviceCodeEntity();
+
+ $deviceCodeEntity->setIdentifier($deviceCode);
+ $deviceCodeEntity->setExpiryDateTime(new DateTimeImmutable('now +1 hour'));
+ $deviceCodeEntity->setClient($clientEntity);
+
+ // The user identifier should be set when the user authenticates on the
+ // OAuth server, along with whether they approved the request
+ $deviceCodeEntity->setUserApproved(true);
+ $deviceCodeEntity->setUserIdentifier('1');
+
+ return $deviceCodeEntity;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function revokeDeviceCode($codeId): void
+ {
+ // Some logic to revoke device code
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isDeviceCodeRevoked($codeId): bool
+ {
+ return false;
+ }
+}
diff --git a/examples/src/Repositories/RefreshTokenRepository.php b/examples/src/Repositories/RefreshTokenRepository.php
index 007529cda..fadd12ad8 100644
--- a/examples/src/Repositories/RefreshTokenRepository.php
+++ b/examples/src/Repositories/RefreshTokenRepository.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace OAuth2ServerExamples\Repositories;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
@@ -18,7 +21,7 @@ class RefreshTokenRepository implements RefreshTokenRepositoryInterface
/**
* {@inheritdoc}
*/
- public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity)
+ public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity): void
{
// Some logic to persist the refresh token in a database
}
@@ -26,7 +29,7 @@ public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshToken
/**
* {@inheritdoc}
*/
- public function revokeRefreshToken($tokenId)
+ public function revokeRefreshToken($tokenId): void
{
// Some logic to revoke the refresh token in a database
}
@@ -34,7 +37,7 @@ public function revokeRefreshToken($tokenId)
/**
* {@inheritdoc}
*/
- public function isRefreshTokenRevoked($tokenId)
+ public function isRefreshTokenRevoked($tokenId): bool
{
return false; // The refresh token has not been revoked
}
@@ -42,7 +45,7 @@ public function isRefreshTokenRevoked($tokenId)
/**
* {@inheritdoc}
*/
- public function getNewRefreshToken()
+ public function getNewRefreshToken(): ?RefreshTokenEntityInterface
{
return new RefreshTokenEntity();
}
diff --git a/examples/src/Repositories/ScopeRepository.php b/examples/src/Repositories/ScopeRepository.php
index f0c6191bb..bfe5c93be 100644
--- a/examples/src/Repositories/ScopeRepository.php
+++ b/examples/src/Repositories/ScopeRepository.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,18 +8,23 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace OAuth2ServerExamples\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
+use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use OAuth2ServerExamples\Entities\ScopeEntity;
+use function array_key_exists;
+
class ScopeRepository implements ScopeRepositoryInterface
{
/**
* {@inheritdoc}
*/
- public function getScopeEntityByIdentifier($scopeIdentifier)
+ public function getScopeEntityByIdentifier($scopeIdentifier): ?ScopeEntityInterface
{
$scopes = [
'basic' => [
@@ -29,8 +35,8 @@ public function getScopeEntityByIdentifier($scopeIdentifier)
],
];
- if (\array_key_exists($scopeIdentifier, $scopes) === false) {
- return;
+ if (array_key_exists($scopeIdentifier, $scopes) === false) {
+ return null;
}
$scope = new ScopeEntity();
@@ -46,8 +52,9 @@ public function finalizeScopes(
array $scopes,
$grantType,
ClientEntityInterface $clientEntity,
- $userIdentifier = null
- ) {
+ $userIdentifier = null,
+ $authCodeId = null
+ ): array {
// Example of programatically modifying the final scope of the access token
if ((int) $userIdentifier === 1) {
$scope = new ScopeEntity();
diff --git a/examples/src/Repositories/UserRepository.php b/examples/src/Repositories/UserRepository.php
index 88836cd6a..14bbdcdc6 100644
--- a/examples/src/Repositories/UserRepository.php
+++ b/examples/src/Repositories/UserRepository.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,9 +8,12 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace OAuth2ServerExamples\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
+use League\OAuth2\Server\Entities\UserEntityInterface;
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
use OAuth2ServerExamples\Entities\UserEntity;
@@ -23,11 +27,11 @@ public function getUserEntityByUserCredentials(
$password,
$grantType,
ClientEntityInterface $clientEntity
- ) {
+ ): ?UserEntityInterface {
if ($username === 'alex' && $password === 'whisky') {
return new UserEntity();
}
- return;
+ return null;
}
}
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
new file mode 100644
index 000000000..01f95f02b
--- /dev/null
+++ b/phpcs.xml.dist
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+ src
+ tests
+ examples
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/phpstan.neon b/phpstan.neon
deleted file mode 100644
index ba1fb4915..000000000
--- a/phpstan.neon
+++ /dev/null
@@ -1,8 +0,0 @@
-includes:
- - vendor/phpstan/phpstan-phpunit/extension.neon
- - vendor/phpstan/phpstan-phpunit/rules.neon
-services:
- -
- class: LeagueTests\PHPStan\AbstractGrantExtension
- tags:
- - phpstan.broker.dynamicMethodReturnTypeExtension
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
new file mode 100644
index 000000000..7bef99433
--- /dev/null
+++ b/phpstan.neon.dist
@@ -0,0 +1,9 @@
+parameters:
+ level: 8
+ paths:
+ - src
+ - tests
+ ignoreErrors:
+ -
+ message: '#Call to an undefined method League\\OAuth2\\Server\\ResponseTypes\\ResponseTypeInterface::getAccessToken\(\)\.#'
+ path: tests/Grant/ClientCredentialsGrantTest.php
diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php
index 4d6862157..c894bbd6b 100644
--- a/src/AuthorizationServer.php
+++ b/src/AuthorizationServer.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,18 +8,20 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server;
use DateInterval;
use Defuse\Crypto\Key;
-use League\Event\EmitterAwareInterface;
-use League\Event\EmitterAwareTrait;
+use League\OAuth2\Server\EventEmitting\EmitterAwareInterface;
+use League\OAuth2\Server\EventEmitting\EmitterAwarePolyfill;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\GrantTypeInterface;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
-use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
+use League\OAuth2\Server\RequestTypes\AuthorizationRequestInterface;
use League\OAuth2\Server\ResponseTypes\AbstractResponseType;
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
@@ -27,86 +30,42 @@
class AuthorizationServer implements EmitterAwareInterface
{
- use EmitterAwareTrait;
+ use EmitterAwarePolyfill;
/**
* @var GrantTypeInterface[]
*/
- protected $enabledGrantTypes = [];
+ protected array $enabledGrantTypes = [];
/**
* @var DateInterval[]
*/
- protected $grantTypeAccessTokenTTL = [];
-
- /**
- * @var CryptKey
- */
- protected $privateKey;
-
- /**
- * @var CryptKey
- */
- protected $publicKey;
+ protected array $grantTypeAccessTokenTTL = [];
- /**
- * @var ResponseTypeInterface
- */
- protected $responseType;
+ protected CryptKeyInterface $privateKey;
- /**
- * @var ClientRepositoryInterface
- */
- private $clientRepository;
-
- /**
- * @var AccessTokenRepositoryInterface
- */
- private $accessTokenRepository;
+ protected CryptKeyInterface $publicKey;
- /**
- * @var ScopeRepositoryInterface
- */
- private $scopeRepository;
+ protected ResponseTypeInterface $responseType;
- /**
- * @var string|Key
- */
- private $encryptionKey;
+ private string|Key $encryptionKey;
- /**
- * @var string
- */
- private $defaultScope = '';
+ private string $defaultScope = '';
- /**
- * @var bool
- */
- private $revokeRefreshTokens = true;
+ private bool $revokeRefreshTokens = true;
/**
- * New server instance.
- *
- * @param ClientRepositoryInterface $clientRepository
- * @param AccessTokenRepositoryInterface $accessTokenRepository
- * @param ScopeRepositoryInterface $scopeRepository
- * @param CryptKey|string $privateKey
- * @param string|Key $encryptionKey
- * @param null|ResponseTypeInterface $responseType
+ * New server instance
*/
public function __construct(
- ClientRepositoryInterface $clientRepository,
- AccessTokenRepositoryInterface $accessTokenRepository,
- ScopeRepositoryInterface $scopeRepository,
- $privateKey,
- $encryptionKey,
- ResponseTypeInterface $responseType = null
+ private ClientRepositoryInterface $clientRepository,
+ private AccessTokenRepositoryInterface $accessTokenRepository,
+ private ScopeRepositoryInterface $scopeRepository,
+ CryptKeyInterface|string $privateKey,
+ Key|string $encryptionKey,
+ ResponseTypeInterface|null $responseType = null
) {
- $this->clientRepository = $clientRepository;
- $this->accessTokenRepository = $accessTokenRepository;
- $this->scopeRepository = $scopeRepository;
-
- if ($privateKey instanceof CryptKey === false) {
+ if ($privateKey instanceof CryptKeyInterface === false) {
$privateKey = new CryptKey($privateKey);
}
@@ -123,12 +82,9 @@ public function __construct(
}
/**
- * Enable a grant type on the server.
- *
- * @param GrantTypeInterface $grantType
- * @param null|DateInterval $accessTokenTTL
+ * Enable a grant type on the server
*/
- public function enableGrantType(GrantTypeInterface $grantType, DateInterval $accessTokenTTL = null)
+ public function enableGrantType(GrantTypeInterface $grantType, DateInterval|null $accessTokenTTL = null): void
{
if ($accessTokenTTL === null) {
$accessTokenTTL = new DateInterval('PT1H');
@@ -150,13 +106,9 @@ public function enableGrantType(GrantTypeInterface $grantType, DateInterval $acc
/**
* Validate an authorization request
*
- * @param ServerRequestInterface $request
- *
* @throws OAuthServerException
- *
- * @return AuthorizationRequest
*/
- public function validateAuthorizationRequest(ServerRequestInterface $request)
+ public function validateAuthorizationRequest(ServerRequestInterface $request): AuthorizationRequestInterface
{
foreach ($this->enabledGrantTypes as $grantType) {
if ($grantType->canRespondToAuthorizationRequest($request)) {
@@ -169,44 +121,62 @@ public function validateAuthorizationRequest(ServerRequestInterface $request)
/**
* Complete an authorization request
- *
- * @param AuthorizationRequest $authRequest
- * @param ResponseInterface $response
- *
- * @return ResponseInterface
*/
- public function completeAuthorizationRequest(AuthorizationRequest $authRequest, ResponseInterface $response)
- {
+ public function completeAuthorizationRequest(
+ AuthorizationRequestInterface $authRequest,
+ ResponseInterface $response
+ ): ResponseInterface {
return $this->enabledGrantTypes[$authRequest->getGrantTypeId()]
->completeAuthorizationRequest($authRequest)
->generateHttpResponse($response);
}
/**
- * Return an access token response.
- *
- * @param ServerRequestInterface $request
- * @param ResponseInterface $response
+ * Respond to device authorization request
*
* @throws OAuthServerException
+ */
+ public function respondToDeviceAuthorizationRequest(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
+ {
+ foreach ($this->enabledGrantTypes as $grantType) {
+ if ($grantType->canRespondToDeviceAuthorizationRequest($request)) {
+ return $grantType
+ ->respondToDeviceAuthorizationRequest($request)
+ ->generateHttpResponse($response);
+ }
+ }
+
+ throw OAuthServerException::unsupportedGrantType();
+ }
+
+ /**
+ * Complete a device authorization request
+ */
+ public function completeDeviceAuthorizationRequest(string $deviceCode, string $userId, bool $userApproved): void
+ {
+ $this->enabledGrantTypes['urn:ietf:params:oauth:grant-type:device_code']
+ ->completeDeviceAuthorizationRequest($deviceCode, $userId, $userApproved);
+ }
+
+ /**
+ * Return an access token response.
*
- * @return ResponseInterface
+ * @throws OAuthServerException
*/
- public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response)
+ public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
foreach ($this->enabledGrantTypes as $grantType) {
if (!$grantType->canRespondToAccessTokenRequest($request)) {
continue;
}
+
$tokenResponse = $grantType->respondToAccessTokenRequest(
$request,
$this->getResponseType(),
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()]
);
- if ($tokenResponse instanceof ResponseTypeInterface) {
- return $tokenResponse->generateHttpResponse($response);
- }
+ return $tokenResponse->generateHttpResponse($response);
}
throw OAuthServerException::unsupportedGrantType();
@@ -214,10 +184,8 @@ public function respondToAccessTokenRequest(ServerRequestInterface $request, Res
/**
* Get the token type that grants will return in the HTTP response.
- *
- * @return ResponseTypeInterface
*/
- protected function getResponseType()
+ protected function getResponseType(): ResponseTypeInterface
{
$responseType = clone $this->responseType;
@@ -232,18 +200,14 @@ protected function getResponseType()
/**
* Set the default scope for the authorization server.
- *
- * @param string $defaultScope
*/
- public function setDefaultScope($defaultScope)
+ public function setDefaultScope(string $defaultScope): void
{
$this->defaultScope = $defaultScope;
}
/**
* Sets whether to revoke refresh tokens or not (for all grant types).
- *
- * @param bool $revokeRefreshTokens
*/
public function revokeRefreshTokens(bool $revokeRefreshTokens): void
{
diff --git a/src/AuthorizationValidators/AuthorizationValidatorInterface.php b/src/AuthorizationValidators/AuthorizationValidatorInterface.php
index 7e49f8477..05d9fb88c 100644
--- a/src/AuthorizationValidators/AuthorizationValidatorInterface.php
+++ b/src/AuthorizationValidators/AuthorizationValidatorInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\AuthorizationValidators;
use Psr\Http\Message\ServerRequestInterface;
@@ -14,12 +17,8 @@
interface AuthorizationValidatorInterface
{
/**
- * Determine the access token in the authorization header and append OAUth properties to the request
- * as attributes.
- *
- * @param ServerRequestInterface $request
- *
- * @return ServerRequestInterface
+ * Determine the access token in the authorization header and append OAUth
+ * properties to the request as attributes.
*/
- public function validateAuthorization(ServerRequestInterface $request);
+ public function validateAuthorization(ServerRequestInterface $request): ServerRequestInterface;
}
diff --git a/src/AuthorizationValidators/BearerTokenValidator.php b/src/AuthorizationValidators/BearerTokenValidator.php
index c817d53b1..0442dd48e 100644
--- a/src/AuthorizationValidators/BearerTokenValidator.php
+++ b/src/AuthorizationValidators/BearerTokenValidator.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,62 +8,48 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\AuthorizationValidators;
+use DateInterval;
use DateTimeZone;
use Lcobucci\Clock\SystemClock;
use Lcobucci\JWT\Configuration;
+use Lcobucci\JWT\Exception;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Rsa\Sha256;
+use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
-use League\OAuth2\Server\CryptKey;
+use League\OAuth2\Server\CryptKeyInterface;
use League\OAuth2\Server\CryptTrait;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use Psr\Http\Message\ServerRequestInterface;
+use RuntimeException;
+
+use function date_default_timezone_get;
+use function preg_replace;
+use function trim;
class BearerTokenValidator implements AuthorizationValidatorInterface
{
use CryptTrait;
- /**
- * @var AccessTokenRepositoryInterface
- */
- private $accessTokenRepository;
-
- /**
- * @var CryptKey
- */
- protected $publicKey;
-
- /**
- * @var Configuration
- */
- private $jwtConfiguration;
+ protected CryptKeyInterface $publicKey;
- /**
- * @var \DateInterval|null
- */
- private $jwtValidAtDateLeeway;
+ private Configuration $jwtConfiguration;
- /**
- * @param AccessTokenRepositoryInterface $accessTokenRepository
- * @param \DateInterval|null $jwtValidAtDateLeeway
- */
- public function __construct(AccessTokenRepositoryInterface $accessTokenRepository, \DateInterval $jwtValidAtDateLeeway = null)
+ public function __construct(private AccessTokenRepositoryInterface $accessTokenRepository, private ?DateInterval $jwtValidAtDateLeeway = null)
{
- $this->accessTokenRepository = $accessTokenRepository;
- $this->jwtValidAtDateLeeway = $jwtValidAtDateLeeway;
}
/**
* Set the public key
- *
- * @param CryptKey $key
*/
- public function setPublicKey(CryptKey $key)
+ public function setPublicKey(CryptKeyInterface $key): void
{
$this->publicKey = $key;
@@ -72,19 +59,26 @@ public function setPublicKey(CryptKey $key)
/**
* Initialise the JWT configuration.
*/
- private function initJwtConfiguration()
+ private function initJwtConfiguration(): void
{
$this->jwtConfiguration = Configuration::forSymmetricSigner(
new Sha256(),
InMemory::plainText('empty', 'empty')
);
- $clock = new SystemClock(new DateTimeZone(\date_default_timezone_get()));
+ $clock = new SystemClock(new DateTimeZone(date_default_timezone_get()));
+
+ $publicKeyContents = $this->publicKey->getKeyContents();
+
+ if ($publicKeyContents === '') {
+ throw new RuntimeException('Public key is empty');
+ }
+
$this->jwtConfiguration->setValidationConstraints(
new LooseValidAt($clock, $this->jwtValidAtDateLeeway),
new SignedWith(
new Sha256(),
- InMemory::plainText($this->publicKey->getKeyContents(), $this->publicKey->getPassPhrase() ?? '')
+ InMemory::plainText($publicKeyContents, $this->publicKey->getPassPhrase() ?? '')
)
);
}
@@ -92,19 +86,23 @@ private function initJwtConfiguration()
/**
* {@inheritdoc}
*/
- public function validateAuthorization(ServerRequestInterface $request)
+ public function validateAuthorization(ServerRequestInterface $request): ServerRequestInterface
{
if ($request->hasHeader('authorization') === false) {
throw OAuthServerException::accessDenied('Missing "Authorization" header');
}
$header = $request->getHeader('authorization');
- $jwt = \trim((string) \preg_replace('/^\s*Bearer\s/', '', $header[0]));
+ $jwt = trim((string) preg_replace('/^\s*Bearer\s/', '', $header[0]));
+
+ if ($jwt === '') {
+ throw OAuthServerException::accessDenied('Missing "Bearer" token');
+ }
try {
// Attempt to parse the JWT
$token = $this->jwtConfiguration->parser()->parse($jwt);
- } catch (\Lcobucci\JWT\Exception $exception) {
+ } catch (Exception $exception) {
throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception);
}
@@ -113,7 +111,11 @@ public function validateAuthorization(ServerRequestInterface $request)
$constraints = $this->jwtConfiguration->validationConstraints();
$this->jwtConfiguration->validator()->assert($token, ...$constraints);
} catch (RequiredConstraintsViolated $exception) {
- throw OAuthServerException::accessDenied('Access token could not be verified');
+ throw OAuthServerException::accessDenied('Access token could not be verified', null, $exception);
+ }
+
+ if (!$token instanceof UnencryptedToken) {
+ throw OAuthServerException::accessDenied('Access token is not an instance of UnencryptedToken');
}
$claims = $token->claims();
@@ -126,20 +128,8 @@ public function validateAuthorization(ServerRequestInterface $request)
// Return the request with additional attributes
return $request
->withAttribute('oauth_access_token_id', $claims->get('jti'))
- ->withAttribute('oauth_client_id', $this->convertSingleRecordAudToString($claims->get('aud')))
+ ->withAttribute('oauth_client_id', $claims->get('aud')[0])
->withAttribute('oauth_user_id', $claims->get('sub'))
->withAttribute('oauth_scopes', $claims->get('scopes'));
}
-
- /**
- * Convert single record arrays into strings to ensure backwards compatibility between v4 and v3.x of lcobucci/jwt
- *
- * @param mixed $aud
- *
- * @return array|string
- */
- private function convertSingleRecordAudToString($aud)
- {
- return \is_array($aud) && \count($aud) === 1 ? $aud[0] : $aud;
- }
}
diff --git a/src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php b/src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php
index 3d7ad59c5..1417acf10 100644
--- a/src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php
+++ b/src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Lukáš Unger
@@ -7,24 +8,19 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\CodeChallengeVerifiers;
interface CodeChallengeVerifierInterface
{
/**
* Return code challenge method.
- *
- * @return string
*/
- public function getMethod();
+ public function getMethod(): string;
/**
* Verify the code challenge.
- *
- * @param string $codeVerifier
- * @param string $codeChallenge
- *
- * @return bool
*/
- public function verifyCodeChallenge($codeVerifier, $codeChallenge);
+ public function verifyCodeChallenge(string $codeVerifier, string $codeChallenge): bool;
}
diff --git a/src/CodeChallengeVerifiers/PlainVerifier.php b/src/CodeChallengeVerifiers/PlainVerifier.php
index cf6b70af1..53d669459 100644
--- a/src/CodeChallengeVerifiers/PlainVerifier.php
+++ b/src/CodeChallengeVerifiers/PlainVerifier.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Lukáš Unger
@@ -7,30 +8,27 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\CodeChallengeVerifiers;
+use function hash_equals;
+
class PlainVerifier implements CodeChallengeVerifierInterface
{
/**
* Return code challenge method.
- *
- * @return string
*/
- public function getMethod()
+ public function getMethod(): string
{
return 'plain';
}
/**
* Verify the code challenge.
- *
- * @param string $codeVerifier
- * @param string $codeChallenge
- *
- * @return bool
*/
- public function verifyCodeChallenge($codeVerifier, $codeChallenge)
+ public function verifyCodeChallenge(string $codeVerifier, string $codeChallenge): bool
{
- return \hash_equals($codeVerifier, $codeChallenge);
+ return hash_equals($codeVerifier, $codeChallenge);
}
}
diff --git a/src/CodeChallengeVerifiers/S256Verifier.php b/src/CodeChallengeVerifiers/S256Verifier.php
index f70790a3c..7f99ead5f 100644
--- a/src/CodeChallengeVerifiers/S256Verifier.php
+++ b/src/CodeChallengeVerifiers/S256Verifier.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Lukáš Unger
@@ -7,32 +8,33 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\CodeChallengeVerifiers;
+use function base64_encode;
+use function hash;
+use function hash_equals;
+use function rtrim;
+use function strtr;
+
class S256Verifier implements CodeChallengeVerifierInterface
{
/**
* Return code challenge method.
- *
- * @return string
*/
- public function getMethod()
+ public function getMethod(): string
{
return 'S256';
}
/**
* Verify the code challenge.
- *
- * @param string $codeVerifier
- * @param string $codeChallenge
- *
- * @return bool
*/
- public function verifyCodeChallenge($codeVerifier, $codeChallenge)
+ public function verifyCodeChallenge(string $codeVerifier, string $codeChallenge): bool
{
- return \hash_equals(
- \strtr(\rtrim(\base64_encode(\hash('sha256', $codeVerifier, true)), '='), '+/', '-_'),
+ return hash_equals(
+ strtr(rtrim(base64_encode(hash('sha256', $codeVerifier, true)), '='), '+/', '-_'),
$codeChallenge
);
}
diff --git a/src/CryptKey.php b/src/CryptKey.php
index 14cd8e7b4..944c56a05 100644
--- a/src/CryptKey.php
+++ b/src/CryptKey.php
@@ -1,4 +1,5 @@
passPhrase = $passPhrase;
-
- if (\strpos($keyPath, self::FILE_PREFIX) !== 0 && $this->isValidKey($keyPath, $this->passPhrase ?? '')) {
+ if (strpos($keyPath, self::FILE_PREFIX) !== 0 && $this->isValidKey($keyPath, $this->passPhrase ?? '')) {
$this->keyContents = $keyPath;
$this->keyPath = '';
// There's no file, so no need for permission check.
$keyPermissionsCheck = false;
- } elseif (\is_file($keyPath)) {
- if (\strpos($keyPath, self::FILE_PREFIX) !== 0) {
+ } elseif (is_file($keyPath)) {
+ if (strpos($keyPath, self::FILE_PREFIX) !== 0) {
$keyPath = self::FILE_PREFIX . $keyPath;
}
- if (!\is_readable($keyPath)) {
- throw new LogicException(\sprintf('Key path "%s" does not exist or is not readable', $keyPath));
+ if (!is_readable($keyPath)) {
+ throw new LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
+ }
+
+ $keyContents = file_get_contents($keyPath);
+
+ if ($keyContents === false) {
+ throw new LogicException('Unable to read key from file ' . $keyPath);
}
- $this->keyContents = \file_get_contents($keyPath);
+
+ $this->keyContents = $keyContents;
$this->keyPath = $keyPath;
+
if (!$this->isValidKey($this->keyContents, $this->passPhrase ?? '')) {
throw new LogicException('Unable to read key from file ' . $keyPath);
}
} else {
- throw new LogicException('Unable to read key from file ' . $keyPath);
+ throw new LogicException('Invalid key supplied');
}
if ($keyPermissionsCheck === true) {
// Verify the permissions of the key
- $keyPathPerms = \decoct(\fileperms($this->keyPath) & 0777);
- if (\in_array($keyPathPerms, ['400', '440', '600', '640', '660'], true) === false) {
- \trigger_error(
- \sprintf(
+ $keyPathPerms = decoct(fileperms($this->keyPath) & 0777);
+ 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',
$this->keyPath,
$keyPathPerms
@@ -84,9 +90,7 @@ public function __construct($keyPath, $passPhrase = null, $keyPermissionsCheck =
}
/**
- * Get key contents
- *
- * @return string Key contents
+ * {@inheritdoc}
*/
public function getKeyContents(): string
{
@@ -95,21 +99,20 @@ public function getKeyContents(): string
/**
* Validate key contents.
- *
- * @param string $contents
- * @param string $passPhrase
- *
- * @return bool
*/
- private function isValidKey($contents, $passPhrase)
+ private function isValidKey(string $contents, string $passPhrase): bool
{
- $pkey = \openssl_pkey_get_private($contents, $passPhrase) ?: \openssl_pkey_get_public($contents);
- if ($pkey === false) {
+ $privateKey = openssl_pkey_get_private($contents, $passPhrase);
+
+ $key = $privateKey instanceof OpenSSLAsymmetricKey ? $privateKey : openssl_pkey_get_public($contents);
+
+ if ($key === false) {
return false;
}
- $details = \openssl_pkey_get_details($pkey);
- return $details !== false && \in_array(
+ $details = openssl_pkey_get_details($key);
+
+ return $details !== false && in_array(
$details['type'] ?? -1,
[OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_EC],
true
@@ -117,21 +120,17 @@ private function isValidKey($contents, $passPhrase)
}
/**
- * Retrieve key path.
- *
- * @return string
+ * {@inheritdoc}
*/
- public function getKeyPath()
+ public function getKeyPath(): string
{
return $this->keyPath;
}
/**
- * Retrieve key pass phrase.
- *
- * @return null|string
+ * {@inheritdoc}
*/
- public function getPassPhrase()
+ public function getPassPhrase(): ?string
{
return $this->passPhrase;
}
diff --git a/src/CryptKeyInterface.php b/src/CryptKeyInterface.php
new file mode 100644
index 000000000..7e56d0b76
--- /dev/null
+++ b/src/CryptKeyInterface.php
@@ -0,0 +1,25 @@
+encryptionKey instanceof Key) {
return Crypto::encrypt($unencryptedData, $this->encryptionKey);
}
- if (\is_string($this->encryptionKey)) {
+ if (is_string($this->encryptionKey)) {
return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey);
}
@@ -52,20 +50,16 @@ protected function encrypt($unencryptedData)
/**
* Decrypt data with encryptionKey.
*
- * @param string $encryptedData
- *
* @throws LogicException
- *
- * @return string
*/
- protected function decrypt($encryptedData)
+ protected function decrypt(string $encryptedData): string
{
try {
if ($this->encryptionKey instanceof Key) {
return Crypto::decrypt($encryptedData, $this->encryptionKey);
}
- if (\is_string($this->encryptionKey)) {
+ if (is_string($this->encryptionKey)) {
return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey);
}
@@ -75,12 +69,7 @@ protected function decrypt($encryptedData)
}
}
- /**
- * Set the encryption key
- *
- * @param string|Key $key
- */
- public function setEncryptionKey($key = null)
+ public function setEncryptionKey(Key|string|null $key = null): void
{
$this->encryptionKey = $key;
}
diff --git a/src/Entities/AccessTokenEntityInterface.php b/src/Entities/AccessTokenEntityInterface.php
index fd459914e..3c998b4d2 100644
--- a/src/Entities/AccessTokenEntityInterface.php
+++ b/src/Entities/AccessTokenEntityInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,19 +8,21 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Entities;
-use League\OAuth2\Server\CryptKey;
+use League\OAuth2\Server\CryptKeyInterface;
interface AccessTokenEntityInterface extends TokenInterface
{
/**
* Set a private key used to encrypt the access token.
*/
- public function setPrivateKey(CryptKey $privateKey);
+ public function setPrivateKey(CryptKeyInterface $privateKey): void;
/**
* Generate a string representation of the access token.
*/
- public function __toString();
+ public function toString(): string;
}
diff --git a/src/Entities/AuthCodeEntityInterface.php b/src/Entities/AuthCodeEntityInterface.php
index 00e939c2c..68c6a2f5b 100644
--- a/src/Entities/AuthCodeEntityInterface.php
+++ b/src/Entities/AuthCodeEntityInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,17 +8,13 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Entities;
interface AuthCodeEntityInterface extends TokenInterface
{
- /**
- * @return string|null
- */
- public function getRedirectUri();
+ public function getRedirectUri(): string|null;
- /**
- * @param string $uri
- */
- public function setRedirectUri($uri);
+ public function setRedirectUri(string $uri): void;
}
diff --git a/src/Entities/ClientEntityInterface.php b/src/Entities/ClientEntityInterface.php
index 971c61244..f3838b11c 100644
--- a/src/Entities/ClientEntityInterface.php
+++ b/src/Entities/ClientEntityInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Entities;
interface ClientEntityInterface
@@ -14,30 +17,25 @@ interface ClientEntityInterface
/**
* Get the client's identifier.
*
- * @return string
+ * @return non-empty-string
*/
- public function getIdentifier();
+ public function getIdentifier(): string;
/**
* Get the client's name.
- *
- * @return string
*/
- public function getName();
+ public function getName(): string;
/**
- * Returns the registered redirect URI (as a string).
- *
- * Alternatively return an indexed array of redirect URIs.
+ * Returns the registered redirect URI (as a string). Alternatively return
+ * an indexed array of redirect URIs.
*
* @return string|string[]
*/
- public function getRedirectUri();
+ public function getRedirectUri(): string|array;
/**
* Returns true if the client is confidential.
- *
- * @return bool
*/
- public function isConfidential();
+ public function isConfidential(): bool;
}
diff --git a/src/Entities/DeviceCodeEntityInterface.php b/src/Entities/DeviceCodeEntityInterface.php
new file mode 100644
index 000000000..6969eca01
--- /dev/null
+++ b/src/Entities/DeviceCodeEntityInterface.php
@@ -0,0 +1,40 @@
+
+ * @copyright Copyright (c) Alex Bilbie
+ * @license http://mit-license.org/
+ *
+ * @link https://github.com/thephpleague/oauth2-server
+ */
+
+declare(strict_types=1);
+
+namespace League\OAuth2\Server\Entities;
+
+use DateTimeImmutable;
+
+interface DeviceCodeEntityInterface extends TokenInterface
+{
+ public function getUserCode(): string;
+
+ public function setUserCode(string $userCode): void;
+
+ public function getVerificationUri(): string;
+
+ public function setVerificationUri(string $verificationUri): void;
+
+ public function getVerificationUriComplete(): string;
+
+ public function getLastPolledAt(): ?DateTimeImmutable;
+
+ public function setLastPolledAt(DateTimeImmutable $lastPolledAt): void;
+
+ public function getInterval(): int;
+
+ public function setInterval(int $interval): void;
+
+ public function getUserApproved(): bool;
+
+ public function setUserApproved(bool $userApproved): void;
+}
diff --git a/src/Entities/RefreshTokenEntityInterface.php b/src/Entities/RefreshTokenEntityInterface.php
index 551ad0527..f377cd9e1 100644
--- a/src/Entities/RefreshTokenEntityInterface.php
+++ b/src/Entities/RefreshTokenEntityInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Entities;
use DateTimeImmutable;
@@ -15,43 +18,31 @@ interface RefreshTokenEntityInterface
{
/**
* Get the token's identifier.
- *
- * @return string
*/
- public function getIdentifier();
+ public function getIdentifier(): string;
/**
* Set the token's identifier.
- *
- * @param mixed $identifier
*/
- public function setIdentifier($identifier);
+ public function setIdentifier(mixed $identifier): void;
/**
* Get the token's expiry date time.
- *
- * @return DateTimeImmutable
*/
- public function getExpiryDateTime();
+ public function getExpiryDateTime(): DateTimeImmutable;
/**
* Set the date time when the token expires.
- *
- * @param DateTimeImmutable $dateTime
*/
- public function setExpiryDateTime(DateTimeImmutable $dateTime);
+ public function setExpiryDateTime(DateTimeImmutable $dateTime): void;
/**
* Set the access token that the refresh token was associated with.
- *
- * @param AccessTokenEntityInterface $accessToken
*/
- public function setAccessToken(AccessTokenEntityInterface $accessToken);
+ public function setAccessToken(AccessTokenEntityInterface $accessToken): void;
/**
* Get the access token that the refresh token was originally associated with.
- *
- * @return AccessTokenEntityInterface
*/
- public function getAccessToken();
+ public function getAccessToken(): AccessTokenEntityInterface;
}
diff --git a/src/Entities/ScopeEntityInterface.php b/src/Entities/ScopeEntityInterface.php
index 26748e0c0..37c40bf55 100644
--- a/src/Entities/ScopeEntityInterface.php
+++ b/src/Entities/ScopeEntityInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Entities;
use JsonSerializable;
@@ -15,8 +18,6 @@ interface ScopeEntityInterface extends JsonSerializable
{
/**
* Get the scope's identifier.
- *
- * @return string
*/
- public function getIdentifier();
+ public function getIdentifier(): string;
}
diff --git a/src/Entities/TokenInterface.php b/src/Entities/TokenInterface.php
index 7b063e138..96d2bd418 100644
--- a/src/Entities/TokenInterface.php
+++ b/src/Entities/TokenInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Entities;
use DateTimeImmutable;
@@ -15,71 +18,55 @@ interface TokenInterface
{
/**
* Get the token's identifier.
- *
- * @return string
*/
- public function getIdentifier();
+ public function getIdentifier(): string;
/**
* Set the token's identifier.
- *
- * @param mixed $identifier
*/
- public function setIdentifier($identifier);
+ public function setIdentifier(mixed $identifier): void;
/**
* Get the token's expiry date time.
- *
- * @return DateTimeImmutable
*/
- public function getExpiryDateTime();
+ public function getExpiryDateTime(): DateTimeImmutable;
/**
* Set the date time when the token expires.
- *
- * @param DateTimeImmutable $dateTime
*/
- public function setExpiryDateTime(DateTimeImmutable $dateTime);
+ public function setExpiryDateTime(DateTimeImmutable $dateTime): void;
/**
* Set the identifier of the user associated with the token.
*
- * @param string|int|null $identifier The identifier of the user
+ * @param non-empty-string $identifier
*/
- public function setUserIdentifier($identifier);
+ public function setUserIdentifier(string $identifier): void;
/**
* Get the token user's identifier.
- *
- * @return string|int|null
*/
- public function getUserIdentifier();
+ public function getUserIdentifier(): string|int|null;
/**
* Get the client that the token was issued to.
- *
- * @return ClientEntityInterface
*/
- public function getClient();
+ public function getClient(): ClientEntityInterface;
/**
* Set the client that the token was issued to.
- *
- * @param ClientEntityInterface $client
*/
- public function setClient(ClientEntityInterface $client);
+ public function setClient(ClientEntityInterface $client): void;
/**
* Associate a scope with the token.
- *
- * @param ScopeEntityInterface $scope
*/
- public function addScope(ScopeEntityInterface $scope);
+ public function addScope(ScopeEntityInterface $scope): void;
/**
* Return an array of scopes associated with the token.
*
* @return ScopeEntityInterface[]
*/
- public function getScopes();
+ public function getScopes(): array;
}
diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php
index 81b634397..6b1387b5f 100644
--- a/src/Entities/Traits/AccessTokenTrait.php
+++ b/src/Entities/Traits/AccessTokenTrait.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Entities\Traits;
use DateTimeImmutable;
@@ -14,26 +17,21 @@
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use Lcobucci\JWT\Token;
-use League\OAuth2\Server\CryptKey;
+use League\OAuth2\Server\CryptKeyInterface;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
+use RuntimeException;
trait AccessTokenTrait
{
- /**
- * @var CryptKey
- */
- private $privateKey;
+ private CryptKeyInterface $privateKey;
- /**
- * @var Configuration
- */
- private $jwtConfiguration;
+ private Configuration $jwtConfiguration;
/**
* Set the private key used to encrypt this access token.
*/
- public function setPrivateKey(CryptKey $privateKey)
+ public function setPrivateKey(CryptKeyInterface $privateKey): void
{
$this->privateKey = $privateKey;
}
@@ -41,21 +39,25 @@ public function setPrivateKey(CryptKey $privateKey)
/**
* Initialise the JWT Configuration.
*/
- public function initJwtConfiguration()
+ public function initJwtConfiguration(): void
{
+ $privateKeyContents = $this->privateKey->getKeyContents();
+
+ if ($privateKeyContents === '') {
+ throw new RuntimeException('Private key is empty');
+ }
+
$this->jwtConfiguration = Configuration::forAsymmetricSigner(
new Sha256(),
- InMemory::plainText($this->privateKey->getKeyContents(), $this->privateKey->getPassPhrase() ?? ''),
+ InMemory::plainText($privateKeyContents, $this->privateKey->getPassPhrase() ?? ''),
InMemory::plainText('empty', 'empty')
);
}
/**
* Generate a JWT from the access token
- *
- * @return Token
*/
- private function convertToJWT()
+ private function convertToJWT(): Token
{
$this->initJwtConfiguration();
@@ -65,7 +67,7 @@ private function convertToJWT()
->issuedAt(new DateTimeImmutable())
->canOnlyBeUsedAfter(new DateTimeImmutable())
->expiresAt($this->getExpiryDateTime())
- ->relatedTo((string) $this->getUserIdentifier())
+ ->relatedTo($this->getSubjectIdentifier())
->withClaim('scopes', $this->getScopes())
->getToken($this->jwtConfiguration->signer(), $this->jwtConfiguration->signingKey());
}
@@ -73,33 +75,35 @@ private function convertToJWT()
/**
* Generate a string representation from the access token
*/
- public function __toString()
+ public function toString(): string
{
return $this->convertToJWT()->toString();
}
- /**
- * @return ClientEntityInterface
- */
- abstract public function getClient();
+ abstract public function getClient(): ClientEntityInterface;
+
+ abstract public function getExpiryDateTime(): DateTimeImmutable;
/**
- * @return DateTimeImmutable
+ * @return non-empty-string|null
*/
- abstract public function getExpiryDateTime();
+ abstract public function getUserIdentifier(): string|null;
/**
- * @return string|int
+ * @return ScopeEntityInterface[]
*/
- abstract public function getUserIdentifier();
+ abstract public function getScopes(): array;
/**
- * @return ScopeEntityInterface[]
+ * @return non-empty-string
*/
- abstract public function getScopes();
+ abstract public function getIdentifier(): string;
/**
- * @return string
+ * @return non-empty-string
*/
- abstract public function getIdentifier();
+ private function getSubjectIdentifier(): string
+ {
+ return $this->getUserIdentifier() ?? $this->getClient()->getIdentifier();
+ }
}
diff --git a/src/Entities/Traits/AuthCodeTrait.php b/src/Entities/Traits/AuthCodeTrait.php
index 542d36789..403500b62 100644
--- a/src/Entities/Traits/AuthCodeTrait.php
+++ b/src/Entities/Traits/AuthCodeTrait.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,27 +8,20 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Entities\Traits;
trait AuthCodeTrait
{
- /**
- * @var null|string
- */
- protected $redirectUri;
+ protected ?string $redirectUri = null;
- /**
- * @return string|null
- */
- public function getRedirectUri()
+ public function getRedirectUri(): string|null
{
return $this->redirectUri;
}
- /**
- * @param string $uri
- */
- public function setRedirectUri($uri)
+ public function setRedirectUri(string $uri): void
{
$this->redirectUri = $uri;
}
diff --git a/src/Entities/Traits/ClientTrait.php b/src/Entities/Traits/ClientTrait.php
index 370163c35..b179cfac4 100644
--- a/src/Entities/Traits/ClientTrait.php
+++ b/src/Entities/Traits/ClientTrait.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,55 +8,47 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Entities\Traits;
trait ClientTrait
{
- /**
- * @var string
- */
- protected $name;
+ protected string $name;
/**
* @var string|string[]
*/
- protected $redirectUri;
+ protected string|array $redirectUri;
- /**
- * @var bool
- */
- protected $isConfidential = false;
+ protected bool $isConfidential = false;
/**
* Get the client's name.
*
- * @return string
*
* @codeCoverageIgnore
*/
- public function getName()
+ public function getName(): string
{
return $this->name;
}
/**
- * Returns the registered redirect URI (as a string).
- *
- * Alternatively return an indexed array of redirect URIs.
+ * Returns the registered redirect URI (as a string). Alternatively return
+ * an indexed array of redirect URIs.
*
* @return string|string[]
*/
- public function getRedirectUri()
+ public function getRedirectUri(): string|array
{
return $this->redirectUri;
}
/**
* Returns true if the client is confidential.
- *
- * @return bool
*/
- public function isConfidential()
+ public function isConfidential(): bool
{
return $this->isConfidential;
}
diff --git a/src/Entities/Traits/DeviceCodeTrait.php b/src/Entities/Traits/DeviceCodeTrait.php
new file mode 100644
index 000000000..ea8b2acc2
--- /dev/null
+++ b/src/Entities/Traits/DeviceCodeTrait.php
@@ -0,0 +1,103 @@
+
+ * @copyright Copyright (c) Alex Bilbie
+ * @license http://mit-license.org/
+ *
+ * @link https://github.com/thephpleague/oauth2-server
+ */
+
+declare(strict_types=1);
+
+namespace League\OAuth2\Server\Entities\Traits;
+
+use DateTimeImmutable;
+use League\OAuth2\Server\Entities\ClientEntityInterface;
+use League\OAuth2\Server\Entities\ScopeEntityInterface;
+
+trait DeviceCodeTrait
+{
+ private bool $userApproved = false;
+ private bool $includeVerificationUriComplete = false;
+ private int $interval = 5;
+ private string $userCode;
+ private string $verificationUri;
+ private ?DateTimeImmutable $lastPolledAt = null;
+
+ public function getUserCode(): string
+ {
+ return $this->userCode;
+ }
+
+ public function setUserCode(string $userCode): void
+ {
+ $this->userCode = $userCode;
+ }
+
+ public function getVerificationUri(): string
+ {
+ return $this->verificationUri;
+ }
+
+ public function setVerificationUri(string $verificationUri): void
+ {
+ $this->verificationUri = $verificationUri;
+ }
+
+ public function getVerificationUriComplete(): string
+ {
+ return $this->verificationUri . '?user_code=' . $this->userCode;
+ }
+
+ abstract public function getClient(): ClientEntityInterface;
+
+ abstract public function getExpiryDateTime(): DateTimeImmutable;
+
+ /**
+ * @return ScopeEntityInterface[]
+ */
+ abstract public function getScopes(): array;
+
+ abstract public function getIdentifier(): string;
+
+ public function getLastPolledAt(): ?DateTimeImmutable
+ {
+ return $this->lastPolledAt;
+ }
+
+ public function setLastPolledAt(DateTimeImmutable $lastPolledAt): void
+ {
+ $this->lastPolledAt = $lastPolledAt;
+ }
+
+ public function getInterval(): int
+ {
+ return $this->interval;
+ }
+
+ public function setInterval(int $interval): void
+ {
+ $this->interval = $interval;
+ }
+
+ public function getUserApproved(): bool
+ {
+ return $this->userApproved;
+ }
+
+ public function setUserApproved(bool $userApproved): void
+ {
+ $this->userApproved = $userApproved;
+ }
+
+ public function getVerificationUriCompleteInAuthResponse(): bool
+ {
+ return $this->includeVerificationUriComplete;
+ }
+
+ public function setVerificationUriCompleteInAuthResponse(bool $includeVerificationUriComplete): void
+ {
+ $this->includeVerificationUriComplete = $includeVerificationUriComplete;
+ }
+}
diff --git a/src/Entities/Traits/EntityTrait.php b/src/Entities/Traits/EntityTrait.php
index 05452923a..87a8f6cf6 100644
--- a/src/Entities/Traits/EntityTrait.php
+++ b/src/Entities/Traits/EntityTrait.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,27 +8,26 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Entities\Traits;
trait EntityTrait
{
/**
- * @var string
+ * @var non-empty-string
*/
- protected $identifier;
+ protected string $identifier;
/**
- * @return mixed
+ * @return non-empty-string
*/
- public function getIdentifier()
+ public function getIdentifier(): string
{
return $this->identifier;
}
- /**
- * @param mixed $identifier
- */
- public function setIdentifier($identifier)
+ public function setIdentifier(mixed $identifier): void
{
$this->identifier = $identifier;
}
diff --git a/src/Entities/Traits/RefreshTokenTrait.php b/src/Entities/Traits/RefreshTokenTrait.php
index f0f15444c..a0d4c8885 100644
--- a/src/Entities/Traits/RefreshTokenTrait.php
+++ b/src/Entities/Traits/RefreshTokenTrait.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Entities\Traits;
use DateTimeImmutable;
@@ -14,20 +17,14 @@
trait RefreshTokenTrait
{
- /**
- * @var AccessTokenEntityInterface
- */
- protected $accessToken;
+ protected AccessTokenEntityInterface $accessToken;
- /**
- * @var DateTimeImmutable
- */
- protected $expiryDateTime;
+ protected DateTimeImmutable $expiryDateTime;
/**
* {@inheritdoc}
*/
- public function setAccessToken(AccessTokenEntityInterface $accessToken)
+ public function setAccessToken(AccessTokenEntityInterface $accessToken): void
{
$this->accessToken = $accessToken;
}
@@ -35,27 +32,23 @@ public function setAccessToken(AccessTokenEntityInterface $accessToken)
/**
* {@inheritdoc}
*/
- public function getAccessToken()
+ public function getAccessToken(): AccessTokenEntityInterface
{
return $this->accessToken;
}
/**
* Get the token's expiry date time.
- *
- * @return DateTimeImmutable
*/
- public function getExpiryDateTime()
+ public function getExpiryDateTime(): DateTimeImmutable
{
return $this->expiryDateTime;
}
/**
* Set the date time when the token expires.
- *
- * @param DateTimeImmutable $dateTime
*/
- public function setExpiryDateTime(DateTimeImmutable $dateTime)
+ public function setExpiryDateTime(DateTimeImmutable $dateTime): void
{
$this->expiryDateTime = $dateTime;
}
diff --git a/src/Entities/Traits/ScopeTrait.php b/src/Entities/Traits/ScopeTrait.php
index 7eacc3359..71bedac76 100644
--- a/src/Entities/Traits/ScopeTrait.php
+++ b/src/Entities/Traits/ScopeTrait.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Andrew Millington
@@ -7,23 +8,19 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Entities\Traits;
trait ScopeTrait
{
/**
* Serialize the object to the scopes string identifier when using json_encode().
- *
- * @return string
*/
- #[\ReturnTypeWillChange]
- public function jsonSerialize()
+ public function jsonSerialize(): string
{
return $this->getIdentifier();
}
- /**
- * @return string
- */
- abstract public function getIdentifier();
+ abstract public function getIdentifier(): string;
}
diff --git a/src/Entities/Traits/TokenEntityTrait.php b/src/Entities/Traits/TokenEntityTrait.php
index 83b172322..ad472acd9 100644
--- a/src/Entities/Traits/TokenEntityTrait.php
+++ b/src/Entities/Traits/TokenEntityTrait.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,40 +8,36 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Entities\Traits;
use DateTimeImmutable;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
+use function array_values;
+
trait TokenEntityTrait
{
/**
* @var ScopeEntityInterface[]
*/
- protected $scopes = [];
+ protected array $scopes = [];
- /**
- * @var DateTimeImmutable
- */
- protected $expiryDateTime;
+ protected DateTimeImmutable $expiryDateTime;
/**
- * @var string|int|null
+ * @var non-empty-string|null
*/
- protected $userIdentifier;
+ protected string|null $userIdentifier = null;
- /**
- * @var ClientEntityInterface
- */
- protected $client;
+ protected ClientEntityInterface $client;
/**
* Associate a scope with the token.
- *
- * @param ScopeEntityInterface $scope
*/
- public function addScope(ScopeEntityInterface $scope)
+ public function addScope(ScopeEntityInterface $scope): void
{
$this->scopes[$scope->getIdentifier()] = $scope;
}
@@ -50,27 +47,23 @@ public function addScope(ScopeEntityInterface $scope)
*
* @return ScopeEntityInterface[]
*/
- public function getScopes()
+ public function getScopes(): array
{
- return \array_values($this->scopes);
+ return array_values($this->scopes);
}
/**
* Get the token's expiry date time.
- *
- * @return DateTimeImmutable
*/
- public function getExpiryDateTime()
+ public function getExpiryDateTime(): DateTimeImmutable
{
return $this->expiryDateTime;
}
/**
* Set the date time when the token expires.
- *
- * @param DateTimeImmutable $dateTime
*/
- public function setExpiryDateTime(DateTimeImmutable $dateTime)
+ public function setExpiryDateTime(DateTimeImmutable $dateTime): void
{
$this->expiryDateTime = $dateTime;
}
@@ -78,9 +71,9 @@ public function setExpiryDateTime(DateTimeImmutable $dateTime)
/**
* Set the identifier of the user associated with the token.
*
- * @param string|int|null $identifier The identifier of the user
+ * @param non-empty-string $identifier The identifier of the user
*/
- public function setUserIdentifier($identifier)
+ public function setUserIdentifier(string $identifier): void
{
$this->userIdentifier = $identifier;
}
@@ -88,29 +81,25 @@ public function setUserIdentifier($identifier)
/**
* Get the token user's identifier.
*
- * @return string|int|null
+ * @return non-empty-string|null
*/
- public function getUserIdentifier()
+ public function getUserIdentifier(): string|null
{
return $this->userIdentifier;
}
/**
* Get the client that the token was issued to.
- *
- * @return ClientEntityInterface
*/
- public function getClient()
+ public function getClient(): ClientEntityInterface
{
return $this->client;
}
/**
* Set the client that the token was issued to.
- *
- * @param ClientEntityInterface $client
*/
- public function setClient(ClientEntityInterface $client)
+ public function setClient(ClientEntityInterface $client): void
{
$this->client = $client;
}
diff --git a/src/Entities/UserEntityInterface.php b/src/Entities/UserEntityInterface.php
index c71cb9c52..ef88a84d2 100644
--- a/src/Entities/UserEntityInterface.php
+++ b/src/Entities/UserEntityInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,14 +8,14 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Entities;
interface UserEntityInterface
{
/**
* Return the user's identifier.
- *
- * @return mixed
*/
- public function getIdentifier();
+ public function getIdentifier(): mixed;
}
diff --git a/src/EventEmitting/AbstractEvent.php b/src/EventEmitting/AbstractEvent.php
new file mode 100644
index 000000000..80c229aaa
--- /dev/null
+++ b/src/EventEmitting/AbstractEvent.php
@@ -0,0 +1,44 @@
+name;
+ }
+
+ /**
+ * Backwards compatibility method
+ *
+ * @deprecated use eventName instead
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ public function isPropagationStopped(): bool
+ {
+ return $this->propagationStopped;
+ }
+
+ public function stopPropagation(): self
+ {
+ $this->propagationStopped = true;
+
+ return $this;
+ }
+}
diff --git a/src/EventEmitting/EmitterAwareInterface.php b/src/EventEmitting/EmitterAwareInterface.php
new file mode 100644
index 000000000..206428a1e
--- /dev/null
+++ b/src/EventEmitting/EmitterAwareInterface.php
@@ -0,0 +1,12 @@
+emitter ?? new EventEmitter();
+ }
+
+ public function setEmitter(EventEmitter $emitter): self
+ {
+ $this->emitter = $emitter;
+
+ return $this;
+ }
+
+ public function getEventDispatcher(): EventDispatcherInterface
+ {
+ return $this->getEmitter();
+ }
+
+ public function getListenerRegistry(): ListenerRegistry
+ {
+ return $this->getEmitter();
+ }
+}
diff --git a/src/EventEmitting/EventEmitter.php b/src/EventEmitting/EventEmitter.php
new file mode 100644
index 000000000..25c15c5ba
--- /dev/null
+++ b/src/EventEmitting/EventEmitter.php
@@ -0,0 +1,23 @@
+subscribeTo($event, $listener, $priority);
+
+ return $this;
+ }
+
+ public function emit(object $event): object
+ {
+ return $this->dispatch($event);
+ }
+}
diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php
index cf737795e..a5cc8d6d0 100644
--- a/src/Exception/OAuthServerException.php
+++ b/src/Exception/OAuthServerException.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Exception;
use Exception;
@@ -14,67 +17,32 @@
use Psr\Http\Message\ServerRequestInterface;
use Throwable;
+use function htmlspecialchars;
+use function http_build_query;
+use function sprintf;
+use function strpos;
+use function strstr;
+
class OAuthServerException extends Exception
{
/**
- * @var int
- */
- private $httpStatusCode;
-
- /**
- * @var string
- */
- private $errorType;
-
- /**
- * @var null|string
- */
- private $hint;
-
- /**
- * @var null|string
+ * @var array
*/
- private $redirectUri;
+ private array $payload;
- /**
- * @var string
- */
- private $queryDelimiter;
-
- /**
- * @var array
- */
- private $payload;
-
- /**
- * @var ServerRequestInterface
- */
- private $serverRequest;
+ private ServerRequestInterface $serverRequest;
/**
* Throw a new exception.
- *
- * @param string $message Error message
- * @param int $code Error code
- * @param string $errorType Error type
- * @param int $httpStatusCode HTTP status code to send (default = 400)
- * @param null|string $hint A helper hint
- * @param null|string $redirectUri A HTTP URI to redirect the user back to
- * @param Throwable $previous Previous exception
- * @param string $queryDelimiter Query delimiter of the redirect URI
*/
- public function __construct($message, $code, $errorType, $httpStatusCode = 400, $hint = null, $redirectUri = null, Throwable $previous = null, $queryDelimiter = '?')
+ final public function __construct(string $message, int $code, private string $errorType, private int $httpStatusCode = 400, private ?string $hint = null, private ?string $redirectUri = null, Throwable $previous = null, private ?string $queryDelimiter)
{
parent::__construct($message, $code, $previous);
- $this->httpStatusCode = $httpStatusCode;
- $this->errorType = $errorType;
- $this->hint = $hint;
- $this->redirectUri = $redirectUri;
- $this->queryDelimiter = $queryDelimiter;
$this->payload = [
'error' => $errorType,
'error_description' => $message,
];
+
if ($hint !== null) {
$this->payload['hint'] = $hint;
}
@@ -83,47 +51,35 @@ public function __construct($message, $code, $errorType, $httpStatusCode = 400,
/**
* Returns the current payload.
*
- * @return array
+ * @return array
*/
- public function getPayload()
+ public function getPayload(): array
{
- $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;
+ return $this->payload;
}
/**
* Updates the current payload.
*
- * @param array $payload
+ * @param array $payload
*/
- public function setPayload(array $payload)
+ public function setPayload(array $payload): void
{
$this->payload = $payload;
}
/**
* Set the server request that is responsible for generating the exception
- *
- * @param ServerRequestInterface $serverRequest
*/
- public function setServerRequest(ServerRequestInterface $serverRequest)
+ public function setServerRequest(ServerRequestInterface $serverRequest): void
{
$this->serverRequest = $serverRequest;
}
/**
* Unsupported grant type error.
- *
- * @return static
*/
- public static function unsupportedGrantType()
+ public static function unsupportedGrantType(): static
{
$errorMessage = 'The authorization grant type is not supported by the authorization server.';
$hint = 'Check that all required parameters have been provided';
@@ -133,30 +89,20 @@ public static function unsupportedGrantType()
/**
* Invalid request error.
- *
- * @param string $parameter The invalid parameter
- * @param null|string $hint
- * @param Throwable $previous Previous exception
- *
- * @return static
*/
- public static function invalidRequest($parameter, $hint = null, Throwable $previous = null)
+ public static function invalidRequest(string $parameter, ?string $hint = null, Throwable $previous = null): static
{
$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;
+ $hint = ($hint === null) ? sprintf('Check the `%s` parameter', $parameter) : $hint;
return new static($errorMessage, 3, 'invalid_request', 400, $hint, null, $previous);
}
/**
* Invalid client error.
- *
- * @param ServerRequestInterface $serverRequest
- *
- * @return static
*/
- public static function invalidClient(ServerRequestInterface $serverRequest)
+ public static function invalidClient(ServerRequestInterface $serverRequest): static
{
$exception = new static('Client authentication failed', 4, 'invalid_client', 401);
@@ -166,24 +112,18 @@ public static function invalidClient(ServerRequestInterface $serverRequest)
}
/**
- * Invalid scope error.
- *
- * @param string $scope The bad scope
- * @param null|string $redirectUri A HTTP URI to redirect the user back to
- * @param string $queryDelimiter Query delimiter of the redirect URI
- *
- * @return static
+ * Invalid scope error
*/
- public static function invalidScope($scope, $redirectUri = null, $queryDelimiter = '?')
+ public static function invalidScope(string $scope, string|null $redirectUri = null, string $queryDelimiter = '?'): static
{
$errorMessage = 'The requested scope is invalid, unknown, or malformed';
- if (empty($scope)) {
+ if ($scope === '') {
$hint = 'Specify a scope in the request or set a default scope';
} else {
- $hint = \sprintf(
+ $hint = sprintf(
'Check the `%s` scope',
- \htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false)
+ htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false)
);
}
@@ -192,10 +132,8 @@ public static function invalidScope($scope, $redirectUri = null, $queryDelimiter
/**
* Invalid credentials error.
- *
- * @return static
*/
- public static function invalidCredentials()
+ public static function invalidCredentials(): static
{
return new static('The user credentials were incorrect.', 6, 'invalid_grant', 400);
}
@@ -203,14 +141,9 @@ public static function invalidCredentials()
/**
* Server error.
*
- * @param string $hint
- * @param Throwable $previous
- *
- * @return static
- *
* @codeCoverageIgnore
*/
- public static function serverError($hint, Throwable $previous = null)
+ public static function serverError(string $hint, Throwable $previous = null): static
{
return new static(
'The authorization server encountered an unexpected condition which prevented it from fulfilling'
@@ -226,29 +159,21 @@ public static function serverError($hint, Throwable $previous = null)
/**
* Invalid refresh token.
- *
- * @param null|string $hint
- * @param Throwable $previous
- *
- * @return static
*/
- public static function invalidRefreshToken($hint = null, Throwable $previous = null)
+ public static function invalidRefreshToken(?string $hint = null, Throwable $previous = null): static
{
- return new static('The refresh token is invalid.', 8, 'invalid_request', 401, $hint, null, $previous);
+ return new static('The refresh token is invalid.', 8, 'invalid_grant', 400, $hint, null, $previous);
}
/**
* Access denied.
- *
- * @param null|string $hint
- * @param null|string $redirectUri
- * @param Throwable $previous
- * @param string $queryDelimiter
- *
- * @return static
*/
- public static function accessDenied($hint = null, $redirectUri = null, Throwable $previous = null, $queryDelimiter = '?')
- {
+ public static function accessDenied(
+ ?string $hint = null,
+ ?string $redirectUri = null,
+ Throwable $previous = null,
+ string $queryDelimiter = '?'
+ ): static {
return new static(
'The resource owner or authorization server denied the request.',
9,
@@ -263,12 +188,8 @@ public static function accessDenied($hint = null, $redirectUri = null, Throwable
/**
* Invalid grant.
- *
- * @param string $hint
- *
- * @return static
*/
- public static function invalidGrant($hint = '')
+ public static function invalidGrant(string $hint = ''): static
{
return new static(
'The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token '
@@ -281,24 +202,71 @@ public static function invalidGrant($hint = '')
);
}
+ public function getErrorType(): string
+ {
+ return $this->errorType;
+ }
+
/**
- * @return string
+ * Expired token error.
+ *
+ * @param Throwable $previous Previous exception
+ *
+ * @return static
*/
- public function getErrorType()
+ public static function expiredToken(?string $hint = null, Throwable $previous = null): static
{
- return $this->errorType;
+ $errorMessage = 'The `device_code` has expired and the device ' .
+ 'authorization session has concluded.';
+
+ return new static($errorMessage, 11, 'expired_token', 400, $hint, null, $previous);
+ }
+
+ public static function authorizationPending(string $hint = '', Throwable $previous = null): static
+ {
+ return new static(
+ 'The authorization request is still pending as the end user ' .
+ 'hasn\'t yet completed the user interaction steps. The client ' .
+ 'SHOULD repeat the Access Token Request to the token endpoint',
+ 12,
+ 'authorization_pending',
+ 400,
+ $hint,
+ null,
+ $previous
+ );
}
/**
- * Generate a HTTP response.
+ * Slow down error used with the Device Authorization Grant.
*
- * @param ResponseInterface $response
- * @param bool $useFragment True if errors should be in the URI fragment instead of query string
- * @param int $jsonOptions options passed to json_encode
*
- * @return ResponseInterface
+ * @return static
*/
- public function generateHttpResponse(ResponseInterface $response, $useFragment = false, $jsonOptions = 0)
+ public static function slowDown(string $hint = '', Throwable $previous = null): static
+ {
+ return new static(
+ 'The authorization request is still pending and polling should ' .
+ 'continue, but the interval MUST be increased ' .
+ 'by 5 seconds for this and all subsequent requests.',
+ 13,
+ 'slow_down',
+ 400,
+ $hint,
+ null,
+ $previous
+ );
+ }
+
+ /**
+ {
+ return $this->errorType;
+ }
+
+ /**
+ * Generate a HTTP response.
+ */
+ public function generateHttpResponse(ResponseInterface $response, bool $useFragment = false, int $jsonOptions = 0): ResponseInterface
{
$headers = $this->getHttpHeaders();
@@ -306,16 +274,18 @@ public function generateHttpResponse(ResponseInterface $response, $useFragment =
if ($this->redirectUri !== null) {
$queryDelimiter = $useFragment === true ? '#' : $this->queryDelimiter;
- $this->redirectUri .= (\strstr($this->redirectUri, $queryDelimiter) === false) ? $queryDelimiter : '&';
+ $this->redirectUri .= (strstr($this->redirectUri, $queryDelimiter) === false) ? $queryDelimiter : '&';
- return $response->withStatus(302)->withHeader('Location', $this->redirectUri . \http_build_query($payload));
+ return $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload));
}
foreach ($headers as $header => $content) {
$response = $response->withHeader($header, $content);
}
- $responseBody = \json_encode($payload, $jsonOptions) ?: 'JSON encoding of payload failed';
+ $jsonEncodedPayload = json_encode($payload, $jsonOptions);
+
+ $responseBody = $jsonEncodedPayload === false ? 'JSON encoding of payload failed' : $jsonEncodedPayload;
$response->getBody()->write($responseBody);
@@ -325,9 +295,9 @@ public function generateHttpResponse(ResponseInterface $response, $useFragment =
/**
* Get all headers that have to be send with the error response.
*
- * @return array Array with header values
+ * @return array Array with header values
*/
- public function getHttpHeaders()
+ public function getHttpHeaders(): array
{
$headers = [
'Content-type' => 'application/json',
@@ -342,7 +312,7 @@ public function getHttpHeaders()
// include the "WWW-Authenticate" response header field
// matching the authentication scheme used by the client.
if ($this->errorType === 'invalid_client' && $this->requestHasAuthorizationHeader()) {
- $authScheme = \strpos($this->serverRequest->getHeader('Authorization')[0], 'Bearer') === 0 ? 'Bearer' : 'Basic';
+ $authScheme = strpos($this->serverRequest->getHeader('Authorization')[0], 'Bearer') === 0 ? 'Bearer' : 'Basic';
$headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"';
}
@@ -357,38 +327,29 @@ public function getHttpHeaders()
* 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()
+ public function hasRedirect(): bool
{
return $this->redirectUri !== null;
}
/**
* Returns the Redirect URI used for redirecting.
- *
- * @return string|null
*/
- public function getRedirectUri()
+ public function getRedirectUri(): ?string
{
return $this->redirectUri;
}
/**
* Returns the HTTP status code to send when the exceptions is output.
- *
- * @return int
*/
- public function getHttpStatusCode()
+ public function getHttpStatusCode(): int
{
return $this->httpStatusCode;
}
- /**
- * @return null|string
- */
- public function getHint()
+ public function getHint(): ?string
{
return $this->hint;
}
@@ -398,10 +359,8 @@ public function getHint()
*
* Returns true if the header is present and not an empty string, false
* otherwise.
- *
- * @return bool
*/
- private function requestHasAuthorizationHeader()
+ private function requestHasAuthorizationHeader(): bool
{
if (!$this->serverRequest->hasHeader('Authorization')) {
return false;
@@ -414,7 +373,7 @@ private function requestHasAuthorizationHeader()
// For practical purposes that case should be treated as though the
// header isn't present.
// See https://github.com/thephpleague/oauth2-server/issues/1162
- if (empty($authorizationHeader) || empty($authorizationHeader[0])) {
+ if ($authorizationHeader === [] || $authorizationHeader[0] === '') {
return false;
}
diff --git a/src/Exception/UniqueTokenIdentifierConstraintViolationException.php b/src/Exception/UniqueTokenIdentifierConstraintViolationException.php
index 175ab3a88..4c2f281f6 100644
--- a/src/Exception/UniqueTokenIdentifierConstraintViolationException.php
+++ b/src/Exception/UniqueTokenIdentifierConstraintViolationException.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,14 +8,13 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Exception;
class UniqueTokenIdentifierConstraintViolationException extends OAuthServerException
{
- /**
- * @return UniqueTokenIdentifierConstraintViolationException
- */
- public static function create()
+ public static function create(): UniqueTokenIdentifierConstraintViolationException
{
$errorMessage = 'Could not create unique access token identifier';
diff --git a/src/Grant/AbstractAuthorizeGrant.php b/src/Grant/AbstractAuthorizeGrant.php
index 716c9bbf4..4fe552c17 100644
--- a/src/Grant/AbstractAuthorizeGrant.php
+++ b/src/Grant/AbstractAuthorizeGrant.php
@@ -1,4 +1,5 @@
clientRepository = $clientRepository;
}
- /**
- * @param AccessTokenRepositoryInterface $accessTokenRepository
- */
- public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository)
+ public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository): void
{
$this->accessTokenRepository = $accessTokenRepository;
}
- /**
- * @param ScopeRepositoryInterface $scopeRepository
- */
- public function setScopeRepository(ScopeRepositoryInterface $scopeRepository)
+ public function setScopeRepository(ScopeRepositoryInterface $scopeRepository): void
{
$this->scopeRepository = $scopeRepository;
}
- /**
- * @param RefreshTokenRepositoryInterface $refreshTokenRepository
- */
- public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
+ public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository): void
{
$this->refreshTokenRepository = $refreshTokenRepository;
}
- /**
- * @param AuthCodeRepositoryInterface $authCodeRepository
- */
- public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository)
+ public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository): void
{
$this->authCodeRepository = $authCodeRepository;
}
- /**
- * @param UserRepositoryInterface $userRepository
- */
- public function setUserRepository(UserRepositoryInterface $userRepository)
+ public function setUserRepository(UserRepositoryInterface $userRepository): void
{
$this->userRepository = $userRepository;
}
@@ -149,33 +121,25 @@ public function setUserRepository(UserRepositoryInterface $userRepository)
/**
* {@inheritdoc}
*/
- public function setRefreshTokenTTL(DateInterval $refreshTokenTTL)
+ public function setRefreshTokenTTL(DateInterval $refreshTokenTTL): void
{
$this->refreshTokenTTL = $refreshTokenTTL;
}
/**
* Set the private key
- *
- * @param CryptKey $key
*/
- public function setPrivateKey(CryptKey $key)
+ public function setPrivateKey(CryptKeyInterface $key): void
{
$this->privateKey = $key;
}
- /**
- * @param string $scope
- */
- public function setDefaultScope($scope)
+ public function setDefaultScope(string $scope): void
{
$this->defaultScope = $scope;
}
- /**
- * @param bool $revokeRefreshTokens
- */
- public function revokeRefreshTokens(bool $revokeRefreshTokens)
+ public function revokeRefreshTokens(bool $revokeRefreshTokens): void
{
$this->revokeRefreshTokens = $revokeRefreshTokens;
}
@@ -183,13 +147,9 @@ public function revokeRefreshTokens(bool $revokeRefreshTokens)
/**
* Validate the client.
*
- * @param ServerRequestInterface $request
- *
* @throws OAuthServerException
- *
- * @return ClientEntityInterface
*/
- protected function validateClient(ServerRequestInterface $request)
+ protected function validateClient(ServerRequestInterface $request): ClientEntityInterface
{
[$clientId, $clientSecret] = $this->getClientCredentials($request);
@@ -198,14 +158,13 @@ protected function validateClient(ServerRequestInterface $request)
throw OAuthServerException::invalidClient($request);
}
-
$client = $this->getClientEntityOrFail($clientId, $request);
// 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($redirectUri)) {
+ if (!is_string($redirectUri)) {
throw OAuthServerException::invalidRequest('redirect_uri');
}
@@ -224,13 +183,8 @@ protected function validateClient(ServerRequestInterface $request)
* doesn't actually enforce non-null returns/exception-on-no-client so
* getClientEntity might return null. By contrast, this method will
* always either return a ClientEntityInterface or throw.
- *
- * @param string $clientId
- * @param ServerRequestInterface $request
- *
- * @return ClientEntityInterface
*/
- protected function getClientEntityOrFail($clientId, ServerRequestInterface $request)
+ protected function getClientEntityOrFail(string $clientId, ServerRequestInterface $request): ClientEntityInterface
{
$client = $this->clientRepository->getClientEntity($clientId);
@@ -246,23 +200,21 @@ protected function getClientEntityOrFail($clientId, ServerRequestInterface $requ
* Gets the client credentials from the request from the request body or
* the Http Basic Authorization header
*
- * @param ServerRequestInterface $request
- *
- * @return array
+ * @return string[]
*/
- protected function getClientCredentials(ServerRequestInterface $request)
+ protected function getClientCredentials(ServerRequestInterface $request): array
{
[$basicAuthUser, $basicAuthPassword] = $this->getBasicAuthCredentials($request);
$clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser);
- if (\is_null($clientId)) {
+ if (is_null($clientId)) {
throw OAuthServerException::invalidRequest('client_id');
}
$clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword);
- if ($clientSecret !== null && !\is_string($clientSecret)) {
+ if ($clientSecret !== null && !is_string($clientSecret)) {
throw OAuthServerException::invalidRequest('client_secret');
}
@@ -270,12 +222,8 @@ protected function getClientCredentials(ServerRequestInterface $request)
}
/**
- * 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
+ * Validate redirectUri from the request. If a redirect URI is provided
+ * ensure it matches what is pre-registered
*
* @throws OAuthServerException
*/
@@ -283,8 +231,9 @@ protected function validateRedirectUri(
string $redirectUri,
ClientEntityInterface $client,
ServerRequestInterface $request
- ) {
+ ): void {
$validator = new RedirectUriValidator($client->getRedirectUri());
+
if (!$validator->validateRedirectUri($redirectUri)) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient($request);
@@ -294,26 +243,23 @@ protected function validateRedirectUri(
/**
* Validate scopes in the request.
*
- * @param string|array|null $scopes
- * @param string $redirectUri
- * @param string $queryDelimiter
+ * @param null|string|string[] $scopes
*
* @throws OAuthServerException
*
* @return ScopeEntityInterface[]
*/
- public function validateScopes($scopes, $redirectUri = null, $queryDelimiter = '?')
- {
+ public function validateScopes(
+ string|array|null $scopes,
+ string $redirectUri = null,
+ string $queryDelimiter = '?'
+ ): array {
if ($scopes === null) {
$scopes = [];
- } elseif (\is_string($scopes)) {
+ } elseif (is_string($scopes)) {
$scopes = $this->convertScopesQueryStringToArray($scopes);
}
- if (!\is_array($scopes)) {
- throw OAuthServerException::invalidRequest('scope');
- }
-
$validScopes = [];
foreach ($scopes as $scopeItem) {
@@ -332,27 +278,19 @@ public function validateScopes($scopes, $redirectUri = null, $queryDelimiter = '
/**
* Converts a scopes query string to an array to easily iterate for validation.
*
- * @param string $scopes
- *
- * @return array
+ * @return string[]
*/
- private function convertScopesQueryStringToArray(string $scopes)
+ private function convertScopesQueryStringToArray(string $scopes): array
{
- return \array_filter(\explode(self::SCOPE_DELIMITER_STRING, \trim($scopes)), function ($scope) {
+ return array_filter(explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), function ($scope) {
return $scope !== '';
});
}
/**
* Retrieve request parameter.
- *
- * @param string $parameter
- * @param ServerRequestInterface $request
- * @param mixed $default
- *
- * @return null|string
*/
- protected function getRequestParameter($parameter, ServerRequestInterface $request, $default = null)
+ protected function getRequestParameter(string $parameter, ServerRequestInterface $request, mixed $default = null): mixed
{
$requestParameters = (array) $request->getParsedBody();
@@ -366,70 +304,52 @@ protected function getRequestParameter($parameter, ServerRequestInterface $reque
* not exist, or is otherwise an invalid HTTP Basic header, return
* [null, null].
*
- * @param ServerRequestInterface $request
- *
* @return string[]|null[]
*/
- protected function getBasicAuthCredentials(ServerRequestInterface $request)
+ protected function getBasicAuthCredentials(ServerRequestInterface $request): array
{
if (!$request->hasHeader('Authorization')) {
return [null, null];
}
$header = $request->getHeader('Authorization')[0];
- if (\strpos($header, 'Basic ') !== 0) {
+ if (strpos($header, 'Basic ') !== 0) {
return [null, null];
}
- if (!($decoded = \base64_decode(\substr($header, 6)))) {
+ $decoded = base64_decode(substr($header, 6), true);
+
+ if ($decoded === false) {
return [null, null];
}
- if (\strpos($decoded, ':') === false) {
+ if (strpos($decoded, ':') === false) {
return [null, null]; // HTTP Basic header without colon isn't valid
}
- return \explode(':', $decoded, 2);
+ return explode(':', $decoded, 2);
}
/**
* Retrieve query string parameter.
- *
- * @param string $parameter
- * @param ServerRequestInterface $request
- * @param mixed $default
- *
- * @return null|string
*/
- protected function getQueryStringParameter($parameter, ServerRequestInterface $request, $default = null)
+ protected function getQueryStringParameter(string $parameter, ServerRequestInterface $request, mixed $default = null): ?string
{
return isset($request->getQueryParams()[$parameter]) ? $request->getQueryParams()[$parameter] : $default;
}
/**
* Retrieve cookie parameter.
- *
- * @param string $parameter
- * @param ServerRequestInterface $request
- * @param mixed $default
- *
- * @return null|string
*/
- protected function getCookieParameter($parameter, ServerRequestInterface $request, $default = null)
+ protected function getCookieParameter(string $parameter, ServerRequestInterface $request, mixed $default = null): ?string
{
return isset($request->getCookieParams()[$parameter]) ? $request->getCookieParams()[$parameter] : $default;
}
/**
* Retrieve server parameter.
- *
- * @param string $parameter
- * @param ServerRequestInterface $request
- * @param mixed $default
- *
- * @return null|string
*/
- protected function getServerParameter($parameter, ServerRequestInterface $request, $default = null)
+ protected function getServerParameter(string $parameter, ServerRequestInterface $request, mixed $default = null): ?string
{
return isset($request->getServerParams()[$parameter]) ? $request->getServerParams()[$parameter] : $default;
}
@@ -437,22 +357,17 @@ protected function getServerParameter($parameter, ServerRequestInterface $reques
/**
* Issue an access token.
*
- * @param DateInterval $accessTokenTTL
- * @param ClientEntityInterface $client
- * @param string|null $userIdentifier
* @param ScopeEntityInterface[] $scopes
*
* @throws OAuthServerException
* @throws UniqueTokenIdentifierConstraintViolationException
- *
- * @return AccessTokenEntityInterface
*/
protected function issueAccessToken(
DateInterval $accessTokenTTL,
ClientEntityInterface $client,
- $userIdentifier,
+ string|int|null $userIdentifier,
array $scopes = []
- ) {
+ ): AccessTokenEntityInterface {
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
$accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier);
@@ -471,29 +386,27 @@ protected function issueAccessToken(
}
}
}
+
+ // This should never be hit. It is here to work around a PHPStan false error
+ return $accessToken;
}
/**
* Issue an auth code.
*
- * @param DateInterval $authCodeTTL
- * @param ClientEntityInterface $client
- * @param string $userIdentifier
- * @param string|null $redirectUri
+ * @param non-empty-string $userIdentifier
* @param ScopeEntityInterface[] $scopes
*
* @throws OAuthServerException
* @throws UniqueTokenIdentifierConstraintViolationException
- *
- * @return AuthCodeEntityInterface
*/
protected function issueAuthCode(
DateInterval $authCodeTTL,
ClientEntityInterface $client,
- $userIdentifier,
- $redirectUri,
+ string $userIdentifier,
+ ?string $redirectUri,
array $scopes = []
- ) {
+ ): AuthCodeEntityInterface {
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
$authCode = $this->authCodeRepository->getNewAuthCode();
@@ -521,17 +434,16 @@ protected function issueAuthCode(
}
}
}
+
+ // This should never be hit. It is here to work around a PHPStan false error
+ return $authCode;
}
/**
- * @param AccessTokenEntityInterface $accessToken
- *
* @throws OAuthServerException
* @throws UniqueTokenIdentifierConstraintViolationException
- *
- * @return RefreshTokenEntityInterface|null
*/
- protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
+ protected function issueRefreshToken(AccessTokenEntityInterface $accessToken): ?RefreshTokenEntityInterface
{
$refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
@@ -556,25 +468,26 @@ protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
}
}
}
+
+ // This should never be hit. It is here to work around a PHPStan false error
+ return $refreshToken;
}
/**
* Generate a new unique identifier.
*
- * @param int $length
- *
* @throws OAuthServerException
- *
- * @return string
*/
- protected function generateUniqueIdentifier($length = 40)
+ protected function generateUniqueIdentifier(int $length = 40): string
{
try {
- return \bin2hex(\random_bytes($length));
+ if ($length < 1) {
+ throw new DomainException('Length must be a positive integer');
+ }
+
+ return bin2hex(random_bytes($length));
// @codeCoverageIgnoreStart
- } catch (TypeError $e) {
- throw OAuthServerException::serverError('An unexpected error has occurred', $e);
- } catch (Error $e) {
+ } catch (TypeError | Error $e) {
throw OAuthServerException::serverError('An unexpected error has occurred', $e);
} catch (Exception $e) {
// If you get this message, the CSPRNG failed hard.
@@ -586,12 +499,12 @@ protected function generateUniqueIdentifier($length = 40)
/**
* {@inheritdoc}
*/
- public function canRespondToAccessTokenRequest(ServerRequestInterface $request)
+ public function canRespondToAccessTokenRequest(ServerRequestInterface $request): bool
{
$requestParameters = (array) $request->getParsedBody();
return (
- \array_key_exists('grant_type', $requestParameters)
+ array_key_exists('grant_type', $requestParameters)
&& $requestParameters['grant_type'] === $this->getIdentifier()
);
}
@@ -599,7 +512,7 @@ public function canRespondToAccessTokenRequest(ServerRequestInterface $request)
/**
* {@inheritdoc}
*/
- public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
+ public function canRespondToAuthorizationRequest(ServerRequestInterface $request): bool
{
return false;
}
@@ -607,7 +520,7 @@ public function canRespondToAuthorizationRequest(ServerRequestInterface $request
/**
* {@inheritdoc}
*/
- public function validateAuthorizationRequest(ServerRequestInterface $request)
+ public function validateAuthorizationRequest(ServerRequestInterface $request): AuthorizationRequestInterface
{
throw new LogicException('This grant cannot validate an authorization request');
}
@@ -615,8 +528,56 @@ public function validateAuthorizationRequest(ServerRequestInterface $request)
/**
* {@inheritdoc}
*/
- public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
+ public function completeAuthorizationRequest(AuthorizationRequestInterface $authorizationRequest): ResponseTypeInterface
{
throw new LogicException('This grant cannot complete an authorization request');
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function canRespondToDeviceAuthorizationRequest(ServerRequestInterface $request): bool
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function respondToDeviceAuthorizationRequest(ServerRequestInterface $request): DeviceCodeResponse
+ {
+ throw new LogicException('This grant cannot validate a device authorization request');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function completeDeviceAuthorizationRequest(string $deviceCode, string $userId, bool $userApproved): void
+ {
+ throw new LogicException('This grant cannot complete a device authorization request');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setIntervalVisibility(bool $intervalVisibility): void
+ {
+ throw new LogicException('This grant does not support the interval parameter');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getIntervalVisibility(): bool
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setIncludeVerificationUriComplete(bool $includeVerificationUriComplete): void
+ {
+ throw new LogicException('This grant does not support the verification_uri_complete parameter');
+ }
}
diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php
index 8336cf649..83bddac18 100644
--- a/src/Grant/AuthCodeGrant.php
+++ b/src/Grant/AuthCodeGrant.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Grant;
use DateInterval;
@@ -23,48 +26,51 @@
use League\OAuth2\Server\RequestAccessTokenEvent;
use League\OAuth2\Server\RequestEvent;
use League\OAuth2\Server\RequestRefreshTokenEvent;
-use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
+use League\OAuth2\Server\RequestTypes\AuthorizationRequestInterface;
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use LogicException;
use Psr\Http\Message\ServerRequestInterface;
use stdClass;
+use function array_key_exists;
+use function array_keys;
+use function array_map;
+use function count;
+use function hash_algos;
+use function implode;
+use function in_array;
+use function is_array;
+use function is_string;
+use function json_decode;
+use function json_encode;
+use function preg_match;
+use function property_exists;
+use function sprintf;
+use function time;
+
class AuthCodeGrant extends AbstractAuthorizeGrant
{
- /**
- * @var DateInterval
- */
- private $authCodeTTL;
-
- /**
- * @var bool
- */
- private $requireCodeChallengeForPublicClients = true;
+ private bool $requireCodeChallengeForPublicClients = true;
/**
* @var CodeChallengeVerifierInterface[]
*/
- private $codeChallengeVerifiers = [];
+ private array $codeChallengeVerifiers = [];
/**
- * @param AuthCodeRepositoryInterface $authCodeRepository
- * @param RefreshTokenRepositoryInterface $refreshTokenRepository
- * @param DateInterval $authCodeTTL
- *
* @throws Exception
*/
public function __construct(
AuthCodeRepositoryInterface $authCodeRepository,
RefreshTokenRepositoryInterface $refreshTokenRepository,
- DateInterval $authCodeTTL
+ private DateInterval $authCodeTTL
) {
$this->setAuthCodeRepository($authCodeRepository);
$this->setRefreshTokenRepository($refreshTokenRepository);
- $this->authCodeTTL = $authCodeTTL;
$this->refreshTokenTTL = new DateInterval('P1M');
- if (\in_array('sha256', \hash_algos(), true)) {
+ if (in_array('sha256', hash_algos(), true)) {
$s256Verifier = new S256Verifier();
$this->codeChallengeVerifiers[$s256Verifier->getMethod()] = $s256Verifier;
}
@@ -76,7 +82,7 @@ public function __construct(
/**
* Disable the requirement for a code challenge for public clients.
*/
- public function disableRequireCodeChallengeForPublicClients()
+ public function disableRequireCodeChallengeForPublicClients(): void
{
$this->requireCodeChallengeForPublicClients = false;
}
@@ -84,19 +90,13 @@ public function disableRequireCodeChallengeForPublicClients()
/**
* Respond to an access token request.
*
- * @param ServerRequestInterface $request
- * @param ResponseTypeInterface $responseType
- * @param DateInterval $accessTokenTTL
- *
* @throws OAuthServerException
- *
- * @return ResponseTypeInterface
*/
public function respondToAccessTokenRequest(
ServerRequestInterface $request,
ResponseTypeInterface $responseType,
DateInterval $accessTokenTTL
- ) {
+ ): ResponseTypeInterface {
list($clientId) = $this->getClientCredentials($request);
$client = $this->getClientEntityOrFail($clientId, $request);
@@ -108,12 +108,12 @@ public function respondToAccessTokenRequest(
$encryptedAuthCode = $this->getRequestParameter('code', $request, null);
- if (!\is_string($encryptedAuthCode)) {
+ if (!is_string($encryptedAuthCode)) {
throw OAuthServerException::invalidRequest('code');
}
try {
- $authCodePayload = \json_decode($this->decrypt($encryptedAuthCode));
+ $authCodePayload = json_decode($this->decrypt($encryptedAuthCode));
$this->validateAuthorizationCode($authCodePayload, $client, $request);
@@ -121,7 +121,8 @@ public function respondToAccessTokenRequest(
$this->validateScopes($authCodePayload->scopes),
$this->getIdentifier(),
$client,
- $authCodePayload->user_id
+ $authCodePayload->user_id,
+ $authCodePayload->auth_code_id
);
} catch (LogicException $e) {
throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code', $e);
@@ -130,14 +131,14 @@ public function respondToAccessTokenRequest(
$codeVerifier = $this->getRequestParameter('code_verifier', $request, null);
// If a code challenge isn't present but a code verifier is, reject the request to block PKCE downgrade attack
- if (empty($authCodePayload->code_challenge) && $codeVerifier !== null) {
+ if (!isset($authCodePayload->code_challenge) && $codeVerifier !== null) {
throw OAuthServerException::invalidRequest(
'code_challenge',
'code_verifier received when no code_challenge is present'
);
}
- if (!empty($authCodePayload->code_challenge)) {
+ if (isset($authCodePayload->code_challenge)) {
$this->validateCodeChallenge($authCodePayload, $codeVerifier);
}
@@ -160,7 +161,7 @@ public function respondToAccessTokenRequest(
return $responseType;
}
- private function validateCodeChallenge($authCodePayload, $codeVerifier)
+ private function validateCodeChallenge(object $authCodePayload, ?string $codeVerifier): void
{
if ($codeVerifier === null) {
throw OAuthServerException::invalidRequest('code_verifier');
@@ -168,23 +169,23 @@ private function validateCodeChallenge($authCodePayload, $codeVerifier)
// Validate code_verifier according to RFC-7636
// @see: https://tools.ietf.org/html/rfc7636#section-4.1
- if (\preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeVerifier) !== 1) {
+ if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeVerifier) !== 1) {
throw OAuthServerException::invalidRequest(
'code_verifier',
'Code Verifier must follow the specifications of RFC-7636.'
);
}
- if (\property_exists($authCodePayload, 'code_challenge_method')) {
+ if (property_exists($authCodePayload, 'code_challenge_method')) {
if (isset($this->codeChallengeVerifiers[$authCodePayload->code_challenge_method])) {
$codeChallengeVerifier = $this->codeChallengeVerifiers[$authCodePayload->code_challenge_method];
- if ($codeChallengeVerifier->verifyCodeChallenge($codeVerifier, $authCodePayload->code_challenge) === false) {
+ if (!isset($authCodePayload->code_challenge) || $codeChallengeVerifier->verifyCodeChallenge($codeVerifier, $authCodePayload->code_challenge) === false) {
throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.');
}
} else {
throw OAuthServerException::serverError(
- \sprintf(
+ sprintf(
'Unsupported code challenge method `%s`',
$authCodePayload->code_challenge_method
)
@@ -195,26 +196,22 @@ private function validateCodeChallenge($authCodePayload, $codeVerifier)
/**
* Validate the authorization code.
- *
- * @param stdClass $authCodePayload
- * @param ClientEntityInterface $client
- * @param ServerRequestInterface $request
*/
private function validateAuthorizationCode(
- $authCodePayload,
+ stdClass $authCodePayload,
ClientEntityInterface $client,
ServerRequestInterface $request
- ) {
- if (!\property_exists($authCodePayload, 'auth_code_id')) {
+ ): void {
+ if (!property_exists($authCodePayload, 'auth_code_id')) {
throw OAuthServerException::invalidRequest('code', 'Authorization code malformed');
}
- if (\time() > $authCodePayload->expire_time) {
+ 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');
+ throw OAuthServerException::invalidGrant('Authorization code has been revoked');
}
if ($authCodePayload->client_id !== $client->getIdentifier()) {
@@ -223,7 +220,7 @@ private function validateAuthorizationCode(
// The redirect URI is required in this request
$redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
- if (empty($authCodePayload->redirect_uri) === false && $redirectUri === null) {
+ if ($authCodePayload->redirect_uri !== '' && $redirectUri === null) {
throw OAuthServerException::invalidRequest('redirect_uri');
}
@@ -234,10 +231,8 @@ private function validateAuthorizationCode(
/**
* Return the grant identifier that can be used in matching up requests.
- *
- * @return string
*/
- public function getIdentifier()
+ public function getIdentifier(): string
{
return 'authorization_code';
}
@@ -245,10 +240,10 @@ public function getIdentifier()
/**
* {@inheritdoc}
*/
- public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
+ public function canRespondToAuthorizationRequest(ServerRequestInterface $request): bool
{
return (
- \array_key_exists('response_type', $request->getQueryParams())
+ array_key_exists('response_type', $request->getQueryParams())
&& $request->getQueryParams()['response_type'] === 'code'
&& isset($request->getQueryParams()['client_id'])
);
@@ -257,7 +252,7 @@ public function canRespondToAuthorizationRequest(ServerRequestInterface $request
/**
* {@inheritdoc}
*/
- public function validateAuthorizationRequest(ServerRequestInterface $request)
+ public function validateAuthorizationRequest(ServerRequestInterface $request): AuthorizationRequestInterface
{
$clientId = $this->getQueryStringParameter(
'client_id',
@@ -274,19 +269,17 @@ public function validateAuthorizationRequest(ServerRequestInterface $request)
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
if ($redirectUri !== null) {
- if (!\is_string($redirectUri)) {
- throw OAuthServerException::invalidRequest('redirect_uri');
- }
-
$this->validateRedirectUri($redirectUri, $client, $request);
- } elseif (empty($client->getRedirectUri()) ||
- (\is_array($client->getRedirectUri()) && \count($client->getRedirectUri()) !== 1)) {
+ } elseif (
+ $client->getRedirectUri() === '' ||
+ (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1)
+ ) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient($request);
}
- $defaultClientRedirectUri = \is_array($client->getRedirectUri())
+ $defaultClientRedirectUri = is_array($client->getRedirectUri())
? $client->getRedirectUri()[0]
: $client->getRedirectUri();
@@ -297,7 +290,7 @@ public function validateAuthorizationRequest(ServerRequestInterface $request)
$stateParameter = $this->getQueryStringParameter('state', $request);
- $authorizationRequest = new AuthorizationRequest();
+ $authorizationRequest = $this->createAuthorizationRequest();
$authorizationRequest->setGrantTypeId($this->getIdentifier());
$authorizationRequest->setClient($client);
$authorizationRequest->setRedirectUri($redirectUri);
@@ -313,21 +306,28 @@ public function validateAuthorizationRequest(ServerRequestInterface $request)
if ($codeChallenge !== null) {
$codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain');
- if (\array_key_exists($codeChallengeMethod, $this->codeChallengeVerifiers) === false) {
+ if ($codeChallengeMethod === null) {
+ throw OAuthServerException::invalidRequest(
+ 'code_challenge_method',
+ 'Code challenge method must be provided when `code_challenge` is set.'
+ );
+ }
+
+ if (array_key_exists($codeChallengeMethod, $this->codeChallengeVerifiers) === false) {
throw OAuthServerException::invalidRequest(
'code_challenge_method',
- 'Code challenge method must be one of ' . \implode(', ', \array_map(
+ 'Code challenge method must be one of ' . implode(', ', array_map(
function ($method) {
return '`' . $method . '`';
},
- \array_keys($this->codeChallengeVerifiers)
+ array_keys($this->codeChallengeVerifiers)
))
);
}
// Validate code_challenge according to RFC-7636
// @see: https://tools.ietf.org/html/rfc7636#section-4.2
- if (\preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeChallenge) !== 1) {
+ if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeChallenge) !== 1) {
throw OAuthServerException::invalidRequest(
'code_challenge',
'Code challenge must follow the specifications of RFC-7636.'
@@ -346,7 +346,7 @@ function ($method) {
/**
* {@inheritdoc}
*/
- public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
+ public function completeAuthorizationRequest(AuthorizationRequestInterface $authorizationRequest): ResponseTypeInterface
{
if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) {
throw new LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest');
@@ -376,7 +376,7 @@ public function completeAuthorizationRequest(AuthorizationRequest $authorization
'code_challenge_method' => $authorizationRequest->getCodeChallengeMethod(),
];
- $jsonPayload = \json_encode($payload);
+ $jsonPayload = json_encode($payload);
if ($jsonPayload === false) {
throw new LogicException('An error was encountered when JSON encoding the authorization request response');
@@ -410,14 +410,10 @@ public function completeAuthorizationRequest(AuthorizationRequest $authorization
/**
* Get the client redirect URI if not set in the request.
- *
- * @param AuthorizationRequest $authorizationRequest
- *
- * @return string
*/
- private function getClientRedirectUri(AuthorizationRequest $authorizationRequest)
+ private function getClientRedirectUri(AuthorizationRequestInterface $authorizationRequest): string
{
- return \is_array($authorizationRequest->getClient()->getRedirectUri())
+ return is_array($authorizationRequest->getClient()->getRedirectUri())
? $authorizationRequest->getClient()->getRedirectUri()[0]
: $authorizationRequest->getClient()->getRedirectUri();
}
diff --git a/src/Grant/ClientCredentialsGrant.php b/src/Grant/ClientCredentialsGrant.php
index d342b269f..bee6abaa1 100644
--- a/src/Grant/ClientCredentialsGrant.php
+++ b/src/Grant/ClientCredentialsGrant.php
@@ -1,4 +1,5 @@
getClientCredentials($request);
$client = $this->getClientEntityOrFail($clientId, $request);
@@ -64,7 +67,7 @@ public function respondToAccessTokenRequest(
/**
* {@inheritdoc}
*/
- public function getIdentifier()
+ public function getIdentifier(): string
{
return 'client_credentials';
}
diff --git a/src/Grant/DeviceCodeGrant.php b/src/Grant/DeviceCodeGrant.php
new file mode 100644
index 000000000..89568a19b
--- /dev/null
+++ b/src/Grant/DeviceCodeGrant.php
@@ -0,0 +1,332 @@
+
+ * @copyright Copyright (c) Alex Bilbie
+ * @license http://mit-license.org/
+ *
+ * @link https://github.com/thephpleague/oauth2-server
+ */
+
+declare(strict_types=1);
+
+namespace League\OAuth2\Server\Grant;
+
+use DateInterval;
+use DateTimeImmutable;
+use Error;
+use Exception;
+use League\OAuth2\Server\Entities\ClientEntityInterface;
+use League\OAuth2\Server\Entities\DeviceCodeEntityInterface;
+use League\OAuth2\Server\Entities\ScopeEntityInterface;
+use League\OAuth2\Server\Exception\OAuthServerException;
+use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
+use League\OAuth2\Server\Repositories\DeviceCodeRepositoryInterface;
+use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
+use League\OAuth2\Server\RequestEvent;
+use League\OAuth2\Server\ResponseTypes\DeviceCodeResponse;
+use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use TypeError;
+
+use function is_null;
+use function random_int;
+use function strlen;
+use function time;
+
+/**
+ * Device Code grant class.
+ */
+class DeviceCodeGrant extends AbstractGrant
+{
+ protected DeviceCodeRepositoryInterface $deviceCodeRepository;
+ private bool $includeVerificationUriComplete = false;
+ private bool $intervalVisibility = false;
+ private string $verificationUri;
+
+ public function __construct(
+ DeviceCodeRepositoryInterface $deviceCodeRepository,
+ RefreshTokenRepositoryInterface $refreshTokenRepository,
+ private DateInterval $deviceCodeTTL,
+ string $verificationUri,
+ private int $retryInterval = 5
+ ) {
+ $this->setDeviceCodeRepository($deviceCodeRepository);
+ $this->setRefreshTokenRepository($refreshTokenRepository);
+
+ $this->refreshTokenTTL = new DateInterval('P1M');
+
+ $this->setVerificationUri($verificationUri);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function canRespondToDeviceAuthorizationRequest(ServerRequestInterface $request): bool
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function respondToDeviceAuthorizationRequest(ServerRequestInterface $request): DeviceCodeResponse
+ {
+ $clientId = $this->getRequestParameter(
+ 'client_id',
+ $request,
+ $this->getServerParameter('PHP_AUTH_USER', $request)
+ );
+
+ if ($clientId === null) {
+ throw OAuthServerException::invalidRequest('client_id');
+ }
+
+ $client = $this->getClientEntityOrFail($clientId, $request);
+
+ $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope));
+
+ $deviceCodeEntity = $this->issueDeviceCode(
+ $this->deviceCodeTTL,
+ $client,
+ $this->verificationUri,
+ $scopes
+ );
+
+ $response = new DeviceCodeResponse();
+
+ if ($this->includeVerificationUriComplete === true) {
+ $response->includeVerificationUriComplete();
+ }
+
+ $response->setDeviceCodeEntity($deviceCodeEntity);
+
+ return $response;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function completeDeviceAuthorizationRequest(string $deviceCode, string $userId, bool $userApproved): void
+ {
+ $deviceCode = $this->deviceCodeRepository->getDeviceCodeEntityByDeviceCode($deviceCode);
+
+ if ($deviceCode instanceof DeviceCodeEntityInterface === false) {
+ throw OAuthServerException::invalidRequest('device_code', 'Device code does not exist');
+ }
+
+ if ($userId === '') {
+ throw OAuthServerException::invalidRequest('user_id', 'User ID is required');
+ }
+
+ $deviceCode->setUserIdentifier($userId);
+ $deviceCode->setUserApproved($userApproved);
+
+ $this->deviceCodeRepository->persistDeviceCode($deviceCode);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function respondToAccessTokenRequest(
+ ServerRequestInterface $request,
+ ResponseTypeInterface $responseType,
+ DateInterval $accessTokenTTL
+ ): ResponseTypeInterface {
+ // Validate request
+ $client = $this->validateClient($request);
+ $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope));
+ $deviceCodeEntity = $this->validateDeviceCode($request, $client);
+
+ $deviceCodeEntity->setLastPolledAt(new DateTimeImmutable());
+ $this->deviceCodeRepository->persistDeviceCode($deviceCodeEntity);
+
+ // If device code has no user associated, respond with pending
+ if (is_null($deviceCodeEntity->getUserIdentifier())) {
+ throw OAuthServerException::authorizationPending();
+ }
+
+ if ($deviceCodeEntity->getUserApproved() === false) {
+ throw OAuthServerException::accessDenied();
+ }
+
+ // Finalize the requested scopes
+ $finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, (string) $deviceCodeEntity->getUserIdentifier());
+
+ // Issue and persist new access token
+ $accessToken = $this->issueAccessToken($accessTokenTTL, $client, (string) $deviceCodeEntity->getUserIdentifier(), $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);
+
+ if ($refreshToken !== null) {
+ $this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
+ $responseType->setRefreshToken($refreshToken);
+ }
+
+ $this->deviceCodeRepository->revokeDeviceCode($deviceCodeEntity->getIdentifier());
+
+ return $responseType;
+ }
+
+ /**
+ * @throws OAuthServerException
+ */
+ protected function validateDeviceCode(ServerRequestInterface $request, ClientEntityInterface $client): DeviceCodeEntityInterface
+ {
+ $deviceCode = $this->getRequestParameter('device_code', $request);
+
+ if (is_null($deviceCode)) {
+ throw OAuthServerException::invalidRequest('device_code');
+ }
+
+ $deviceCodeEntity = $this->deviceCodeRepository->getDeviceCodeEntityByDeviceCode(
+ $deviceCode
+ );
+
+ if ($deviceCodeEntity instanceof DeviceCodeEntityInterface === false) {
+ $this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
+
+ throw OAuthServerException::invalidGrant();
+ }
+
+ if (time() > $deviceCodeEntity->getExpiryDateTime()->getTimestamp()) {
+ throw OAuthServerException::expiredToken('device_code');
+ }
+
+ if ($this->deviceCodeRepository->isDeviceCodeRevoked($deviceCode) === true) {
+ throw OAuthServerException::invalidRequest('device_code', 'Device code has been revoked');
+ }
+
+ if ($deviceCodeEntity->getClient()->getIdentifier() !== $client->getIdentifier()) {
+ throw OAuthServerException::invalidRequest('device_code', 'Device code was not issued to this client');
+ }
+
+ if ($this->deviceCodePolledTooSoon($deviceCodeEntity->getLastPolledAt()) === true) {
+ throw OAuthServerException::slowDown();
+ }
+
+ return $deviceCodeEntity;
+ }
+
+ private function deviceCodePolledTooSoon(?DateTimeImmutable $lastPoll): bool
+ {
+ return $lastPoll !== null && $lastPoll->getTimestamp() + $this->retryInterval > time();
+ }
+
+ /**
+ * Set the verification uri
+ */
+ public function setVerificationUri(string $verificationUri): void
+ {
+ $this->verificationUri = $verificationUri;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getIdentifier(): string
+ {
+ return 'urn:ietf:params:oauth:grant-type:device_code';
+ }
+
+ private function setDeviceCodeRepository(DeviceCodeRepositoryInterface $deviceCodeRepository): void
+ {
+ $this->deviceCodeRepository = $deviceCodeRepository;
+ }
+
+ /**
+ * Issue a device code.
+ *
+ * @param ScopeEntityInterface[] $scopes
+ *
+ * @throws OAuthServerException
+ * @throws UniqueTokenIdentifierConstraintViolationException
+ */
+ protected function issueDeviceCode(
+ DateInterval $deviceCodeTTL,
+ ClientEntityInterface $client,
+ string $verificationUri,
+ array $scopes = [],
+ ): DeviceCodeEntityInterface {
+ $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
+
+ $deviceCode = $this->deviceCodeRepository->getNewDeviceCode();
+ $deviceCode->setExpiryDateTime((new DateTimeImmutable())->add($deviceCodeTTL));
+ $deviceCode->setClient($client);
+ $deviceCode->setVerificationUri($verificationUri);
+
+ if ($this->getIntervalVisibility() === true) {
+ $deviceCode->setInterval($this->retryInterval);
+ }
+
+ foreach ($scopes as $scope) {
+ $deviceCode->addScope($scope);
+ }
+
+ while ($maxGenerationAttempts-- > 0) {
+ $deviceCode->setIdentifier($this->generateUniqueIdentifier());
+ $deviceCode->setUserCode($this->generateUserCode());
+
+ try {
+ $this->deviceCodeRepository->persistDeviceCode($deviceCode);
+
+ return $deviceCode;
+ } catch (UniqueTokenIdentifierConstraintViolationException $e) {
+ if ($maxGenerationAttempts === 0) {
+ throw $e;
+ }
+ }
+ }
+
+ // This should never be hit. It is here to work around a PHPStan false error
+ return $deviceCode;
+ }
+
+ /**
+ * Generate a new user code.
+ *
+ * @throws OAuthServerException
+ */
+ protected function generateUserCode(int $length = 8): string
+ {
+ try {
+ $userCode = '';
+ $userCodeCharacters = 'BCDFGHJKLMNPQRSTVWXZ';
+
+ while (strlen($userCode) < $length) {
+ $userCode .= $userCodeCharacters[random_int(0, 19)];
+ }
+
+ return $userCode;
+ // @codeCoverageIgnoreStart
+ } 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', $e);
+ }
+ // @codeCoverageIgnoreEnd
+ }
+
+ public function setIntervalVisibility(bool $intervalVisibility): void
+ {
+ $this->intervalVisibility = $intervalVisibility;
+ }
+
+ public function getIntervalVisibility(): bool
+ {
+ return $this->intervalVisibility;
+ }
+
+ public function setIncludeVerificationUriComplete(bool $includeVerificationUriComplete): void
+ {
+ $this->includeVerificationUriComplete = $includeVerificationUriComplete;
+ }
+}
diff --git a/src/Grant/GrantTypeInterface.php b/src/Grant/GrantTypeInterface.php
index 41ebeb5ff..4e7dcf0f0 100644
--- a/src/Grant/GrantTypeInterface.php
+++ b/src/Grant/GrantTypeInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Grant;
use DateInterval;
@@ -14,50 +17,35 @@
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\RequestEvent;
-use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
+use League\OAuth2\Server\RequestTypes\AuthorizationRequestInterface;
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use LogicException;
use Psr\Http\Message\ServerRequestInterface;
+use function count;
+use function is_array;
+use function is_null;
+use function time;
+
class ImplicitGrant extends AbstractAuthorizeGrant
{
- /**
- * @var DateInterval
- */
- private $accessTokenTTL;
-
- /**
- * @var string
- */
- private $queryDelimiter;
-
- /**
- * @param DateInterval $accessTokenTTL
- * @param string $queryDelimiter
- */
- public function __construct(DateInterval $accessTokenTTL, $queryDelimiter = '#')
+ public function __construct(private DateInterval $accessTokenTTL, private string $queryDelimiter = '#')
{
- $this->accessTokenTTL = $accessTokenTTL;
- $this->queryDelimiter = $queryDelimiter;
}
/**
- * @param DateInterval $refreshTokenTTL
- *
- * @throw LogicException
+ * @throws LogicException
*/
- public function setRefreshTokenTTL(DateInterval $refreshTokenTTL)
+ public function setRefreshTokenTTL(DateInterval $refreshTokenTTL): void
{
throw new LogicException('The Implicit Grant does not return refresh tokens');
}
/**
- * @param RefreshTokenRepositoryInterface $refreshTokenRepository
- *
- * @throw LogicException
+ * @throws LogicException
*/
- public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
+ public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository): void
{
throw new LogicException('The Implicit Grant does not return refresh tokens');
}
@@ -65,42 +53,34 @@ public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refre
/**
* {@inheritdoc}
*/
- public function canRespondToAccessTokenRequest(ServerRequestInterface $request)
+ public function canRespondToAccessTokenRequest(ServerRequestInterface $request): bool
{
return false;
}
/**
* Return the grant identifier that can be used in matching up requests.
- *
- * @return string
*/
- public function getIdentifier()
+ public function getIdentifier(): string
{
return 'implicit';
}
/**
* Respond to an incoming request.
- *
- * @param ServerRequestInterface $request
- * @param ResponseTypeInterface $responseType
- * @param DateInterval $accessTokenTTL
- *
- * @return ResponseTypeInterface
*/
public function respondToAccessTokenRequest(
ServerRequestInterface $request,
ResponseTypeInterface $responseType,
DateInterval $accessTokenTTL
- ) {
+ ): ResponseTypeInterface {
throw new LogicException('This grant does not used this method');
}
/**
* {@inheritdoc}
*/
- public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
+ public function canRespondToAuthorizationRequest(ServerRequestInterface $request): bool
{
return (
isset($request->getQueryParams()['response_type'])
@@ -112,7 +92,7 @@ public function canRespondToAuthorizationRequest(ServerRequestInterface $request
/**
* {@inheritdoc}
*/
- public function validateAuthorizationRequest(ServerRequestInterface $request)
+ public function validateAuthorizationRequest(ServerRequestInterface $request): AuthorizationRequestInterface
{
$clientId = $this->getQueryStringParameter(
'client_id',
@@ -120,7 +100,7 @@ public function validateAuthorizationRequest(ServerRequestInterface $request)
$this->getServerParameter('PHP_AUTH_USER', $request)
);
- if (\is_null($clientId)) {
+ if (is_null($clientId)) {
throw OAuthServerException::invalidRequest('client_id');
}
@@ -129,17 +109,15 @@ public function validateAuthorizationRequest(ServerRequestInterface $request)
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
if ($redirectUri !== null) {
- if (!\is_string($redirectUri)) {
- throw OAuthServerException::invalidRequest('redirect_uri');
- }
-
$this->validateRedirectUri($redirectUri, $client, $request);
- } elseif (\is_array($client->getRedirectUri()) && \count($client->getRedirectUri()) !== 1
- || empty($client->getRedirectUri())) {
+ } elseif (
+ is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1
+ || $client->getRedirectUri() === ''
+ ) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient($request);
} else {
- $redirectUri = \is_array($client->getRedirectUri())
+ $redirectUri = is_array($client->getRedirectUri())
? $client->getRedirectUri()[0]
: $client->getRedirectUri();
}
@@ -152,11 +130,7 @@ public function validateAuthorizationRequest(ServerRequestInterface $request)
$stateParameter = $this->getQueryStringParameter('state', $request);
- if ($stateParameter !== null && !\is_string($stateParameter)) {
- throw OAuthServerException::invalidRequest('state');
- }
-
- $authorizationRequest = new AuthorizationRequest();
+ $authorizationRequest = $this->createAuthorizationRequest();
$authorizationRequest->setGrantTypeId($this->getIdentifier());
$authorizationRequest->setClient($client);
$authorizationRequest->setRedirectUri($redirectUri);
@@ -173,17 +147,17 @@ public function validateAuthorizationRequest(ServerRequestInterface $request)
/**
* {@inheritdoc}
*/
- public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
+ public function completeAuthorizationRequest(AuthorizationRequestInterface $authorizationRequest): ResponseTypeInterface
{
if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) {
throw new LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest');
}
- $finalRedirectUri = ($authorizationRequest->getRedirectUri() === null)
- ? \is_array($authorizationRequest->getClient()->getRedirectUri())
+ $clientRegisteredRedirectUri = is_array($authorizationRequest->getClient()->getRedirectUri())
? $authorizationRequest->getClient()->getRedirectUri()[0]
- : $authorizationRequest->getClient()->getRedirectUri()
- : $authorizationRequest->getRedirectUri();
+ : $authorizationRequest->getClient()->getRedirectUri();
+
+ $finalRedirectUri = $authorizationRequest->getRedirectUri() ?? $clientRegisteredRedirectUri;
// The user approved the client, redirect them back with an access token
if ($authorizationRequest->isAuthorizationApproved() === true) {
@@ -207,9 +181,9 @@ public function completeAuthorizationRequest(AuthorizationRequest $authorization
$this->makeRedirectUri(
$finalRedirectUri,
[
- 'access_token' => (string) $accessToken,
+ 'access_token' => $accessToken->toString(),
'token_type' => 'Bearer',
- 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - \time(),
+ 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - time(),
'state' => $authorizationRequest->getState(),
],
$this->queryDelimiter
diff --git a/src/Grant/PasswordGrant.php b/src/Grant/PasswordGrant.php
index fd32d2688..1b92dc1da 100644
--- a/src/Grant/PasswordGrant.php
+++ b/src/Grant/PasswordGrant.php
@@ -1,4 +1,5 @@
validateClient($request);
$scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope));
$user = $this->validateUser($request, $client);
- // 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 access token
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes);
@@ -75,24 +80,19 @@ public function respondToAccessTokenRequest(
}
/**
- * @param ServerRequestInterface $request
- * @param ClientEntityInterface $client
- *
* @throws OAuthServerException
- *
- * @return UserEntityInterface
*/
- protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client)
+ protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client): UserEntityInterface
{
$username = $this->getRequestParameter('username', $request);
- if (!\is_string($username)) {
+ if (!is_string($username)) {
throw OAuthServerException::invalidRequest('username');
}
$password = $this->getRequestParameter('password', $request);
- if (!\is_string($password)) {
+ if (!is_string($password)) {
throw OAuthServerException::invalidRequest('password');
}
@@ -115,7 +115,7 @@ protected function validateUser(ServerRequestInterface $request, ClientEntityInt
/**
* {@inheritdoc}
*/
- public function getIdentifier()
+ public function getIdentifier(): string
{
return 'password';
}
diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php
index 2dedf15c3..5a5b55e30 100644
--- a/src/Grant/RefreshTokenGrant.php
+++ b/src/Grant/RefreshTokenGrant.php
@@ -1,4 +1,5 @@
setRefreshTokenRepository($refreshTokenRepository);
@@ -43,7 +49,7 @@ public function respondToAccessTokenRequest(
ServerRequestInterface $request,
ResponseTypeInterface $responseType,
DateInterval $accessTokenTTL
- ) {
+ ): ResponseTypeInterface {
// Validate request
$client = $this->validateClient($request);
$oldRefreshToken = $this->validateOldRefreshToken($request, $client->getIdentifier());
@@ -51,18 +57,20 @@ public function respondToAccessTokenRequest(
$this->getRequestParameter(
'scope',
$request,
- \implode(self::SCOPE_DELIMITER_STRING, $oldRefreshToken['scopes'])
+ implode(self::SCOPE_DELIMITER_STRING, $oldRefreshToken['scopes'])
)
);
// The OAuth spec says that a refreshed access token can have the original scopes or fewer so ensure
// the request doesn't include any new scopes
foreach ($scopes as $scope) {
- if (\in_array($scope->getIdentifier(), $oldRefreshToken['scopes'], true) === false) {
+ if (in_array($scope->getIdentifier(), $oldRefreshToken['scopes'], true) === false) {
throw OAuthServerException::invalidScope($scope->getIdentifier());
}
}
+ $scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client);
+
// Expire old tokens
$this->accessTokenRepository->revokeAccessToken($oldRefreshToken['access_token_id']);
if ($this->revokeRefreshTokens) {
@@ -88,17 +96,14 @@ public function respondToAccessTokenRequest(
}
/**
- * @param ServerRequestInterface $request
- * @param string $clientId
- *
* @throws OAuthServerException
*
- * @return array
+ * @return array
*/
- protected function validateOldRefreshToken(ServerRequestInterface $request, $clientId)
+ protected function validateOldRefreshToken(ServerRequestInterface $request, string $clientId): array
{
$encryptedRefreshToken = $this->getRequestParameter('refresh_token', $request);
- if (!\is_string($encryptedRefreshToken)) {
+ if (!is_string($encryptedRefreshToken)) {
throw OAuthServerException::invalidRequest('refresh_token');
}
@@ -109,13 +114,13 @@ protected function validateOldRefreshToken(ServerRequestInterface $request, $cli
throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token', $e);
}
- $refreshTokenData = \json_decode($refreshToken, true);
+ $refreshTokenData = json_decode($refreshToken, true);
if ($refreshTokenData['client_id'] !== $clientId) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_CLIENT_FAILED, $request));
throw OAuthServerException::invalidRefreshToken('Token is not linked to client');
}
- if ($refreshTokenData['expire_time'] < \time()) {
+ if ($refreshTokenData['expire_time'] < time()) {
throw OAuthServerException::invalidRefreshToken('Token has expired');
}
@@ -129,7 +134,7 @@ protected function validateOldRefreshToken(ServerRequestInterface $request, $cli
/**
* {@inheritdoc}
*/
- public function getIdentifier()
+ public function getIdentifier(): string
{
return 'refresh_token';
}
diff --git a/src/Middleware/AuthorizationServerMiddleware.php b/src/Middleware/AuthorizationServerMiddleware.php
index 9b78b4585..e59d35590 100644
--- a/src/Middleware/AuthorizationServerMiddleware.php
+++ b/src/Middleware/AuthorizationServerMiddleware.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,9 +8,10 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Middleware;
-use Exception;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use Psr\Http\Message\ResponseInterface;
@@ -17,37 +19,16 @@
class AuthorizationServerMiddleware
{
- /**
- * @var AuthorizationServer
- */
- private $server;
-
- /**
- * @param AuthorizationServer $server
- */
- public function __construct(AuthorizationServer $server)
+ public function __construct(private AuthorizationServer $server)
{
- $this->server = $server;
}
- /**
- * @param ServerRequestInterface $request
- * @param ResponseInterface $response
- * @param callable $next
- *
- * @return ResponseInterface
- */
- public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
+ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next): ResponseInterface
{
try {
$response = $this->server->respondToAccessTokenRequest($request, $response);
} catch (OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
- // @codeCoverageIgnoreStart
- } catch (Exception $exception) {
- return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
- ->generateHttpResponse($response);
- // @codeCoverageIgnoreEnd
}
// Pass the request and response on to the next responder in the chain
diff --git a/src/Middleware/ResourceServerMiddleware.php b/src/Middleware/ResourceServerMiddleware.php
index e152a9999..460e77712 100644
--- a/src/Middleware/ResourceServerMiddleware.php
+++ b/src/Middleware/ResourceServerMiddleware.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,9 +8,10 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Middleware;
-use Exception;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\ResourceServer;
use Psr\Http\Message\ResponseInterface;
@@ -17,37 +19,16 @@
class ResourceServerMiddleware
{
- /**
- * @var ResourceServer
- */
- private $server;
-
- /**
- * @param ResourceServer $server
- */
- public function __construct(ResourceServer $server)
+ public function __construct(private ResourceServer $server)
{
- $this->server = $server;
}
- /**
- * @param ServerRequestInterface $request
- * @param ResponseInterface $response
- * @param callable $next
- *
- * @return ResponseInterface
- */
- public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
+ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next): ResponseInterface
{
try {
$request = $this->server->validateAuthenticatedRequest($request);
} catch (OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
- // @codeCoverageIgnoreStart
- } catch (Exception $exception) {
- return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
- ->generateHttpResponse($response);
- // @codeCoverageIgnoreEnd
}
// Pass the request and response on to the next responder in the chain
diff --git a/src/RedirectUriValidators/RedirectUriValidator.php b/src/RedirectUriValidators/RedirectUriValidator.php
index c758ad954..dd4b56834 100644
--- a/src/RedirectUriValidators/RedirectUriValidator.php
+++ b/src/RedirectUriValidators/RedirectUriValidator.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,42 +8,43 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\RedirectUriValidators;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\Uri;
+use function in_array;
+use function is_string;
+
class RedirectUriValidator implements RedirectUriValidatorInterface
{
/**
- * @var array
+ * @var string[]
*/
- private $allowedRedirectUris;
+ private array $allowedRedirectUris;
/**
* New validator instance for the given uri
*
- * @param string|array $allowedRedirectUris
+ * @param string[]|string $allowedRedirectUris
*/
- public function __construct($allowedRedirectUri)
+ public function __construct(array|string $allowedRedirectUris)
{
- if (\is_string($allowedRedirectUri)) {
- $this->allowedRedirectUris = [$allowedRedirectUri];
- } elseif (\is_array($allowedRedirectUri)) {
- $this->allowedRedirectUris = $allowedRedirectUri;
+ if (is_string($allowedRedirectUris)) {
+ $this->allowedRedirectUris = [$allowedRedirectUris];
} else {
- $this->allowedRedirectUris = [];
+ $this->allowedRedirectUris = $allowedRedirectUris;
}
}
/**
* Validates the redirect uri.
*
- * @param string $redirectUri
- *
* @return bool Return true if valid, false otherwise
*/
- public function validateRedirectUri($redirectUri)
+ public function validateRedirectUri(string $redirectUri): bool
{
if ($this->isLoopbackUri($redirectUri)) {
return $this->matchUriExcludingPort($redirectUri);
@@ -55,43 +57,31 @@ public function validateRedirectUri($redirectUri)
* According to section 7.3 of rfc8252, loopback uris are:
* - "http://127.0.0.1:{port}/{path}" for IPv4
* - "http://[::1]:{port}/{path}" for IPv6
- *
- * @param string $redirectUri
- *
- * @return bool
*/
- private function isLoopbackUri($redirectUri)
+ private function isLoopbackUri(string $redirectUri): bool
{
try {
- $uri = Uri::createFromString($redirectUri);
+ $uri = Uri::new($redirectUri);
} catch (SyntaxError $e) {
return false;
}
return $uri->getScheme() === 'http'
- && (\in_array($uri->getHost(), ['127.0.0.1', '[::1]'], true));
+ && (in_array($uri->getHost(), ['127.0.0.1', '[::1]'], true));
}
/**
* Find an exact match among allowed uris
- *
- * @param string $redirectUri
- *
- * @return bool Return true if an exact match is found, false otherwise
*/
- private function matchExactUri($redirectUri)
+ private function matchExactUri(string $redirectUri): bool
{
- return \in_array($redirectUri, $this->allowedRedirectUris, true);
+ return in_array($redirectUri, $this->allowedRedirectUris, true);
}
/**
* Find a match among allowed uris, allowing for different port numbers
- *
- * @param string $redirectUri
- *
- * @return bool Return true if a match is found, false otherwise
*/
- private function matchUriExcludingPort($redirectUri)
+ private function matchUriExcludingPort(string $redirectUri): bool
{
$parsedUrl = $this->parseUrlAndRemovePort($redirectUri);
@@ -106,14 +96,10 @@ private function matchUriExcludingPort($redirectUri)
/**
* Parse an url like \parse_url, excluding the port
- *
- * @param string $url
- *
- * @return array
*/
- private function parseUrlAndRemovePort($url)
+ private function parseUrlAndRemovePort(string $url): string
{
- $uri = Uri::createFromString($url);
+ $uri = Uri::new($url);
return (string) $uri->withPort(null);
}
diff --git a/src/RedirectUriValidators/RedirectUriValidatorInterface.php b/src/RedirectUriValidators/RedirectUriValidatorInterface.php
index d039085ab..df64a219c 100644
--- a/src/RedirectUriValidators/RedirectUriValidatorInterface.php
+++ b/src/RedirectUriValidators/RedirectUriValidatorInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,16 +8,14 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\RedirectUriValidators;
interface RedirectUriValidatorInterface
{
/**
* Validates the redirect uri.
- *
- * @param string $redirectUri
- *
- * @return bool Return true if valid, false otherwise
*/
- public function validateRedirectUri($redirectUri);
+ public function validateRedirectUri(string $redirectUri): bool;
}
diff --git a/src/Repositories/AccessTokenRepositoryInterface.php b/src/Repositories/AccessTokenRepositoryInterface.php
index 72ddf1f4c..d392716ed 100644
--- a/src/Repositories/AccessTokenRepositoryInterface.php
+++ b/src/Repositories/AccessTokenRepositoryInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
@@ -22,36 +25,20 @@ interface AccessTokenRepositoryInterface extends RepositoryInterface
/**
* Create a new access token
*
- * @param ClientEntityInterface $clientEntity
* @param ScopeEntityInterface[] $scopes
- * @param mixed $userIdentifier
- *
- * @return AccessTokenEntityInterface
*/
- public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null);
+ public function getNewToken(
+ ClientEntityInterface $clientEntity,
+ array $scopes,
+ mixed $userIdentifier = null
+ ): AccessTokenEntityInterface;
/**
- * Persists a new access token to permanent storage.
- *
- * @param AccessTokenEntityInterface $accessTokenEntity
- *
* @throws UniqueTokenIdentifierConstraintViolationException
*/
- public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity);
+ public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity): void;
- /**
- * Revoke an access token.
- *
- * @param string $tokenId
- */
- public function revokeAccessToken($tokenId);
+ public function revokeAccessToken(string $tokenId): void;
- /**
- * Check if the access token has been revoked.
- *
- * @param string $tokenId
- *
- * @return bool Return true if this token has been revoked
- */
- public function isAccessTokenRevoked($tokenId);
+ public function isAccessTokenRevoked(string $tokenId): bool;
}
diff --git a/src/Repositories/AuthCodeRepositoryInterface.php b/src/Repositories/AuthCodeRepositoryInterface.php
index 2dc285b83..89ff86b87 100644
--- a/src/Repositories/AuthCodeRepositoryInterface.php
+++ b/src/Repositories/AuthCodeRepositoryInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
@@ -17,35 +20,14 @@
*/
interface AuthCodeRepositoryInterface extends RepositoryInterface
{
- /**
- * Creates a new AuthCode
- *
- * @return AuthCodeEntityInterface
- */
- public function getNewAuthCode();
+ public function getNewAuthCode(): AuthCodeEntityInterface;
/**
- * Persists a new auth code to permanent storage.
- *
- * @param AuthCodeEntityInterface $authCodeEntity
- *
* @throws UniqueTokenIdentifierConstraintViolationException
*/
- public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity);
+ public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity): void;
- /**
- * Revoke an auth code.
- *
- * @param string $codeId
- */
- public function revokeAuthCode($codeId);
+ public function revokeAuthCode(string $codeId): void;
- /**
- * Check if the auth code has been revoked.
- *
- * @param string $codeId
- *
- * @return bool Return true if this code has been revoked
- */
- public function isAuthCodeRevoked($codeId);
+ public function isAuthCodeRevoked(string $codeId): bool;
}
diff --git a/src/Repositories/ClientRepositoryInterface.php b/src/Repositories/ClientRepositoryInterface.php
index 7eef494f4..63134ca9d 100644
--- a/src/Repositories/ClientRepositoryInterface.php
+++ b/src/Repositories/ClientRepositoryInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
@@ -18,21 +21,11 @@ interface ClientRepositoryInterface extends RepositoryInterface
{
/**
* Get a client.
- *
- * @param string $clientIdentifier The client's identifier
- *
- * @return ClientEntityInterface|null
*/
- public function getClientEntity($clientIdentifier);
+ public function getClientEntity(string $clientIdentifier): ?ClientEntityInterface;
/**
* Validate a client's secret.
- *
- * @param string $clientIdentifier The client's identifier
- * @param null|string $clientSecret The client's secret (if sent)
- * @param null|string $grantType The type of grant the client is using (if sent)
- *
- * @return bool
*/
- public function validateClient($clientIdentifier, $clientSecret, $grantType);
+ public function validateClient(string $clientIdentifier, ?string $clientSecret, ?string $grantType): bool;
}
diff --git a/src/Repositories/DeviceCodeRepositoryInterface.php b/src/Repositories/DeviceCodeRepositoryInterface.php
new file mode 100644
index 000000000..09575ab18
--- /dev/null
+++ b/src/Repositories/DeviceCodeRepositoryInterface.php
@@ -0,0 +1,50 @@
+
+ * @copyright Copyright (c) Alex Bilbie
+ * @license http://mit-license.org/
+ *
+ * @link https://github.com/thephpleague/oauth2-server
+ */
+
+declare(strict_types=1);
+
+namespace League\OAuth2\Server\Repositories;
+
+use League\OAuth2\Server\Entities\DeviceCodeEntityInterface;
+use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
+
+interface DeviceCodeRepositoryInterface extends RepositoryInterface
+{
+ /**
+ * Creates a new DeviceCode
+ */
+ public function getNewDeviceCode(): DeviceCodeEntityInterface;
+
+ /**
+ * Persists a device code to permanent storage.
+ *
+ * @throws UniqueTokenIdentifierConstraintViolationException
+ */
+ public function persistDeviceCode(DeviceCodeEntityInterface $deviceCodeEntity): void;
+
+ /**
+ * Get a device code entity.
+ */
+ public function getDeviceCodeEntityByDeviceCode(
+ string $deviceCodeEntity
+ ): ?DeviceCodeEntityInterface;
+
+ /**
+ * Revoke a device code.
+ */
+ public function revokeDeviceCode(string $codeId): void;
+
+ /**
+ * Check if the device code has been revoked.
+ *
+ * @return bool Return true if this code has been revoked
+ */
+ public function isDeviceCodeRevoked(string $codeId): bool;
+}
diff --git a/src/Repositories/RefreshTokenRepositoryInterface.php b/src/Repositories/RefreshTokenRepositoryInterface.php
index a769cf6d3..a25e50133 100644
--- a/src/Repositories/RefreshTokenRepositoryInterface.php
+++ b/src/Repositories/RefreshTokenRepositoryInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
@@ -17,35 +20,14 @@
*/
interface RefreshTokenRepositoryInterface extends RepositoryInterface
{
- /**
- * Creates a new refresh token
- *
- * @return RefreshTokenEntityInterface|null
- */
- public function getNewRefreshToken();
+ public function getNewRefreshToken(): ?RefreshTokenEntityInterface;
/**
- * Create a new refresh token_name.
- *
- * @param RefreshTokenEntityInterface $refreshTokenEntity
- *
* @throws UniqueTokenIdentifierConstraintViolationException
*/
- public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity);
+ public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity): void;
- /**
- * Revoke the refresh token.
- *
- * @param string $tokenId
- */
- public function revokeRefreshToken($tokenId);
+ public function revokeRefreshToken(string $tokenId): void;
- /**
- * Check if the refresh token has been revoked.
- *
- * @param string $tokenId
- *
- * @return bool Return true if this token has been revoked
- */
- public function isRefreshTokenRevoked($tokenId);
+ public function isRefreshTokenRevoked(string $tokenId): bool;
}
diff --git a/src/Repositories/RepositoryInterface.php b/src/Repositories/RepositoryInterface.php
index 9c27b4b0a..00b1dc106 100644
--- a/src/Repositories/RepositoryInterface.php
+++ b/src/Repositories/RepositoryInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Repositories;
/**
diff --git a/src/Repositories/ScopeRepositoryInterface.php b/src/Repositories/ScopeRepositoryInterface.php
index 997aac2c8..95bfdbb9a 100644
--- a/src/Repositories/ScopeRepositoryInterface.php
+++ b/src/Repositories/ScopeRepositoryInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
@@ -21,26 +24,22 @@ interface ScopeRepositoryInterface extends RepositoryInterface
* Return information about a scope.
*
* @param string $identifier The scope identifier
- *
- * @return ScopeEntityInterface|null
*/
- public function getScopeEntityByIdentifier($identifier);
+ public function getScopeEntityByIdentifier(string $identifier): ?ScopeEntityInterface;
/**
* Given a client, grant type and optional user identifier validate the set of scopes requested are valid and optionally
* append additional scopes or remove requested scopes.
*
* @param ScopeEntityInterface[] $scopes
- * @param string $grantType
- * @param ClientEntityInterface $clientEntity
- * @param null|string $userIdentifier
*
* @return ScopeEntityInterface[]
*/
public function finalizeScopes(
array $scopes,
- $grantType,
+ string $grantType,
ClientEntityInterface $clientEntity,
- $userIdentifier = null
- );
+ string|int|null $userIdentifier = null,
+ ?string $authCodeId = null
+ ): array;
}
diff --git a/src/Repositories/UserRepositoryInterface.php b/src/Repositories/UserRepositoryInterface.php
index 8ad49aa7c..b8cf53d09 100644
--- a/src/Repositories/UserRepositoryInterface.php
+++ b/src/Repositories/UserRepositoryInterface.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
@@ -16,18 +19,11 @@ interface UserRepositoryInterface extends RepositoryInterface
{
/**
* Get a user entity.
- *
- * @param string $username
- * @param string $password
- * @param string $grantType The grant type used
- * @param ClientEntityInterface $clientEntity
- *
- * @return UserEntityInterface|null
*/
public function getUserEntityByUserCredentials(
- $username,
- $password,
- $grantType,
+ string $username,
+ string $password,
+ string $grantType,
ClientEntityInterface $clientEntity
- );
+ ): ?UserEntityInterface;
}
diff --git a/src/RequestAccessTokenEvent.php b/src/RequestAccessTokenEvent.php
index c2f478284..1200c44c5 100644
--- a/src/RequestAccessTokenEvent.php
+++ b/src/RequestAccessTokenEvent.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
@@ -14,27 +17,15 @@
class RequestAccessTokenEvent extends RequestEvent
{
- /**
- * @var AccessTokenEntityInterface
- */
- private $accessToken;
-
- /**
- * @param string $name
- * @param ServerRequestInterface $request
- */
- public function __construct($name, ServerRequestInterface $request, AccessTokenEntityInterface $accessToken)
+ public function __construct(string $name, ServerRequestInterface $request, private AccessTokenEntityInterface $accessToken)
{
parent::__construct($name, $request);
- $this->accessToken = $accessToken;
}
/**
- * @return AccessTokenEntityInterface
- *
* @codeCoverageIgnore
*/
- public function getAccessToken()
+ public function getAccessToken(): AccessTokenEntityInterface
{
return $this->accessToken;
}
diff --git a/src/RequestEvent.php b/src/RequestEvent.php
index 4f7dad097..e88cd3137 100644
--- a/src/RequestEvent.php
+++ b/src/RequestEvent.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,43 +8,31 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server;
-use League\Event\Event;
+use League\OAuth2\Server\EventEmitting\AbstractEvent;
use Psr\Http\Message\ServerRequestInterface;
-class RequestEvent extends Event
+class RequestEvent extends AbstractEvent
{
- const CLIENT_AUTHENTICATION_FAILED = 'client.authentication.failed';
- const USER_AUTHENTICATION_FAILED = 'user.authentication.failed';
- const REFRESH_TOKEN_CLIENT_FAILED = 'refresh_token.client.failed';
+ public const CLIENT_AUTHENTICATION_FAILED = 'client.authentication.failed';
+ public const USER_AUTHENTICATION_FAILED = 'user.authentication.failed';
+ public const REFRESH_TOKEN_CLIENT_FAILED = 'refresh_token.client.failed';
- const REFRESH_TOKEN_ISSUED = 'refresh_token.issued';
- const ACCESS_TOKEN_ISSUED = 'access_token.issued';
+ public const REFRESH_TOKEN_ISSUED = 'refresh_token.issued';
+ public const ACCESS_TOKEN_ISSUED = 'access_token.issued';
- /**
- * @var ServerRequestInterface
- */
- private $request;
-
- /**
- * RequestEvent constructor.
- *
- * @param string $name
- * @param ServerRequestInterface $request
- */
- public function __construct($name, ServerRequestInterface $request)
+ public function __construct(string $name, private ServerRequestInterface $request)
{
parent::__construct($name);
- $this->request = $request;
}
/**
- * @return ServerRequestInterface
- *
* @codeCoverageIgnore
*/
- public function getRequest()
+ public function getRequest(): ServerRequestInterface
{
return $this->request;
}
diff --git a/src/RequestRefreshTokenEvent.php b/src/RequestRefreshTokenEvent.php
index 326a115ed..f2ac556cd 100644
--- a/src/RequestRefreshTokenEvent.php
+++ b/src/RequestRefreshTokenEvent.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
@@ -14,27 +17,15 @@
class RequestRefreshTokenEvent extends RequestEvent
{
- /**
- * @var RefreshTokenEntityInterface
- */
- private $refreshToken;
-
- /**
- * @param string $name
- * @param ServerRequestInterface $request
- */
- public function __construct($name, ServerRequestInterface $request, RefreshTokenEntityInterface $refreshToken)
+ public function __construct(string $name, ServerRequestInterface $request, private RefreshTokenEntityInterface $refreshToken)
{
parent::__construct($name, $request);
- $this->refreshToken = $refreshToken;
}
/**
- * @return RefreshTokenEntityInterface
- *
* @codeCoverageIgnore
*/
- public function getRefreshToken()
+ public function getRefreshToken(): RefreshTokenEntityInterface
{
return $this->refreshToken;
}
diff --git a/src/RequestTypes/AuthorizationRequest.php b/src/RequestTypes/AuthorizationRequest.php
index 6441e144d..09b11562a 100644
--- a/src/RequestTypes/AuthorizationRequest.php
+++ b/src/RequestTypes/AuthorizationRequest.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,121 +8,89 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server\RequestTypes;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Entities\UserEntityInterface;
-class AuthorizationRequest
+class AuthorizationRequest implements AuthorizationRequestInterface
{
/**
* The grant type identifier
- *
- * @var string
*/
- protected $grantTypeId;
+ protected string $grantTypeId;
/**
* The client identifier
- *
- * @var ClientEntityInterface
*/
- protected $client;
+ protected ClientEntityInterface $client;
/**
* The user identifier
- *
- * @var UserEntityInterface
*/
- protected $user;
+ protected UserEntityInterface $user;
/**
* An array of scope identifiers
*
* @var ScopeEntityInterface[]
*/
- protected $scopes = [];
+ protected array $scopes = [];
/**
* Has the user authorized the authorization request
- *
- * @var bool
*/
- protected $authorizationApproved = false;
+ protected bool $authorizationApproved = false;
/**
* The redirect URI used in the request
- *
- * @var string|null
*/
- protected $redirectUri;
+ protected ?string $redirectUri = null;
/**
* The state parameter on the authorization request
- *
- * @var string|null
*/
- protected $state;
+ protected ?string $state = null;
/**
* The code challenge (if provided)
- *
- * @var string
*/
- protected $codeChallenge;
+ protected string $codeChallenge;
/**
* The code challenge method (if provided)
- *
- * @var string
*/
- protected $codeChallengeMethod;
+ protected string $codeChallengeMethod;
- /**
- * @return string
- */
- public function getGrantTypeId()
+ public function getGrantTypeId(): string
{
return $this->grantTypeId;
}
- /**
- * @param string $grantTypeId
- */
- public function setGrantTypeId($grantTypeId)
+ public function setGrantTypeId(string $grantTypeId): void
{
$this->grantTypeId = $grantTypeId;
}
- /**
- * @return ClientEntityInterface
- */
- public function getClient()
+ public function getClient(): ClientEntityInterface
{
return $this->client;
}
- /**
- * @param ClientEntityInterface $client
- */
- public function setClient(ClientEntityInterface $client)
+ public function setClient(ClientEntityInterface $client): void
{
$this->client = $client;
}
- /**
- * @return UserEntityInterface|null
- */
- public function getUser()
+ public function getUser(): ?UserEntityInterface
{
- return $this->user;
+ return $this->user ?? null;
}
- /**
- * @param UserEntityInterface $user
- */
- public function setUser(UserEntityInterface $user)
+ public function setUser(UserEntityInterface $user): void
{
$this->user = $user;
}
@@ -129,7 +98,7 @@ public function setUser(UserEntityInterface $user)
/**
* @return ScopeEntityInterface[]
*/
- public function getScopes()
+ public function getScopes(): array
{
return $this->scopes;
}
@@ -137,87 +106,57 @@ public function getScopes()
/**
* @param ScopeEntityInterface[] $scopes
*/
- public function setScopes(array $scopes)
+ public function setScopes(array $scopes): void
{
$this->scopes = $scopes;
}
- /**
- * @return bool
- */
- public function isAuthorizationApproved()
+ public function isAuthorizationApproved(): bool
{
return $this->authorizationApproved;
}
- /**
- * @param bool $authorizationApproved
- */
- public function setAuthorizationApproved($authorizationApproved)
+ public function setAuthorizationApproved(bool $authorizationApproved): void
{
$this->authorizationApproved = $authorizationApproved;
}
- /**
- * @return string|null
- */
- public function getRedirectUri()
+ public function getRedirectUri(): ?string
{
return $this->redirectUri;
}
- /**
- * @param string|null $redirectUri
- */
- public function setRedirectUri($redirectUri)
+ public function setRedirectUri(?string $redirectUri): void
{
$this->redirectUri = $redirectUri;
}
- /**
- * @return string|null
- */
- public function getState()
+ public function getState(): ?string
{
return $this->state;
}
- /**
- * @param string $state
- */
- public function setState($state)
+ public function setState(string $state): void
{
$this->state = $state;
}
- /**
- * @return string
- */
- public function getCodeChallenge()
+ public function getCodeChallenge(): ?string
{
- return $this->codeChallenge;
+ return $this->codeChallenge ?? null;
}
- /**
- * @param string $codeChallenge
- */
- public function setCodeChallenge($codeChallenge)
+ public function setCodeChallenge(string $codeChallenge): void
{
$this->codeChallenge = $codeChallenge;
}
- /**
- * @return string
- */
- public function getCodeChallengeMethod()
+ public function getCodeChallengeMethod(): ?string
{
- return $this->codeChallengeMethod;
+ return $this->codeChallengeMethod ?? null;
}
- /**
- * @param string $codeChallengeMethod
- */
- public function setCodeChallengeMethod($codeChallengeMethod)
+ public function setCodeChallengeMethod(string $codeChallengeMethod): void
{
$this->codeChallengeMethod = $codeChallengeMethod;
}
diff --git a/src/RequestTypes/AuthorizationRequestInterface.php b/src/RequestTypes/AuthorizationRequestInterface.php
new file mode 100644
index 000000000..6ae358439
--- /dev/null
+++ b/src/RequestTypes/AuthorizationRequestInterface.php
@@ -0,0 +1,62 @@
+
+ * @copyright Copyright (c) Alex Bilbie
+ * @license http://mit-license.org/
+ *
+ * @link https://github.com/thephpleague/oauth2-server
+ */
+
+declare(strict_types=1);
+
+namespace League\OAuth2\Server\RequestTypes;
+
+use League\OAuth2\Server\Entities\ClientEntityInterface;
+use League\OAuth2\Server\Entities\ScopeEntityInterface;
+use League\OAuth2\Server\Entities\UserEntityInterface;
+
+interface AuthorizationRequestInterface
+{
+ public function getUser(): UserEntityInterface|null;
+
+ public function setState(string $state): void;
+
+ public function getClient(): ClientEntityInterface;
+
+ public function setAuthorizationApproved(bool $authorizationApproved): void;
+
+ /**
+ * @param ScopeEntityInterface[] $scopes
+ */
+ public function setScopes(array $scopes): void;
+
+ public function setRedirectUri(?string $redirectUri): void;
+
+ public function getRedirectUri(): ?string;
+
+ public function getCodeChallengeMethod(): ?string;
+
+ public function setGrantTypeId(string $grantTypeId): void;
+
+ public function setUser(UserEntityInterface $user): void;
+
+ public function setClient(ClientEntityInterface $client): void;
+
+ public function setCodeChallenge(string $codeChallenge): void;
+
+ public function isAuthorizationApproved(): bool;
+
+ public function getState(): ?string;
+
+ public function getCodeChallenge(): ?string;
+
+ public function setCodeChallengeMethod(string $codeChallengeMethod): void;
+
+ /**
+ * @return ScopeEntityInterface[]
+ */
+ public function getScopes(): array;
+
+ public function getGrantTypeId(): string;
+}
diff --git a/src/ResourceServer.php b/src/ResourceServer.php
index e1f98d6d1..e89e8d24a 100644
--- a/src/ResourceServer.php
+++ b/src/ResourceServer.php
@@ -1,4 +1,5 @@
* @copyright Copyright (c) Alex Bilbie
@@ -7,6 +8,8 @@
* @link https://github.com/thephpleague/oauth2-server
*/
+declare(strict_types=1);
+
namespace League\OAuth2\Server;
use League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface;
@@ -17,36 +20,16 @@
class ResourceServer
{
- /**
- * @var AccessTokenRepositoryInterface
- */
- private $accessTokenRepository;
+ private CryptKeyInterface $publicKey;
- /**
- * @var CryptKey
- */
- private $publicKey;
-
- /**
- * @var null|AuthorizationValidatorInterface
- */
- private $authorizationValidator;
+ private ?AuthorizationValidatorInterface $authorizationValidator = null;
- /**
- * New server instance.
- *
- * @param AccessTokenRepositoryInterface $accessTokenRepository
- * @param CryptKey|string $publicKey
- * @param null|AuthorizationValidatorInterface $authorizationValidator
- */
public function __construct(
- AccessTokenRepositoryInterface $accessTokenRepository,
- $publicKey,
+ private AccessTokenRepositoryInterface $accessTokenRepository,
+ CryptKeyInterface|string $publicKey,
AuthorizationValidatorInterface $authorizationValidator = null
) {
- $this->accessTokenRepository = $accessTokenRepository;
-
- if ($publicKey instanceof CryptKey === false) {
+ if ($publicKey instanceof CryptKeyInterface === false) {
$publicKey = new CryptKey($publicKey);
}
$this->publicKey = $publicKey;
@@ -54,10 +37,7 @@ public function __construct(
$this->authorizationValidator = $authorizationValidator;
}
- /**
- * @return AuthorizationValidatorInterface
- */
- protected function getAuthorizationValidator()
+ protected function getAuthorizationValidator(): AuthorizationValidatorInterface
{
if ($this->authorizationValidator instanceof AuthorizationValidatorInterface === false) {
$this->authorizationValidator = new BearerTokenValidator($this->accessTokenRepository);
@@ -73,13 +53,9 @@ protected function getAuthorizationValidator()
/**
* Determine the access token validity.
*
- * @param ServerRequestInterface $request
- *
* @throws OAuthServerException
- *
- * @return ServerRequestInterface
*/
- public function validateAuthenticatedRequest(ServerRequestInterface $request)
+ public function validateAuthenticatedRequest(ServerRequestInterface $request): ServerRequestInterface
{
return $this->getAuthorizationValidator()->validateAuthorization($request);
}
diff --git a/src/ResponseTypes/AbstractResponseType.php b/src/ResponseTypes/AbstractResponseType.php
index 192f52aae..2af00e224 100644
--- a/src/ResponseTypes/AbstractResponseType.php
+++ b/src/ResponseTypes/AbstractResponseType.php
@@ -1,4 +1,5 @@
accessToken = $accessToken;
}
- /**
- * {@inheritdoc}
- */
- public function setRefreshToken(RefreshTokenEntityInterface $refreshToken)
+ public function setRefreshToken(RefreshTokenEntityInterface $refreshToken): void
{
$this->refreshToken = $refreshToken;
}
- /**
- * Set the private key
- *
- * @param CryptKey $key
- */
- public function setPrivateKey(CryptKey $key)
+ public function setPrivateKey(CryptKeyInterface $key): void
{
$this->privateKey = $key;
}
diff --git a/src/ResponseTypes/BearerTokenResponse.php b/src/ResponseTypes/BearerTokenResponse.php
index 33c1606e8..c33bdc712 100644
--- a/src/ResponseTypes/BearerTokenResponse.php
+++ b/src/ResponseTypes/BearerTokenResponse.php
@@ -1,4 +1,5 @@
accessToken->getExpiryDateTime()->getTimestamp();
$responseParams = [
'token_type' => 'Bearer',
- 'expires_in' => $expireDateTime - \time(),
- 'access_token' => (string) $this->accessToken,
+ 'expires_in' => $expireDateTime - time(),
+ 'access_token' => $this->accessToken->toString(),
];
- if ($this->refreshToken instanceof RefreshTokenEntityInterface) {
- $refreshTokenPayload = \json_encode([
- 'client_id' => $this->accessToken->getClient()->getIdentifier(),
- 'refresh_token_id' => $this->refreshToken->getIdentifier(),
- 'access_token_id' => $this->accessToken->getIdentifier(),
- 'scopes' => $this->accessToken->getScopes(),
- 'user_id' => $this->accessToken->getUserIdentifier(),
- 'expire_time' => $this->refreshToken->getExpiryDateTime()->getTimestamp(),
+ if (isset($this->refreshToken)) {
+ $refreshTokenPayload = json_encode([
+ 'client_id' => $this->accessToken->getClient()->getIdentifier(),
+ 'refresh_token_id' => $this->refreshToken->getIdentifier(),
+ 'access_token_id' => $this->accessToken->getIdentifier(),
+ 'scopes' => $this->accessToken->getScopes(),
+ 'user_id' => $this->accessToken->getUserIdentifier(),
+ 'expire_time' => $this->refreshToken->getExpiryDateTime()->getTimestamp(),
]);
if ($refreshTokenPayload === false) {
@@ -48,7 +51,7 @@ public function generateHttpResponse(ResponseInterface $response)
$responseParams['refresh_token'] = $this->encrypt($refreshTokenPayload);
}
- $responseParams = \json_encode(\array_merge($this->getExtraParams($this->accessToken), $responseParams));
+ $responseParams = json_encode(array_merge($this->getExtraParams($this->accessToken), $responseParams));
if ($responseParams === false) {
throw new LogicException('Error encountered JSON encoding response parameters');
@@ -70,11 +73,9 @@ public function generateHttpResponse(ResponseInterface $response)
* AuthorizationServer::getResponseType() to pull in your version of
* this class rather than the default.
*
- * @param AccessTokenEntityInterface $accessToken
- *
- * @return array
+ * @return mixed[]
*/
- protected function getExtraParams(AccessTokenEntityInterface $accessToken)
+ protected function getExtraParams(AccessTokenEntityInterface $accessToken): array
{
return [];
}
diff --git a/src/ResponseTypes/DeviceCodeResponse.php b/src/ResponseTypes/DeviceCodeResponse.php
new file mode 100644
index 000000000..339c75bd3
--- /dev/null
+++ b/src/ResponseTypes/DeviceCodeResponse.php
@@ -0,0 +1,93 @@
+
+ * @copyright Copyright (c) Alex Bilbie
+ * @license http://mit-license.org/
+ *
+ * @link https://github.com/thephpleague/oauth2-server
+ */
+
+declare(strict_types=1);
+
+namespace League\OAuth2\Server\ResponseTypes;
+
+use League\OAuth2\Server\Entities\DeviceCodeEntityInterface;
+use LogicException;
+use Psr\Http\Message\ResponseInterface;
+
+use function json_encode;
+use function time;
+
+class DeviceCodeResponse extends AbstractResponseType
+{
+ protected DeviceCodeEntityInterface $deviceCodeEntity;
+ private bool $includeVerificationUriComplete = false;
+ private const DEFAULT_INTERVAL = 5;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function generateHttpResponse(ResponseInterface $response): ResponseInterface
+ {
+ $expireDateTime = $this->deviceCodeEntity->getExpiryDateTime()->getTimestamp();
+
+ $responseParams = [
+ 'device_code' => $this->deviceCodeEntity->getIdentifier(),
+ 'user_code' => $this->deviceCodeEntity->getUserCode(),
+ 'verification_uri' => $this->deviceCodeEntity->getVerificationUri(),
+ 'expires_in' => $expireDateTime - time(),
+ ];
+
+ if ($this->includeVerificationUriComplete === true) {
+ $responseParams['verification_uri_complete'] = $this->deviceCodeEntity->getVerificationUriComplete();
+ }
+
+ if ($this->deviceCodeEntity->getInterval() !== self::DEFAULT_INTERVAL) {
+ $responseParams['interval'] = $this->deviceCodeEntity->getInterval();
+ }
+
+ $responseParams = json_encode($responseParams);
+
+ if ($responseParams === false) {
+ throw new LogicException('Error encountered JSON encoding response parameters');
+ }
+
+ $response = $response
+ ->withStatus(200)
+ ->withHeader('pragma', 'no-cache')
+ ->withHeader('cache-control', 'no-store')
+ ->withHeader('content-type', 'application/json; charset=UTF-8');
+
+ $response->getBody()->write($responseParams);
+
+ return $response;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setDeviceCodeEntity(DeviceCodeEntityInterface $deviceCodeEntity): void
+ {
+ $this->deviceCodeEntity = $deviceCodeEntity;
+ }
+
+ public function includeVerificationUriComplete(): void
+ {
+ $this->includeVerificationUriComplete = true;
+ }
+
+ /**
+ * Add custom fields to your Bearer Token response here, then override
+ * AuthorizationServer::getResponseType() to pull in your version of
+ * this class rather than the default.
+ *
+ * @return mixed[]
+ */
+ protected function getExtraParams(DeviceCodeEntityInterface $deviceCode): array
+ {
+ return [];
+ }
+}
diff --git a/src/ResponseTypes/RedirectResponse.php b/src/ResponseTypes/RedirectResponse.php
index e4639148d..cff6af6eb 100644
--- a/src/ResponseTypes/RedirectResponse.php
+++ b/src/ResponseTypes/RedirectResponse.php
@@ -1,4 +1,5 @@
redirectUri = $redirectUri;
}
- /**
- * @param ResponseInterface $response
- *
- * @return ResponseInterface
- */
- public function generateHttpResponse(ResponseInterface $response)
+ public function generateHttpResponse(ResponseInterface $response): ResponseInterface
{
return $response->withStatus(302)->withHeader('Location', $this->redirectUri);
}
diff --git a/src/ResponseTypes/ResponseTypeInterface.php b/src/ResponseTypes/ResponseTypeInterface.php
index 5eddd6079..8e70ae9f8 100644
--- a/src/ResponseTypes/ResponseTypeInterface.php
+++ b/src/ResponseTypes/ResponseTypeInterface.php
@@ -1,4 +1,5 @@
getMockBuilder(ClientRepositoryInterface::class)->getMock(),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/Stubs/private.key',
- \base64_encode(\random_bytes(36)),
+ base64_encode(random_bytes(36)),
new StubResponseType()
);
$server->enableGrantType(new GrantType(), new DateInterval('PT1M'));
$authRequest = $server->validateAuthorizationRequest($this->createMock(ServerRequestInterface::class));
- $this->assertSame(GrantType::class, $authRequest->getGrantTypeId());
+ self::assertSame(GrantType::class, $authRequest->getGrantTypeId());
}
- */
- public function testRespondToRequestInvalidGrantType()
+ public function testRespondToRequestInvalidGrantType(): void
{
$server = new AuthorizationServer(
$this->getMockBuilder(ClientRepositoryInterface::class)->getMock(),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/Stubs/private.key',
- \base64_encode(\random_bytes(36)),
+ base64_encode(random_bytes(36)),
new StubResponseType()
);
$server->enableGrantType(new ClientCredentialsGrant(), new DateInterval('PT1M'));
try {
- $server->respondToAccessTokenRequest(ServerRequestFactory::fromGlobals(), new Response);
+ $server->respondToAccessTokenRequest(ServerRequestFactory::fromGlobals(), new Response());
} catch (OAuthServerException $e) {
- $this->assertEquals('unsupported_grant_type', $e->getErrorType());
- $this->assertEquals(400, $e->getHttpStatusCode());
+ self::assertEquals('unsupported_grant_type', $e->getErrorType());
+ self::assertEquals(400, $e->getHttpStatusCode());
}
}
- public function testRespondToRequest()
+ public function testRespondToRequest(): void
{
$client = new ClientEntity();
@@ -91,6 +97,7 @@ public function testRespondToRequest()
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepository->method('getClientEntity')->willReturn($client);
+ $clientRepository->method('validateClient')->willReturn(true);
$scope = new ScopeEntity();
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
@@ -105,7 +112,7 @@ public function testRespondToRequest()
$accessTokenRepositoryMock,
$scopeRepositoryMock,
'file://' . __DIR__ . '/Stubs/private.key',
- \base64_encode(\random_bytes(36)),
+ base64_encode(random_bytes(36)),
new StubResponseType()
);
@@ -115,11 +122,11 @@ public function testRespondToRequest()
$_POST['grant_type'] = 'client_credentials';
$_POST['client_id'] = 'foo';
$_POST['client_secret'] = 'bar';
- $response = $server->respondToAccessTokenRequest(ServerRequestFactory::fromGlobals(), new Response);
- $this->assertEquals(200, $response->getStatusCode());
+ $response = $server->respondToAccessTokenRequest(ServerRequestFactory::fromGlobals(), new Response());
+ self::assertEquals(200, $response->getStatusCode());
}
- public function testGetResponseType()
+ public function testGetResponseType(): void
{
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
@@ -131,14 +138,14 @@ public function testGetResponseType()
'file://' . __DIR__ . '/Stubs/public.key'
);
- $abstractGrantReflection = new \ReflectionClass($server);
+ $abstractGrantReflection = new ReflectionClass($server);
$method = $abstractGrantReflection->getMethod('getResponseType');
$method->setAccessible(true);
- $this->assertInstanceOf(BearerTokenResponse::class, $method->invoke($server));
+ self::assertInstanceOf(BearerTokenResponse::class, $method->invoke($server));
}
- public function testGetResponseTypeExtended()
+ public function testGetResponseTypeExtended(): void
{
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$privateKey = 'file://' . __DIR__ . '/Stubs/private.key';
@@ -152,13 +159,13 @@ public function testGetResponseTypeExtended()
'file://' . __DIR__ . '/Stubs/public.key'
);
- $abstractGrantReflection = new \ReflectionClass($server);
+ $abstractGrantReflection = new ReflectionClass($server);
$method = $abstractGrantReflection->getMethod('getResponseType');
$method->setAccessible(true);
$responseType = $method->invoke($server);
- $responseTypeReflection = new \ReflectionClass($responseType);
+ $responseTypeReflection = new ReflectionClass($responseType);
$privateKeyProperty = $responseTypeReflection->getProperty('privateKey');
$privateKeyProperty->setAccessible(true);
@@ -167,23 +174,25 @@ public function testGetResponseTypeExtended()
$encryptionKeyProperty->setAccessible(true);
// generated instances should have keys setup
- $this->assertSame($privateKey, $privateKeyProperty->getValue($responseType)->getKeyPath());
- $this->assertSame($encryptionKey, $encryptionKeyProperty->getValue($responseType));
+ self::assertSame($privateKey, $privateKeyProperty->getValue($responseType)->getKeyPath());
+ self::assertSame($encryptionKey, $encryptionKeyProperty->getValue($responseType));
}
- public function testMultipleRequestsGetDifferentResponseTypeInstances()
+ public function testMultipleRequestsGetDifferentResponseTypeInstances(): void
{
$privateKey = 'file://' . __DIR__ . '/Stubs/private.key';
$encryptionKey = 'file://' . __DIR__ . '/Stubs/public.key';
- $responseTypePrototype = new class extends BearerTokenResponse {
- /* @return null|CryptKey */
- public function getPrivateKey()
+ $responseTypePrototype = new class () extends BearerTokenResponse {
+ protected CryptKeyInterface $privateKey;
+ protected Key|string|null $encryptionKey = null;
+
+ public function getPrivateKey(): CryptKeyInterface
{
return $this->privateKey;
}
- public function getEncryptionKey()
+ public function getEncryptionKey(): Key|string|null
{
return $this->encryptionKey;
}
@@ -200,30 +209,26 @@ public function getEncryptionKey()
$responseTypePrototype
);
- $abstractGrantReflection = new \ReflectionClass($server);
+ $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());
+ self::assertSame($privateKey, $responseTypeA->getPrivateKey()->getKeyPath());
+ self::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);
+ self::assertSame(get_class($responseTypePrototype), get_class($responseTypeA));
+ self::assertSame(get_class($responseTypePrototype), get_class($responseTypeB));
+ self::assertNotSame($responseTypePrototype, $responseTypeA);
+ self::assertNotSame($responseTypePrototype, $responseTypeB);
+ self::assertNotSame($responseTypeA, $responseTypeB);
}
- public function testCompleteAuthorizationRequest()
+ public function testCompleteAuthorizationRequest(): void
{
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
@@ -247,7 +252,9 @@ public function testCompleteAuthorizationRequest()
$server->enableGrantType($grant);
$client = new ClientEntity();
- $client->setRedirectUri(self::REDIRECT_URI);
+
+ $client->setRedirectUri('http://foo/bar');
+ $client->setIdentifier('clientId');
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
@@ -255,13 +262,15 @@ public function testCompleteAuthorizationRequest()
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
- $this->assertInstanceOf(
- ResponseInterface::class,
- $server->completeAuthorizationRequest($authRequest, new Response)
- );
+ $response = $server->completeAuthorizationRequest($authRequest, new Response());
+
+ $locationHeader = $response->getHeader('Location')[0];
+
+ self::assertStringStartsWith('http://foo/bar', $locationHeader);
+ self::assertStringContainsString('code=', $locationHeader);
}
- public function testValidateAuthorizationRequest()
+ public function testValidateAuthorizationRequest(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
@@ -305,56 +314,10 @@ public function testValidateAuthorizationRequest()
]
);
- $this->assertInstanceOf(AuthorizationRequest::class, $server->validateAuthorizationRequest($request));
- }
-
- public function testValidateAuthorizationRequestWithMissingRedirectUri()
- {
- $client = new ClientEntity();
- $client->setConfidential();
-
- $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
- $clientRepositoryMock->method('getClientEntity')->willReturn($client);
-
- $grant = new AuthCodeGrant(
- $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
- $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
- new DateInterval('PT10M')
- );
- $grant->setClientRepository($clientRepositoryMock);
-
- $server = new AuthorizationServer(
- $clientRepositoryMock,
- $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
- $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
- 'file://' . __DIR__ . '/Stubs/private.key',
- 'file://' . __DIR__ . '/Stubs/public.key'
- );
- $server->enableGrantType($grant);
-
- $request = new ServerRequest(
- [],
- [],
- null,
- null,
- 'php://input',
- $headers = [],
- $cookies = [],
- $queryParams = [
- 'response_type' => 'code',
- 'client_id' => 'foo',
- ]
- );
-
- try {
- $server->validateAuthorizationRequest($request);
- } catch (OAuthServerException $e) {
- $this->assertEquals('invalid_client', $e->getErrorType());
- $this->assertEquals(401, $e->getHttpStatusCode());
- }
+ self::assertInstanceOf(AuthorizationRequest::class, $server->validateAuthorizationRequest($request));
}
- public function testValidateAuthorizationRequestUnregistered()
+ public function testValidateAuthorizationRequestUnregistered(): void
{
$server = new AuthorizationServer(
$this->getMockBuilder(ClientRepositoryInterface::class)->getMock(),
@@ -369,7 +332,7 @@ public function testValidateAuthorizationRequestUnregistered()
'client_id' => 'foo',
]);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(2);
$server->validateAuthorizationRequest($request);
diff --git a/tests/AuthorizationValidators/BearerTokenValidatorTest.php b/tests/AuthorizationValidators/BearerTokenValidatorTest.php
index 94899ddd4..148473eea 100644
--- a/tests/AuthorizationValidators/BearerTokenValidatorTest.php
+++ b/tests/AuthorizationValidators/BearerTokenValidatorTest.php
@@ -1,5 +1,7 @@
getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
@@ -36,14 +41,14 @@ public function testBearerTokenValidatorAcceptsValidToken()
->withClaim('scopes', 'scope1 scope2 scope3 scope4')
->getToken(new Sha256(), InMemory::file(__DIR__ . '/../Stubs/private.key'));
- $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $validJwt->toString()));
+ $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $validJwt->toString()));
$validRequest = $bearerTokenValidator->validateAuthorization($request);
- $this->assertArrayHasKey('authorization', $validRequest->getHeaders());
+ self::assertArrayHasKey('authorization', $validRequest->getHeaders());
}
- public function testBearerTokenValidatorRejectsExpiredToken()
+ public function testBearerTokenValidatorRejectsExpiredToken(): void
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
@@ -64,22 +69,22 @@ public function testBearerTokenValidatorRejectsExpiredToken()
->withClaim('scopes', 'scope1 scope2 scope3 scope4')
->getToken(new Sha256(), InMemory::file(__DIR__ . '/../Stubs/private.key'));
- $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $expiredJwt->toString()));
+ $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $expiredJwt->toString()));
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(9);
$bearerTokenValidator->validateAuthorization($request);
}
- public function testBearerTokenValidatorAcceptsExpiredTokenWithinLeeway()
+ public function testBearerTokenValidatorAcceptsExpiredTokenWithinLeeway(): void
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
// We fake generating this token 10 seconds into the future, an extreme example of possible time drift between servers
$future = (new DateTimeImmutable())->add(new DateInterval('PT10S'));
- $bearerTokenValidator = new BearerTokenValidator($accessTokenRepositoryMock, new \DateInterval('PT10S'));
+ $bearerTokenValidator = new BearerTokenValidator($accessTokenRepositoryMock, new DateInterval('PT10S'));
$bearerTokenValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$bearerTokenValidatorReflection = new ReflectionClass(BearerTokenValidator::class);
@@ -96,21 +101,21 @@ public function testBearerTokenValidatorAcceptsExpiredTokenWithinLeeway()
->withClaim('scopes', 'scope1 scope2 scope3 scope4')
->getToken(new Sha256(), InMemory::file(__DIR__ . '/../Stubs/private.key'));
- $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $jwtTokenFromFutureWithinLeeway->toString()));
+ $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $jwtTokenFromFutureWithinLeeway->toString()));
$validRequest = $bearerTokenValidator->validateAuthorization($request);
- $this->assertArrayHasKey('authorization', $validRequest->getHeaders());
+ self::assertArrayHasKey('authorization', $validRequest->getHeaders());
}
- public function testBearerTokenValidatorRejectsExpiredTokenBeyondLeeway()
+ public function testBearerTokenValidatorRejectsExpiredTokenBeyondLeeway(): void
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
// We fake generating this token 20 seconds into the future, an extreme example of possible time drift between servers
$future = (new DateTimeImmutable())->add(new DateInterval('PT20S'));
- $bearerTokenValidator = new BearerTokenValidator($accessTokenRepositoryMock, new \DateInterval('PT10S'));
+ $bearerTokenValidator = new BearerTokenValidator($accessTokenRepositoryMock, new DateInterval('PT10S'));
$bearerTokenValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$bearerTokenValidatorReflection = new ReflectionClass(BearerTokenValidator::class);
@@ -127,9 +132,9 @@ public function testBearerTokenValidatorRejectsExpiredTokenBeyondLeeway()
->withClaim('scopes', 'scope1 scope2 scope3 scope4')
->getToken(new Sha256(), InMemory::file(__DIR__ . '/../Stubs/private.key'));
- $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $jwtTokenFromFutureBeyondLeeway->toString()));
+ $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $jwtTokenFromFutureBeyondLeeway->toString()));
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(9);
$bearerTokenValidator->validateAuthorization($request);
diff --git a/tests/Bootstrap.php b/tests/Bootstrap.php
index a31ef6f34..6445d351e 100644
--- a/tests/Bootstrap.php
+++ b/tests/Bootstrap.php
@@ -1,6 +1,6 @@
assertEquals('plain', $verifier->getMethod());
+ self::assertEquals('plain', $verifier->getMethod());
}
- public function testVerifyCodeChallenge()
+ public function testVerifyCodeChallenge(): void
{
$verifier = new PlainVerifier();
- $this->assertTrue($verifier->verifyCodeChallenge('foo', 'foo'));
- $this->assertFalse($verifier->verifyCodeChallenge('foo', 'bar'));
+ self::assertTrue($verifier->verifyCodeChallenge('foo', 'foo'));
+ self::assertFalse($verifier->verifyCodeChallenge('foo', 'bar'));
}
}
diff --git a/tests/CodeChallengeVerifiers/S256VerifierTest.php b/tests/CodeChallengeVerifiers/S256VerifierTest.php
index 58185a452..4bcbbe0e3 100644
--- a/tests/CodeChallengeVerifiers/S256VerifierTest.php
+++ b/tests/CodeChallengeVerifiers/S256VerifierTest.php
@@ -1,37 +1,44 @@
assertEquals('S256', $verifier->getMethod());
+ self::assertEquals('S256', $verifier->getMethod());
}
- public function testVerifyCodeChallengeSucceeds()
+ public function testVerifyCodeChallengeSucceeds(): void
{
$codeChallenge = $this->createCodeChallenge('foo');
$verifier = new S256Verifier();
- $this->assertTrue($verifier->verifyCodeChallenge('foo', $codeChallenge));
+ self::assertTrue($verifier->verifyCodeChallenge('foo', $codeChallenge));
}
- public function testVerifyCodeChallengeFails()
+ public function testVerifyCodeChallengeFails(): void
{
$codeChallenge = $this->createCodeChallenge('bar');
$verifier = new S256Verifier();
- $this->assertFalse($verifier->verifyCodeChallenge('foo', $codeChallenge));
+ self::assertFalse($verifier->verifyCodeChallenge('foo', $codeChallenge));
}
- private function createCodeChallenge($codeVerifier)
+ private function createCodeChallenge(string $codeVerifier): string
{
- return \strtr(\rtrim(\base64_encode(\hash('sha256', $codeVerifier, true)), '='), '+/', '-_');
+ return strtr(rtrim(base64_encode(hash('sha256', $codeVerifier, true)), '='), '+/', '-_');
}
}
diff --git a/tests/Exception/OAuthServerExceptionTest.php b/tests/Exception/OAuthServerExceptionTest.php
index 38b86d433..27922b427 100644
--- a/tests/Exception/OAuthServerExceptionTest.php
+++ b/tests/Exception/OAuthServerExceptionTest.php
@@ -1,5 +1,7 @@
withParsedBody([
@@ -25,11 +29,11 @@ public function testInvalidClientExceptionSetsAuthenticateHeader()
} catch (OAuthServerException $e) {
$response = $e->generateHttpResponse(new Response());
- $this->assertTrue($response->hasHeader('WWW-Authenticate'));
+ self::assertTrue($response->hasHeader('WWW-Authenticate'));
}
}
- public function testInvalidClientExceptionSetsBearerAuthenticateHeader()
+ public function testInvalidClientExceptionSetsBearerAuthenticateHeader(): void
{
$serverRequest = (new ServerRequest())
->withParsedBody([
@@ -42,11 +46,11 @@ public function testInvalidClientExceptionSetsBearerAuthenticateHeader()
} catch (OAuthServerException $e) {
$response = $e->generateHttpResponse(new Response());
- $this->assertEquals(['Bearer realm="OAuth"'], $response->getHeader('WWW-Authenticate'));
+ self::assertEquals(['Bearer realm="OAuth"'], $response->getHeader('WWW-Authenticate'));
}
}
- public function testInvalidClientExceptionOmitsAuthenticateHeader()
+ public function testInvalidClientExceptionOmitsAuthenticateHeader(): void
{
$serverRequest = (new ServerRequest())
->withParsedBody([
@@ -58,11 +62,11 @@ public function testInvalidClientExceptionOmitsAuthenticateHeader()
} catch (OAuthServerException $e) {
$response = $e->generateHttpResponse(new Response());
- $this->assertFalse($response->hasHeader('WWW-Authenticate'));
+ self::assertFalse($response->hasHeader('WWW-Authenticate'));
}
}
- public function testInvalidClientExceptionOmitsAuthenticateHeaderGivenEmptyAuthorizationHeader()
+ public function testInvalidClientExceptionOmitsAuthenticateHeaderGivenEmptyAuthorizationHeader(): void
{
$serverRequest = (new ServerRequest())
->withParsedBody([
@@ -75,7 +79,7 @@ public function testInvalidClientExceptionOmitsAuthenticateHeaderGivenEmptyAutho
} catch (OAuthServerException $e) {
$response = $e->generateHttpResponse(new Response());
- $this->assertFalse($response->hasHeader('WWW-Authenticate'));
+ self::assertFalse($response->hasHeader('WWW-Authenticate'));
}
}
@@ -84,7 +88,7 @@ public function testInvalidClientExceptionOmitsAuthenticateHeaderGivenEmptyAutho
*
* @throws OAuthServerException
*/
- private function issueInvalidClientException($serverRequest)
+ private function issueInvalidClientException(ServerRequestInterface $serverRequest): void
{
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('validateClient')->willReturn(false);
@@ -92,7 +96,7 @@ private function issueInvalidClientException($serverRequest)
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setClientRepository($clientRepositoryMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
$validateClientMethod->setAccessible(true);
@@ -100,48 +104,48 @@ private function issueInvalidClientException($serverRequest)
$validateClientMethod->invoke($grantMock, $serverRequest);
}
- public function testHasRedirect()
+ public function testHasRedirect(): void
{
$exceptionWithRedirect = OAuthServerException::accessDenied('some hint', 'https://example.com/error');
- $this->assertTrue($exceptionWithRedirect->hasRedirect());
+ self::assertTrue($exceptionWithRedirect->hasRedirect());
}
- public function testDoesNotHaveRedirect()
+ public function testDoesNotHaveRedirect(): void
{
$exceptionWithoutRedirect = OAuthServerException::accessDenied('Some hint');
- $this->assertFalse($exceptionWithoutRedirect->hasRedirect());
+ self::assertFalse($exceptionWithoutRedirect->hasRedirect());
}
- public function testHasPrevious()
+ public function testHasPrevious(): void
{
$previous = new Exception('This is the previous');
$exceptionWithPrevious = OAuthServerException::accessDenied(null, null, $previous);
$previousMessage = $exceptionWithPrevious->getPrevious() !== null ? $exceptionWithPrevious->getPrevious()->getMessage() : null;
- $this->assertSame('This is the previous', $previousMessage);
+ self::assertSame('This is the previous', $previousMessage);
}
- public function testDoesNotHavePrevious()
+ public function testDoesNotHavePrevious(): void
{
$exceptionWithoutPrevious = OAuthServerException::accessDenied();
- $this->assertNull($exceptionWithoutPrevious->getPrevious());
+ self::assertNull($exceptionWithoutPrevious->getPrevious());
}
- public function testCanGetRedirectionUri()
+ public function testCanGetRedirectionUri(): void
{
$exceptionWithRedirect = OAuthServerException::accessDenied('some hint', 'https://example.com/error');
- $this->assertSame('https://example.com/error', $exceptionWithRedirect->getRedirectUri());
+ self::assertSame('https://example.com/error', $exceptionWithRedirect->getRedirectUri());
}
- public function testInvalidCredentialsIsInvalidGrant()
+ public function testInvalidCredentialsIsInvalidGrant(): void
{
$exception = OAuthServerException::invalidCredentials();
- $this->assertSame('invalid_grant', $exception->getErrorType());
+ self::assertSame('invalid_grant', $exception->getErrorType());
}
}
diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php
index 618545efe..5c80e7d6a 100644
--- a/tests/Grant/AbstractGrantTest.php
+++ b/tests/Grant/AbstractGrantTest.php
@@ -1,5 +1,7 @@
getMockForAbstractClass(AbstractGrant::class);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
- $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . \base64_encode('Open:Sesame'));
+ $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . base64_encode('Open:Sesame'));
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
- $this->assertSame(['Open', 'Sesame'], $basicAuthMethod->invoke($grantMock, $serverRequest));
+ self::assertSame(['Open', 'Sesame'], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
- public function testHttpBasicNoPassword()
+ public function testHttpBasicNoPassword(): void
{
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
- $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . \base64_encode('Open:'));
+ $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . base64_encode('Open:'));
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
- $this->assertSame(['Open', ''], $basicAuthMethod->invoke($grantMock, $serverRequest));
+ self::assertSame(['Open', ''], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
- public function testHttpBasicNotBasic()
+ public function testHttpBasicNotBasic(): void
{
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
- $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Foo ' . \base64_encode('Open:Sesame'));
+ $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Foo ' . base64_encode('Open:Sesame'));
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
- $this->assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest));
+ self::assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
- public function testHttpBasicNotBase64()
+ public function testHttpBasicNotBase64(): void
{
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ||');
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
- $this->assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest));
+ self::assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
- public function testHttpBasicNoColon()
+ public function testHttpBasicNoColon(): void
{
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
- $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . \base64_encode('OpenSesame'));
+ $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . base64_encode('OpenSesame'));
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
- $this->assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest));
+ self::assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
- public function testGetClientCredentialsClientSecretNotAString()
+ public function testGetClientCredentialsClientSecretNotAString(): void
{
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
@@ -97,7 +104,7 @@ public function testGetClientCredentialsClientSecretNotAString()
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setClientRepository($clientRepositoryMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$serverRequest = new ServerRequest(
[],
@@ -116,24 +123,26 @@ public function testGetClientCredentialsClientSecretNotAString()
$getClientCredentialsMethod = $abstractGrantReflection->getMethod('getClientCredentials');
$getClientCredentialsMethod->setAccessible(true);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$getClientCredentialsMethod->invoke($grantMock, $serverRequest, true, true);
}
- public function testValidateClientPublic()
+ public function testValidateClientPublic(): void
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setClientRepository($clientRepositoryMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
@@ -143,22 +152,24 @@ public function testValidateClientPublic()
$validateClientMethod->setAccessible(true);
$result = $validateClientMethod->invoke($grantMock, $serverRequest);
- $this->assertEquals($client, $result);
+ self::assertEquals($client, $result);
}
- public function testValidateClientConfidential()
+ public function testValidateClientConfidential(): void
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setClientRepository($clientRepositoryMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
@@ -170,10 +181,10 @@ public function testValidateClientConfidential()
$validateClientMethod->setAccessible(true);
$result = $validateClientMethod->invoke($grantMock, $serverRequest, true, true);
- $this->assertEquals($client, $result);
+ self::assertEquals($client, $result);
}
- public function testValidateClientMissingClientId()
+ public function testValidateClientMissingClientId(): void
{
$client = new ClientEntity();
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
@@ -183,18 +194,18 @@ public function testValidateClientMissingClientId()
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setClientRepository($clientRepositoryMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
$validateClientMethod->setAccessible(true);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$validateClientMethod->invoke($grantMock, $serverRequest, true, true);
}
- public function testValidateClientMissingClientSecret()
+ public function testValidateClientMissingClientSecret(): void
{
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('validateClient')->willReturn(false);
@@ -203,7 +214,7 @@ public function testValidateClientMissingClientSecret()
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setClientRepository($clientRepositoryMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
@@ -212,12 +223,12 @@ public function testValidateClientMissingClientSecret()
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
$validateClientMethod->setAccessible(true);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$validateClientMethod->invoke($grantMock, $serverRequest, true, true);
}
- public function testValidateClientInvalidClientSecret()
+ public function testValidateClientInvalidClientSecret(): void
{
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('validateClient')->willReturn(false);
@@ -226,7 +237,7 @@ public function testValidateClientInvalidClientSecret()
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setClientRepository($clientRepositoryMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
@@ -236,12 +247,12 @@ public function testValidateClientInvalidClientSecret()
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
$validateClientMethod->setAccessible(true);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$validateClientMethod->invoke($grantMock, $serverRequest, true, true);
}
- public function testValidateClientInvalidRedirectUri()
+ public function testValidateClientInvalidRedirectUri(): void
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
@@ -252,7 +263,7 @@ public function testValidateClientInvalidRedirectUri()
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setClientRepository($clientRepositoryMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
@@ -262,12 +273,12 @@ public function testValidateClientInvalidRedirectUri()
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
$validateClientMethod->setAccessible(true);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$validateClientMethod->invoke($grantMock, $serverRequest, true, true);
}
- public function testValidateClientInvalidRedirectUriArray()
+ public function testValidateClientInvalidRedirectUriArray(): void
{
$client = new ClientEntity();
$client->setRedirectUri(['http://foo/bar']);
@@ -278,7 +289,7 @@ public function testValidateClientInvalidRedirectUriArray()
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setClientRepository($clientRepositoryMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
@@ -288,12 +299,12 @@ public function testValidateClientInvalidRedirectUriArray()
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
$validateClientMethod->setAccessible(true);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$validateClientMethod->invoke($grantMock, $serverRequest, true, true);
}
- public function testValidateClientMalformedRedirectUri()
+ public function testValidateClientMalformedRedirectUri(): void
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
@@ -304,7 +315,7 @@ public function testValidateClientMalformedRedirectUri()
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setClientRepository($clientRepositoryMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
@@ -314,12 +325,12 @@ public function testValidateClientMalformedRedirectUri()
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
$validateClientMethod->setAccessible(true);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$validateClientMethod->invoke($grantMock, $serverRequest, true, true);
}
- public function testValidateClientBadClient()
+ public function testValidateClientBadClient(): void
{
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('validateClient')->willReturn(false);
@@ -328,7 +339,7 @@ public function testValidateClientBadClient()
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setClientRepository($clientRepositoryMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
@@ -338,28 +349,29 @@ public function testValidateClientBadClient()
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
$validateClientMethod->setAccessible(true);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$validateClientMethod->invoke($grantMock, $serverRequest, true);
}
- public function testCanRespondToRequest()
+ public function testCanRespondToRequest(): void
{
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->method('getIdentifier')->willReturn('foobar');
+ $grantMock->setDefaultScope('defaultScope');
$serverRequest = (new ServerRequest())->withParsedBody([
'grant_type' => 'foobar',
]);
- $this->assertTrue($grantMock->canRespondToAccessTokenRequest($serverRequest));
+ self::assertTrue($grantMock->canRespondToAccessTokenRequest($serverRequest));
}
- public function testIssueRefreshToken()
+ public function testIssueRefreshToken(): void
{
$refreshTokenRepoMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepoMock
- ->expects($this->once())
+ ->expects(self::once())
->method('getNewRefreshToken')
->willReturn(new RefreshTokenEntity());
@@ -368,39 +380,40 @@ public function testIssueRefreshToken()
$grantMock->setRefreshTokenTTL(new DateInterval('PT1M'));
$grantMock->setRefreshTokenRepository($refreshTokenRepoMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$issueRefreshTokenMethod = $abstractGrantReflection->getMethod('issueRefreshToken');
$issueRefreshTokenMethod->setAccessible(true);
$accessToken = new AccessTokenEntity();
+
/** @var RefreshTokenEntityInterface $refreshToken */
$refreshToken = $issueRefreshTokenMethod->invoke($grantMock, $accessToken);
- $this->assertInstanceOf(RefreshTokenEntityInterface::class, $refreshToken);
- $this->assertEquals($accessToken, $refreshToken->getAccessToken());
+
+ self::assertEquals($accessToken, $refreshToken->getAccessToken());
}
- public function testIssueNullRefreshToken()
+ public function testIssueNullRefreshToken(): void
{
$refreshTokenRepoMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepoMock
- ->expects($this->once())
+ ->expects(self::once())
->method('getNewRefreshToken')
->willReturn(null);
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
- $grantMock->setRefreshTokenTTL(new \DateInterval('PT1M'));
+ $grantMock->setRefreshTokenTTL(new DateInterval('PT1M'));
$grantMock->setRefreshTokenRepository($refreshTokenRepoMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$issueRefreshTokenMethod = $abstractGrantReflection->getMethod('issueRefreshToken');
$issueRefreshTokenMethod->setAccessible(true);
$accessToken = new AccessTokenEntity();
- $this->assertNull($issueRefreshTokenMethod->invoke($grantMock, $accessToken));
+ self::assertNull($issueRefreshTokenMethod->invoke($grantMock, $accessToken));
}
- public function testIssueAccessToken()
+ public function testIssueAccessToken(): void
{
$accessTokenRepoMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepoMock->method('getNewToken')->willReturn(new AccessTokenEntity());
@@ -410,7 +423,7 @@ public function testIssueAccessToken()
$grantMock->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grantMock->setAccessTokenRepository($accessTokenRepoMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$issueAccessTokenMethod = $abstractGrantReflection->getMethod('issueAccessToken');
$issueAccessTokenMethod->setAccessible(true);
@@ -422,23 +435,27 @@ public function testIssueAccessToken()
123,
[new ScopeEntity()]
);
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $accessToken);
+
+ self::assertNotEmpty($accessToken->getIdentifier());
}
- public function testIssueAuthCode()
+ public function testIssueAuthCode(): void
{
$authCodeRepoMock = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock();
- $authCodeRepoMock->expects($this->once())->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
+ $authCodeRepoMock->expects(self::once())->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setAuthCodeRepository($authCodeRepoMock);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$issueAuthCodeMethod = $abstractGrantReflection->getMethod('issueAuthCode');
$issueAuthCodeMethod->setAccessible(true);
- $this->assertInstanceOf(
+ $scope = new ScopeEntity();
+ $scope->setIdentifier('scopeId');
+
+ self::assertInstanceOf(
AuthCodeEntityInterface::class,
$issueAuthCodeMethod->invoke(
$grantMock,
@@ -446,17 +463,17 @@ public function testIssueAuthCode()
new ClientEntity(),
123,
'http://foo/bar',
- [new ScopeEntity()]
+ [$scope]
)
);
}
- public function testGetCookieParameter()
+ public function testGetCookieParameter(): void
{
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->method('getIdentifier')->willReturn('foobar');
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$method = $abstractGrantReflection->getMethod('getCookieParameter');
$method->setAccessible(true);
@@ -464,16 +481,16 @@ public function testGetCookieParameter()
'foo' => 'bar',
]);
- $this->assertEquals('bar', $method->invoke($grantMock, 'foo', $serverRequest));
- $this->assertEquals('foo', $method->invoke($grantMock, 'bar', $serverRequest, 'foo'));
+ self::assertEquals('bar', $method->invoke($grantMock, 'foo', $serverRequest));
+ self::assertEquals('foo', $method->invoke($grantMock, 'bar', $serverRequest, 'foo'));
}
- public function testGetQueryStringParameter()
+ public function testGetQueryStringParameter(): void
{
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->method('getIdentifier')->willReturn('foobar');
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$method = $abstractGrantReflection->getMethod('getQueryStringParameter');
$method->setAccessible(true);
@@ -481,24 +498,24 @@ public function testGetQueryStringParameter()
'foo' => 'bar',
]);
- $this->assertEquals('bar', $method->invoke($grantMock, 'foo', $serverRequest));
- $this->assertEquals('foo', $method->invoke($grantMock, 'bar', $serverRequest, 'foo'));
+ self::assertEquals('bar', $method->invoke($grantMock, 'foo', $serverRequest));
+ self::assertEquals('foo', $method->invoke($grantMock, 'bar', $serverRequest, 'foo'));
}
- public function testValidateScopes()
+ public function testValidateScopes(): void
{
$scope = new ScopeEntity();
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
- $scopeRepositoryMock->expects($this->exactly(3))->method('getScopeEntityByIdentifier')->willReturn($scope);
+ $scopeRepositoryMock->expects(self::exactly(3))->method('getScopeEntityByIdentifier')->willReturn($scope);
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setScopeRepository($scopeRepositoryMock);
- $this->assertEquals([$scope, $scope, $scope], $grantMock->validateScopes('basic test 0 '));
+ self::assertEquals([$scope, $scope, $scope], $grantMock->validateScopes('basic test 0 '));
}
- public function testValidateScopesBadScope()
+ public function testValidateScopesBadScope(): void
{
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn(null);
@@ -507,42 +524,42 @@ public function testValidateScopesBadScope()
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setScopeRepository($scopeRepositoryMock);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$grantMock->validateScopes('basic ');
}
- public function testGenerateUniqueIdentifier()
+ public function testGenerateUniqueIdentifier(): void
{
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
- $abstractGrantReflection = new \ReflectionClass($grantMock);
+ $abstractGrantReflection = new ReflectionClass($grantMock);
$method = $abstractGrantReflection->getMethod('generateUniqueIdentifier');
$method->setAccessible(true);
- $this->assertIsString($method->invoke($grantMock));
+ self::assertIsString($method->invoke($grantMock));
}
- public function testCanRespondToAuthorizationRequest()
+ public function testCanRespondToAuthorizationRequest(): void
{
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
- $this->assertFalse($grantMock->canRespondToAuthorizationRequest(new ServerRequest()));
+ self::assertFalse($grantMock->canRespondToAuthorizationRequest(new ServerRequest()));
}
- public function testValidateAuthorizationRequest()
+ public function testValidateAuthorizationRequest(): void
{
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
- $this->expectException(\LogicException::class);
+ $this->expectException(LogicException::class);
$grantMock->validateAuthorizationRequest(new ServerRequest());
}
- public function testCompleteAuthorizationRequest()
+ public function testCompleteAuthorizationRequest(): void
{
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
- $this->expectException(\LogicException::class);
+ $this->expectException(LogicException::class);
$grantMock->completeAuthorizationRequest(new AuthorizationRequest());
}
diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php
index 7c2c20d11..4a3c1e935 100644
--- a/tests/Grant/AuthCodeGrantTest.php
+++ b/tests/Grant/AuthCodeGrantTest.php
@@ -1,12 +1,13 @@
cryptStub = new CryptTraitStub();
}
- public function testGetIdentifier()
+ public function testGetIdentifier(): void
{
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
@@ -55,10 +59,10 @@ public function testGetIdentifier()
new DateInterval('PT10M')
);
- $this->assertEquals('authorization_code', $grant->getIdentifier());
+ self::assertEquals('authorization_code', $grant->getIdentifier());
}
- public function testCanRespondToAuthorizationRequest()
+ public function testCanRespondToAuthorizationRequest(): void
{
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
@@ -80,10 +84,10 @@ public function testCanRespondToAuthorizationRequest()
]
);
- $this->assertTrue($grant->canRespondToAuthorizationRequest($request));
+ self::assertTrue($grant->canRespondToAuthorizationRequest($request));
}
- public function testValidateAuthorizationRequest()
+ public function testValidateAuthorizationRequest(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
@@ -120,10 +124,10 @@ public function testValidateAuthorizationRequest()
]
);
- $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request));
+ self::assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request));
}
- public function testValidateAuthorizationRequestRedirectUriArray()
+ public function testValidateAuthorizationRequestRedirectUriArray(): void
{
$client = new ClientEntity();
$client->setRedirectUri([self::REDIRECT_URI]);
@@ -159,10 +163,10 @@ public function testValidateAuthorizationRequestRedirectUriArray()
]
);
- $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request));
+ self::assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request));
}
- public function testValidateAuthorizationRequestWithoutRedirectUri()
+ public function testValidateAuthorizationRequestWithoutRedirectUri(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
@@ -199,12 +203,12 @@ public function testValidateAuthorizationRequestWithoutRedirectUri()
);
$authorizationRequest = $grant->validateAuthorizationRequest($request);
- $this->assertInstanceOf(AuthorizationRequest::class, $authorizationRequest);
+ self::assertInstanceOf(AuthorizationRequest::class, $authorizationRequest);
- $this->assertEmpty($authorizationRequest->getRedirectUri());
+ self::assertEmpty($authorizationRequest->getRedirectUri());
}
- public function testValidateAuthorizationRequestCodeChallenge()
+ public function testValidateAuthorizationRequestCodeChallenge(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
@@ -241,15 +245,16 @@ public function testValidateAuthorizationRequestCodeChallenge()
]
);
- $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request));
+ self::assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request));
}
- public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooShort()
+ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooShort(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
@@ -257,26 +262,29 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooSho
new DateInterval('PT10M')
);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
$grant->setClientRepository($clientRepositoryMock);
+ $grant->setScopeRepository($scopeRepositoryMock);
$request = (new ServerRequest())->withQueryParams([
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
- 'code_challenge' => \str_repeat('A', 42),
+ 'code_challenge' => str_repeat('A', 42),
]);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$grant->validateAuthorizationRequest($request);
}
- public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLong()
+ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLong(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
@@ -285,25 +293,28 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLon
);
$grant->setClientRepository($clientRepositoryMock);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
+ $grant->setScopeRepository($scopeRepositoryMock);
$request = (new ServerRequest())->withQueryParams([
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
- 'code_challenge' => \str_repeat('A', 129),
+ 'code_challenge' => str_repeat('A', 129),
]);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$grant->validateAuthorizationRequest($request);
}
- public function testValidateAuthorizationRequestCodeChallengeInvalidCharacters()
+ public function testValidateAuthorizationRequestCodeChallengeInvalidCharacters(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
@@ -312,20 +323,22 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidCharacters()
);
$grant->setClientRepository($clientRepositoryMock);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
+ $grant->setScopeRepository($scopeRepositoryMock);
$request = (new ServerRequest())->withQueryParams([
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
- 'code_challenge' => \str_repeat('A', 42) . '!',
+ 'code_challenge' => str_repeat('A', 42) . '!',
]);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$grant->validateAuthorizationRequest($request);
}
- public function testValidateAuthorizationRequestMissingClientId()
+ public function testValidateAuthorizationRequestMissingClientId(): void
{
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
@@ -340,13 +353,13 @@ public function testValidateAuthorizationRequestMissingClientId()
'response_type' => 'code',
]);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(3);
$grant->validateAuthorizationRequest($request);
}
- public function testValidateAuthorizationRequestInvalidClientId()
+ public function testValidateAuthorizationRequestInvalidClientId(): void
{
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn(null);
@@ -363,13 +376,13 @@ public function testValidateAuthorizationRequestInvalidClientId()
'client_id' => 'foo',
]);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(4);
$grant->validateAuthorizationRequest($request);
}
- public function testValidateAuthorizationRequestBadRedirectUriString()
+ public function testValidateAuthorizationRequestBadRedirectUriString(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
@@ -389,13 +402,13 @@ public function testValidateAuthorizationRequestBadRedirectUriString()
'redirect_uri' => 'http://bar',
]);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(4);
$grant->validateAuthorizationRequest($request);
}
- public function testValidateAuthorizationRequestBadRedirectUriArray()
+ public function testValidateAuthorizationRequestBadRedirectUriArray(): void
{
$client = new ClientEntity();
$client->setRedirectUri([self::REDIRECT_URI]);
@@ -415,13 +428,13 @@ public function testValidateAuthorizationRequestBadRedirectUriArray()
'redirect_uri' => 'http://bar',
]);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(4);
$grant->validateAuthorizationRequest($request);
}
- public function testValidateAuthorizationRequestInvalidCodeChallengeMethod()
+ public function testValidateAuthorizationRequestInvalidCodeChallengeMethod(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
@@ -450,16 +463,17 @@ public function testValidateAuthorizationRequestInvalidCodeChallengeMethod()
'code_challenge_method' => 'foo',
]);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(3);
$grant->validateAuthorizationRequest($request);
}
- public function testCompleteAuthorizationRequest()
+ public function testCompleteAuthorizationRequest(): void
{
$client = new ClientEntity();
- $client->setRedirectUri(self::REDIRECT_URI);
+ $client->setIdentifier('clientId');
+ $client->setRedirectUri('http://foo/bar');
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
@@ -477,13 +491,38 @@ public function testCompleteAuthorizationRequest()
);
$grant->setEncryptionKey($this->cryptStub->getKey());
- $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
+ self::assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
}
- public function testCompleteAuthorizationRequestDenied()
+ public function testCompleteAuthorizationRequestWithMultipleRedirectUrisOnClient(): void
{
$client = new ClientEntity();
- $client->setRedirectUri(self::REDIRECT_URI);
+ $client->setIdentifier('clientId');
+ $client->setRedirectUri(['uriOne', 'uriTwo']);
+
+ $authRequest = new AuthorizationRequest();
+ $authRequest->setAuthorizationApproved(true);
+ $authRequest->setClient($client);
+ $authRequest->setGrantTypeId('authorization_code');
+ $authRequest->setUser(new UserEntity());
+
+ $authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock();
+ $authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
+
+ $grant = new AuthCodeGrant(
+ $authCodeRepository,
+ $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
+ new DateInterval('PT10M')
+ );
+ $grant->setEncryptionKey($this->cryptStub->getKey());
+
+ self::assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
+ }
+
+ public function testCompleteAuthorizationRequestDenied(): void
+ {
+ $client = new ClientEntity();
+ $client->setRedirectUri('http://foo/bar');
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(false);
@@ -501,20 +540,24 @@ public function testCompleteAuthorizationRequestDenied()
);
$grant->setEncryptionKey($this->cryptStub->getKey());
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(9);
$grant->completeAuthorizationRequest($authRequest);
}
- public function testRespondToAccessTokenRequest()
+ public function testRespondToAccessTokenRequest(): void
{
$client = new ClientEntity();
+
$client->setIdentifier('foo');
$client->setRedirectUri(self::REDIRECT_URI);
$client->setConfidential();
+
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
@@ -555,16 +598,14 @@ public function testRespondToAccessTokenRequest()
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => self::REDIRECT_URI,
+ ], JSON_THROW_ON_ERROR)
),
]
);
@@ -572,11 +613,10 @@ public function testRespondToAccessTokenRequest()
/** @var StubResponseType $response */
$response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken());
- $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
+ self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
}
- public function testRespondToAccessTokenRequestUsingHttpBasicAuth()
+ public function testRespondToAccessTokenRequestUsingHttpBasicAuth(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
@@ -597,7 +637,7 @@ public function testRespondToAccessTokenRequestUsingHttpBasicAuth()
$authCodeGrant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$refreshTokenRepositoryMock,
- new \DateInterval('PT10M')
+ new DateInterval('PT10M')
);
$authCodeGrant->setClientRepository($clientRepositoryMock);
@@ -621,28 +661,25 @@ public function testRespondToAccessTokenRequestUsingHttpBasicAuth()
'grant_type' => 'authorization_code',
'redirect_uri' => self::REDIRECT_URI,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'client_id' => 'foo',
- 'expire_time' => \time() + 3600,
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'client_id' => 'foo',
+ 'expire_time' => time() + 3600,
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => self::REDIRECT_URI,
+ ], JSON_THROW_ON_ERROR)
),
]
);
/** @var StubResponseType $response */
- $response = $authCodeGrant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M'));
+ $response = $authCodeGrant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken());
- $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
+ self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
}
- public function testRespondToAccessTokenRequestForPublicClient()
+ public function testRespondToAccessTokenRequestForPublicClient(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -689,16 +726,14 @@ public function testRespondToAccessTokenRequestForPublicClient()
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => self::REDIRECT_URI,
+ ], JSON_THROW_ON_ERROR)
),
]
);
@@ -706,11 +741,10 @@ public function testRespondToAccessTokenRequestForPublicClient()
/** @var StubResponseType $response */
$response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken());
- $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
+ self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
}
- public function testRespondToAccessTokenRequestNullRefreshToken()
+ public function testRespondToAccessTokenRequestNullRefreshToken(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -734,7 +768,7 @@ public function testRespondToAccessTokenRequestNullRefreshToken()
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$refreshTokenRepositoryMock,
- new \DateInterval('PT10M')
+ new DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -757,35 +791,36 @@ public function testRespondToAccessTokenRequestNullRefreshToken()
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => self::REDIRECT_URI,
+ ], JSON_THROW_ON_ERROR)
),
]
);
/** @var StubResponseType $response */
- $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M'));
+ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken());
- $this->assertNull($response->getRefreshToken());
+ self::assertNull($response->getRefreshToken());
}
- public function testRespondToAccessTokenRequestCodeChallengePlain()
+ public function testRespondToAccessTokenRequestCodeChallengePlain(): void
{
$client = new ClientEntity();
+
$client->setIdentifier('foo');
$client->setRedirectUri(self::REDIRECT_URI);
$client->setConfidential();
+
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
@@ -828,18 +863,16 @@ public function testRespondToAccessTokenRequestCodeChallengePlain()
'redirect_uri' => self::REDIRECT_URI,
'code_verifier' => self::CODE_VERIFIER,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- 'code_challenge' => self::CODE_VERIFIER,
- 'code_challenge_method' => 'plain',
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => self::REDIRECT_URI,
+ 'code_challenge' => self::CODE_VERIFIER,
+ 'code_challenge_method' => 'plain',
+ ], JSON_THROW_ON_ERROR)
),
]
);
@@ -847,18 +880,21 @@ public function testRespondToAccessTokenRequestCodeChallengePlain()
/** @var StubResponseType $response */
$response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken());
- $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
+ self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
}
- public function testRespondToAccessTokenRequestCodeChallengeS256()
+ public function testRespondToAccessTokenRequestCodeChallengeS256(): void
{
$client = new ClientEntity();
+
$client->setIdentifier('foo');
$client->setRedirectUri(self::REDIRECT_URI);
$client->setConfidential();
+
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
@@ -901,18 +937,16 @@ public function testRespondToAccessTokenRequestCodeChallengeS256()
'redirect_uri' => self::REDIRECT_URI,
'code_verifier' => self::CODE_VERIFIER,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- 'code_challenge' => self::CODE_CHALLENGE,
- 'code_challenge_method' => 'S256',
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => self::REDIRECT_URI,
+ 'code_challenge' => self::CODE_CHALLENGE,
+ 'code_challenge_method' => 'S256',
+ ], JSON_THROW_ON_ERROR)
),
]
);
@@ -920,11 +954,10 @@ public function testRespondToAccessTokenRequestCodeChallengeS256()
/** @var StubResponseType $response */
$response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken());
- $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
+ self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
}
- public function testPKCEDowngradeBlocked()
+ public function testPKCEDowngradeBlocked(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -932,6 +965,7 @@ public function testPKCEDowngradeBlocked()
$client->setConfidential();
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
@@ -974,35 +1008,40 @@ public function testPKCEDowngradeBlocked()
'redirect_uri' => self::REDIRECT_URI,
'code_verifier' => self::CODE_VERIFIER,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
+ json_encode(
[
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
'client_id' => 'foo',
'user_id' => 123,
'scopes' => ['foo'],
'redirect_uri' => self::REDIRECT_URI,
- ]
+ ],
+ JSON_THROW_ON_ERROR
)
),
]
);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(3);
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
}
- public function testRespondToAccessTokenRequestMissingRedirectUri()
+ public function testRespondToAccessTokenRequestMissingRedirectUri(): void
{
$client = new ClientEntity();
+
$client->setIdentifier('foo');
$client->setConfidential();
$client->setRedirectUri(self::REDIRECT_URI);
+
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
@@ -1025,32 +1064,34 @@ public function testRespondToAccessTokenRequestMissingRedirectUri()
'client_id' => 'foo',
'grant_type' => 'authorization_code',
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'redirect_uri' => self::REDIRECT_URI,
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'redirect_uri' => 'http://foo/bar',
+ ], JSON_THROW_ON_ERROR)
),
]
);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(3);
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
}
- public function testRespondToAccessTokenRequestRedirectUriMismatch()
+ public function testRespondToAccessTokenRequestRedirectUriMismatch(): void
{
$client = new ClientEntity();
+
$client->setIdentifier('foo');
$client->setConfidential();
$client->setRedirectUri('http://bar/foo');
+
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
@@ -1074,31 +1115,33 @@ public function testRespondToAccessTokenRequestRedirectUriMismatch()
'grant_type' => 'authorization_code',
'redirect_uri' => 'http://bar/foo',
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'redirect_uri' => self::REDIRECT_URI,
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'redirect_uri' => 'http://foo/bar',
+ ], JSON_THROW_ON_ERROR)
),
]
);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(3);
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
}
- public function testRespondToAccessTokenRequestMissingCode()
+ public function testRespondToAccessTokenRequestMissingCode(): void
{
$client = new ClientEntity();
+
$client->setRedirectUri(self::REDIRECT_URI);
$client->setConfidential();
+
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
@@ -1130,14 +1173,14 @@ public function testRespondToAccessTokenRequestMissingCode()
]
);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(3);
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
}
- public function testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode()
+ public function testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
@@ -1168,16 +1211,14 @@ public function testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
'code' => $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,
- ]
- )
+ json_encode([
+ 'client_id' => 'foo',
+ 'refresh_token_id' => 'zyxwvu',
+ 'access_token_id' => 'abcdef',
+ 'scopes' => ['foo'],
+ 'user_id' => 123,
+ 'expire_time' => time() + 3600,
+ ], JSON_THROW_ON_ERROR)
),
]
);
@@ -1186,11 +1227,11 @@ public function testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
} catch (OAuthServerException $e) {
- $this->assertEquals('Authorization code malformed', $e->getHint());
+ self::assertEquals('Authorization code malformed', $e->getHint());
}
}
- public function testRespondToAccessTokenRequestWithAuthCodeNotAString()
+ public function testRespondToAccessTokenRequestWithAuthCodeNotAString(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
@@ -1224,11 +1265,11 @@ public function testRespondToAccessTokenRequestWithAuthCodeNotAString()
]
);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
}
- public function testRespondToAccessTokenRequestExpiredCode()
+ public function testRespondToAccessTokenRequestExpiredCode(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
@@ -1259,16 +1300,14 @@ public function testRespondToAccessTokenRequestExpiredCode()
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() - 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() - 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => 'http://foo/bar',
+ ], JSON_THROW_ON_ERROR)
),
]
);
@@ -1277,18 +1316,22 @@ public function testRespondToAccessTokenRequestExpiredCode()
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
} catch (OAuthServerException $e) {
- $this->assertEquals($e->getHint(), 'Authorization code has expired');
+ self::assertEquals($e->getHint(), 'Authorization code has expired');
}
}
- public function testRespondToAccessTokenRequestRevokedCode()
+ public function testRespondToAccessTokenRequestRevokedCode(): void
{
$client = new ClientEntity();
+
$client->setIdentifier('foo');
$client->setRedirectUri(self::REDIRECT_URI);
$client->setConfidential();
+
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
@@ -1323,16 +1366,14 @@ public function testRespondToAccessTokenRequestRevokedCode()
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => 'http://foo/bar',
+ ], JSON_THROW_ON_ERROR)
),
]
);
@@ -1341,18 +1382,23 @@ public function testRespondToAccessTokenRequestRevokedCode()
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
} catch (OAuthServerException $e) {
- $this->assertEquals($e->getHint(), 'Authorization code has been revoked');
+ self::assertEquals($e->getHint(), 'Authorization code has been revoked');
+ self::assertEquals($e->getErrorType(), 'invalid_grant');
}
}
- public function testRespondToAccessTokenRequestClientMismatch()
+ public function testRespondToAccessTokenRequestClientMismatch(): void
{
$client = new ClientEntity();
+
$client->setIdentifier('foo');
$client->setRedirectUri(self::REDIRECT_URI);
$client->setConfidential();
+
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
@@ -1384,16 +1430,14 @@ public function testRespondToAccessTokenRequestClientMismatch()
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'bar',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'bar',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => 'http://foo/bar',
+ ], JSON_THROW_ON_ERROR)
),
]
);
@@ -1402,18 +1446,22 @@ public function testRespondToAccessTokenRequestClientMismatch()
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
} catch (OAuthServerException $e) {
- $this->assertEquals($e->getHint(), 'Authorization code was not issued to this client');
+ self::assertEquals($e->getHint(), 'Authorization code was not issued to this client');
}
}
- public function testRespondToAccessTokenRequestBadCodeEncryption()
+ public function testRespondToAccessTokenRequestBadCodeEncryption(): void
{
$client = new ClientEntity();
+
$client->setIdentifier('foo');
$client->setRedirectUri(self::REDIRECT_URI);
$client->setConfidential();
+
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
@@ -1452,18 +1500,22 @@ public function testRespondToAccessTokenRequestBadCodeEncryption()
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
} catch (OAuthServerException $e) {
- $this->assertEquals($e->getHint(), 'Cannot decrypt the authorization code');
+ self::assertEquals($e->getHint(), 'Cannot decrypt the authorization code');
}
}
- public function testRespondToAccessTokenRequestBadCodeVerifierPlain()
+ public function testRespondToAccessTokenRequestBadCodeVerifierPlain(): void
{
$client = new ClientEntity();
+
$client->setIdentifier('foo');
$client->setRedirectUri(self::REDIRECT_URI);
$client->setConfidential();
+
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
@@ -1505,18 +1557,16 @@ public function testRespondToAccessTokenRequestBadCodeVerifierPlain()
'redirect_uri' => self::REDIRECT_URI,
'code_verifier' => self::CODE_VERIFIER,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- 'code_challenge' => 'foobar',
- 'code_challenge_method' => 'plain',
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => self::REDIRECT_URI,
+ 'code_challenge' => 'foobar',
+ 'code_challenge_method' => 'plain',
+ ], JSON_THROW_ON_ERROR)
),
]
);
@@ -1525,18 +1575,22 @@ public function testRespondToAccessTokenRequestBadCodeVerifierPlain()
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
} catch (OAuthServerException $e) {
- $this->assertEquals($e->getHint(), 'Failed to verify `code_verifier`.');
+ self::assertEquals($e->getHint(), 'Failed to verify `code_verifier`.');
}
}
- public function testRespondToAccessTokenRequestBadCodeVerifierS256()
+ public function testRespondToAccessTokenRequestBadCodeVerifierS256(): void
{
$client = new ClientEntity();
+
$client->setIdentifier('foo');
$client->setRedirectUri(self::REDIRECT_URI);
$client->setConfidential();
+
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
@@ -1578,18 +1632,16 @@ public function testRespondToAccessTokenRequestBadCodeVerifierS256()
'redirect_uri' => self::REDIRECT_URI,
'code_verifier' => 'nope',
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- 'code_challenge' => 'foobar',
- 'code_challenge_method' => 'S256',
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => self::REDIRECT_URI,
+ 'code_challenge' => 'foobar',
+ 'code_challenge_method' => 'S256',
+ ], JSON_THROW_ON_ERROR)
),
]
);
@@ -1598,18 +1650,22 @@ public function testRespondToAccessTokenRequestBadCodeVerifierS256()
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
} catch (OAuthServerException $e) {
- $this->assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.');
+ self::assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.');
}
}
- public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInvalidChars()
+ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInvalidChars(): void
{
$client = new ClientEntity();
+
$client->setIdentifier('foo');
$client->setRedirectUri(self::REDIRECT_URI);
$client->setConfidential();
+
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
@@ -1651,18 +1707,16 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva
'redirect_uri' => self::REDIRECT_URI,
'code_verifier' => 'dqX7C-RbqjHYtytmhGTigKdZCXfxq-+xbsk9_GxUcaE', // Malformed code. Contains `+`.
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- 'code_challenge' => self::CODE_CHALLENGE,
- 'code_challenge_method' => 'S256',
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => self::REDIRECT_URI,
+ 'code_challenge' => self::CODE_CHALLENGE,
+ 'code_challenge_method' => 'S256',
+ ], JSON_THROW_ON_ERROR)
),
]
);
@@ -1671,18 +1725,22 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
} catch (OAuthServerException $e) {
- $this->assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.');
+ self::assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.');
}
}
- public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInvalidLength()
+ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInvalidLength(): void
{
$client = new ClientEntity();
+
$client->setIdentifier('foo');
$client->setRedirectUri(self::REDIRECT_URI);
$client->setConfidential();
+
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
@@ -1724,18 +1782,16 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva
'redirect_uri' => self::REDIRECT_URI,
'code_verifier' => 'dqX7C-RbqjHY', // Malformed code. Invalid length.
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- 'code_challenge' => 'R7T1y1HPNFvs1WDCrx4lfoBS6KD2c71pr8OHvULjvv8',
- 'code_challenge_method' => 'S256',
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => self::REDIRECT_URI,
+ 'code_challenge' => 'R7T1y1HPNFvs1WDCrx4lfoBS6KD2c71pr8OHvULjvv8',
+ 'code_challenge_method' => 'S256',
+ ], JSON_THROW_ON_ERROR)
),
]
);
@@ -1744,18 +1800,22 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
} catch (OAuthServerException $e) {
- $this->assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.');
+ self::assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.');
}
}
- public function testRespondToAccessTokenRequestMissingCodeVerifier()
+ public function testRespondToAccessTokenRequestMissingCodeVerifier(): void
{
$client = new ClientEntity();
+
$client->setIdentifier('foo');
$client->setRedirectUri(self::REDIRECT_URI);
$client->setConfidential();
+
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
@@ -1796,18 +1856,16 @@ public function testRespondToAccessTokenRequestMissingCodeVerifier()
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- 'code_challenge' => 'foobar',
- 'code_challenge_method' => 'plain',
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => self::REDIRECT_URI,
+ 'code_challenge' => 'foobar',
+ 'code_challenge_method' => 'plain',
+ ], JSON_THROW_ON_ERROR)
),
]
);
@@ -1816,14 +1874,16 @@ public function testRespondToAccessTokenRequestMissingCodeVerifier()
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
} catch (OAuthServerException $e) {
- $this->assertEquals($e->getHint(), 'Check the `code_verifier` parameter');
+ self::assertEquals($e->getHint(), 'Check the `code_verifier` parameter');
}
}
- public function testAuthCodeRepositoryUniqueConstraintCheck()
+ public function testAuthCodeRepositoryUniqueConstraintCheck(): void
{
$client = new ClientEntity();
+ $client->setIdentifier('clientId');
$client->setRedirectUri(self::REDIRECT_URI);
+ $client->setIdentifier('clientIdentifier');
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
@@ -1834,13 +1894,13 @@ public function testAuthCodeRepositoryUniqueConstraintCheck()
$authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock();
$authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
- $matcher = $this->exactly(2);
-
$authCodeRepository
- ->expects($matcher)
+ ->expects(self::exactly(2))
->method('persistNewAuthCode')
- ->willReturnCallback(function () use ($matcher) {
- if ($matcher->getInvocationCount() === 1) {
+ ->willReturnCallback(function (): void {
+ static $counter = 0;
+
+ if (1 === ++$counter) {
throw UniqueTokenIdentifierConstraintViolationException::create();
}
});
@@ -1853,14 +1913,18 @@ public function testAuthCodeRepositoryUniqueConstraintCheck()
$grant->setEncryptionKey($this->cryptStub->getKey());
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
- $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
+ self::assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
}
- public function testAuthCodeRepositoryFailToPersist()
+ public function testAuthCodeRepositoryFailToPersist(): void
{
+ $client = new ClientEntity();
+
+ $client->setRedirectUri('http://foo/bar');
+
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
- $authRequest->setClient(new ClientEntity());
+ $authRequest->setClient($client);
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
@@ -1875,17 +1939,21 @@ public function testAuthCodeRepositoryFailToPersist()
);
$grant->setEncryptionKey($this->cryptStub->getKey());
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(7);
- $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
+ self::assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
}
- public function testAuthCodeRepositoryFailToPersistUniqueNoInfiniteLoop()
+ public function testAuthCodeRepositoryFailToPersistUniqueNoInfiniteLoop(): void
{
+ $client = new ClientEntity();
+
+ $client->setRedirectUri('http://foo/bar');
+
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
- $authRequest->setClient(new ClientEntity());
+ $authRequest->setClient($client);
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
@@ -1899,13 +1967,13 @@ public function testAuthCodeRepositoryFailToPersistUniqueNoInfiniteLoop()
new DateInterval('PT10M')
);
- $this->expectException(\League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException::class);
+ $this->expectException(UniqueTokenIdentifierConstraintViolationException::class);
$this->expectExceptionCode(100);
- $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
+ self::assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
}
- public function testRefreshTokenRepositoryUniqueConstraintCheck()
+ public function testRefreshTokenRepositoryUniqueConstraintCheck(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -1925,13 +1993,13 @@ public function testRefreshTokenRepositoryUniqueConstraintCheck()
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
- $matcher = $this->exactly(2);
-
$refreshTokenRepositoryMock
- ->expects($matcher)
+ ->expects(self::exactly(2))
->method('persistNewRefreshToken')
- ->willReturnCallback(function () use ($matcher) {
- if ($matcher->getInvocationCount() === 1) {
+ ->willReturnCallback(function (): void {
+ static $count = 0;
+
+ if (1 === ++$count) {
throw UniqueTokenIdentifierConstraintViolationException::create();
}
});
@@ -1962,16 +2030,14 @@ public function testRefreshTokenRepositoryUniqueConstraintCheck()
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => self::REDIRECT_URI,
+ ], JSON_THROW_ON_ERROR)
),
]
);
@@ -1979,11 +2045,10 @@ public function testRefreshTokenRepositoryUniqueConstraintCheck()
/** @var StubResponseType $response */
$response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken());
- $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
+ self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
}
- public function testRefreshTokenRepositoryFailToPersist()
+ public function testRefreshTokenRepositoryFailToPersist(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -2030,31 +2095,28 @@ public function testRefreshTokenRepositoryFailToPersist()
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => self::REDIRECT_URI,
+ ], JSON_THROW_ON_ERROR)
),
]
);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(7);
/** @var StubResponseType $response */
$response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken());
- $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
+ self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
}
- public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop()
+ public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -2101,31 +2163,28 @@ public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop()
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
'code' => $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'auth_code_id' => \uniqid(),
- 'expire_time' => \time() + 3600,
- 'client_id' => 'foo',
- 'user_id' => 123,
- 'scopes' => ['foo'],
- 'redirect_uri' => self::REDIRECT_URI,
- ]
- )
+ json_encode([
+ 'auth_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_id' => 123,
+ 'scopes' => ['foo'],
+ 'redirect_uri' => self::REDIRECT_URI,
+ ], JSON_THROW_ON_ERROR)
),
]
);
- $this->expectException(\League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException::class);
+ $this->expectException(UniqueTokenIdentifierConstraintViolationException::class);
$this->expectExceptionCode(100);
/** @var StubResponseType $response */
$response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken());
- $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
+ self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
}
- public function testCompleteAuthorizationRequestNoUser()
+ public function testCompleteAuthorizationRequestNoUser(): void
{
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
@@ -2133,12 +2192,12 @@ public function testCompleteAuthorizationRequestNoUser()
new DateInterval('PT10M')
);
- $this->expectException(\LogicException::class);
+ $this->expectException(LogicException::class);
$grant->completeAuthorizationRequest(new AuthorizationRequest());
}
- public function testPublicClientAuthCodeRequestRejectedWhenCodeChallengeRequiredButNotGiven()
+ public function testPublicClientAuthCodeRequestRejectedWhenCodeChallengeRequiredButNotGiven(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
@@ -2164,6 +2223,7 @@ public function testPublicClientAuthCodeRequestRejectedWhenCodeChallengeRequired
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
+ 'state' => 'foo',
]);
$this->expectException(OAuthServerException::class);
@@ -2172,7 +2232,7 @@ public function testPublicClientAuthCodeRequestRejectedWhenCodeChallengeRequired
$grant->validateAuthorizationRequest($request);
}
- public function testUseValidRedirectUriIfScopeCheckFails()
+ public function testUseValidRedirectUriIfScopeCheckFails(): void
{
$client = new ClientEntity();
$client->setRedirectUri([self::REDIRECT_URI, 'http://bar/foo']);
@@ -2214,48 +2274,7 @@ public function testUseValidRedirectUriIfScopeCheckFails()
} catch (OAuthServerException $e) {
$response = $e->generateHttpResponse(new Response());
- $this->assertStringStartsWith('http://bar/foo', $response->getHeader('Location')[0]);
+ self::assertStringStartsWith('http://bar/foo', $response->getHeader('Location')[0]);
}
}
-
- public function testThrowExceptionWhenNoClientRedirectUriRegistered()
- {
- $client = (new ClientEntity())->setConfidential();
-
- $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
- $clientRepositoryMock->method('getClientEntity')->willReturn($client);
-
- $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
- $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn(null);
-
- $grant = new AuthCodeGrant(
- $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
- $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
- new DateInterval('PT10M')
- );
-
- $grant->setClientRepository($clientRepositoryMock);
- $grant->setScopeRepository($scopeRepositoryMock);
- $grant->setDefaultScope(self::DEFAULT_SCOPE);
-
- $request = new ServerRequest(
- [],
- [],
- null,
- null,
- 'php://input',
- [],
- [],
- [
- 'response_type' => 'code',
- 'client_id' => 'foo',
- 'redirect_uri' => 'http://bar/foo',
- ]
- );
-
- $this->expectException(OAuthServerException::class);
- $this->expectExceptionCode(4);
-
- $grant->validateAuthorizationRequest($request);
- }
}
diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php
index 13ea78bae..264e026e2 100644
--- a/tests/Grant/ClientCredentialsGrantTest.php
+++ b/tests/Grant/ClientCredentialsGrantTest.php
@@ -1,11 +1,12 @@
assertEquals('client_credentials', $grant->getIdentifier());
+ self::assertEquals('client_credentials', $grant->getIdentifier());
}
- public function testRespondToRequest()
+ public function testRespondToRequest(): void
{
$client = new ClientEntity();
$client->setConfidential();
@@ -34,6 +35,7 @@ public function testRespondToRequest()
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
@@ -57,8 +59,8 @@ public function testRespondToRequest()
]);
$responseType = new StubResponseType();
- $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
+ $response = $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken());
+ self::assertNotEmpty($response->getAccessToken()->getIdentifier());
}
}
diff --git a/tests/Grant/DeviceCodeGrantTest.php b/tests/Grant/DeviceCodeGrantTest.php
new file mode 100644
index 000000000..396ea760f
--- /dev/null
+++ b/tests/Grant/DeviceCodeGrantTest.php
@@ -0,0 +1,798 @@
+cryptStub = new CryptTraitStub();
+ }
+
+ public function testGetIdentifier(): void
+ {
+ $deviceCodeRepositoryMock = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock();
+ $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
+
+ $grant = new DeviceCodeGrant(
+ $deviceCodeRepositoryMock,
+ $refreshTokenRepositoryMock,
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+
+ $this::assertEquals('urn:ietf:params:oauth:grant-type:device_code', $grant->getIdentifier());
+ }
+
+ public function testCanRespondToDeviceAuthorizationRequest(): void
+ {
+ $grant = new DeviceCodeGrant(
+ $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock(),
+ $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+
+ $request = (new ServerRequest())->withParsedBody([
+ 'client_id' => 'foo',
+ 'scope' => 'basic',
+ ]);
+
+ $this::assertTrue($grant->canRespondToDeviceAuthorizationRequest($request));
+ }
+
+ public function testRespondToDeviceAuthorizationRequest(): void
+ {
+ $client = new ClientEntity();
+ $client->setIdentifier('foo');
+
+ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepositoryMock->method('getClientEntity')->willReturn($client);
+
+ $deviceCodeRepository = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock();
+ $deviceCodeRepository->method('getNewDeviceCode')->willReturn(new DeviceCodeEntity());
+
+ $scope = new ScopeEntity();
+ $scope->setIdentifier('basic');
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
+
+ $grant = new DeviceCodeGrant(
+ $deviceCodeRepository,
+ $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+
+ $grant->setClientRepository($clientRepositoryMock);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
+ $grant->setEncryptionKey($this->cryptStub->getKey());
+ $grant->setScopeRepository($scopeRepositoryMock);
+
+ $request = (new ServerRequest())->withParsedBody([
+ 'client_id' => 'foo',
+ 'scope' => 'basic',
+ ]);
+
+ $deviceCodeResponse = $grant->respondToDeviceAuthorizationRequest($request);
+
+ $responseJson = json_decode($deviceCodeResponse->generateHttpResponse(new Response())->getBody()->__toString());
+
+ self::assertObjectHasProperty('device_code', $responseJson);
+ self::assertObjectHasProperty('user_code', $responseJson);
+ self::assertObjectHasProperty('verification_uri', $responseJson);
+ self::assertEquals('http://foo/bar', $responseJson->verification_uri);
+ }
+
+ public function testRespondToDeviceAuthorizationRequestWithVerificationUriComplete(): void
+ {
+ $client = new ClientEntity();
+ $client->setIdentifier('foo');
+
+ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepositoryMock->method('getClientEntity')->willReturn($client);
+
+ $deviceCodeRepository = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock();
+ $deviceCodeRepository->method('getNewDeviceCode')->willReturn(new DeviceCodeEntity());
+
+ $scope = new ScopeEntity();
+ $scope->setIdentifier('basic');
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
+
+ $grant = new DeviceCodeGrant(
+ $deviceCodeRepository,
+ $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+
+ $grant->setIncludeVerificationUriComplete(true);
+
+ $grant->setClientRepository($clientRepositoryMock);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
+ $grant->setEncryptionKey($this->cryptStub->getKey());
+ $grant->setScopeRepository($scopeRepositoryMock);
+
+ $request = (new ServerRequest())->withParsedBody([
+ 'client_id' => 'foo',
+ 'scope' => 'basic',
+ ]);
+
+ $deviceCodeResponse = $grant->respondToDeviceAuthorizationRequest($request);
+
+ $responseJson = json_decode($deviceCodeResponse->generateHttpResponse(new Response())->getBody()->__toString());
+
+ self::assertObjectHasProperty('device_code', $responseJson);
+ self::assertObjectHasProperty('user_code', $responseJson);
+ self::assertObjectHasProperty('verification_uri', $responseJson);
+ self::assertObjectHasProperty('verification_uri_complete', $responseJson);
+ self::assertEquals('http://foo/bar', $responseJson->verification_uri);
+ self::assertEquals('http://foo/bar?user_code=' . $responseJson->user_code, $responseJson->verification_uri_complete);
+ }
+
+ public function testValidateDeviceAuthorizationRequestMissingClient(): void
+ {
+ $client = new ClientEntity();
+ $client->setIdentifier('foo');
+
+ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepositoryMock->method('getClientEntity')->willReturn($client);
+
+ $scope = new ScopeEntity();
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
+
+ $grant = new DeviceCodeGrant(
+ $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock(),
+ $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+ $grant->setClientRepository($clientRepositoryMock);
+ $grant->setScopeRepository($scopeRepositoryMock);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
+
+ $request = (new ServerRequest())->withParsedBody([
+ 'scope' => 'basic',
+ ]);
+
+ $this->expectException(OAuthServerException::class);
+
+ $grant->respondToDeviceAuthorizationRequest($request);
+ }
+
+ public function testValidateDeviceAuthorizationRequestEmptyScope(): void
+ {
+ $client = new ClientEntity();
+ $client->setIdentifier('foo');
+
+ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepositoryMock->method('getClientEntity')->willReturn($client);
+
+ $scope = new ScopeEntity();
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
+
+ $grant = new DeviceCodeGrant(
+ $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock(),
+ $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+
+ $grant->setClientRepository($clientRepositoryMock);
+ $grant->setScopeRepository($scopeRepositoryMock);
+
+ $request = (new ServerRequest())->withParsedBody([
+ 'scope' => '',
+ ]);
+
+ $this->expectException(OAuthServerException::class);
+
+ $grant->respondToDeviceAuthorizationRequest($request);
+ }
+
+ public function testValidateDeviceAuthorizationRequestClientMismatch(): void
+ {
+ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepositoryMock->method('getClientEntity')->willReturn(null);
+
+ $scope = new ScopeEntity();
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
+
+ $grant = new DeviceCodeGrant(
+ $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock(),
+ $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+ $grant->setClientRepository($clientRepositoryMock);
+ $grant->setScopeRepository($scopeRepositoryMock);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
+
+ $request = (new ServerRequest())->withParsedBody([
+ 'client_id' => 'bar',
+ 'scope' => 'basic',
+ ]);
+
+ $this->expectException(OAuthServerException::class);
+
+ $grant->respondToDeviceAuthorizationRequest($request);
+ }
+
+ public function testCompleteDeviceAuthorizationRequest(): void
+ {
+ $deviceCode = new DeviceCodeEntity();
+ $deviceCode->setUserCode('foo');
+
+ $deviceCodeRepository = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock();
+ $deviceCodeRepository->method('getDeviceCodeEntityByDeviceCode')->willReturn($deviceCode);
+
+ $grant = new DeviceCodeGrant(
+ $deviceCodeRepository,
+ $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
+ new DateInterval('PT10M'),
+ 'http://foo/bar',
+ );
+
+ $grant->setEncryptionKey($this->cryptStub->getKey());
+
+ $grant->completeDeviceAuthorizationRequest($deviceCode->getUserCode(), 'userId', true);
+
+ $this::assertEquals('userId', $deviceCode->getUserIdentifier());
+ }
+
+ public function testDeviceAuthorizationResponse(): void
+ {
+ $client = new ClientEntity();
+ $client->setIdentifier('clientId');
+ $client->setConfidential();
+ $client->setRedirectUri('http://foo/bar');
+
+ $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepository->method('getClientEntity')->willReturn($client);
+
+ $scopeEntity = new ScopeEntity();
+ $scopeEntity->setIdentifier('basic');
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
+ $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
+
+ $accessRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
+ $accessRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
+
+ $deviceCodeRepository = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock();
+ $deviceCodeRepository->method('getNewDeviceCode')->willReturn(new DeviceCodeEntity());
+
+ $server = new AuthorizationServer(
+ $clientRepository,
+ $accessRepositoryMock,
+ $scopeRepositoryMock,
+ 'file://' . __DIR__ . '/../Stubs/private.key',
+ base64_encode(random_bytes(36)),
+ new StubResponseType()
+ );
+
+ $server->setDefaultScope(self::DEFAULT_SCOPE);
+
+ $serverRequest = (new ServerRequest())->withParsedBody([
+ 'client_id' => 'foo',
+ ]);
+
+ $deviceCodeGrant = new DeviceCodeGrant(
+ $deviceCodeRepository,
+ $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+
+ $deviceCodeGrant->setEncryptionKey($this->cryptStub->getKey());
+
+ $server->enableGrantType($deviceCodeGrant);
+
+ $response = $server->respondToDeviceAuthorizationRequest($serverRequest, new Response());
+
+ $responseObject = json_decode($response->getBody()->__toString());
+
+ $this::assertObjectHasProperty('device_code', $responseObject);
+ $this::assertObjectHasProperty('user_code', $responseObject);
+ $this::assertObjectHasProperty('verification_uri', $responseObject);
+ $this::assertObjectHasProperty('expires_in', $responseObject);
+ }
+
+ public function testRespondToAccessTokenRequest(): void
+ {
+ $client = new ClientEntity();
+ $client->setIdentifier('foo');
+ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
+
+ $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
+ $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
+ $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
+
+ $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
+ $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
+ $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
+
+ $deviceCodeRepositoryMock = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock();
+ $deviceCodeEntity = new DeviceCodeEntity();
+
+ $deviceCodeEntity->setUserIdentifier('baz');
+ $deviceCodeEntity->setIdentifier('deviceCodeEntityIdentifier');
+ $deviceCodeEntity->setUserCode('123456');
+ $deviceCodeEntity->setExpiryDateTime(new DateTimeImmutable('+1 hour'));
+ $deviceCodeEntity->setClient($client);
+
+ $deviceCodeRepositoryMock->method('getDeviceCodeEntityByDeviceCode')->willReturn($deviceCodeEntity);
+
+ $scope = new ScopeEntity();
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
+ $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
+
+ $grant = new DeviceCodeGrant(
+ $deviceCodeRepositoryMock,
+ $refreshTokenRepositoryMock,
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+
+ $grant->setClientRepository($clientRepositoryMock);
+ $grant->setAccessTokenRepository($accessTokenRepositoryMock);
+ $grant->setScopeRepository($scopeRepositoryMock);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
+ $grant->setEncryptionKey($this->cryptStub->getKey());
+ $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
+
+ $grant->completeDeviceAuthorizationRequest($deviceCodeEntity->getUserCode(), '1', true);
+
+ $serverRequest = (new ServerRequest())->withParsedBody([
+ 'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code',
+ 'device_code' => $this->cryptStub->doEncrypt(
+ json_encode(
+ [
+ 'device_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_code' => '12345678',
+ 'scopes' => ['foo'],
+ 'verification_uri' => 'http://foo/bar',
+ ],
+ JSON_THROW_ON_ERROR
+ )
+ ),
+ 'client_id' => 'foo',
+ ]);
+
+ $responseType = new StubResponseType();
+ $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
+
+ $this::assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
+ }
+
+ public function testRespondToRequestMissingClient(): void
+ {
+ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepositoryMock->method('getClientEntity')->willReturn(null);
+
+ $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
+ $deviceCodeRepositoryMock = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock();
+ $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
+
+ $grant = new DeviceCodeGrant(
+ $deviceCodeRepositoryMock,
+ $refreshTokenRepositoryMock,
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+
+ $grant->setClientRepository($clientRepositoryMock);
+ $grant->setAccessTokenRepository($accessTokenRepositoryMock);
+
+ $serverRequest = (new ServerRequest())->withQueryParams([
+ 'device_code' => $this->cryptStub->doEncrypt(
+ json_encode(
+ [
+ 'device_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_code' => '12345678',
+ 'scopes' => ['foo'],
+ 'verification_uri' => 'http://foo/bar',
+ ],
+ JSON_THROW_ON_ERROR
+ )
+ ),
+ ]);
+
+ $responseType = new StubResponseType();
+
+ $this->expectException(OAuthServerException::class);
+
+ $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
+ }
+
+ public function testRespondToRequestMissingDeviceCode(): void
+ {
+ $client = new ClientEntity();
+ $client->setIdentifier('foo');
+ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
+
+ $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
+ $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
+ $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
+
+ $deviceCodeRepositoryMock = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock();
+ $deviceCodeEntity = new DeviceCodeEntity();
+ $deviceCodeEntity->setUserIdentifier('baz');
+ $deviceCodeRepositoryMock->method('getDeviceCodeEntityByDeviceCode')->willReturn($deviceCodeEntity);
+
+ $scope = new ScopeEntity();
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
+ $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
+
+ $grant = new DeviceCodeGrant(
+ $deviceCodeRepositoryMock,
+ $refreshTokenRepositoryMock,
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+
+ $grant->setClientRepository($clientRepositoryMock);
+ $grant->setScopeRepository($scopeRepositoryMock);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
+ $grant->setEncryptionKey($this->cryptStub->getKey());
+ $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
+
+ $serverRequest = (new ServerRequest())->withParsedBody([
+ 'client_id' => 'foo',
+ ]);
+
+ $responseType = new StubResponseType();
+
+ $this->expectException(OAuthServerException::class);
+ $this->expectExceptionCode(3);
+
+ $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
+ }
+
+ public function testIssueSlowDownError(): void
+ {
+ $client = new ClientEntity();
+ $client->setIdentifier('foo');
+ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
+
+ $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
+ $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
+ $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
+
+ $deviceCodeRepositoryMock = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock();
+ $deviceCodeEntity = new DeviceCodeEntity();
+ $deviceCodeEntity->setLastPolledAt(new DateTimeImmutable());
+ $deviceCodeEntity->setExpiryDateTime(new DateTimeImmutable('+1 hour'));
+ $deviceCodeEntity->setClient($client);
+ $deviceCodeRepositoryMock->method('getDeviceCodeEntityByDeviceCode')->willReturn($deviceCodeEntity);
+
+ $scope = new ScopeEntity();
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
+ $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
+
+ $grant = new DeviceCodeGrant(
+ $deviceCodeRepositoryMock,
+ $refreshTokenRepositoryMock,
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+
+ $grant->setClientRepository($clientRepositoryMock);
+ $grant->setScopeRepository($scopeRepositoryMock);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
+ $grant->setEncryptionKey($this->cryptStub->getKey());
+ $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
+
+ $serverRequest = (new ServerRequest())->withParsedBody([
+ 'client_id' => 'foo',
+ 'device_code' => $this->cryptStub->doEncrypt(
+ json_encode(
+ [
+ 'device_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_code' => '12345678',
+ 'scopes' => ['foo'],
+ 'verification_uri' => 'http://foo/bar',
+ ],
+ JSON_THROW_ON_ERROR
+ )
+ ),
+ ]);
+
+ $responseType = new StubResponseType();
+
+ $this->expectException(OAuthServerException::class);
+ $this->expectExceptionCode(13);
+
+ $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
+ }
+
+ public function testIssueAuthorizationPendingError(): void
+ {
+ $client = new ClientEntity();
+ $client->setIdentifier('foo');
+ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
+
+ $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
+ $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
+ $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
+
+ $deviceCodeRepositoryMock = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock();
+ $deviceCodeEntity = new DeviceCodeEntity();
+ $deviceCodeEntity->setExpiryDateTime(new DateTimeImmutable('+1 hour'));
+ $deviceCodeEntity->setClient($client);
+ $deviceCodeRepositoryMock->method('getDeviceCodeEntityByDeviceCode')->willReturn($deviceCodeEntity);
+
+ $scope = new ScopeEntity();
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
+ $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
+
+ $grant = new DeviceCodeGrant(
+ $deviceCodeRepositoryMock,
+ $refreshTokenRepositoryMock,
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+
+ $grant->setClientRepository($clientRepositoryMock);
+ $grant->setScopeRepository($scopeRepositoryMock);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
+ $grant->setEncryptionKey($this->cryptStub->getKey());
+ $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
+
+ $serverRequest = (new ServerRequest())->withParsedBody([
+ 'client_id' => 'foo',
+ 'device_code' => $this->cryptStub->doEncrypt(
+ json_encode(
+ [
+ 'device_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_code' => '12345678',
+ 'scopes' => ['foo'],
+ 'verification_uri' => 'http://foo/bar',
+ ],
+ JSON_THROW_ON_ERROR
+ )
+ ),
+ ]);
+
+ $responseType = new StubResponseType();
+
+ $this->expectException(OAuthServerException::class);
+ $this->expectExceptionCode(12);
+
+ $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
+ }
+
+ public function testIssueExpiredTokenError(): void
+ {
+ $client = new ClientEntity();
+ $client->setIdentifier('foo');
+ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
+
+ $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
+ $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
+ $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
+
+ $deviceCodeRepositoryMock = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock();
+ $deviceCodeEntity = new DeviceCodeEntity();
+ $deviceCodeEntity->setExpiryDateTime(new DateTimeImmutable('-1 hour'));
+ $deviceCodeEntity->setClient($client);
+ $deviceCodeRepositoryMock->method('getDeviceCodeEntityByDeviceCode')->willReturn($deviceCodeEntity);
+
+ $scope = new ScopeEntity();
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
+ $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
+
+ $grant = new DeviceCodeGrant(
+ $deviceCodeRepositoryMock,
+ $refreshTokenRepositoryMock,
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+
+ $grant->setClientRepository($clientRepositoryMock);
+ $grant->setScopeRepository($scopeRepositoryMock);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
+ $grant->setEncryptionKey($this->cryptStub->getKey());
+ $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
+
+ $serverRequest = (new ServerRequest())->withParsedBody([
+ 'client_id' => 'foo',
+ 'device_code' => $this->cryptStub->doEncrypt(
+ json_encode(
+ [
+ 'device_code_id' => uniqid(),
+ 'expire_time' => time() - 3600,
+ 'client_id' => 'foo',
+ 'user_code' => '12345678',
+ 'scopes' => ['foo'],
+ 'verification_uri' => 'http://foo/bar',
+ ],
+ JSON_THROW_ON_ERROR
+ )
+ ),
+ ]);
+
+ $responseType = new StubResponseType();
+
+ $this->expectException(OAuthServerException::class);
+ $this->expectExceptionCode(11);
+
+ $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
+ }
+
+ public function testSettingDeviceCodeIntervalRate(): void
+ {
+ $client = new ClientEntity();
+ $client->setIdentifier('foo');
+
+ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepositoryMock->method('getClientEntity')->willReturn($client);
+
+ $deviceCode = new DeviceCodeEntity();
+
+ $deviceCodeRepository = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock();
+ $deviceCodeRepository->method('getNewDeviceCode')->willReturn($deviceCode);
+
+ $scope = new ScopeEntity();
+ $scope->setIdentifier('basic');
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
+
+ $grant = new DeviceCodeGrant(
+ $deviceCodeRepository,
+ $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
+ new DateInterval('PT10M'),
+ 'http://foo/bar',
+ self::INTERVAL_RATE
+ );
+
+ $grant->setClientRepository($clientRepositoryMock);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
+ $grant->setEncryptionKey($this->cryptStub->getKey());
+ $grant->setScopeRepository($scopeRepositoryMock);
+ $grant->setIntervalVisibility(true);
+
+ $request = (new ServerRequest())->withParsedBody([
+ 'client_id' => 'foo',
+ 'scope' => 'basic',
+ ]);
+
+ $deviceCodeResponse = $grant
+ ->respondToDeviceAuthorizationRequest($request)
+ ->generateHttpResponse(new Response());
+
+ $deviceCode = json_decode((string) $deviceCodeResponse->getBody());
+
+ $this::assertObjectHasProperty('interval', $deviceCode);
+ $this::assertEquals(self::INTERVAL_RATE, $deviceCode->interval);
+ }
+
+ public function testIssueAccessDeniedError(): void
+ {
+ $client = new ClientEntity();
+ $client->setIdentifier('foo');
+ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
+
+ $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
+ $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
+ $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
+
+ $deviceCodeRepositoryMock = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock();
+
+ $deviceCode = new DeviceCodeEntity();
+
+ $deviceCode->setExpiryDateTime(new DateTimeImmutable('+1 hour'));
+ $deviceCode->setClient($client);
+ $deviceCode->setUserCode('12345678');
+ $deviceCodeRepositoryMock->method('getDeviceCodeEntityByDeviceCode')->willReturn($deviceCode);
+
+ $scope = new ScopeEntity();
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
+ $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
+
+ $grant = new DeviceCodeGrant(
+ $deviceCodeRepositoryMock,
+ $refreshTokenRepositoryMock,
+ new DateInterval('PT10M'),
+ 'http://foo/bar'
+ );
+
+ $grant->setClientRepository($clientRepositoryMock);
+ $grant->setScopeRepository($scopeRepositoryMock);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
+ $grant->setEncryptionKey($this->cryptStub->getKey());
+ $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
+
+ $grant->completeDeviceAuthorizationRequest($deviceCode->getUserCode(), '1', false);
+
+ $serverRequest = (new ServerRequest())->withParsedBody([
+ 'client_id' => 'foo',
+ 'device_code' => $this->cryptStub->doEncrypt(
+ json_encode(
+ [
+ 'device_code_id' => uniqid(),
+ 'expire_time' => time() + 3600,
+ 'client_id' => 'foo',
+ 'user_code' => '12345678',
+ 'scopes' => ['foo'],
+ 'verification_uri' => 'http://foo/bar',
+ ],
+ JSON_THROW_ON_ERROR
+ ),
+ ),
+ ]);
+
+ $responseType = new StubResponseType();
+
+ $this->expectException(OAuthServerException::class);
+ $this->expectExceptionCode(9);
+
+ $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
+ }
+}
diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php
index 5f69242c7..515629247 100644
--- a/tests/Grant/ImplicitGrantTest.php
+++ b/tests/Grant/ImplicitGrantTest.php
@@ -1,5 +1,7 @@
cryptStub = new CryptTraitStub();
}
- public function testGetIdentifier()
+ public function testGetIdentifier(): void
{
$grant = new ImplicitGrant(new DateInterval('PT10M'));
- $this->assertEquals('implicit', $grant->getIdentifier());
+ self::assertEquals('implicit', $grant->getIdentifier());
}
- public function testCanRespondToAccessTokenRequest()
+ public function testCanRespondToAccessTokenRequest(): void
{
$grant = new ImplicitGrant(new DateInterval('PT10M'));
- $this->assertFalse(
+ self::assertFalse(
$grant->canRespondToAccessTokenRequest(new ServerRequest())
);
}
- public function testRespondToAccessTokenRequest()
+ public function testRespondToAccessTokenRequest(): void
{
$grant = new ImplicitGrant(new DateInterval('PT10M'));
- $this->expectException(\LogicException::class);
+ $this->expectException(LogicException::class);
$grant->respondToAccessTokenRequest(
new ServerRequest(),
@@ -65,7 +65,7 @@ public function testRespondToAccessTokenRequest()
);
}
- public function testCanRespondToAuthorizationRequest()
+ public function testCanRespondToAuthorizationRequest(): void
{
$grant = new ImplicitGrant(new DateInterval('PT10M'));
@@ -74,10 +74,10 @@ public function testCanRespondToAuthorizationRequest()
'client_id' => 'foo',
]);
- $this->assertTrue($grant->canRespondToAuthorizationRequest($request));
+ self::assertTrue($grant->canRespondToAuthorizationRequest($request));
}
- public function testValidateAuthorizationRequest()
+ public function testValidateAuthorizationRequest(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
@@ -99,10 +99,10 @@ public function testValidateAuthorizationRequest()
'redirect_uri' => self::REDIRECT_URI,
]);
- $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request));
+ self::assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request));
}
- public function testValidateAuthorizationRequestRedirectUriArray()
+ public function testValidateAuthorizationRequestRedirectUriArray(): void
{
$client = new ClientEntity();
$client->setRedirectUri([self::REDIRECT_URI]);
@@ -124,10 +124,10 @@ public function testValidateAuthorizationRequestRedirectUriArray()
'redirect_uri' => self::REDIRECT_URI,
]);
- $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request));
+ self::assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request));
}
- public function testValidateAuthorizationRequestMissingClientId()
+ public function testValidateAuthorizationRequestMissingClientId(): void
{
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
@@ -136,13 +136,13 @@ public function testValidateAuthorizationRequestMissingClientId()
$request = (new ServerRequest())->withQueryParams(['response_type' => 'code']);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(3);
$grant->validateAuthorizationRequest($request);
}
- public function testValidateAuthorizationRequestInvalidClientId()
+ public function testValidateAuthorizationRequestInvalidClientId(): void
{
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn(null);
@@ -155,13 +155,13 @@ public function testValidateAuthorizationRequestInvalidClientId()
'client_id' => 'foo',
]);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(4);
$grant->validateAuthorizationRequest($request);
}
- public function testValidateAuthorizationRequestBadRedirectUriString()
+ public function testValidateAuthorizationRequestBadRedirectUriString(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
@@ -177,13 +177,13 @@ public function testValidateAuthorizationRequestBadRedirectUriString()
'redirect_uri' => 'http://bar',
]);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(4);
$grant->validateAuthorizationRequest($request);
}
- public function testValidateAuthorizationRequestBadRedirectUriArray()
+ public function testValidateAuthorizationRequestBadRedirectUriArray(): void
{
$client = new ClientEntity();
$client->setRedirectUri([self::REDIRECT_URI]);
@@ -199,17 +199,17 @@ public function testValidateAuthorizationRequestBadRedirectUriArray()
'redirect_uri' => 'http://bar',
]);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(4);
$grant->validateAuthorizationRequest($request);
}
- public function testCompleteAuthorizationRequest()
+ public function testCompleteAuthorizationRequest(): void
{
$client = new ClientEntity();
$client->setIdentifier('identifier');
- $client->setRedirectUri(self::REDIRECT_URI);
+ $client->setRedirectUri('https://foo/bar');
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
@@ -219,6 +219,7 @@ public function testCompleteAuthorizationRequest()
$accessToken = new AccessTokenEntity();
$accessToken->setClient($client);
+ $accessToken->setUserIdentifier('userId');
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken);
@@ -227,18 +228,19 @@ public function testCompleteAuthorizationRequest()
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
- $grant = new ImplicitGrant(new \DateInterval('PT10M'));
+ $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));
+ self::assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
}
- public function testCompleteAuthorizationRequestDenied()
+ public function testCompleteAuthorizationRequestDenied(): void
{
$client = new ClientEntity();
- $client->setRedirectUri(self::REDIRECT_URI);
+ $client->setIdentifier('clientId');
+ $client->setRedirectUri('https://foo/bar');
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(false);
@@ -253,22 +255,22 @@ public function testCompleteAuthorizationRequestDenied()
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
- $grant = new ImplicitGrant(new \DateInterval('PT10M'));
+ $grant = new ImplicitGrant(new DateInterval('PT10M'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(9);
$grant->completeAuthorizationRequest($authRequest);
}
- public function testAccessTokenRepositoryUniqueConstraintCheck()
+ public function testAccessTokenRepositoryUniqueConstraintCheck(): void
{
$client = new ClientEntity();
- $client->setIdentifier('identifier');
- $client->setRedirectUri(self::REDIRECT_URI);
+ $client->setIdentifier('clientId');
+ $client->setRedirectUri('https://foo/bar');
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
@@ -278,18 +280,19 @@ public function testAccessTokenRepositoryUniqueConstraintCheck()
$accessToken = new AccessTokenEntity();
$accessToken->setClient($client);
+ $accessToken->setUserIdentifier('userId');
/** @var AccessTokenRepositoryInterface|\PHPUnit\Framework\MockObject\MockObject $accessTokenRepositoryMock */
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken);
- $matcher = $this->exactly(2);
-
$accessTokenRepositoryMock
- ->expects($matcher)
+ ->expects(self::exactly(2))
->method('persistNewAccessToken')
- ->willReturnCallback(function () use ($matcher) {
- if ($matcher->getInvocationCount() === 1) {
+ ->willReturnCallback(function (): void {
+ static $count = 0;
+
+ if (1 === ++$count) {
throw UniqueTokenIdentifierConstraintViolationException::create();
}
});
@@ -297,19 +300,24 @@ public function testAccessTokenRepositoryUniqueConstraintCheck()
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
- $grant = new ImplicitGrant(new \DateInterval('PT10M'));
+ $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));
+ self::assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest));
}
- public function testAccessTokenRepositoryFailToPersist()
+ public function testAccessTokenRepositoryFailToPersist(): void
{
+ $client = new ClientEntity();
+
+ $client->setRedirectUri('https://foo/bar');
+
$authRequest = new AuthorizationRequest();
+
$authRequest->setAuthorizationApproved(true);
- $authRequest->setClient(new ClientEntity());
+ $authRequest->setClient($client);
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
@@ -321,22 +329,27 @@ public function testAccessTokenRepositoryFailToPersist()
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
- $grant = new ImplicitGrant(new \DateInterval('PT10M'));
+ $grant = new ImplicitGrant(new DateInterval('PT10M'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(7);
$grant->completeAuthorizationRequest($authRequest);
}
- public function testAccessTokenRepositoryFailToPersistUniqueNoInfiniteLoop()
+ public function testAccessTokenRepositoryFailToPersistUniqueNoInfiniteLoop(): void
{
+ $client = new ClientEntity();
+
+ $client->setRedirectUri('https://foo/bar');
+
$authRequest = new AuthorizationRequest();
+
$authRequest->setAuthorizationApproved(true);
- $authRequest->setClient(new ClientEntity());
+ $authRequest->setClient($client);
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
@@ -348,42 +361,42 @@ public function testAccessTokenRepositoryFailToPersistUniqueNoInfiniteLoop()
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
- $grant = new ImplicitGrant(new \DateInterval('PT10M'));
+ $grant = new ImplicitGrant(new DateInterval('PT10M'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
- $this->expectException(\League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException::class);
+ $this->expectException(UniqueTokenIdentifierConstraintViolationException::class);
$this->expectExceptionCode(100);
$grant->completeAuthorizationRequest($authRequest);
}
- public function testSetRefreshTokenTTL()
+ public function testSetRefreshTokenTTL(): void
{
$grant = new ImplicitGrant(new DateInterval('PT10M'));
- $this->expectException(\LogicException::class);
+ $this->expectException(LogicException::class);
$grant->setRefreshTokenTTL(new DateInterval('PT10M'));
}
- public function testSetRefreshTokenRepository()
+ public function testSetRefreshTokenRepository(): void
{
$grant = new ImplicitGrant(new DateInterval('PT10M'));
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
- $this->expectException(\LogicException::class);
+ $this->expectException(LogicException::class);
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
}
- public function testCompleteAuthorizationRequestNoUser()
+ public function testCompleteAuthorizationRequestNoUser(): void
{
$grant = new ImplicitGrant(new DateInterval('PT10M'));
- $this->expectException(\LogicException::class);
+ $this->expectException(LogicException::class);
$grant->completeAuthorizationRequest(new AuthorizationRequest());
}
diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php
index b53ab2357..8c60a8c78 100644
--- a/tests/Grant/PasswordGrantTest.php
+++ b/tests/Grant/PasswordGrantTest.php
@@ -1,12 +1,14 @@
getMockBuilder(UserRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
- $this->assertEquals('password', $grant->getIdentifier());
+ self::assertEquals('password', $grant->getIdentifier());
}
- public function testRespondToRequest()
+ public function testRespondToRequest(): void
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
@@ -76,17 +79,17 @@ public function testRespondToRequest()
$responseType = new StubResponseType();
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken());
- $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
+ self::assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
}
- public function testRespondToRequestNullRefreshToken()
+ public function testRespondToRequestNullRefreshToken(): void
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
@@ -119,13 +122,12 @@ public function testRespondToRequestNullRefreshToken()
]);
$responseType = new StubResponseType();
- $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
+ $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken());
- $this->assertNull($responseType->getRefreshToken());
+ self::assertNull($responseType->getRefreshToken());
}
- public function testRespondToRequestMissingUsername()
+ public function testRespondToRequestMissingUsername(): void
{
$client = new ClientEntity();
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
@@ -148,12 +150,12 @@ public function testRespondToRequestMissingUsername()
$responseType = new StubResponseType();
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
}
- public function testRespondToRequestMissingPassword()
+ public function testRespondToRequestMissingPassword(): void
{
$client = new ClientEntity();
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
@@ -177,18 +179,19 @@ public function testRespondToRequestMissingPassword()
$responseType = new StubResponseType();
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
}
- public function testRespondToRequestBadCredentials()
+ public function testRespondToRequestBadCredentials(): void
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
@@ -197,9 +200,14 @@ public function testRespondToRequestBadCredentials()
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn(new ScopeEntity());
+
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
$grant->setClientRepository($clientRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
+ $grant->setDefaultScope(self::DEFAULT_SCOPE);
+ $grant->setScopeRepository($scopeRepositoryMock);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
@@ -210,7 +218,7 @@ public function testRespondToRequestBadCredentials()
$responseType = new StubResponseType();
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(6);
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php
index 8f56fac4c..747ba5ead 100644
--- a/tests/Grant/RefreshTokenGrantTest.php
+++ b/tests/Grant/RefreshTokenGrantTest.php
@@ -1,12 +1,14 @@
cryptStub = new CryptTraitStub();
}
- public function testGetIdentifier()
+ public function testGetIdentifier(): void
{
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
- $this->assertEquals('refresh_token', $grant->getIdentifier());
+ self::assertEquals('refresh_token', $grant->getIdentifier());
}
- public function testRespondToRequest()
+ public function testRespondToRequest(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -49,19 +50,21 @@ public function testRespondToRequest()
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$scopeEntity = new ScopeEntity();
$scopeEntity->setIdentifier('foo');
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
+ $scopeRepositoryMock->method('finalizeScopes')->willReturn([$scopeEntity]);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
- $accessTokenRepositoryMock->expects($this->once())->method('persistNewAccessToken')->willReturnSelf();
+ $accessTokenRepositoryMock->expects(self::once())->method('persistNewAccessToken')->willReturnSelf();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
- $refreshTokenRepositoryMock->expects($this->once())->method('persistNewRefreshToken')->willReturnSelf();
+ $refreshTokenRepositoryMock->expects(self::once())->method('persistNewRefreshToken')->willReturnSelf();
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
$grant->setClientRepository($clientRepositoryMock);
@@ -71,34 +74,39 @@ public function testRespondToRequest()
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grant->revokeRefreshTokens(true);
- $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,
- ]
- )
+ $oldRefreshToken = json_encode(
+ [
+ 'client_id' => 'foo',
+ 'refresh_token_id' => 'zyxwvu',
+ 'access_token_id' => 'abcdef',
+ 'scopes' => ['foo'],
+ 'user_id' => 123,
+ 'expire_time' => time() + 3600,
+ ]
+ );
+
+ if ($oldRefreshToken === false) {
+ self::fail('json_encode failed');
+ }
+
+ $encryptedOldRefreshToken = $this->cryptStub->doEncrypt(
+ $oldRefreshToken
);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
'client_secret' => 'bar',
- 'refresh_token' => $oldRefreshToken,
+ 'refresh_token' => $encryptedOldRefreshToken,
'scopes' => ['foo'],
]);
$responseType = new StubResponseType();
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken());
- $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
+ self::assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
}
- public function testRespondToRequestNullRefreshToken()
+ public function testRespondToRequestNullRefreshToken(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -106,20 +114,22 @@ public function testRespondToRequestNullRefreshToken()
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$scopeEntity = new ScopeEntity();
$scopeEntity->setIdentifier('foo');
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
+ $scopeRepositoryMock->method('finalizeScopes')->willReturn([$scopeEntity]);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
- $accessTokenRepositoryMock->expects($this->once())->method('persistNewAccessToken')->willReturnSelf();
+ $accessTokenRepositoryMock->expects(self::once())->method('persistNewAccessToken')->willReturnSelf();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(null);
- $refreshTokenRepositoryMock->expects($this->never())->method('persistNewRefreshToken');
+ $refreshTokenRepositoryMock->expects(self::never())->method('persistNewRefreshToken');
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
$grant->setClientRepository($clientRepositoryMock);
@@ -128,34 +138,39 @@ public function testRespondToRequestNullRefreshToken()
$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,
- ]
- )
+ $oldRefreshToken = json_encode(
+ [
+ 'client_id' => 'foo',
+ 'refresh_token_id' => 'zyxwvu',
+ 'access_token_id' => 'abcdef',
+ 'scopes' => ['foo'],
+ 'user_id' => 123,
+ 'expire_time' => time() + 3600,
+ ]
+ );
+
+ if ($oldRefreshToken === false) {
+ self::fail('json_encode failed');
+ }
+
+ $encryptedOldRefreshToken = $this->cryptStub->doEncrypt(
+ $oldRefreshToken
);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
'client_secret' => 'bar',
- 'refresh_token' => $oldRefreshToken,
+ 'refresh_token' => $encryptedOldRefreshToken,
'scopes' => ['foo'],
]);
$responseType = new StubResponseType();
- $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
+ $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken());
- $this->assertNull($responseType->getRefreshToken());
+ self::assertNull($responseType->getRefreshToken());
}
- public function testRespondToReducedScopes()
+ public function testRespondToReducedScopes(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -163,6 +178,7 @@ public function testRespondToReducedScopes()
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
@@ -176,6 +192,7 @@ public function testRespondToReducedScopes()
$scope->setIdentifier('foo');
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
+ $scopeRepositoryMock->method('finalizeScopes')->willReturn([$scope]);
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
$grant->setClientRepository($clientRepositoryMock);
@@ -185,34 +202,39 @@ public function testRespondToReducedScopes()
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grant->revokeRefreshTokens(true);
- $oldRefreshToken = $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'client_id' => 'foo',
- 'refresh_token_id' => 'zyxwvu',
- 'access_token_id' => 'abcdef',
- 'scopes' => ['foo', 'bar'],
- 'user_id' => 123,
- 'expire_time' => \time() + 3600,
- ]
- )
+ $oldRefreshToken = json_encode(
+ [
+ 'client_id' => 'foo',
+ 'refresh_token_id' => 'zyxwvu',
+ 'access_token_id' => 'abcdef',
+ 'scopes' => ['foo', 'bar'],
+ 'user_id' => 123,
+ 'expire_time' => time() + 3600,
+ ]
+ );
+
+ if ($oldRefreshToken === false) {
+ self::fail('json_encode failed');
+ }
+
+ $encryptedOldRefreshToken = $this->cryptStub->doEncrypt(
+ $oldRefreshToken
);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
'client_secret' => 'bar',
- 'refresh_token' => $oldRefreshToken,
+ 'refresh_token' => $encryptedOldRefreshToken,
'scope' => 'foo',
]);
$responseType = new StubResponseType();
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
- $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken());
- $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
+ self::assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
}
- public function testRespondToUnexpectedScope()
+ public function testRespondToUnexpectedScope(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -220,6 +242,7 @@ public function testRespondToUnexpectedScope()
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
@@ -239,35 +262,41 @@ public function testRespondToUnexpectedScope()
$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', 'bar'],
- 'user_id' => 123,
- 'expire_time' => \time() + 3600,
- ]
- )
+ $oldRefreshToken = json_encode(
+ [
+ 'client_id' => 'foo',
+ 'refresh_token_id' => 'zyxwvu',
+ 'access_token_id' => 'abcdef',
+ 'scopes' => ['foo', 'bar'],
+ 'user_id' => 123,
+ 'expire_time' => time() + 3600,
+ ]
+ );
+
+ if ($oldRefreshToken === false) {
+ self::fail('json_encode failed');
+ }
+
+ $encryptedOldRefreshToken = $this->cryptStub->doEncrypt(
+ $oldRefreshToken
);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
'client_secret' => 'bar',
- 'refresh_token' => $oldRefreshToken,
+ 'refresh_token' => $encryptedOldRefreshToken,
'scope' => 'foobar',
]);
$responseType = new StubResponseType();
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(5);
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
}
- public function testRespondToRequestMissingOldToken()
+ public function testRespondToRequestMissingOldToken(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -275,6 +304,7 @@ public function testRespondToRequestMissingOldToken()
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
@@ -292,13 +322,13 @@ public function testRespondToRequestMissingOldToken()
$responseType = new StubResponseType();
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(3);
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
}
- public function testRespondToRequestInvalidOldToken()
+ public function testRespondToRequestInvalidOldToken(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -306,6 +336,7 @@ public function testRespondToRequestInvalidOldToken()
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
@@ -326,13 +357,13 @@ public function testRespondToRequestInvalidOldToken()
$responseType = new StubResponseType();
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(8);
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
}
- public function testRespondToRequestClientMismatch()
+ public function testRespondToRequestClientMismatch(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -340,6 +371,7 @@ public function testRespondToRequestClientMismatch()
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
@@ -353,34 +385,40 @@ public function testRespondToRequestClientMismatch()
$grant->setEncryptionKey($this->cryptStub->getKey());
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
- $oldRefreshToken = $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'client_id' => 'bar',
- 'refresh_token_id' => 'zyxwvu',
- 'access_token_id' => 'abcdef',
- 'scopes' => ['foo'],
- 'user_id' => 123,
- 'expire_time' => \time() + 3600,
- ]
- )
+ $oldRefreshToken = json_encode(
+ [
+ 'client_id' => 'bar',
+ 'refresh_token_id' => 'zyxwvu',
+ 'access_token_id' => 'abcdef',
+ 'scopes' => ['foo'],
+ 'user_id' => 123,
+ 'expire_time' => time() + 3600,
+ ]
+ );
+
+ if ($oldRefreshToken === false) {
+ self::fail('json_encode failed');
+ }
+
+ $encryptedOldRefreshToken = $this->cryptStub->doEncrypt(
+ $oldRefreshToken
);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
'client_secret' => 'bar',
- 'refresh_token' => $oldRefreshToken,
+ 'refresh_token' => $encryptedOldRefreshToken,
]);
$responseType = new StubResponseType();
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(8);
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
}
- public function testRespondToRequestExpiredToken()
+ public function testRespondToRequestExpiredToken(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -388,6 +426,7 @@ public function testRespondToRequestExpiredToken()
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
@@ -398,34 +437,40 @@ public function testRespondToRequestExpiredToken()
$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,
- ]
- )
+ $oldRefreshToken = json_encode(
+ [
+ 'client_id' => 'foo',
+ 'refresh_token_id' => 'zyxwvu',
+ 'access_token_id' => 'abcdef',
+ 'scopes' => ['foo'],
+ 'user_id' => 123,
+ 'expire_time' => time() - 3600,
+ ]
+ );
+
+ if ($oldRefreshToken === false) {
+ self::fail('json_encode failed');
+ }
+
+ $encryptedOldRefreshToken = $this->cryptStub->doEncrypt(
+ $oldRefreshToken
);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
'client_secret' => 'bar',
- 'refresh_token' => $oldRefreshToken,
+ 'refresh_token' => $encryptedOldRefreshToken,
]);
$responseType = new StubResponseType();
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(8);
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
}
- public function testRespondToRequestRevokedToken()
+ public function testRespondToRequestRevokedToken(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');
@@ -433,6 +478,7 @@ public function testRespondToRequestRevokedToken()
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
@@ -444,34 +490,120 @@ public function testRespondToRequestRevokedToken()
$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,
- ]
- )
+ $oldRefreshToken = json_encode(
+ [
+ 'client_id' => 'foo',
+ 'refresh_token_id' => 'zyxwvu',
+ 'access_token_id' => 'abcdef',
+ 'scopes' => ['foo'],
+ 'user_id' => 123,
+ 'expire_time' => time() + 3600,
+ ]
+ );
+
+ if ($oldRefreshToken === false) {
+ self::fail('json_encode failed');
+ }
+
+ $encryptedOldRefreshToken = $this->cryptStub->doEncrypt(
+ $oldRefreshToken
);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
'client_secret' => 'bar',
- 'refresh_token' => $oldRefreshToken,
+ 'refresh_token' => $encryptedOldRefreshToken,
]);
$responseType = new StubResponseType();
- $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class);
+ $this->expectException(OAuthServerException::class);
$this->expectExceptionCode(8);
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
}
- public function testRevokedRefreshToken()
+ public function testRespondToRequestFinalizeScopes(): void
+ {
+ $client = new ClientEntity();
+
+ $client->setIdentifier('foo');
+ $client->setRedirectUri('http://foo/bar');
+
+ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
+ $clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
+
+ $fooScopeEntity = new ScopeEntity();
+ $fooScopeEntity->setIdentifier('foo');
+
+ $barScopeEntity = new ScopeEntity();
+ $barScopeEntity->setIdentifier('bar');
+
+ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
+ $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($fooScopeEntity, $barScopeEntity);
+
+ $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
+ $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
+
+ $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
+ $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
+ $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
+
+ $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'));
+
+
+ $scopes = [$fooScopeEntity, $barScopeEntity];
+ $finalizedScopes = [$fooScopeEntity];
+
+ $scopeRepositoryMock
+ ->expects(self::once())
+ ->method('finalizeScopes')
+ ->with($scopes, $grant->getIdentifier(), $client)
+ ->willReturn($finalizedScopes);
+
+ $accessTokenRepositoryMock
+ ->method('getNewToken')
+ ->with($client, $finalizedScopes)
+ ->willReturn(new AccessTokenEntity());
+
+ $oldRefreshToken = json_encode(
+ [
+ 'client_id' => 'foo',
+ 'refresh_token_id' => 'zyxwvu',
+ 'access_token_id' => 'abcdef',
+ 'scopes' => ['foo', 'bar'],
+ 'user_id' => 123,
+ 'expire_time' => time() + 3600,
+ ]
+ );
+
+ if ($oldRefreshToken === false) {
+ self::fail('json_encode failed');
+ }
+
+ $encryptedOldRefreshToken = $this->cryptStub->doEncrypt(
+ $oldRefreshToken
+ );
+
+ $serverRequest = (new ServerRequest())->withParsedBody([
+ 'client_id' => 'foo',
+ 'client_secret' => 'bar',
+ 'refresh_token' => $encryptedOldRefreshToken,
+ 'scope' => ['foo', 'bar'],
+ ]);
+
+ $responseType = new StubResponseType();
+
+ $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M'));
+ }
+
+ public function testRevokedRefreshToken(): void
{
$refreshTokenId = 'foo';
@@ -481,39 +613,47 @@ public function testRevokedRefreshToken()
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$scopeEntity = new ScopeEntity();
$scopeEntity->setIdentifier('foo');
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
+ $scopeRepositoryMock->method('finalizeScopes')->willReturn([$scopeEntity]);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
- $accessTokenRepositoryMock->expects($this->once())->method('persistNewAccessToken')->willReturnSelf();
+ $accessTokenRepositoryMock->expects(self::once())->method('persistNewAccessToken')->willReturnSelf();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('isRefreshTokenRevoked')
- ->will($this->onConsecutiveCalls(false, true));
- $refreshTokenRepositoryMock->expects($this->once())->method('revokeRefreshToken')->with($this->equalTo($refreshTokenId));
-
- $oldRefreshToken = $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'client_id' => 'foo',
- 'refresh_token_id' => $refreshTokenId,
- 'access_token_id' => 'abcdef',
- 'scopes' => ['foo'],
- 'user_id' => 123,
- 'expire_time' => \time() + 3600,
- ]
- )
+ ->will(self::onConsecutiveCalls(false, true));
+ $refreshTokenRepositoryMock->expects(self::once())->method('revokeRefreshToken')->with(self::equalTo($refreshTokenId));
+
+ $oldRefreshToken = json_encode(
+ [
+ 'client_id' => 'foo',
+ 'refresh_token_id' => $refreshTokenId,
+ 'access_token_id' => 'abcdef',
+ 'scopes' => ['foo'],
+ 'user_id' => 123,
+ 'expire_time' => time() + 3600,
+ ]
+ );
+
+ if ($oldRefreshToken === false) {
+ self::fail('json_encode failed');
+ }
+
+ $encryptedOldRefreshToken = $this->cryptStub->doEncrypt(
+ $oldRefreshToken
);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
'client_secret' => 'bar',
- 'refresh_token' => $oldRefreshToken,
+ 'refresh_token' => $encryptedOldRefreshToken,
'scope' => ['foo'],
]);
@@ -526,10 +666,10 @@ public function testRevokedRefreshToken()
$grant->revokeRefreshTokens(true);
$grant->respondToAccessTokenRequest($serverRequest, new StubResponseType(), new DateInterval('PT5M'));
- Assert::assertTrue($refreshTokenRepositoryMock->isRefreshTokenRevoked($refreshTokenId));
+ self::assertTrue($refreshTokenRepositoryMock->isRefreshTokenRevoked($refreshTokenId));
}
- public function testUnrevokedRefreshToken()
+ public function testUnrevokedRefreshToken(): void
{
$refreshTokenId = 'foo';
@@ -539,38 +679,46 @@ public function testUnrevokedRefreshToken()
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
+ $clientRepositoryMock->method('validateClient')->willReturn(true);
$scopeEntity = new ScopeEntity();
$scopeEntity->setIdentifier('foo');
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
+ $scopeRepositoryMock->method('finalizeScopes')->willReturn([$scopeEntity]);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
- $accessTokenRepositoryMock->expects($this->once())->method('persistNewAccessToken')->willReturnSelf();
+ $accessTokenRepositoryMock->expects(self::once())->method('persistNewAccessToken')->willReturnSelf();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('isRefreshTokenRevoked')->willReturn(false);
- $refreshTokenRepositoryMock->expects($this->never())->method('revokeRefreshToken');
-
- $oldRefreshToken = $this->cryptStub->doEncrypt(
- \json_encode(
- [
- 'client_id' => 'foo',
- 'refresh_token_id' => $refreshTokenId,
- 'access_token_id' => 'abcdef',
- 'scopes' => ['foo'],
- 'user_id' => 123,
- 'expire_time' => \time() + 3600,
- ]
- )
+ $refreshTokenRepositoryMock->expects(self::never())->method('revokeRefreshToken');
+
+ $oldRefreshToken = json_encode(
+ [
+ 'client_id' => 'foo',
+ 'refresh_token_id' => $refreshTokenId,
+ 'access_token_id' => 'abcdef',
+ 'scopes' => ['foo'],
+ 'user_id' => 123,
+ 'expire_time' => time() + 3600,
+ ]
+ );
+
+ if ($oldRefreshToken === false) {
+ self::fail('json_encode failed');
+ }
+
+ $encryptedOldRefreshToken = $this->cryptStub->doEncrypt(
+ $oldRefreshToken
);
$serverRequest = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
'client_secret' => 'bar',
- 'refresh_token' => $oldRefreshToken,
+ 'refresh_token' => $encryptedOldRefreshToken,
'scope' => ['foo'],
]);
@@ -580,8 +728,10 @@ public function testUnrevokedRefreshToken()
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setEncryptionKey($this->cryptStub->getKey());
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
+ $grant->revokeRefreshTokens(false);
+
$grant->respondToAccessTokenRequest($serverRequest, new StubResponseType(), new DateInterval('PT5M'));
- Assert::assertFalse($refreshTokenRepositoryMock->isRefreshTokenRevoked($refreshTokenId));
+ self::assertFalse($refreshTokenRepositoryMock->isRefreshTokenRevoked($refreshTokenId));
}
}
diff --git a/tests/Middleware/AuthorizationServerMiddlewareTest.php b/tests/Middleware/AuthorizationServerMiddlewareTest.php
index 3e7e18da1..ea02a3732 100644
--- a/tests/Middleware/AuthorizationServerMiddlewareTest.php
+++ b/tests/Middleware/AuthorizationServerMiddlewareTest.php
@@ -1,5 +1,7 @@
setConfidential();
@@ -30,8 +36,9 @@ public function testValidResponse()
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepository->method('getClientEntity')->willReturn($client);
+ $clientRepository->method('validateClient')->willReturn(true);
- $scopeEntity = new ScopeEntity;
+ $scopeEntity = new ScopeEntity();
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
@@ -44,7 +51,7 @@ public function testValidResponse()
$accessRepositoryMock,
$scopeRepositoryMock,
'file://' . __DIR__ . '/../Stubs/private.key',
- \base64_encode(\random_bytes(36)),
+ base64_encode(random_bytes(36)),
new StubResponseType()
);
@@ -62,13 +69,13 @@ public function testValidResponse()
$request,
new Response(),
function () {
- return \func_get_args()[1];
+ return func_get_args()[1];
}
);
- $this->assertEquals(200, $response->getStatusCode());
+ self::assertEquals(200, $response->getStatusCode());
}
- public function testOAuthErrorResponse()
+ public function testOAuthErrorResponse(): void
{
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepository->method('validateClient')->willReturn(false);
@@ -78,7 +85,7 @@ public function testOAuthErrorResponse()
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/../Stubs/private.key',
- \base64_encode(\random_bytes(36)),
+ base64_encode(random_bytes(36)),
new StubResponseType()
);
@@ -96,33 +103,33 @@ public function testOAuthErrorResponse()
$request,
new Response(),
function () {
- return \func_get_args()[1];
+ return func_get_args()[1];
}
);
- $this->assertEquals(401, $response->getStatusCode());
+ self::assertEquals(401, $response->getStatusCode());
}
- public function testOAuthErrorResponseRedirectUri()
+ public function testOAuthErrorResponseRedirectUri(): void
{
$exception = OAuthServerException::invalidScope('test', 'http://foo/bar');
$response = $exception->generateHttpResponse(new Response());
- $this->assertEquals(302, $response->getStatusCode());
- $this->assertEquals(
- 'http://foo/bar?error=invalid_scope&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',
+ self::assertEquals(302, $response->getStatusCode());
+ self::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',
$response->getHeader('location')[0]
);
}
- public function testOAuthErrorResponseRedirectUriFragment()
+ public function testOAuthErrorResponseRedirectUriFragment(): void
{
$exception = OAuthServerException::invalidScope('test', 'http://foo/bar');
$response = $exception->generateHttpResponse(new Response(), true);
- $this->assertEquals(302, $response->getStatusCode());
- $this->assertEquals(
- 'http://foo/bar#error=invalid_scope&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',
+ self::assertEquals(302, $response->getStatusCode());
+ self::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',
$response->getHeader('location')[0]
);
}
diff --git a/tests/Middleware/ResourceServerMiddlewareTest.php b/tests/Middleware/ResourceServerMiddlewareTest.php
index 106111885..4a6d3b79e 100644
--- a/tests/Middleware/ResourceServerMiddlewareTest.php
+++ b/tests/Middleware/ResourceServerMiddlewareTest.php
@@ -1,5 +1,7 @@
getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
@@ -28,30 +33,30 @@ public function testValidResponse()
$accessToken = new AccessTokenEntity();
$accessToken->setIdentifier('test');
- $accessToken->setUserIdentifier(123);
+ $accessToken->setUserIdentifier('123');
$accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H')));
$accessToken->setClient($client);
$accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
- $token = (string) $accessToken;
+ $token = $accessToken->toString();
- $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $token));
+ $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $token));
$middleware = new ResourceServerMiddleware($server);
$response = $middleware->__invoke(
$request,
new Response(),
function () {
- $this->assertEquals('test', \func_get_args()[0]->getAttribute('oauth_access_token_id'));
+ self::assertEquals('test', func_get_args()[0]->getAttribute('oauth_access_token_id'));
- return \func_get_args()[1];
+ return func_get_args()[1];
}
);
- $this->assertEquals(200, $response->getStatusCode());
+ self::assertEquals(200, $response->getStatusCode());
}
- public function testValidResponseExpiredToken()
+ public function testValidResponseExpiredToken(): void
{
$server = new ResourceServer(
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
@@ -63,30 +68,30 @@ public function testValidResponseExpiredToken()
$accessToken = new AccessTokenEntity();
$accessToken->setIdentifier('test');
- $accessToken->setUserIdentifier(123);
+ $accessToken->setUserIdentifier('123');
$accessToken->setExpiryDateTime((new DateTimeImmutable())->sub(new DateInterval('PT1H')));
$accessToken->setClient($client);
$accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
- $token = (string) $accessToken;
+ $token = $accessToken->toString();
- $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $token));
+ $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $token));
$middleware = new ResourceServerMiddleware($server);
$response = $middleware->__invoke(
$request,
new Response(),
function () {
- $this->assertEquals('test', \func_get_args()[0]->getAttribute('oauth_access_token_id'));
+ self::assertEquals('test', func_get_args()[0]->getAttribute('oauth_access_token_id'));
- return \func_get_args()[1];
+ return func_get_args()[1];
}
);
- $this->assertEquals(401, $response->getStatusCode());
+ self::assertEquals(401, $response->getStatusCode());
}
- public function testErrorResponse()
+ public function testErrorResponse(): void
{
$server = new ResourceServer(
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
@@ -100,10 +105,10 @@ public function testErrorResponse()
$request,
new Response(),
function () {
- return \func_get_args()[1];
+ return func_get_args()[1];
}
);
- $this->assertEquals(401, $response->getStatusCode());
+ self::assertEquals(401, $response->getStatusCode());
}
}
diff --git a/tests/PHPStan/AbstractGrantExtension.php b/tests/PHPStan/AbstractGrantExtension.php
index da65ab961..f23062d5b 100644
--- a/tests/PHPStan/AbstractGrantExtension.php
+++ b/tests/PHPStan/AbstractGrantExtension.php
@@ -1,5 +1,6 @@
getName(), [
+ return in_array($methodReflection->getName(), [
'getRequestParameter',
'getQueryStringParameter',
'getCookieParameter',
@@ -33,7 +36,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
{
return TypeCombinator::union(...[
new StringType(),
- isset($methodCall->args[2]) ? $scope->getType($methodCall->args[2]->value) : new NullType(),
+ isset($methodCall->getArgs[2]) ? $scope->getType($methodCall->getArgs[2]->value) : new NullType(),
]);
}
}
diff --git a/tests/RedirectUriValidators/RedirectUriValidatorTest.php b/tests/RedirectUriValidators/RedirectUriValidatorTest.php
index 2f297c264..c648d7ab1 100644
--- a/tests/RedirectUriValidators/RedirectUriValidatorTest.php
+++ b/tests/RedirectUriValidators/RedirectUriValidatorTest.php
@@ -1,5 +1,7 @@
assertFalse(
+ self::assertFalse(
$validator->validateRedirectUri($invalidRedirectUri),
'Non loopback URI must match in every part'
);
}
- public function testValidNonLoopbackUri()
+ public function testValidNonLoopbackUri(): void
{
$validator = new RedirectUriValidator([
'https://example.com:8443/endpoint',
@@ -31,73 +33,73 @@ public function testValidNonLoopbackUri()
$validRedirectUri = 'https://example.com:8443/endpoint';
- $this->assertTrue(
+ self::assertTrue(
$validator->validateRedirectUri($validRedirectUri),
'Redirect URI must be valid when matching in every part'
);
}
- public function testInvalidLoopbackUri()
+ public function testInvalidLoopbackUri(): void
{
$validator = new RedirectUriValidator('http://127.0.0.1:8443/endpoint');
$invalidRedirectUri = 'http://127.0.0.1:8443/different/endpoint';
- $this->assertFalse(
+ self::assertFalse(
$validator->validateRedirectUri($invalidRedirectUri),
'Valid loopback redirect URI can change only the port number'
);
}
- public function testValidLoopbackUri()
+ public function testValidLoopbackUri(): void
{
$validator = new RedirectUriValidator('http://127.0.0.1:8443/endpoint');
$validRedirectUri = 'http://127.0.0.1:8080/endpoint';
- $this->assertTrue(
+ self::assertTrue(
$validator->validateRedirectUri($validRedirectUri),
'Loopback redirect URI can change the port number'
);
}
- public function testValidIpv6LoopbackUri()
+ public function testValidIpv6LoopbackUri(): void
{
$validator = new RedirectUriValidator('http://[::1]:8443/endpoint');
$validRedirectUri = 'http://[::1]:8080/endpoint';
- $this->assertTrue(
+ self::assertTrue(
$validator->validateRedirectUri($validRedirectUri),
'Loopback redirect URI can change the port number'
);
}
- public function testCanValidateUrn()
+ public function testCanValidateUrn(): void
{
$validator = new RedirectUriValidator('urn:ietf:wg:oauth:2.0:oob');
- $this->assertTrue(
+ self::assertTrue(
$validator->validateRedirectUri('urn:ietf:wg:oauth:2.0:oob'),
'An invalid pre-registered urn was provided'
);
}
- public function canValidateCustomSchemeHost()
+ public function canValidateCustomSchemeHost(): void
{
$validator = new RedirectUriValidator('msal://redirect');
- $this->assertTrue(
+ self::assertTrue(
$validator->validateRedirectUri('msal://redirect'),
'An invalid, pre-registered, custom scheme uri was provided'
);
}
- public function canValidateCustomSchemePath()
+ public function canValidateCustomSchemePath(): void
{
$validator = new RedirectUriValidator('com.example.app:/oauth2redirect/example-provider');
- $this->assertTrue(
+ self::assertTrue(
$validator->validateRedirectUri('com.example.app:/oauth2redirect/example-provider'),
'An invalid, pre-registered, custom scheme uri was provided'
);
diff --git a/tests/ResourceServerTest.php b/tests/ResourceServerTest.php
index 7281070e2..41ac2e854 100644
--- a/tests/ResourceServerTest.php
+++ b/tests/ResourceServerTest.php
@@ -1,5 +1,6 @@
getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
@@ -21,7 +22,7 @@ public function testValidateAuthenticatedRequest()
try {
$server->validateAuthenticatedRequest(ServerRequestFactory::fromGlobals());
} catch (OAuthServerException $e) {
- $this->assertEquals('Missing "Authorization" header', $e->getHint());
+ self::assertEquals('Missing "Authorization" header', $e->getHint());
}
}
}
diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php
index da7242651..386fb628b 100644
--- a/tests/ResponseTypes/BearerResponseTypeTest.php
+++ b/tests/ResponseTypes/BearerResponseTypeTest.php
@@ -1,5 +1,7 @@
setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
- $responseType->setEncryptionKey(\base64_encode(\random_bytes(36)));
+ $responseType->setEncryptionKey(base64_encode(random_bytes(36)));
$client = new ClientEntity();
$client->setIdentifier('clientName');
@@ -38,6 +44,7 @@ public function testGenerateHttpResponse()
$accessToken->setClient($client);
$accessToken->addScope($scope);
$accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
+ $accessToken->setUserIdentifier('userId');
$refreshToken = new RefreshTokenEntity();
$refreshToken->setIdentifier('abcdef');
@@ -49,25 +56,24 @@ public function testGenerateHttpResponse()
$response = $responseType->generateHttpResponse(new Response());
- $this->assertInstanceOf(ResponseInterface::class, $response);
- $this->assertEquals(200, $response->getStatusCode());
- $this->assertEquals('no-cache', $response->getHeader('pragma')[0]);
- $this->assertEquals('no-store', $response->getHeader('cache-control')[0]);
- $this->assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]);
+ self::assertEquals(200, $response->getStatusCode());
+ self::assertEquals('no-cache', $response->getHeader('pragma')[0]);
+ self::assertEquals('no-store', $response->getHeader('cache-control')[0]);
+ self::assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]);
$response->getBody()->rewind();
- $json = \json_decode($response->getBody()->getContents());
- $this->assertEquals('Bearer', $json->token_type);
- $this->assertObjectHasAttribute('expires_in', $json);
- $this->assertObjectHasAttribute('access_token', $json);
- $this->assertObjectHasAttribute('refresh_token', $json);
+ $json = json_decode($response->getBody()->getContents());
+ self::assertEquals('Bearer', $json->token_type);
+ self::assertObjectHasProperty('expires_in', $json);
+ self::assertObjectHasProperty('access_token', $json);
+ self::assertObjectHasProperty('refresh_token', $json);
}
- public function testGenerateHttpResponseWithExtraParams()
+ public function testGenerateHttpResponseWithExtraParams(): void
{
$responseType = new BearerTokenResponseWithParams();
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
- $responseType->setEncryptionKey(\base64_encode(\random_bytes(36)));
+ $responseType->setEncryptionKey(base64_encode(random_bytes(36)));
$client = new ClientEntity();
$client->setIdentifier('clientName');
@@ -81,6 +87,7 @@ public function testGenerateHttpResponseWithExtraParams()
$accessToken->setClient($client);
$accessToken->addScope($scope);
$accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
+ $accessToken->setUserIdentifier('userId');
$refreshToken = new RefreshTokenEntity();
$refreshToken->setIdentifier('abcdef');
@@ -92,35 +99,34 @@ public function testGenerateHttpResponseWithExtraParams()
$response = $responseType->generateHttpResponse(new Response());
- $this->assertInstanceOf(ResponseInterface::class, $response);
- $this->assertEquals(200, $response->getStatusCode());
- $this->assertEquals('no-cache', $response->getHeader('pragma')[0]);
- $this->assertEquals('no-store', $response->getHeader('cache-control')[0]);
- $this->assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]);
+ self::assertEquals(200, $response->getStatusCode());
+ self::assertEquals('no-cache', $response->getHeader('pragma')[0]);
+ self::assertEquals('no-store', $response->getHeader('cache-control')[0]);
+ self::assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]);
$response->getBody()->rewind();
- $json = \json_decode($response->getBody()->getContents());
- $this->assertEquals('Bearer', $json->token_type);
- $this->assertObjectHasAttribute('expires_in', $json);
- $this->assertObjectHasAttribute('access_token', $json);
- $this->assertObjectHasAttribute('refresh_token', $json);
-
- $this->assertObjectHasAttribute('foo', $json);
- $this->assertEquals('bar', $json->foo);
+ $json = json_decode($response->getBody()->getContents());
+ self::assertEquals('Bearer', $json->token_type);
+ self::assertObjectHasProperty('expires_in', $json);
+ self::assertObjectHasProperty('access_token', $json);
+ self::assertObjectHasProperty('refresh_token', $json);
+
+ self::assertObjectHasProperty('foo', $json);
+ self::assertEquals('bar', $json->foo);
}
- public function testDetermineAccessTokenInHeaderValidToken()
+ public function testDetermineAccessTokenInHeaderValidToken(): void
{
$responseType = new BearerTokenResponse();
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
- $responseType->setEncryptionKey(\base64_encode(\random_bytes(36)));
+ $responseType->setEncryptionKey(base64_encode(random_bytes(36)));
$client = new ClientEntity();
$client->setIdentifier('clientName');
$accessToken = new AccessTokenEntity();
$accessToken->setIdentifier('abcdef');
- $accessToken->setUserIdentifier(123);
+ $accessToken->setUserIdentifier('123');
$accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H')));
$accessToken->setClient($client);
$accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
@@ -134,7 +140,7 @@ public function testDetermineAccessTokenInHeaderValidToken()
$responseType->setRefreshToken($refreshToken);
$response = $responseType->generateHttpResponse(new Response());
- $json = \json_decode((string) $response->getBody());
+ $json = json_decode((string) $response->getBody());
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false);
@@ -142,30 +148,30 @@ public function testDetermineAccessTokenInHeaderValidToken()
$authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock);
$authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
- $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $json->access_token));
+ $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token));
$request = $authorizationValidator->validateAuthorization($request);
- $this->assertEquals('abcdef', $request->getAttribute('oauth_access_token_id'));
- $this->assertEquals('clientName', $request->getAttribute('oauth_client_id'));
- $this->assertEquals('123', $request->getAttribute('oauth_user_id'));
- $this->assertEquals([], $request->getAttribute('oauth_scopes'));
+ self::assertEquals('abcdef', $request->getAttribute('oauth_access_token_id'));
+ self::assertEquals('clientName', $request->getAttribute('oauth_client_id'));
+ self::assertEquals('123', $request->getAttribute('oauth_user_id'));
+ self::assertEquals([], $request->getAttribute('oauth_scopes'));
}
- public function testDetermineAccessTokenInHeaderInvalidJWT()
+ public function testDetermineAccessTokenInHeaderInvalidJWT(): void
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$responseType = new BearerTokenResponse();
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
- $responseType->setEncryptionKey(\base64_encode(\random_bytes(36)));
+ $responseType->setEncryptionKey(base64_encode(random_bytes(36)));
$client = new ClientEntity();
$client->setIdentifier('clientName');
$accessToken = new AccessTokenEntity();
$accessToken->setIdentifier('abcdef');
- $accessToken->setUserIdentifier(123);
+ $accessToken->setUserIdentifier('123');
$accessToken->setExpiryDateTime((new DateTimeImmutable())->sub(new DateInterval('PT1H')));
$accessToken->setClient($client);
$accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
@@ -179,35 +185,35 @@ public function testDetermineAccessTokenInHeaderInvalidJWT()
$responseType->setRefreshToken($refreshToken);
$response = $responseType->generateHttpResponse(new Response());
- $json = \json_decode((string) $response->getBody());
+ $json = json_decode((string) $response->getBody());
$authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock);
$authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
- $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $json->access_token));
+ $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token));
try {
$authorizationValidator->validateAuthorization($request);
} catch (OAuthServerException $e) {
- $this->assertEquals(
+ self::assertEquals(
'Access token could not be verified',
$e->getHint()
);
}
}
- public function testDetermineAccessTokenInHeaderRevokedToken()
+ public function testDetermineAccessTokenInHeaderRevokedToken(): void
{
$responseType = new BearerTokenResponse();
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
- $responseType->setEncryptionKey(\base64_encode(\random_bytes(36)));
+ $responseType->setEncryptionKey(base64_encode(random_bytes(36)));
$client = new ClientEntity();
$client->setIdentifier('clientName');
$accessToken = new AccessTokenEntity();
$accessToken->setIdentifier('abcdef');
- $accessToken->setUserIdentifier(123);
+ $accessToken->setUserIdentifier('123');
$accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H')));
$accessToken->setClient($client);
$accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
@@ -221,7 +227,7 @@ public function testDetermineAccessTokenInHeaderRevokedToken()
$responseType->setRefreshToken($refreshToken);
$response = $responseType->generateHttpResponse(new Response());
- $json = \json_decode((string) $response->getBody());
+ $json = json_decode((string) $response->getBody());
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(true);
@@ -229,23 +235,23 @@ public function testDetermineAccessTokenInHeaderRevokedToken()
$authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock);
$authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
- $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $json->access_token));
+ $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token));
try {
$authorizationValidator->validateAuthorization($request);
} catch (OAuthServerException $e) {
- $this->assertEquals(
+ self::assertEquals(
'Access token has been revoked',
$e->getHint()
);
}
}
- public function testDetermineAccessTokenInHeaderInvalidToken()
+ public function testDetermineAccessTokenInHeaderInvalidToken(): void
{
$responseType = new BearerTokenResponse();
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
- $responseType->setEncryptionKey(\base64_encode(\random_bytes(36)));
+ $responseType->setEncryptionKey(base64_encode(random_bytes(36)));
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
@@ -257,18 +263,18 @@ public function testDetermineAccessTokenInHeaderInvalidToken()
try {
$authorizationValidator->validateAuthorization($request);
} catch (OAuthServerException $e) {
- $this->assertEquals(
+ self::assertEquals(
'The JWT string must have two dots',
$e->getHint()
);
}
}
- public function testDetermineMissingBearerInHeader()
+ public function testDetermineMissingBearerInHeader(): void
{
$responseType = new BearerTokenResponse();
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
- $responseType->setEncryptionKey(\base64_encode(\random_bytes(36)));
+ $responseType->setEncryptionKey(base64_encode(random_bytes(36)));
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
@@ -280,7 +286,7 @@ public function testDetermineMissingBearerInHeader()
try {
$authorizationValidator->validateAuthorization($request);
} catch (OAuthServerException $e) {
- $this->assertEquals(
+ self::assertEquals(
'Error while decoding from JSON',
$e->getHint()
);
diff --git a/tests/ResponseTypes/BearerTokenResponseWithParams.php b/tests/ResponseTypes/BearerTokenResponseWithParams.php
index 4ba09f4b9..2be1a3272 100644
--- a/tests/ResponseTypes/BearerTokenResponseWithParams.php
+++ b/tests/ResponseTypes/BearerTokenResponseWithParams.php
@@ -1,5 +1,7 @@
+ */
+ protected function getExtraParams(AccessTokenEntityInterface $accessToken): array
{
return ['foo' => 'bar', 'token_type' => 'Should not overwrite'];
}
diff --git a/tests/ResponseTypes/DeviceCodeResponseTypeTest.php b/tests/ResponseTypes/DeviceCodeResponseTypeTest.php
new file mode 100644
index 000000000..93bd9d6b3
--- /dev/null
+++ b/tests/ResponseTypes/DeviceCodeResponseTypeTest.php
@@ -0,0 +1,60 @@
+setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
+ $responseType->setEncryptionKey(base64_encode(random_bytes(36)));
+
+ $client = new ClientEntity();
+ $client->setIdentifier('clientName');
+
+ $scope = new ScopeEntity();
+ $scope->setIdentifier('basic');
+
+ $deviceCode = new DeviceCodeEntity();
+ $deviceCode->setIdentifier('abcdef');
+ $deviceCode->setUserCode('12345678');
+ $deviceCode->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H')));
+ $deviceCode->setClient($client);
+ $deviceCode->addScope($scope);
+ $deviceCode->setVerificationUri('https://example.com/device');
+
+ $responseType->setDeviceCodeEntity($deviceCode);
+
+ $response = $responseType->generateHttpResponse(new Response());
+
+ $this::assertEquals(200, $response->getStatusCode());
+ $this::assertEquals('no-cache', $response->getHeader('pragma')[0]);
+ $this::assertEquals('no-store', $response->getHeader('cache-control')[0]);
+ $this::assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]);
+
+ $response->getBody()->rewind();
+ $json = json_decode($response->getBody()->getContents());
+ $this::assertObjectHasProperty('expires_in', $json);
+ $this::assertObjectHasProperty('device_code', $json);
+ $this::assertEquals('abcdef', $json->device_code);
+ $this::assertObjectHasProperty('verification_uri', $json);
+ $this::assertObjectHasProperty('user_code', $json);
+ }
+}
diff --git a/tests/Stubs/AccessTokenEntity.php b/tests/Stubs/AccessTokenEntity.php
index 77a4d2231..339b29c8b 100644
--- a/tests/Stubs/AccessTokenEntity.php
+++ b/tests/Stubs/AccessTokenEntity.php
@@ -1,5 +1,7 @@
redirectUri = $uri;
}
- public function setConfidential()
+ public function setConfidential(): void
{
$this->isConfidential = true;
}
diff --git a/tests/Stubs/CryptTraitStub.php b/tests/Stubs/CryptTraitStub.php
index d4d8f1c68..19de09008 100644
--- a/tests/Stubs/CryptTraitStub.php
+++ b/tests/Stubs/CryptTraitStub.php
@@ -1,29 +1,35 @@
setEncryptionKey(\base64_encode(\random_bytes(36)));
+ $this->setEncryptionKey(base64_encode(random_bytes(36)));
}
- public function getKey()
+ public function getKey(): string|Key|null
{
return $this->encryptionKey;
}
- public function doEncrypt($unencryptedData)
+ public function doEncrypt(string $unencryptedData): string
{
return $this->encrypt($unencryptedData);
}
- public function doDecrypt($encryptedData)
+ public function doDecrypt(string $encryptedData): string
{
return $this->decrypt($encryptedData);
}
diff --git a/tests/Stubs/DeviceCodeEntity.php b/tests/Stubs/DeviceCodeEntity.php
new file mode 100644
index 000000000..79a0b7b16
--- /dev/null
+++ b/tests/Stubs/DeviceCodeEntity.php
@@ -0,0 +1,17 @@
+emitter = $emitter;
return $this;
}
- public function getEmitter()
+ public function getEmitter(): EventEmitter
{
return $this->emitter;
}
- public function setRefreshTokenTTL(DateInterval $refreshTokenTTL)
+ public function setRefreshTokenTTL(DateInterval $refreshTokenTTL): void
{
}
- public function getIdentifier()
+ public function getIdentifier(): string
{
return 'grant_type_identifier';
}
@@ -44,16 +48,16 @@ public function respondToAccessTokenRequest(
ServerRequestInterface $request,
ResponseTypeInterface $responseType,
DateInterval $accessTokenTTL
- ) {
+ ): ResponseTypeInterface {
return $responseType;
}
- public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
+ public function canRespondToAuthorizationRequest(ServerRequestInterface $request): bool
{
return true;
}
- public function validateAuthorizationRequest(ServerRequestInterface $request)
+ public function validateAuthorizationRequest(ServerRequestInterface $request): AuthorizationRequest
{
$authRequest = new AuthorizationRequest();
$authRequest->setGrantTypeId(self::class);
@@ -61,36 +65,68 @@ public function validateAuthorizationRequest(ServerRequestInterface $request)
return $authRequest;
}
- public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
+ public function completeAuthorizationRequest(AuthorizationRequestInterface $authorizationRequest): BearerTokenResponse
{
+ return new BearerTokenResponse();
}
- public function canRespondToAccessTokenRequest(ServerRequestInterface $request)
+ public function canRespondToAccessTokenRequest(ServerRequestInterface $request): bool
{
return true;
}
- public function setClientRepository(ClientRepositoryInterface $clientRepository)
+ public function setClientRepository(ClientRepositoryInterface $clientRepository): void
{
}
- public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository)
+ public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository): void
{
}
- public function setScopeRepository(ScopeRepositoryInterface $scopeRepository)
+ public function setScopeRepository(ScopeRepositoryInterface $scopeRepository): void
{
}
- public function setDefaultScope($scope)
+ public function setDefaultScope(string $scope): void
{
}
- public function setPrivateKey(CryptKey $privateKey)
+ public function setPrivateKey(CryptKeyInterface $privateKey): void
{
}
- public function setEncryptionKey($key = null)
+ public function setEncryptionKey(Key|string|null $key = null): void
+ {
+ }
+
+ public function revokeRefreshTokens(bool $willRevoke): void
+ {
+ }
+
+ public function canRespondToDeviceAuthorizationRequest(ServerRequestInterface $request): bool
+ {
+ return true;
+ }
+
+ public function completeDeviceAuthorizationRequest(string $deviceCode, string $userId, bool $userApproved): void
+ {
+ }
+
+ public function respondToDeviceAuthorizationRequest(ServerRequestInterface $request): DeviceCodeResponse
+ {
+ return new DeviceCodeResponse();
+ }
+
+ public function setIntervalVisibility(bool $intervalVisibility): void
+ {
+ }
+
+ public function getIntervalVisibility(): bool
+ {
+ return false;
+ }
+
+ public function setIncludeVerificationUriComplete(bool $includeVerificationUriComplete): void
{
}
}
diff --git a/tests/Stubs/RefreshTokenEntity.php b/tests/Stubs/RefreshTokenEntity.php
index f145b7065..9d6d79f27 100644
--- a/tests/Stubs/RefreshTokenEntity.php
+++ b/tests/Stubs/RefreshTokenEntity.php
@@ -1,5 +1,7 @@
getIdentifier();
}
diff --git a/tests/Stubs/StubResponseType.php b/tests/Stubs/StubResponseType.php
index 7aa71f67e..0f8f371a1 100644
--- a/tests/Stubs/StubResponseType.php
+++ b/tests/Stubs/StubResponseType.php
@@ -1,5 +1,7 @@
accessToken;
}
- public function getRefreshToken()
+ public function getRefreshToken(): RefreshTokenEntityInterface|null
{
- return $this->refreshToken;
+ return $this->refreshToken ?? null;
}
- /**
- * @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken
- */
- public function setAccessToken(AccessTokenEntityInterface $accessToken)
+ public function setAccessToken(AccessTokenEntityInterface $accessToken): void
{
$this->accessToken = $accessToken;
}
- /**
- * @param \League\OAuth2\Server\Entities\RefreshTokenEntityInterface $refreshToken
- */
- public function setRefreshToken(RefreshTokenEntityInterface $refreshToken)
+ public function setRefreshToken(RefreshTokenEntityInterface $refreshToken): void
{
$this->refreshToken = $refreshToken;
}
/**
- * @param ServerRequestInterface $request
- *
* @throws \League\OAuth2\Server\Exception\OAuthServerException
- *
- * @return \Psr\Http\Message\ServerRequestInterface
*/
- public function validateAccessToken(ServerRequestInterface $request)
+ public function validateAccessToken(ServerRequestInterface $request): ServerRequestInterface
{
if ($request->getHeader('authorization')[0] === 'Basic test') {
return $request->withAttribute('oauth_access_token_id', 'test');
@@ -58,12 +46,7 @@ public function validateAccessToken(ServerRequestInterface $request)
throw OAuthServerException::accessDenied();
}
- /**
- * @param ResponseInterface $response
- *
- * @return ResponseInterface
- */
- public function generateHttpResponse(ResponseInterface $response)
+ public function generateHttpResponse(ResponseInterface $response): ResponseInterface
{
return new Response();
}
diff --git a/tests/Stubs/UserEntity.php b/tests/Stubs/UserEntity.php
index 6bbdc8c41..4e0fa0123 100644
--- a/tests/Stubs/UserEntity.php
+++ b/tests/Stubs/UserEntity.php
@@ -1,5 +1,7 @@
setIdentifier(123);
+ $this->setIdentifier('123');
}
}
diff --git a/tests/Utils/CryptKeyTest.php b/tests/Utils/CryptKeyTest.php
index 7190864c6..9ec87cb1c 100644
--- a/tests/Utils/CryptKeyTest.php
+++ b/tests/Utils/CryptKeyTest.php
@@ -1,141 +1,152 @@
expectException(\LogicException::class);
+ $this->expectException(LogicException::class);
new CryptKey('undefined file');
}
- public function testKeyCreation()
+ public function testKeyCreation(): void
{
$keyFile = __DIR__ . '/../Stubs/public.key';
$key = new CryptKey($keyFile, 'secret');
- $this->assertEquals('file://' . $keyFile, $key->getKeyPath());
- $this->assertEquals('secret', $key->getPassPhrase());
+ self::assertEquals('file://' . $keyFile, $key->getKeyPath());
+ self::assertEquals('secret', $key->getPassPhrase());
}
- public function testKeyString()
+ public function testKeyString(): void
{
- $keyContent = \file_get_contents(__DIR__ . '/../Stubs/public.key');
+ $keyContent = file_get_contents(__DIR__ . '/../Stubs/public.key');
- if (!\is_string($keyContent)) {
- $this->fail('The public key stub is not a string');
+ if (!is_string($keyContent)) {
+ self::fail('The public key stub is not a string');
}
$key = new CryptKey($keyContent);
- $this->assertEquals(
+ self::assertEquals(
$keyContent,
$key->getKeyContents()
);
- $keyContent = \file_get_contents(__DIR__ . '/../Stubs/private.key.crlf');
+ $keyContent = file_get_contents(__DIR__ . '/../Stubs/private.key.crlf');
- if (!\is_string($keyContent)) {
- $this->fail('The private key (crlf) stub is not a string');
+ if (!is_string($keyContent)) {
+ self::fail('The private key (crlf) stub is not a string');
}
$key = new CryptKey($keyContent);
- $this->assertEquals(
+ self::assertEquals(
$keyContent,
$key->getKeyContents()
);
}
- public function testUnsupportedKeyType()
+ public function testUnsupportedKeyType(): void
{
- if (\str_starts_with(\phpversion(), '8.0')) {
- $this->markTestSkipped('Cannot generate key on PHP 8.0 runner. Investigating');
- }
- $this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Unable to read key');
+ $this->expectException(LogicException::class);
+ $this->expectExceptionMessage('Invalid key supplied');
try {
// Create the keypair
- $res = \openssl_pkey_new([
+ $res = openssl_pkey_new([
'digest_alg' => 'sha512',
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_DSA,
]);
+
+ if ($res === false) {
+ self::fail('The keypair was not created');
+ }
+
// Get private key
- \openssl_pkey_export($res, $keyContent, 'mystrongpassword');
+ openssl_pkey_export($res, $keyContent, 'mystrongpassword');
$path = self::generateKeyPath($keyContent);
new CryptKey($keyContent, 'mystrongpassword');
} finally {
if (isset($path)) {
- @\unlink($path);
+ @unlink($path);
}
}
}
- public function testECKeyType()
+ public function testECKeyType(): void
{
try {
// Create the keypair
- $res = \openssl_pkey_new([
+ $res = openssl_pkey_new([
'digest_alg' => 'sha512',
'curve_name' => 'prime256v1',
'private_key_type' => OPENSSL_KEYTYPE_EC,
]);
+
+ if ($res === false) {
+ self::fail('The keypair was not created');
+ }
+
// Get private key
- \openssl_pkey_export($res, $keyContent, 'mystrongpassword');
+ openssl_pkey_export($res, $keyContent, 'mystrongpassword');
$key = new CryptKey($keyContent, 'mystrongpassword');
- $this->assertEquals('', $key->getKeyPath());
- $this->assertEquals('mystrongpassword', $key->getPassPhrase());
- } catch (\Throwable $e) {
- $this->fail('The EC key was not created');
- } finally {
- if (isset($path)) {
- @\unlink($path);
- }
+ self::assertEquals('', $key->getKeyPath());
+ self::assertEquals('mystrongpassword', $key->getPassPhrase());
+ } catch (Throwable $e) {
+ self::fail('The EC key was not created');
}
}
- public function testRSAKeyType()
+ public function testRSAKeyType(): void
{
try {
// Create the keypair
- $res = \openssl_pkey_new([
+ $res = openssl_pkey_new([
'digest_alg' => 'sha512',
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
]);
+
+ if ($res === false) {
+ self::fail('The keypair was not created');
+ }
+
// Get private key
- \openssl_pkey_export($res, $keyContent, 'mystrongpassword');
+ openssl_pkey_export($res, $keyContent, 'mystrongpassword');
$key = new CryptKey($keyContent, 'mystrongpassword');
- $this->assertEquals('', $key->getKeyPath());
- $this->assertEquals('mystrongpassword', $key->getPassPhrase());
- } catch (\Throwable $e) {
- $this->fail('The RSA key was not created');
- } finally {
- if (isset($path)) {
- @\unlink($path);
- }
+ self::assertEquals('', $key->getKeyPath());
+ self::assertEquals('mystrongpassword', $key->getPassPhrase());
+ } catch (Throwable $e) {
+ self::fail('The RSA key was not created');
}
}
- /**
- * @param string $keyContent
- *
- * @return string
- */
- private static function generateKeyPath($keyContent)
+ private static function generateKeyPath(string $keyContent): string
{
- return 'file://' . \sys_get_temp_dir() . '/' . \sha1($keyContent) . '.key';
+ return 'file://' . sys_get_temp_dir() . '/' . sha1($keyContent) . '.key';
}
}
diff --git a/tests/Utils/CryptTraitTest.php b/tests/Utils/CryptTraitTest.php
index a07d6d50c..b49b0e9e2 100644
--- a/tests/Utils/CryptTraitTest.php
+++ b/tests/Utils/CryptTraitTest.php
@@ -1,41 +1,46 @@
cryptStub = new CryptTraitStub();
}
- public function testEncryptDecryptWithPassword()
+ public function testEncryptDecryptWithPassword(): void
{
- $this->cryptStub->setEncryptionKey(\base64_encode(\random_bytes(36)));
+ $this->cryptStub->setEncryptionKey(base64_encode(random_bytes(36)));
$this->encryptDecrypt();
}
- public function testEncryptDecryptWithKey()
+ public function testEncryptDecryptWithKey(): void
{
$this->cryptStub->setEncryptionKey(Key::createNewRandomKey());
$this->encryptDecrypt();
}
- private function encryptDecrypt()
+ private function encryptDecrypt(): void
{
$payload = 'alex loves whisky';
$encrypted = $this->cryptStub->doEncrypt($payload);
$plainText = $this->cryptStub->doDecrypt($encrypted);
- $this->assertNotEquals($payload, $encrypted);
- $this->assertEquals($payload, $plainText);
+ self::assertNotEquals($payload, $encrypted);
+ self::assertEquals($payload, $plainText);
}
}