Compare commits

...

118 Commits
6.1.0 ... 7.1.1

Author SHA1 Message Date
Andrew Millington
2e47fa7fca Add PR reference 2018-05-21 15:01:37 +01:00
Andrew Millington
fc55621f20 Add link to 7.1.1 release in changelog 2018-05-21 15:00:06 +01:00
Andrew Millington
beec37d95f Modify changelog for 7.1.1 release 2018-05-21 14:58:56 +01:00
Andrew Millington
5a499bf03c Merge pull request #902 from Sephster/fix-745-without-tests
Only Add Authenticate Header if Present in Original Request. Fix #745
2018-05-17 13:34:30 +01:00
Andrew Millington
2e3ee60a2a Remove additional whitespace 2018-05-17 13:27:30 +01:00
Andrew Millington
0242d0c996 Remove spaces at end of line 2018-05-17 13:21:39 +01:00
Andrew Millington
3ea0cdc936 Set authScheme 2018-05-17 13:19:32 +01:00
Andrew Millington
19d782d223 Fix alignment 2018-05-17 13:13:30 +01:00
Andrew Millington
8a25e0a01b Update changelog 2018-05-17 13:12:32 +01:00
Andrew Millington
a3d4f583ed Fix #745 2018-05-17 13:06:03 +01:00
Andrew Millington
28276cb688 Add PSR-7 to the requirements in the readme
This fixes issue #640
2018-05-16 13:36:29 +01:00
Andrew Millington
35c6f28aef Add drupal integration to the readme 2018-05-03 17:06:27 +01:00
Andrew Millington
bd47b58f81 Update changelog for version 7.1.0 2018-04-22 15:16:23 +01:00
Andrew Millington
e38ea4ab34 Update code of conduct link in the readme 2018-04-22 12:58:05 +01:00
Andrew Millington
8b40858509 Rename CONDUCT.md to CODE_OF_CONDUCT.md 2018-04-21 22:25:51 +01:00
Andrew Millington
aa5cc5eac7 Adding contact email address 2018-04-21 22:22:53 +01:00
Andrew Millington
38f26d5229 Update our code of conduct 2018-04-21 22:07:30 +01:00
Andrew Millington
a5a51fad95 Add PR 893 to the changelog 2018-04-21 22:05:08 +01:00
Andrew Millington
52d7952ba5 Merge pull request #893 from Sephster/fix-exception-hint
Change hint so it applies to both the auth and access token requests
2018-04-21 22:02:09 +01:00
Andrew Millington
2375b8c7f3 Merge pull request #856 from lookyman/phpstan-level-7
PHPStan level 7
2018-04-21 22:01:25 +01:00
Andrew Millington
f9a91a79c2 Put upgrade of PHPStan under changed category 2018-04-21 21:59:47 +01:00
Andrew Millington
242dd4dcfe Fix docblock 2018-04-21 21:51:25 +01:00
Andrew Millington
c94ec122aa Fix PR number for changelog 2018-04-21 21:41:48 +01:00
Andrew Millington
05f5d90034 Update changelog 2018-04-21 21:39:28 +01:00
Andrew Millington
491c23c1e9 Merge remote-tracking branch 'upstream/master' into phpstan-level-7 2018-04-21 21:37:24 +01:00
Andrew Millington
27323b5c9a Fix spacing issue 2018-04-21 21:31:48 +01:00
Andrew Millington
80bc291c51 Added null checks before calling set functions 2018-04-21 21:29:21 +01:00
Andrew Millington
8a619e5c1e Change hint so it applies to both the auth and access token requests 2018-04-21 18:07:38 +01:00
Andrew Millington
7e07033b10 Merge pull request #892 from Sephster/fix-issue-837
Revert fix for client ID exception
2018-04-21 17:49:42 +01:00
Andrew Millington
6991777ff3 Fix blank line spacing issue 2018-04-20 18:33:46 +01:00
Andrew Millington
9febc32e14 Add spacing around logical blocks 2018-04-20 18:27:47 +01:00
Andrew Millington
c8b44ff5c7 Revert fix for client ID exception 2018-04-20 18:22:07 +01:00
Andrew Millington
9fc288ce53 Merge pull request #876 from steverhoades/fix-example
Fix fatal error in examples caused by ClientRepositoryInterface change
2018-03-17 20:43:53 +00:00
Steve Rhoades
8f1bf88792 Fix fatal error caused by ClientRepositoryInterface change 2018-03-17 09:30:14 -07:00
Andrew Millington
cc19da50b4 Merge pull request #814 from SunMar/master
Allow CryptTrait to accept a \Defuse\Crypto\Key as encryption key #812
2018-02-28 21:12:39 +00:00
Andrew Millington
bec0de16bb Update Changelog 2018-02-28 21:00:30 +00:00
Andrew Millington
a56acc8dd0 Minor code tidy up 2018-02-28 20:33:19 +00:00
Andrew Millington
c9b07f386c Fix StyleCI issues and remove phpdoc order from StyleCI 2018-02-28 20:01:01 +00:00
Andrew Millington
00a7972f74 Merge remote-tracking branch 'upstream/master' 2018-02-28 19:45:41 +00:00
Andrew Millington
e3266cb50a Fix changelog categorisation 2018-02-26 20:08:02 +00:00
Andrew Millington
2fdd6ce494 Add change for access and refresh token emitters 2018-02-26 20:07:02 +00:00
Andrew Millington
6fd3024c48 Merge pull request #860 from Zaszczyk/new-events-to-emitter-#825
Add new event types: access_token_issued and refresh_token_issued.
2018-02-26 20:01:22 +00:00
Andrew Millington
62e06b7d3a Removing Yoda condition
Removed Yoda condition from code base
2018-02-26 19:51:03 +00:00
Simon Hamp
009c109716 TravisCI fix for PHPStan 2018-02-26 16:04:48 +00:00
Simon Hamp
6723aadfe8 Fix #837
Unifies how we fetch the client_id from the request and allows us to throw a more appropriate exception when the client_id parameter is missing.

Improves the test method for this validation by checking the culpable method in this particular case. The test was missing this by calling the wrong method.
2018-02-26 15:56:28 +00:00
Andrew Millington
e24964af07 Update changelog
Add removal of paragone/random_compat to changelog
2018-02-26 12:57:11 +00:00
Andrew Millington
99e42f6f25 Remove paragonie/random_compat
Removing paragonie/random_compat as no longer supporting PHP 5.x branches
2018-02-26 12:38:31 +00:00
Mateusz Błaszczyk
6700b113a8 Add new event types: access_token_issued and refresh_token_issued. 2018-02-23 17:48:51 +01:00
Andrew Millington
28e1418f64 Change to use correct release date for version 7 2018-02-18 20:29:37 +00:00
Lukáš Unger
143afc9561 PHPStan level 7 2018-02-18 21:20:48 +01:00
Andrew Millington
456c6cfdd2 Merge pull request #854 from Sephster/master
Version 7
2018-02-18 15:57:10 +00:00
Andrew Millington
4f68d2a5f2 Fix release tags in changelog 2018-02-18 15:51:41 +00:00
Andrew Millington
49f66866f7 Fix links for versions 6.0.1 - 6.0.2 2018-02-18 15:38:28 +00:00
Andrew Millington
028d91f670 Add code coverage for scrutinizer 2018-02-18 15:33:41 +00:00
Andrew Millington
9287f587fc Update changelog link 2018-02-18 15:29:59 +00:00
Andrew Millington
70396bec67 Chang Changelog format 2018-02-18 15:28:28 +00:00
Andrew Millington
6679418436 Update readme and changelog 2018-02-18 14:20:37 +00:00
Andrew Millington
e04b8f4e6d Merge pull request #744 from erickjth/fix-pkce-implementation
Fix PKCE implementation with strict verifications
2018-02-18 14:10:08 +00:00
Andrew Millington
e0cc5ee1b0 Merge branch 'master' of https://github.com/thephpleague/oauth2-server into fix-pkce-implementation 2018-02-18 13:57:19 +00:00
Andrew Millington
b78c012796 Change code challenge and verifier to be constants in test 2018-02-18 13:51:34 +00:00
Andrew Millington
bcd2fc38c0 Merge pull request #853 from thephpleague/analysis-zGNlb2
Apply fixes from StyleCI
2018-02-17 20:02:03 +00:00
Andrew Millington
25c3c216a0 Apply fixes from StyleCI 2018-02-17 19:31:59 +00:00
Andrew Millington
8bbb20a012 Merge pull request #852 from lookyman/updated-dependencies
Updated dependencies, more strict static analysis
2018-02-17 17:27:58 +00:00
Lukáš Unger
cd5233392e Updated dependencies, more strict static analysis 2018-02-17 18:07:16 +01:00
Andrew Millington
7a6c35bc29 Merge pull request #849 from lookyman/phpunit-version
Update PHPUnit, run static analysis on tests
2018-02-17 16:28:41 +00:00
Andrew Millington
8614aea887 Revert "Remove temp variables and @var comments"
This reverts commit 7a5c511807.
2018-02-17 16:27:41 +00:00
Andrew Millington
7a5c511807 Remove temp variables and @var comments 2018-02-17 16:15:48 +00:00
Andrew Millington
e0b65a2831 Set default mustValidateSecret to true 2018-02-17 11:46:47 +00:00
Andrew Millington
ee7d52ecaa Merge pull request #607 from lucadegasperi/patch-5
Make ClientRepositoryInterface more flexible
2018-02-17 11:42:48 +00:00
Alex Bilbie
7d1d88cdf1 Merge pull request #850 from weirdan/patch-1
Fixed docblock opener
2018-02-12 10:54:26 +00:00
Bruce Weirdan
80a949601f Fixed docblock opener
It's important for tools relying on docblock types to perform static analysis (think phan, phpstan, psalm, etc)
2018-02-12 12:36:59 +02:00
Andrew Millington
fea577f25b Merge pull request #719 from stratoss/stratoss-patch-exeption
Getter and setter for the payload and ability to pass options to json_encode
2018-02-11 22:48:23 +00:00
Lukáš Unger
1f87c7a7be Update PHPUnit, run static analysis on tests 2018-02-11 23:22:46 +01:00
Andrew Millington
3098f6d7fa Merge pull request #839 from simesy/master
Allow other secure key permissions.
2018-02-11 22:17:35 +00:00
Andrew Millington
06a23a1dd0 Update CryptKey.php
Change the error message to reflect that the server will also accept 440 and 400 as a valid file permission
2018-02-11 22:12:55 +00:00
Alex Bilbie
97089ad49e Merge pull request #848 from lookyman/static-analysis
Static analysis with PHPStan
2018-02-11 21:24:52 +00:00
Lukáš Unger
eca385ab08 Static analysis with PHPStan 2018-02-11 22:20:17 +01:00
Andrew Millington
ef06c29ee8 Merge pull request #840 from liverbool/master
BUGFIX: Wrong redirect uri.
2018-02-11 20:20:41 +00:00
Andrew Millington
5fb9fc929a Reinstate check on client redirect URI to fail if multiple redirect URIs have been listed for the client and one has not been specified in the auth request 2018-02-11 20:10:01 +00:00
Andrew Millington
4c548dbd78 Merge pull request #847 from Sephster/master
Minor Coding Standard Fixes
2018-02-11 19:01:12 +00:00
liverbool
b3cd73cac7 code cleaner
cc.

Co-Authored-By: Andrew Millington <sephster@users.noreply.github.com>
2018-02-09 05:54:05 +07:00
Andrew Millington
3999c41fef Merge pull request #845 from kpn13/master
Do not create key file if it already exists and it is the same
2018-02-06 12:32:29 +00:00
Erick Torres
ce2662ece7 Merge branch 'master' of github.com:thephpleague/oauth2-server into fix-pkce-implementation
# Conflicts:
#	tests/Grant/AuthCodeGrantTest.php
2018-02-05 15:32:15 -05:00
Karim PINCHON
d2641b560d Do not create key file if it already exists and it is the same 2018-01-29 11:05:10 +01:00
Andrew Millington
8bbd218856 Merge pull request #842 from sgomez/fix-challenge-pkce
Fix S256 code challenge method
2018-01-25 23:16:15 +00:00
Andrew Millington
eb9cde5ab7 Merge pull request #805 from raarts/Accept-RSA-key-with-crlf-v2
Also accept an RSA key with crlf
2018-01-23 22:24:52 +00:00
Sergio Gómez
1b692e2298 Fix S256 code challenge method
According to [RFC7636#section-4.3](https://tools.ietf.org/html/rfc7636#section-4.3):

    If the "code_challenge_method" from Section 4.3 was "S256", the
    received "code_verifier" is hashed by SHA-256, base64url-encoded, and
    then compared to the "code_challenge", i.e.:

    BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) == code_challenge

So, the hash must be done before the base64_encode.

The tests are modified to use example data from the
[RFC7636#appendix-B](https://tools.ietf.org/html/rfc7636#appendix-B).
2018-01-18 05:31:44 +01:00
liverbool
d22f222e65 BUGFIX: Wrong redirect uri.
This's bugfix when redirect on error.
2018-01-13 11:52:31 +07:00
Simon Hobbs
cf9acb32b8 Allow some more secure options without tsk-tsk. 2018-01-13 15:29:42 +11:00
Alex Bilbie
92d8052a5b Merge pull request #836 from knewzen/master
remove codesponsor
2018-01-04 20:09:23 +00:00
knewzen
a3289c6ecb remove codesponsor 2018-01-05 01:08:14 +08:00
SunMar
292272d128 Allow CryptTrait to accept a \Defuse\Crypto\Key as encryption key #812 2018-01-04 15:14:03 +01:00
Ron Arts
ef8a741527 In public/private keys, force the header to be on its own line, allow missing \n after the footer 2018-01-04 12:17:31 +01:00
Ron Arts
91d9c11fb4 Fixed tests, allow whitespace at the end of a key 2018-01-03 10:18:32 +01:00
Ron Arts
2ec8d148b0 fix .gitattributes 2018-01-03 09:41:39 +01:00
Erick Torres
01d21b2533 Update statement to generate codeChallenge in AuthCodeGrantTest 2017-12-29 12:32:12 -05:00
Andrew Millington
ff29721ca9 Removing call to setEncryptionKey as no such function on the authorization server 2017-12-29 12:29:47 +00:00
Andrew Millington
5b79b40df9 Fixed count placement to make code more efficient as per scrutinizer feedback 2017-12-29 12:25:39 +00:00
Andrew Millington
b6d9835281 Merge branch 'master' into fix-pkce-implementation 2017-12-28 16:37:37 +00:00
Andrew Millington
57ca83a8ba Removing missing scope tests as temporarily reverted this functionality 2017-12-24 00:18:20 +00:00
Andrew Millington
41bba7f58c Removing missing scope test
Temporarily removing missing scope test as have reverted this functionality for version 6.1.1
2017-12-24 00:07:22 +00:00
Andrew Millington
dcae4af6ce Remove missing scope test
Temporarily removing missing scope test for the AuthGrant
2017-12-24 00:06:18 +00:00
Andrew Millington
a0cabb573c Update AbstractGrant.php
Temporarily removing check on empty scopes as causing issues for Passport users
2017-12-23 23:33:42 +00:00
Ron Arts
f79d3f27cf Incorporate https://github.com/thephpleague/oauth2-server/pull/731. Thanks.
Now can handle cr/lf, cr, and lf endings. And on php5 large keys as well.
2017-10-31 10:14:46 +01:00
Ron Arts
90fec63104 Setup the public.key.crlf with the proper permissions 2017-10-30 16:41:10 +01:00
Ron Arts
4563685375 Also accept an RSA key with crlf 2017-10-30 16:21:17 +01:00
Erick Torres
4270f5bac1 Merge branch 'master' of github.com:erickjth/oauth2-server into fix-pkce-implementation
# Conflicts:
#	src/Grant/AuthCodeGrant.php
2017-09-07 17:24:48 -05:00
Erick Torres
88ccb6ff13 Fix codeVerifier check. Keep code style. 2017-07-07 12:35:42 -05:00
Erick Torres
e2f9b73df3 Fix broken tests 2017-07-07 12:19:11 -05:00
Erick Torres
fbb3586cae Merge branch 'master' of github.com:erickjth/oauth2-server into fix-pkce-implementation
# Conflicts:
#	src/Grant/AuthCodeGrant.php
#	tests/Grant/AuthCodeGrantTest.php
2017-07-07 12:06:32 -05:00
Erick Torres
4710743b87 Add "dist: trusty" into travis setting file 2017-06-16 17:09:13 -05:00
Erick Torres
11ad87b5f5 Update tests / Add missing. 2017-06-16 12:03:14 -05:00
Erick Torres
880e3b4590 Fix invalid code_challenge_method key. 2017-06-16 12:03:04 -05:00
Erick Torres
2167edf1d9 Validate codeVerifier and codeChallenge correctly. 2017-06-16 12:02:48 -05:00
Erick Torres
2482630221 Fix codeVerifier hash verification. 2017-06-16 12:02:34 -05:00
Stanimir Stoyanov
d73b15ae32 Getter and setter for the payload and ability to pass options to json_encode 2017-03-20 14:52:35 +02:00
Stanimir Stoyanov
945624eb51 Merge pull request #1 from thephpleague/master
Merging changes into my fork
2017-03-17 19:36:34 +02:00
Luca Degasperi
655a4b2715 Make ClientRepositoryInterface more flexible
This small change will allow the use of the ```ClientRepositoryInterface``` for more use cases than simply validating clients when authorizing them. There might be some places where this change will affect the behavior. I also think the ```$mustValidateSecret``` is redundant since in an implementation a check could be done wether ```$clientSecret``` is null or not.
2016-06-30 16:49:47 +02:00
48 changed files with 942 additions and 615 deletions

3
.gitattributes vendored
View File

@@ -10,4 +10,5 @@
/phpunit.xml.dist export-ignore
/CHANGELOG.md export-ignore
/CONTRIBUTING.md export-ignore
/README.md export-ignore
/README.md export-ignore

View File

@@ -29,7 +29,6 @@ enabled:
- phpdoc_inline_tag
- phpdoc_no_access
- phpdoc_no_simplified_null_return
- phpdoc_order
- phpdoc_property
- phpdoc_scalar
- phpdoc_separation

View File

@@ -1,22 +1,31 @@
language: php
dist: trusty
sudo: false
cache:
directories:
- vendor
- vendor
env:
- DEPENDENCIES=""
- DEPENDENCIES="--prefer-lowest --prefer-stable"
php:
- 5.6
- 7.0
- 7.1
- 7.2
install:
- travis_retry composer install --no-interaction --prefer-source
- composer update --no-interaction --prefer-dist $DEPENDENCIES
script:
- vendor/bin/phpunit
- vendor/bin/phpunit --coverage-clover=coverage.clover
- vendor/bin/phpstan analyse -l 7 -c phpstan.neon src tests
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover coverage.clover
branches:
only:

View File

@@ -1,352 +1,443 @@
# Changelog
All notable changes to this project will be documented in this file.
## 6.0.2 (released 2017-08-03)
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
* An invalid refresh token that can't be decrypted now returns a HTTP 401 error instead of HTTP 400 (Issue #759)
* Removed chmod from CryptKey and add toggle to disable checking (Issue #776)
* Fixes invalid code challenge method payload key name (Issue #777)
## [Unreleased]
## 6.0.1 (released 2017-07-19)
## [7.1.1] - released 2018-05-21
### Fixed
- No longer set a WWW-Authenticate header for invalid clients if the client did not send an Authorization header in the original request (PR #902)
## [7.1.0] - released 2018-04-22
### Changed
- Changed hint for unsupportedGrantType exception so it no longer references the grant type parameter which isn't always expected (PR #893)
- Upgrade PHPStan checks to level 7 (PR #856)
### Added
- Added event emitters for issued access and refresh tokens (PR #860)
- Can now use Defuse\Crypto\Key for encryption/decryption of keys which is faster than the Cryto class (PR #812)
### Removed
- Remove paragone/random_compat from dependencies
## [7.0.0] - released 2018-02-18
### Added
- Use PHPStan for static analysis of code (PR #848)
- Enforce stricter static analysis checks and upgrade library dependencies (PR #852)
- Provide PHPStan coverage for tests and update PHPUnit (PR #849)
- Get and set methods for OAuth Server Exception payloads. Allow implementer to specify the JSON encode options (PR #719)
### Changed
- ClientRepository interface will now accept null for the Grant type to improve extensibility options (PR #607)
- Do not issue an error if key file permissions are 400 or 440 (PR #839)
- Skip key file creation if the file already exists (PR #845)
- Change changelog format and update readme
### Removed
- Support for PHP 5.6
- Support for version 5.x and 6.x of the library
### Fixed
- PKCE implementation (PR #744)
- Set correct redirect URI when validating scopes (PR #840)
- S256 code challenege method (PR #842)
- Accept RSA key with CRLF line endings (PR #805)
## [6.1.1] - 2017-12-23
- Removed check on empty scopes
## [6.1.0] - 2017-12-23
- Changed the token type issued by the Implicit Grant to be Bearer instead of bearer. (PR #724)
- Replaced call to array_key_exists() with the faster isset() on the Implicit Grant. (PR #749)
- Allow specification of query delimiter character in the Password Grant (PR #801)
- Add Zend Diactoros library dependency to examples (PR #678)
- Can set default scope for the authorization endpoint. If no scope is passed during an authorization request, the default scope will be used if set. If not, the server will issue an invalid scope exception (PR #811)
- Added validation for redirect URIs on the authorization end point to ensure exactly one redirection URI has been passed (PR #573)
## [6.0.2] - 2017-08-03
- An invalid refresh token that can't be decrypted now returns a HTTP 401 error instead of HTTP 400 (Issue #759)
- Removed chmod from CryptKey and add toggle to disable checking (Issue #776)
- Fixes invalid code challenge method payload key name (Issue #777)
## [6.0.1] - 2017-07-19
To address feedback from the security release the following change has been made:
* If an RSA key cannot be chmod'ed to 600 then it will now throw a E_USER_NOTICE instead of an exception.
## 6.0.0 (released 2017-07-01)
- If an RSA key cannot be chmod'ed to 600 then it will now throw a E_USER_NOTICE instead of an exception.
* Breaking change: The `AuthorizationServer` constructor now expects an encryption key string instead of a public key
* Remove support for HHVM
* Remove support for PHP 5.5
## [6.0.0] - 2017-07-01
## 5.1.4 (released 2017-07-01)
- Breaking change: The `AuthorizationServer` constructor now expects an encryption key string instead of a public key
- Remove support for HHVM
- Remove support for PHP 5.5
* Fixed multiple security vulnerabilities as a result of a security audit paid for by the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source). All users of this library are encouraged to update as soon as possible to this version or version 6.0 or greater.
* It is recommended on each `AuthorizationServer` instance you set the `setEncryptionKey()`. This will result in stronger encryption being used. If this method is not set messages will be sent to the defined error handling routines (using `error_log`). Please see the examples and documentation for examples.
* TravisCI now tests PHP 7.1 (Issue #671)
* Fix middleware example fatal error (Issue #682)
* Fix typo in the first README sentence (Issue #690)
* Corrected DateInterval from 1 min to 1 month (Issue #709)
## [5.1.4] - 2017-07-01
## 5.1.3 (released 2016-10-12)
- Fixed multiple security vulnerabilities as a result of a security audit paid for by the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source). All users of this library are encouraged to update as soon as possible to this version or version 6.0 or greater.
- It is recommended on each `AuthorizationServer` instance you set the `setEncryptionKey()`. This will result in stronger encryption being used. If this method is not set messages will be sent to the defined error handling routines (using `error_log`). Please see the examples and documentation for examples.
- TravisCI now tests PHP 7.1 (Issue #671)
- Fix middleware example fatal error (Issue #682)
- Fix typo in the first README sentence (Issue #690)
- Corrected DateInterval from 1 min to 1 month (Issue #709)
* Fixed WWW-Authenticate header (Issue #669)
* Increase the recommended RSA key length from 1024 to 2048 bits (Issue #668)
## [5.1.3] - 2016-10-12
## 5.1.2 (released 2016-09-19)
- Fixed WWW-Authenticate header (Issue #669)
- Increase the recommended RSA key length from 1024 to 2048 bits (Issue #668)
* Fixed `finalizeScopes` call (Issue #650)
## [5.1.2] - 2016-09-19
## 5.1.1 (released 2016-07-26)
- Fixed `finalizeScopes` call (Issue #650)
* Improved test suite (Issue #614)
* Updated docblocks (Issue #616)
* Replace `array_shift` with `foreach` loop (Issue #621)
* Allow easy addition of custom fields to Bearer token response (Issue #624)
* Key file auto-generation from string (Issue #625)
## [5.1.1] - 2016-07-26
## 5.1.0 (released 2016-06-28)
- Improved test suite (Issue #614)
- Updated docblocks (Issue #616)
- Replace `array_shift` with `foreach` loop (Issue #621)
- Allow easy addition of custom fields to Bearer token response (Issue #624)
- Key file auto-generation from string (Issue #625)
* Implemented RFC7636 (Issue #574)
* Unify middleware exception responses (Issue #578)
* Updated examples (Issue #589)
* Ensure state is in access denied redirect (Issue #597)
* Remove redundant `isExpired()` method from entity interfaces and traits (Issue #600)
* Added a check for unique access token constraint violation (Issue #601)
* Look at Authorization header directly for HTTP Basic auth checks (Issue #604)
* Added catch Runtime exception when parsing JWT string (Issue #605)
* Allow `paragonie/random_compat` 2.x (Issue #606)
* Added `indigophp/hash-compat` to Composer suggestions and `require-dev` for PHP 5.5 support
## [5.1.0] - 2016-06-28
## 5.0.3 (released 2016-05-04)
- Implemented RFC7636 (Issue #574)
- Unify middleware exception responses (Issue #578)
- Updated examples (Issue #589)
- Ensure state is in access denied redirect (Issue #597)
- Remove redundant `isExpired()` method from entity interfaces and traits (Issue #600)
- Added a check for unique access token constraint violation (Issue #601)
- Look at Authorization header directly for HTTP Basic auth checks (Issue #604)
- Added catch Runtime exception when parsing JWT string (Issue #605)
- Allow `paragonie/random_compat` 2.x (Issue #606)
- Added `indigophp/hash-compat` to Composer suggestions and `require-dev` for PHP 5.5 support
* Fix hints in PasswordGrant (Issue #560)
* Add meaning of `Resource owner` to terminology.md (Issue #561)
* Use constant for event name instead of explicit string (Issue #563)
* Remove unused request property (Issue #564)
* Correct wrong phpdoc (Issue #569)
* Fixed typo in exception string (Issue #570)
## [5.0.3] - 2016-05-04
## 5.0.2 (released 2016-04-18)
- Fix hints in PasswordGrant (Issue #560)
- Add meaning of `Resource owner` to terminology.md (Issue #561)
- Use constant for event name instead of explicit string (Issue #563)
- Remove unused request property (Issue #564)
- Correct wrong phpdoc (Issue #569)
- Fixed typo in exception string (Issue #570)
* `state` parameter is now correctly returned after implicit grant authorization
* Small code and docblock improvements
## [5.0.2] - 2016-04-18
## 5.0.1 (released 2016-04-18)
- `state` parameter is now correctly returned after implicit grant authorization
- Small code and docblock improvements
* Fixes an issue (#550) whereby it was unclear whether or not to validate a client's secret during a request.
## [5.0.1] - 2016-04-18
## 5.0.0 (released 2016-04-17)
- Fixes an issue (#550) whereby it was unclear whether or not to validate a client's secret during a request.
## [5.0.0] - 2016-04-17
Version 5 is a complete code rewrite.
* JWT support
* PSR-7 support
* Improved exception errors
* Replace all occurrences of the term "Storage" with "Repository"
* Simplify repositories
* Entities conform to interfaces and use traits
* Auth code grant updated
* Allow support for public clients
* Add support for #439
* Client credentials grant updated
* Password grant updated
* Allow support for public clients
* Refresh token grant updated
* Implement Implicit grant
* Bearer token output type
* Remove MAC token output type
* Authorization server rewrite
* Resource server class moved to PSR-7 middleware
* Tests
* Much much better documentation
- Renamed Server class to AuthorizationServer
- Added ResourceServer class
- Run unit tests again PHP 5.5.9 as it's the minimum supported version
- Enable PHPUnit 5.0 support
- Improved examples and documentation
- Make it clearer that the implicit grant doesn't support refresh tokens
- Improved refresh token validation errors
- Fixed refresh token expiry date
Changes since RC2:
## [5.0.0-RC2] - 2016-04-10
* Renamed Server class to AuthorizationServer
* Added ResourceServer class
* Run unit tests again PHP 5.5.9 as it's the minimum supported version
* Enable PHPUnit 5.0 support
* Improved examples and documentation
* Make it clearer that the implicit grant doesn't support refresh tokens
* Improved refresh token validation errors
* Fixed refresh token expiry date
- Allow multiple client redirect URIs (Issue #511)
- Remove unused mac token interface (Issue #503)
- Handle RSA key passphrase (Issue #502)
- Remove access token repository from response types (Issue #501)
- Remove unnecessary methods from entity interfaces (Issue #490)
- Ensure incoming JWT hasn't expired (Issue #509)
- Fix client identifier passed where user identifier is expected (Issue #498)
- Removed built-in entities; added traits to for quick re-use (Issue #504)
- Redirect uri is required only if the "redirect_uri" parameter was included in the authorization request (Issue #514)
- Removed templating for auth code and implicit grants (Issue #499)
## 5.0.0-RC2 (released 2016-04-10)
Changes since RC1:
* Allow multiple client redirect URIs (Issue #511)
* Remove unused mac token interface (Issue #503)
* Handle RSA key passphrase (Issue #502)
* Remove access token repository from response types (Issue #501)
* Remove unnecessary methods from entity interfaces (Issue #490)
* Ensure incoming JWT hasn't expired (Issue #509)
* Fix client identifier passed where user identifier is expected (Issue #498)
* Removed built-in entities; added traits to for quick re-use (Issue #504)
* Redirect uri is required only if the "redirect_uri" parameter was included in the authorization request (Issue #514)
* Removed templating for auth code and implicit grants (Issue #499)
## 5.0.0-RC1 (release 2016-03-24)
## [5.0.0-RC1] - 2016-03-24
Version 5 is a complete code rewrite.
* JWT support
* PSR-7 support
* Improved exception errors
* Replace all occurrences of the term "Storage" with "Repository"
* Simplify repositories
* Entities conform to interfaces and use traits
* Auth code grant updated
* Allow support for public clients
* Add support for #439
* Client credentials grant updated
* Password grant updated
* Allow support for public clients
* Refresh token grant updated
* Implement Implicit grant
* Bearer token output type
* Remove MAC token output type
* Authorization server rewrite
* Resource server class moved to PSR-7 middleware
* Tests
* Much much better documentation
- JWT support
- PSR-7 support
- Improved exception errors
- Replace all occurrences of the term "Storage" with "Repository"
- Simplify repositories
- Entities conform to interfaces and use traits
- Auth code grant updated
- Allow support for public clients
- Add support for #439
- Client credentials grant updated
- Password grant updated
- Allow support for public clients
- Refresh token grant updated
- Implement Implicit grant
- Bearer token output type
- Remove MAC token output type
- Authorization server rewrite
- Resource server class moved to PSR-7 middleware
- Tests
- Much much better documentation
## 4.1.5 (released 2016-01-04)
## [4.1.5] - 2016-01-04
* Enable Symfony 3.0 support (#412)
- Enable Symfony 3.0 support (#412)
## 4.1.4 (released 2015-11-13)
## [4.1.4] - 2015-11-13
* Fix for determining access token in header (Issue #328)
* Refresh tokens are now returned for MAC responses (Issue #356)
* Added integration list to readme (Issue #341)
* Expose parameter passed to exceptions (Issue #345)
* Removed duplicate routing setup code (Issue #346)
* Docs fix (Issues #347, #360, #380)
* Examples fix (Issues #348, #358)
* Fix typo in docblock (Issue #352)
* Improved timeouts for MAC tokens (Issue #364)
* `hash_hmac()` should output raw binary data, not hexits (Issue #370)
* Improved regex for matching all Base64 characters (Issue #371)
* Fix incorrect signature parameter (Issue #372)
* AuthCodeGrant and RefreshTokenGrant don't require client_secret (Issue #377)
* Added priority argument to event listener (Issue #388)
- Fix for determining access token in header (Issue #328)
- Refresh tokens are now returned for MAC responses (Issue #356)
- Added integration list to readme (Issue #341)
- Expose parameter passed to exceptions (Issue #345)
- Removed duplicate routing setup code (Issue #346)
- Docs fix (Issues #347, #360, #380)
- Examples fix (Issues #348, #358)
- Fix typo in docblock (Issue #352)
- Improved timeouts for MAC tokens (Issue #364)
- `hash_hmac()` should output raw binary data, not hexits (Issue #370)
- Improved regex for matching all Base64 characters (Issue #371)
- Fix incorrect signature parameter (Issue #372)
- AuthCodeGrant and RefreshTokenGrant don't require client_secret (Issue #377)
- Added priority argument to event listener (Issue #388)
## 4.1.3 (released 2015-03-22)
## [4.1.3] - 2015-03-22
* Docblock, namespace and inconsistency fixes (Issue #303)
* Docblock type fix (Issue #310)
* Example bug fix (Issue #300)
* Updated league/event to ~2.1 (Issue #311)
* Fixed missing session scope (Issue #319)
* Updated interface docs (Issue #323)
* `.travis.yml` updates
- Docblock, namespace and inconsistency fixes (Issue #303)
- Docblock type fix (Issue #310)
- Example bug fix (Issue #300)
- Updated league/event to ~2.1 (Issue #311)
- Fixed missing session scope (Issue #319)
- Updated interface docs (Issue #323)
- `.travis.yml` updates
## 4.1.2 (released 2015-01-01)
## [4.1.2] - 2015-01-01
* Remove side-effects in hash_equals() implementation (Issue #290)
- Remove side-effects in hash_equals() implementation (Issue #290)
## 4.1.1 (released 2014-12-31)
## [4.1.1] - 2014-12-31
* Changed `symfony/http-foundation` dependency version to `~2.4` so package can be installed in Laravel `4.1.*`
- Changed `symfony/http-foundation` dependency version to `~2.4` so package can be installed in Laravel `4.1.*`
## 4.1.0 (released 2014-12-27)
## [4.1.0] - 2014-12-27
* Added MAC token support (Issue #158)
* Fixed example init code (Issue #280)
* Toggle refresh token rotation (Issue #286)
* Docblock fixes
- Added MAC token support (Issue #158)
- Fixed example init code (Issue #280)
- Toggle refresh token rotation (Issue #286)
- Docblock fixes
## 4.0.5 (released 2014-12-15)
## [4.0.5] - 2014-12-15
* Prevent duplicate session in auth code grant (Issue #282)
- Prevent duplicate session in auth code grant (Issue #282)
## 4.0.4 (released 2014-12-03)
## [4.0.4] - 2014-12-03
* Ensure refresh token hasn't expired (Issue #270)
- Ensure refresh token hasn't expired (Issue #270)
## 4.0.3 (released 2014-12-02)
## [4.0.3] - 2014-12-02
* Fix bad type hintings (Issue #267)
* Do not forget to set the expire time (Issue #268)
- Fix bad type hintings (Issue #267)
- Do not forget to set the expire time (Issue #268)
## 4.0.2 (released 2014-11-21)
## [4.0.2] - 2014-11-21
* Improved interfaces (Issue #255)
* Learnt how to spell delimiter and so `getScopeDelimiter()` and `setScopeDelimiter()` methods have been renamed
* Docblock improvements (Issue #254)
- Improved interfaces (Issue #255)
- Learnt how to spell delimiter and so `getScopeDelimiter()` and `setScopeDelimiter()` methods have been renamed
- Docblock improvements (Issue #254)
## 4.0.1 (released 2014-11-09)
## [4.0.1] - 2014-11-09
* Alias the master branch in composer.json (Issue #243)
* Numerous PHP CodeSniffer fixes (Issue #244)
* .travis.yml update (Issue #245)
* The getAccessToken method should return an AccessTokenEntity object instead of a string in ResourceServer.php (#246)
- Alias the master branch in composer.json (Issue #243)
- Numerous PHP CodeSniffer fixes (Issue #244)
- .travis.yml update (Issue #245)
- The getAccessToken method should return an AccessTokenEntity object instead of a string in ResourceServer.php (#246)
## 4.0.0 (released 2014-11-08)
## [4.0.0] - 2014-11-08
* Complete rewrite
* Check out the documentation - [http://oauth2.thephpleague.com](http://oauth2.thephpleague.com)
- Complete rewrite
- Check out the documentation - [http://oauth2.thephpleague.com](http://oauth2.thephpleague.com)
## 3.2.0 (released 2014-04-16)
## [3.2.0] - 2014-04-16
* Added the ability to change the algorithm that is used to generate the token strings (Issue #151)
- Added the ability to change the algorithm that is used to generate the token strings (Issue #151)
## 3.1.2 (released 2014-02-26)
## [3.1.2] - 2014-02-26
* Support Authorization being an environment variable. [See more](http://fortrabbit.com/docs/essentials/quirks-and-constraints#authorization-header)
- Support Authorization being an environment variable. [See more](http://fortrabbit.com/docs/essentials/quirks-and-constraints#authorization-header)
## 3.1.1 (released 2013-12-05)
## [3.1.1] - 2013-12-05
* Normalize headers when `getallheaders()` is available (Issues #108 and #114)
- Normalize headers when `getallheaders()` is available (Issues #108 and #114)
## 3.1.0 (released 2013-12-05)
## [3.1.0] - 2013-12-05
* No longer necessary to inject the authorisation server into a grant, the server will inject itself
* Added test for 1419ba8cdcf18dd034c8db9f7de86a2594b68605
- No longer necessary to inject the authorisation server into a grant, the server will inject itself
- Added test for 1419ba8cdcf18dd034c8db9f7de86a2594b68605
## 3.0.1 (released 2013-12-02)
## [3.0.1] - 2013-12-02
* Forgot to tell TravisCI from testing PHP 5.3
- Forgot to tell TravisCI from testing PHP 5.3
## 3.0.0 (released 2013-12-02)
## [3.0.0] - 2013-12-02
* Fixed spelling of Implicit grant class (Issue #84)
* Travis CI now tests for PHP 5.5
* Fixes for checking headers for resource server (Issues #79 and #)
* The word "bearer" now has a capital "B" in JSON output to match OAuth 2.0 spec
* All grants no longer remove old sessions by default
* All grants now support custom access token TTL (Issue #92)
* All methods which didn't before return a value now return `$this` to support method chaining
* Removed the build in DB providers - these will be put in their own repos to remove baggage in the main repository
* Removed support for PHP 5.3 because this library now uses traits and will use other modern PHP features going forward
* Moved some grant related functions into a trait to reduce duplicate code
- Fixed spelling of Implicit grant class (Issue #84)
- Travis CI now tests for PHP 5.5
- Fixes for checking headers for resource server (Issues #79 and #)
- The word "bearer" now has a capital "B" in JSON output to match OAuth 2.0 spec
- All grants no longer remove old sessions by default
- All grants now support custom access token TTL (Issue #92)
- All methods which didn't before return a value now return `$this` to support method chaining
- Removed the build in DB providers - these will be put in their own repos to remove baggage in the main repository
- Removed support for PHP 5.3 because this library now uses traits and will use other modern PHP features going forward
- Moved some grant related functions into a trait to reduce duplicate code
## 2.1.1 (released 2013-06-02)
## [2.1.1] - 2013-06-02
* Added conditional `isValid()` flag to check for Authorization header only (thanks @alexmcroberts)
* Fixed semantic meaning of `requireScopeParam()` and `requireStateParam()` by changing their default value to true
* Updated some duff docblocks
* Corrected array key call in Resource.php (Issue #63)
- Added conditional `isValid()` flag to check for Authorization header only (thanks @alexmcroberts)
- Fixed semantic meaning of `requireScopeParam()` and `requireStateParam()` by changing their default value to true
- Updated some duff docblocks
- Corrected array key call in Resource.php (Issue #63)
## 2.1 (released 2013-05-10)
## [2.1.0] - 2013-05-10
* Moved zetacomponents/database to "suggest" in composer.json. If you rely on this feature you now need to include " zetacomponents/database" into "require" key in your own composer.json. (Issue #51)
* New method in Refresh grant called `rotateRefreshTokens()`. Pass in `true` to issue a new refresh token each time an access token is refreshed. This parameter needs to be set to true in order to request reduced scopes with the new access token. (Issue #47)
* Rename `key` column in oauth_scopes table to `scope` as `key` is a reserved SQL word. (Issue #45)
* The `scope` parameter is no longer required by default as per the RFC. (Issue #43)
* You can now set multiple default scopes by passing an array into `setDefaultScope()`. (Issue #42)
* The password and client credentials grants now allow for multiple sessions per user. (Issue #32)
* Scopes associated to authorization codes are not held in their own table (Issue #44)
* Database schema updates.
- Moved zetacomponents/database to "suggest" in composer.json. If you rely on this feature you now need to include " zetacomponents/database" into "require" key in your own composer.json. (Issue #51)
- New method in Refresh grant called `rotateRefreshTokens()`. Pass in `true` to issue a new refresh token each time an access token is refreshed. This parameter needs to be set to true in order to request reduced scopes with the new access token. (Issue #47)
- Rename `key` column in oauth_scopes table to `scope` as `key` is a reserved SQL word. (Issue #45)
- The `scope` parameter is no longer required by default as per the RFC. (Issue #43)
- You can now set multiple default scopes by passing an array into `setDefaultScope()`. (Issue #42)
- The password and client credentials grants now allow for multiple sessions per user. (Issue #32)
- Scopes associated to authorization codes are not held in their own table (Issue #44)
- Database schema updates.
## 2.0.5 (released 2013-05-09)
## [2.0.5] - 2013-05-09
* Fixed `oauth_session_token_scopes` table primary key
* Removed `DEFAULT ''` that has slipped into some tables
* Fixed docblock for `SessionInterface::associateRefreshToken()`
- Fixed `oauth_session_token_scopes` table primary key
- Removed `DEFAULT ''` that has slipped into some tables
- Fixed docblock for `SessionInterface::associateRefreshToken()`
## 2.0.4 (released 2013-05-09)
## [2.0.4] - 2013-05-09
* Renamed primary key in oauth_client_endpoints table
* Adding missing column to oauth_session_authcodes
* SECURITY FIX: A refresh token should be bound to a client ID
- Renamed primary key in oauth_client_endpoints table
- Adding missing column to oauth_session_authcodes
## 2.0.3 (released 2013-05-08)
### Security
- A refresh token should be bound to a client ID
* Fixed a link to code in composer.json
## [2.0.3] - 2013-05-08
## 2.0.2 (released 2013-05-08)
- Fixed a link to code in composer.json
* Updated README with wiki guides
* Removed `null` as default parameters in some methods in the storage interfaces
* Fixed license copyright
## [2.0.2] - 2013-05-08
## 2.0.0 (released 2013-05-08)
- Updated README with wiki guides
- Removed `null` as default parameters in some methods in the storage interfaces
- Fixed license copyright
## [2.0.0] - 2013-05-08
**If you're upgrading from v1.0.8 there are lots of breaking changes**
* Rewrote the session storage interface from scratch so methods are more obvious
* Included a PDO driver which implements the storage interfaces so the library is more "get up and go"
* Further normalised the database structure so all sessions no longer contain infomation related to authorization grant (which may or may not be enabled)
* A session can have multiple associated access tokens
* Individual grants can have custom expire times for access tokens
* Authorization codes now have a TTL of 10 minutes by default (can be manually set)
* Refresh tokens now have a TTL of one week by default (can be manually set)
* The client credentials grant will no longer gives out refresh tokens as per the specification
- Rewrote the session storage interface from scratch so methods are more obvious
- Included a PDO driver which implements the storage interfaces so the library is more "get up and go"
- Further normalised the database structure so all sessions no longer contain infomation related to authorization grant (which may or may not be enabled)
- A session can have multiple associated access tokens
- Individual grants can have custom expire times for access tokens
- Authorization codes now have a TTL of 10 minutes by default (can be manually set)
- Refresh tokens now have a TTL of one week by default (can be manually set)
- The client credentials grant will no longer gives out refresh tokens as per the specification
## 1.0.8 (released 2013-03-18)
## [1.0.8] - 2013-03-18
* Fixed check for required state parameter
* Fixed check that user's credentials are correct in Password grant
- Fixed check for required state parameter
- Fixed check that user's credentials are correct in Password grant
## 1.0.7 (released 2013-03-04)
## [1.0.7] - 2013-03-04
* Added method `requireStateParam()`
* Added method `requireScopeParam()`
- Added method `requireStateParam()`
- Added method `requireScopeParam()`
## 1.0.6 (released 2013-02-22)
## [1.0.6] - 2013-02-22
* Added links to tutorials in the README
* Added missing `state` parameter request to the `checkAuthoriseParams()` method.
- Added links to tutorials in the README
- Added missing `state` parameter request to the `checkAuthoriseParams()` method.
## 1.0.5 (released 2013-02-21)
## [1.0.5] - 2013-02-21
* Fixed the SQL example for SessionInterface::getScopes()
- Fixed the SQL example for SessionInterface::getScopes()
## 1.0.3 (released 2013-02-20)
## [1.0.3] - 2013-02-20
* Changed all instances of the "authentication server" to "authorization server"
- Changed all instances of the "authentication server" to "authorization server"
## 1.0.2 (released 2013-02-20)
## [1.0.2] - 2013-02-20
* Fixed MySQL create table order
* Fixed version number in composer.json
- Fixed MySQL create table order
- Fixed version number in composer.json
## 1.0.1 (released 2013-02-19)
## [1.0.1] - 2013-02-19
* Updated AuthServer.php to use `self::getParam()`
- Updated AuthServer.php to use `self::getParam()`
## 1.0.0 (released 2013-02-15)
## 1.0.0 - 2013-02-15
* First major release
- First major release
[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...HEAD
[7.1.1]: https://github.com/thephpleague/oauth2-server/compare/7.1.0...7.1.1
[7.1.0]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...7.1.0
[7.0.0]: https://github.com/thephpleague/oauth2-server/compare/6.1.1...7.0.0
[6.1.1]: https://github.com/thephpleague/oauth2-server/compare/6.0.0...6.1.1
[6.1.0]: https://github.com/thephpleague/oauth2-server/compare/6.0.2...6.1.0
[6.0.2]: https://github.com/thephpleague/oauth2-server/compare/6.0.1...6.0.2
[6.0.1]: https://github.com/thephpleague/oauth2-server/compare/6.0.0...6.0.1
[6.0.0]: https://github.com/thephpleague/oauth2-server/compare/5.1.4...6.0.0
[5.1.4]: https://github.com/thephpleague/oauth2-server/compare/5.1.3...5.1.4
[5.1.3]: https://github.com/thephpleague/oauth2-server/compare/5.1.2...5.1.3
[5.1.2]: https://github.com/thephpleague/oauth2-server/compare/5.1.1...5.1.2
[5.1.1]: https://github.com/thephpleague/oauth2-server/compare/5.1.0...5.1.1
[5.1.0]: https://github.com/thephpleague/oauth2-server/compare/5.0.2...5.1.0
[5.0.3]: https://github.com/thephpleague/oauth2-server/compare/5.0.3...5.0.2
[5.0.2]: https://github.com/thephpleague/oauth2-server/compare/5.0.1...5.0.2
[5.0.1]: https://github.com/thephpleague/oauth2-server/compare/5.0.0...5.0.1
[5.0.0]: https://github.com/thephpleague/oauth2-server/compare/5.0.0-RC2...5.0.0
[5.0.0-RC2]: https://github.com/thephpleague/oauth2-server/compare/5.0.0-RC1...5.0.0-RC2
[5.0.0-RC1]: https://github.com/thephpleague/oauth2-server/compare/4.1.5...5.0.0-RC1
[4.1.5]: https://github.com/thephpleague/oauth2-server/compare/4.1.4...4.1.5
[4.1.4]: https://github.com/thephpleague/oauth2-server/compare/4.1.3...4.1.4
[4.1.3]: https://github.com/thephpleague/oauth2-server/compare/4.1.2...4.1.3
[4.1.2]: https://github.com/thephpleague/oauth2-server/compare/4.1.1...4.1.2
[4.1.1]: https://github.com/thephpleague/oauth2-server/compare/4.0.0...4.1.1
[4.1.0]: https://github.com/thephpleague/oauth2-server/compare/4.0.5...4.1.0
[4.0.5]: https://github.com/thephpleague/oauth2-server/compare/4.0.4...4.0.5
[4.0.4]: https://github.com/thephpleague/oauth2-server/compare/4.0.3...4.0.4
[4.0.3]: https://github.com/thephpleague/oauth2-server/compare/4.0.2...4.0.3
[4.0.2]: https://github.com/thephpleague/oauth2-server/compare/4.0.1...4.0.2
[4.0.1]: https://github.com/thephpleague/oauth2-server/compare/4.0.0...4.0.1
[4.0.0]: https://github.com/thephpleague/oauth2-server/compare/3.2.0...4.0.0
[3.2.0]: https://github.com/thephpleague/oauth2-server/compare/3.1.2...3.2.0
[3.1.2]: https://github.com/thephpleague/oauth2-server/compare/3.1.1...3.1.2
[3.1.1]: https://github.com/thephpleague/oauth2-server/compare/3.1.0...3.1.1
[3.1.0]: https://github.com/thephpleague/oauth2-server/compare/3.0.1...3.1.0
[3.0.1]: https://github.com/thephpleague/oauth2-server/compare/3.0.0...3.0.1
[3.0.0]: https://github.com/thephpleague/oauth2-server/compare/2.1.1...3.0.0
[2.1.1]: https://github.com/thephpleague/oauth2-server/compare/2.1.0...2.1.1
[2.1.0]: https://github.com/thephpleague/oauth2-server/compare/2.0.5...2.1.0
[2.0.5]: https://github.com/thephpleague/oauth2-server/compare/2.0.4...2.0.5
[2.0.4]: https://github.com/thephpleague/oauth2-server/compare/2.0.3...2.0.4
[2.0.3]: https://github.com/thephpleague/oauth2-server/compare/2.0.2...2.0.3
[2.0.2]: https://github.com/thephpleague/oauth2-server/compare/2.0.0...2.0.2
[2.0.0]: https://github.com/thephpleague/oauth2-server/compare/1.0.8...2.0.0
[1.0.8]: https://github.com/thephpleague/oauth2-server/compare/1.0.7...1.0.8
[1.0.7]: https://github.com/thephpleague/oauth2-server/compare/1.0.6...1.0.7
[1.0.6]: https://github.com/thephpleague/oauth2-server/compare/1.0.5...1.0.6
[1.0.5]: https://github.com/thephpleague/oauth2-server/compare/1.0.3...1.0.5
[1.0.3]: https://github.com/thephpleague/oauth2-server/compare/1.0.2...1.0.3
[1.0.2]: https://github.com/thephpleague/oauth2-server/compare/1.0.1...1.0.2
[1.0.1]: https://github.com/thephpleague/oauth2-server/compare/1.0.0...1.0.1

73
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,73 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
education, socio-economic status, nationality, personal appearance, race,
religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at andrew@noexceptions.io. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org

View File

@@ -1,22 +0,0 @@
# Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses, without explicit permission
* Other unethical or unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community in a direct capacity. Personal views, beliefs and values of individuals do not necessarily reflect those of the organisation or affiliated individuals and organisations.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)

View File

@@ -1,21 +1,16 @@
# PHP OAuth 2.0 Server
### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning:
### Security Notice
### Please upgrade to version `>=5.1.6` (backwards compatible) or `6.x` (one tiny breaking change) to fix some potential security vulnerabilities - [visit this page for more information](https://oauth2.thephpleague.com/v5-security-improvements/)
### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning:
[![Latest Version](http://img.shields.io/packagist/v/league/oauth2-server.svg?style=flat-square)](https://github.com/thephpleague/oauth2-server/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
[![Build Status](https://img.shields.io/travis/thephpleague/oauth2-server/master.svg?style=flat-square)](https://travis-ci.org/thephpleague/oauth2-server)
[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/thephpleague/oauth2-server.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth2-server/code-structure)
[![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/oauth2-server.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth2-server)
[![Total Downloads](https://img.shields.io/packagist/dt/league/oauth2-server.svg?style=flat-square)](https://packagist.org/packages/league/oauth2-server)
[![PHPStan](https://img.shields.io/badge/PHPStan-enabled-brightgreen.svg?style=flat-square)](https://github.com/phpstan/phpstan)
`league/oauth2-server` is a standards compliant implementation of an [OAuth 2.0](https://tools.ietf.org/html/rfc6749) authorization server written in PHP which makes working with OAuth 2.0 trivial. You can easily configure an OAuth 2.0 server to protect your API with access tokens, or allow clients to request new access tokens and refresh them.
It supports out of the box the following grants:
Out of the box it supports the following grants:
* Authorization code grant
* Implicit grant
@@ -36,25 +31,51 @@ This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](ht
The following versions of PHP are supported:
* PHP 5.6
* PHP 7.0
* PHP 7.1
* PHP 7.2
The `openssl` extension is also required.
All HTTP messages passed to the server should be [PSR-7 compliant](https://www.php-fig.org/psr/psr-7/). This ensures interoperability between other packages and frameworks.
## Installation
```
composer require league/oauth2-server
```
## Documentation
The library documentation can be found at [https://oauth2.thephpleague.com](https://oauth2.thephpleague.com).
The library documentation can be found at [https://oauth2.thephpleague.com](https://oauth2.thephpleague.com).
You can contribute to the documentation in the [gh-pages branch](https://github.com/thephpleague/oauth2-server/tree/gh-pages/).
## Testing
The library uses [PHPUnit](https://phpunit.de/) for unit tests and [PHPStan](https://github.com/phpstan/phpstan) for static analysis of the code.
```
vendor/bin/phpunit
vendor/bin/phpstan analyse -l 7 -c phpstan.neon src tests
```
## Continous Integration
We use [Travis CI](https://travis-ci.org/), [Scrutinizer](https://scrutinizer-ci.com/), and [StyleCI](https://styleci.io/) for continuous integration. Check out [our](https://github.com/thephpleague/oauth2-server/blob/master/.travis.yml) [configuration](https://github.com/thephpleague/oauth2-server/blob/master/.scrutinizer.yml) [files](https://github.com/thephpleague/oauth2-server/blob/master/.styleci.yml) if you'd like to know more.
## Community Integrations
* [Drupal](https://www.drupal.org/project/simple_oauth)
* [Laravel Passport](https://github.com/laravel/passport)
* [OAuth 2 Server for CakePHP 3](https://github.com/uafrica/oauth-server)
## Changelog
[See the project releases page](https://github.com/thephpleague/oauth2-server/releases)
See the [project changelog](https://github.com/thephpleague/oauth2-server/blob/master/CHANGELOG.md)
## Contributing
Please see [CONTRIBUTING.md](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) and [CONDUCT.md](https://github.com/thephpleague/oauth2-server/blob/master/CONDUCT.md) for details.
Contributions are always welcome. Please see [CONTRIBUTING.md](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](https://github.com/thephpleague/oauth2-server/blob/master/CODE_OF_CONDUCT.md) for details.
## Support
@@ -62,10 +83,6 @@ Bugs and feature request are tracked on [GitHub](https://github.com/thephpleague
If you have any questions about OAuth _please_ open a ticket here; please **don't** email the address below.
<a target='_blank' rel='nofollow' href='https://app.codesponsor.io/link/N2YMJcLBppt2Eg9E1jGu4gef/thephpleague/oauth2-server'>
<img alt='Sponsor' width='888' height='68' src='https://app.codesponsor.io/embed/N2YMJcLBppt2Eg9E1jGu4gef/thephpleague/oauth2-server.svg' />
</a>
## Commercial Support
If you would like help implementing this library into your existing platform, or would be interested in OAuth advice or training for you and your team please get in touch with [Glynde Labs](https://glyndelabs.com).
@@ -80,11 +97,12 @@ This package is released under the MIT License. See the bundled [LICENSE](https:
## Credits
This code is principally developed and maintained by [Andy Millington](https://twitter.com/Sephster), [Brian
Retterer](https://twitter.com/bretterer), and [Simon Hamp](https://twitter.com/simonhamp).
This code is principally developed and maintained by [Andy Millington](https://twitter.com/Sephster) and [Simon Hamp](https://twitter.com/simonhamp).
Between 2012 and 2017 this library was developed and maintained by [Alex Bilbie](https://alexbilbie.com/).
PHP OAuth 2.0 Server is one of many packages provided by The PHP League. To find out more, please visit [our website](https://thephpleague.com).
Special thanks to [all of these awesome contributors](https://github.com/thephpleague/oauth2-server/contributors).
Additional thanks go to the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source) for funding a security audit of this library.

View File

@@ -4,17 +4,19 @@
"homepage": "https://oauth2.thephpleague.com/",
"license": "MIT",
"require": {
"php": ">=5.6.0",
"php": ">=7.0.0",
"ext-openssl": "*",
"league/event": "^2.1",
"lcobucci/jwt": "^3.1",
"paragonie/random_compat": "^2.0",
"psr/http-message": "^1.0",
"lcobucci/jwt": "^3.2.2",
"psr/http-message": "^1.0.1",
"defuse/php-encryption": "^2.1"
},
"require-dev": {
"phpunit/phpunit": "^4.8.38 || ^5.7.21",
"zendframework/zend-diactoros": "^1.0"
"phpunit/phpunit": "^6.3 || ^7.0",
"zendframework/zend-diactoros": "^1.3.2",
"phpstan/phpstan": "^0.9.2",
"phpstan/phpstan-phpunit": "^0.9.4",
"phpstan/phpstan-strict-rules": "^0.9.0"
},
"repositories": [
{

View File

@@ -49,16 +49,18 @@ $app->get(
],
];
$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) {
for ($i = 0; $i < count($users); $i++) {
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) {
for ($i = 0; $i < count($users); $i++) {
for ($i = 0; $i < $totalUsers; $i++) {
unset($users[$i]['email']);
}
}

View File

@@ -41,7 +41,6 @@ $app = new App([
$privateKeyPath,
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
);
$server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen');
// Enable the implicit grant on the server with a token TTL of 1 hour
$server->enableGrantType(new ImplicitGrant(new \DateInterval('PT1H')));

View File

@@ -17,7 +17,7 @@ class ClientRepository implements ClientRepositoryInterface
/**
* {@inheritdoc}
*/
public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true)
public function getClientEntity($clientIdentifier, $grantType = null, $clientSecret = null, $mustValidateSecret = true)
{
$clients = [
'myawesomeapp' => [

10
phpstan.neon Normal file
View File

@@ -0,0 +1,10 @@
includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
- vendor/phpstan/phpstan-phpunit/strictRules.neon
- vendor/phpstan/phpstan-strict-rules/rules.neon
services:
-
class: LeagueTests\PHPStan\AbstractGrantExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension

View File

@@ -9,6 +9,7 @@
namespace League\OAuth2\Server;
use Defuse\Crypto\Key;
use League\Event\EmitterAwareInterface;
use League\Event\EmitterAwareTrait;
use League\OAuth2\Server\Exception\OAuthServerException;
@@ -17,6 +18,7 @@ use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
use League\OAuth2\Server\ResponseTypes\AbstractResponseType;
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use Psr\Http\Message\ResponseInterface;
@@ -67,7 +69,7 @@ class AuthorizationServer implements EmitterAwareInterface
private $scopeRepository;
/**
* @var string
* @var string|Key
*/
private $encryptionKey;
@@ -83,7 +85,7 @@ class AuthorizationServer implements EmitterAwareInterface
* @param AccessTokenRepositoryInterface $accessTokenRepository
* @param ScopeRepositoryInterface $scopeRepository
* @param CryptKey|string $privateKey
* @param string $encryptionKey
* @param string|Key $encryptionKey
* @param null|ResponseTypeInterface $responseType
*/
public function __construct(
@@ -190,7 +192,6 @@ class AuthorizationServer implements EmitterAwareInterface
if ($tokenResponse instanceof ResponseTypeInterface) {
return $tokenResponse->generateHttpResponse($response);
}
}
throw OAuthServerException::unsupportedGrantType();
@@ -207,7 +208,9 @@ class AuthorizationServer implements EmitterAwareInterface
$this->responseType = new BearerTokenResponse();
}
$this->responseType->setPrivateKey($this->privateKey);
if ($this->responseType instanceof AbstractResponseType === true) {
$this->responseType->setPrivateKey($this->privateKey);
}
$this->responseType->setEncryptionKey($this->encryptionKey);
return $this->responseType;

View File

@@ -14,7 +14,7 @@ namespace League\OAuth2\Server;
class CryptKey
{
const RSA_KEY_PATTERN =
'/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----\n)(.|\n)+(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)$/';
'/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----)\R.*(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)\R?$/s';
/**
* @var string
@@ -48,9 +48,9 @@ class CryptKey
if ($keyPermissionsCheck === true) {
// Verify the permissions of the key
$keyPathPerms = decoct(fileperms($keyPath) & 0777);
if (in_array($keyPathPerms, ['600', '660'], true) === false) {
if (in_array($keyPathPerms, ['400', '440', '600', '660'], true) === false) {
trigger_error(sprintf(
'Key file "%s" permissions are not correct, should be 600 or 660 instead of %s',
'Key file "%s" permissions are not correct, recommend changing to 600 or 660 instead of %s',
$keyPath,
$keyPathPerms
), E_USER_NOTICE);
@@ -73,7 +73,11 @@ class CryptKey
$tmpDir = sys_get_temp_dir();
$keyPath = $tmpDir . '/' . sha1($key) . '.key';
if (!file_exists($keyPath) && !touch($keyPath)) {
if (file_exists($keyPath)) {
return 'file://' . $keyPath;
}
if (!touch($keyPath)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException(sprintf('"%s" key file could not be created', $keyPath));
// @codeCoverageIgnoreEnd

View File

@@ -12,11 +12,12 @@
namespace League\OAuth2\Server;
use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;
trait CryptTrait
{
/**
* @var string
* @var string|Key
*/
protected $encryptionKey;
@@ -32,6 +33,10 @@ trait CryptTrait
protected function encrypt($unencryptedData)
{
try {
if ($this->encryptionKey instanceof Key) {
return Crypto::encrypt($unencryptedData, $this->encryptionKey);
}
return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey);
} catch (\Exception $e) {
throw new \LogicException($e->getMessage());
@@ -50,6 +55,10 @@ trait CryptTrait
protected function decrypt($encryptedData)
{
try {
if ($this->encryptionKey instanceof Key) {
return Crypto::decrypt($encryptedData, $this->encryptionKey);
}
return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey);
} catch (\Exception $e) {
throw new \LogicException($e->getMessage());
@@ -59,7 +68,7 @@ trait CryptTrait
/**
* Set the encryption key
*
* @param string $key
* @param string|Key $key
*/
public function setEncryptionKey($key = null)
{

View File

@@ -9,6 +9,7 @@
namespace League\OAuth2\Server\Entities;
use Lcobucci\JWT\Token;
use League\OAuth2\Server\CryptKey;
interface AccessTokenEntityInterface extends TokenInterface
@@ -18,7 +19,7 @@ interface AccessTokenEntityInterface extends TokenInterface
*
* @param CryptKey $privateKey
*
* @return string
* @return Token
*/
public function convertToJWT(CryptKey $privateKey);
}

View File

@@ -12,7 +12,7 @@ namespace League\OAuth2\Server\Entities;
interface AuthCodeEntityInterface extends TokenInterface
{
/**
* @return string
* @return string|null
*/
public function getRedirectUri();

View File

@@ -21,7 +21,7 @@ interface RefreshTokenEntityInterface
/**
* Set the token's identifier.
*
* @param $identifier
* @param mixed $identifier
*/
public function setIdentifier($identifier);

View File

@@ -21,7 +21,7 @@ interface TokenInterface
/**
* Set the token's identifier.
*
* @param $identifier
* @param mixed $identifier
*/
public function setIdentifier($identifier);
@@ -42,14 +42,14 @@ interface TokenInterface
/**
* Set the identifier of the user associated with the token.
*
* @param string|int $identifier The identifier of the user
* @param string|int|null $identifier The identifier of the user
*/
public function setUserIdentifier($identifier);
/**
* Get the token user's identifier.
*
* @return string|int
* @return string|int|null
*/
public function getUserIdentifier();

View File

@@ -12,6 +12,7 @@ namespace League\OAuth2\Server\Entities\Traits;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use Lcobucci\JWT\Token;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
@@ -23,7 +24,7 @@ trait AccessTokenTrait
*
* @param CryptKey $privateKey
*
* @return string
* @return Token
*/
public function convertToJWT(CryptKey $privateKey)
{

View File

@@ -17,7 +17,7 @@ trait AuthCodeTrait
protected $redirectUri;
/**
* @return string
* @return string|null
*/
public function getRedirectUri()
{

View File

@@ -11,7 +11,7 @@ namespace League\OAuth2\Server\Entities\Traits;
trait EntityTrait
{
/*
/**
* @var string
*/
protected $identifier;

View File

@@ -25,7 +25,7 @@ trait TokenEntityTrait
protected $expiryDateTime;
/**
* @var string|int
* @var string|int|null
*/
protected $userIdentifier;
@@ -77,7 +77,7 @@ trait TokenEntityTrait
/**
* Set the identifier of the user associated with the token.
*
* @param string|int $identifier The identifier of the user
* @param string|int|null $identifier The identifier of the user
*/
public function setUserIdentifier($identifier)
{
@@ -87,7 +87,7 @@ trait TokenEntityTrait
/**
* Get the token user's identifier.
*
* @return string|int
* @return string|int|null
*/
public function getUserIdentifier()
{

View File

@@ -33,6 +33,11 @@ class OAuthServerException extends \Exception
*/
private $redirectUri;
/**
* @var array
*/
private $payload;
/**
* Throw a new exception.
*
@@ -50,6 +55,33 @@ class OAuthServerException extends \Exception
$this->errorType = $errorType;
$this->hint = $hint;
$this->redirectUri = $redirectUri;
$this->payload = [
'error' => $errorType,
'message' => $message,
];
if ($hint !== null) {
$this->payload['hint'] = $hint;
}
}
/**
* Returns the current payload.
*
* @return array
*/
public function getPayload()
{
return $this->payload;
}
/**
* Updates the current payload.
*
* @param array $payload
*/
public function setPayload(array $payload)
{
$this->payload = $payload;
}
/**
@@ -60,7 +92,7 @@ class OAuthServerException extends \Exception
public static function unsupportedGrantType()
{
$errorMessage = 'The authorization grant type is not supported by the authorization server.';
$hint = 'Check the `grant_type` parameter';
$hint = 'Check that all required parameters have been provided';
return new static($errorMessage, 2, 'unsupported_grant_type', 400, $hint);
}
@@ -131,7 +163,7 @@ class OAuthServerException extends \Exception
/**
* Server error.
*
* @param $hint
* @param string $hint
*
* @return static
*
@@ -213,21 +245,15 @@ class OAuthServerException extends \Exception
*
* @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
*/
public function generateHttpResponse(ResponseInterface $response, $useFragment = false)
public function generateHttpResponse(ResponseInterface $response, $useFragment = false, $jsonOptions = 0)
{
$headers = $this->getHttpHeaders();
$payload = [
'error' => $this->getErrorType(),
'message' => $this->getMessage(),
];
if ($this->hint !== null) {
$payload['hint'] = $this->hint;
}
$payload = $this->getPayload();
if ($this->redirectUri !== null) {
if ($useFragment === true) {
@@ -243,7 +269,7 @@ class OAuthServerException extends \Exception
$response = $response->withHeader($header, $content);
}
$response->getBody()->write(json_encode($payload));
$response->getBody()->write(json_encode($payload, $jsonOptions));
return $response->withStatus($this->getHttpStatusCode());
}
@@ -268,13 +294,9 @@ class OAuthServerException extends \Exception
// include the "WWW-Authenticate" response header field
// matching the authentication scheme used by the client.
// @codeCoverageIgnoreStart
if ($this->errorType === 'invalid_client') {
$authScheme = 'Basic';
if (array_key_exists('HTTP_AUTHORIZATION', $_SERVER) !== false
&& strpos($_SERVER['HTTP_AUTHORIZATION'], 'Bearer') === 0
) {
$authScheme = 'Bearer';
}
if ($this->errorType === 'invalid_client' && array_key_exists('HTTP_AUTHORIZATION', $_SERVER) !== false) {
$authScheme = strpos($_SERVER['HTTP_AUTHORIZATION'], 'Bearer') === 0 ? 'Bearer' : 'Basic';
$headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"';
}
// @codeCoverageIgnoreEnd

View File

@@ -204,7 +204,7 @@ abstract class AbstractGrant implements GrantTypeInterface
throw OAuthServerException::invalidClient();
} elseif (
is_array($client->getRedirectUri())
&& in_array($redirectUri, $client->getRedirectUri()) === false
&& in_array($redirectUri, $client->getRedirectUri(), true) === false
) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
@@ -242,10 +242,6 @@ abstract class AbstractGrant implements GrantTypeInterface
$validScopes[] = $scope;
}
if (empty($validScopes)) {
throw OAuthServerException::invalidScope('', $redirectUri);
}
return $validScopes;
}
@@ -345,7 +341,7 @@ abstract class AbstractGrant implements GrantTypeInterface
*
* @param \DateInterval $accessTokenTTL
* @param ClientEntityInterface $client
* @param string $userIdentifier
* @param string|null $userIdentifier
* @param ScopeEntityInterface[] $scopes
*
* @throws OAuthServerException
@@ -390,7 +386,7 @@ abstract class AbstractGrant implements GrantTypeInterface
* @param \DateInterval $authCodeTTL
* @param ClientEntityInterface $client
* @param string $userIdentifier
* @param string $redirectUri
* @param string|null $redirectUri
* @param ScopeEntityInterface[] $scopes
*
* @throws OAuthServerException
@@ -411,7 +407,10 @@ abstract class AbstractGrant implements GrantTypeInterface
$authCode->setExpiryDateTime((new \DateTime())->add($authCodeTTL));
$authCode->setClient($client);
$authCode->setUserIdentifier($userIdentifier);
$authCode->setRedirectUri($redirectUri);
if ($redirectUri !== null) {
$authCode->setRedirectUri($redirectUri);
}
foreach ($scopes as $scope) {
$authCode->addScope($scope);

View File

@@ -134,6 +134,15 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
throw OAuthServerException::invalidRequest('code_verifier');
}
// 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) {
throw OAuthServerException::invalidRequest(
'code_verifier',
'Code Verifier must follow the specifications of RFC-7636.'
);
}
switch ($authCodePayload->code_challenge_method) {
case 'plain':
if (hash_equals($codeVerifier, $authCodePayload->code_challenge) === false) {
@@ -144,7 +153,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
case 'S256':
if (
hash_equals(
hash('sha256', strtr(rtrim(base64_encode($codeVerifier), '='), '+/', '-_')),
strtr(rtrim(base64_encode(hash('sha256', $codeVerifier, true)), '='), '+/', '-_'),
$authCodePayload->code_challenge
) === false
) {
@@ -167,6 +176,10 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $authCodePayload->user_id, $scopes);
$refreshToken = $this->issueRefreshToken($accessToken);
// Send events to emitter
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
// Inject tokens into response type
$responseType->setAccessToken($accessToken);
$responseType->setRefreshToken($refreshToken);
@@ -209,6 +222,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
$request,
$this->getServerParameter('PHP_AUTH_USER', $request)
);
if (is_null($clientId)) {
throw OAuthServerException::invalidRequest('client_id');
}
@@ -226,6 +240,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
}
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
if ($redirectUri !== null) {
if (
is_string($client->getRedirectUri())
@@ -235,23 +250,24 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
throw OAuthServerException::invalidClient();
} elseif (
is_array($client->getRedirectUri())
&& in_array($redirectUri, $client->getRedirectUri()) === false
&& in_array($redirectUri, $client->getRedirectUri(), true) === false
) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
} elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1
|| empty($client->getRedirectUri())
) {
|| empty($client->getRedirectUri())) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
} else {
$redirectUri = is_array($client->getRedirectUri())
? $client->getRedirectUri()[0]
: $client->getRedirectUri();
}
$scopes = $this->validateScopes(
$this->getQueryStringParameter('scope', $request, $this->defaultScope),
is_array($client->getRedirectUri())
? $client->getRedirectUri()[0]
: $client->getRedirectUri()
$redirectUri
);
$stateParameter = $this->getQueryStringParameter('state', $request);
@@ -260,7 +276,11 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
$authorizationRequest->setGrantTypeId($this->getIdentifier());
$authorizationRequest->setClient($client);
$authorizationRequest->setRedirectUri($redirectUri);
$authorizationRequest->setState($stateParameter);
if ($stateParameter !== null) {
$authorizationRequest->setState($stateParameter);
}
$authorizationRequest->setScopes($scopes);
if ($this->enableCodeExchangeProof === true) {
@@ -269,21 +289,24 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
throw OAuthServerException::invalidRequest('code_challenge');
}
if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeChallenge) !== 1) {
throw OAuthServerException::invalidRequest(
'code_challenge',
'The code_challenge must be between 43 and 128 characters'
);
}
$codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain');
if (in_array($codeChallengeMethod, ['plain', 'S256']) === false) {
if (in_array($codeChallengeMethod, ['plain', 'S256'], true) === false) {
throw OAuthServerException::invalidRequest(
'code_challenge_method',
'Code challenge method must be `plain` or `S256`'
);
}
// 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) {
throw OAuthServerException::invalidRequest(
'code_challenged',
'Code challenge must follow the specifications of RFC-7636.'
);
}
$authorizationRequest->setCodeChallenge($codeChallenge);
$authorizationRequest->setCodeChallengeMethod($codeChallengeMethod);
}

View File

@@ -11,6 +11,7 @@
namespace League\OAuth2\Server\Grant;
use League\OAuth2\Server\RequestEvent;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use Psr\Http\Message\ServerRequestInterface;
@@ -37,6 +38,9 @@ class ClientCredentialsGrant extends AbstractGrant
// Issue and persist access token
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, null, $finalizedScopes);
// Send event to emitter
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
// Inject access token into response type
$responseType->setAccessToken($accessToken);

View File

@@ -11,6 +11,7 @@
namespace League\OAuth2\Server\Grant;
use Defuse\Crypto\Key;
use League\Event\EmitterAwareInterface;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
@@ -136,7 +137,7 @@ interface GrantTypeInterface extends EmitterAwareInterface
/**
* Set the encryption key
*
* @param string|null $key
* @param string|Key|null $key
*/
public function setEncryptionKey($key = null);
}

View File

@@ -33,7 +33,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
/**
* @param \DateInterval $accessTokenTTL
* @param string $queryDelimiter
* @param string $queryDelimiter
*/
public function __construct(\DateInterval $accessTokenTTL, $queryDelimiter = '#')
{
@@ -144,23 +144,24 @@ class ImplicitGrant extends AbstractAuthorizeGrant
throw OAuthServerException::invalidClient();
} elseif (
is_array($client->getRedirectUri())
&& in_array($redirectUri, $client->getRedirectUri()) === false
&& in_array($redirectUri, $client->getRedirectUri(), true) === false
) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
} elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1
|| empty($client->getRedirectUri())
) {
|| empty($client->getRedirectUri())) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
} else {
$redirectUri = is_array($client->getRedirectUri())
? $client->getRedirectUri()[0]
: $client->getRedirectUri();
}
$scopes = $this->validateScopes(
$this->getQueryStringParameter('scope', $request, $this->defaultScope),
is_array($client->getRedirectUri())
? $client->getRedirectUri()[0]
: $client->getRedirectUri()
$redirectUri
);
// Finalize the requested scopes
@@ -176,7 +177,11 @@ class ImplicitGrant extends AbstractAuthorizeGrant
$authorizationRequest->setGrantTypeId($this->getIdentifier());
$authorizationRequest->setClient($client);
$authorizationRequest->setRedirectUri($redirectUri);
$authorizationRequest->setState($stateParameter);
if ($stateParameter !== null) {
$authorizationRequest->setState($stateParameter);
}
$authorizationRequest->setScopes($finalizedScopes);
return $authorizationRequest;

View File

@@ -59,6 +59,10 @@ class PasswordGrant extends AbstractGrant
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes);
$refreshToken = $this->issueRefreshToken($accessToken);
// Send events to emitter
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
// Inject tokens into response
$responseType->setAccessToken($accessToken);
$responseType->setRefreshToken($refreshToken);

View File

@@ -11,7 +11,6 @@
namespace League\OAuth2\Server\Grant;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\RequestEvent;
@@ -53,7 +52,7 @@ class RefreshTokenGrant extends AbstractGrant
// The OAuth spec says that a refreshed access token can have the original scopes or fewer so ensure
// the request doesn't include any new scopes
foreach ($scopes as $scope) {
if (in_array($scope->getIdentifier(), $oldRefreshToken['scopes']) === false) {
if (in_array($scope->getIdentifier(), $oldRefreshToken['scopes'], true) === false) {
throw OAuthServerException::invalidScope($scope->getIdentifier());
}
}
@@ -66,6 +65,10 @@ class RefreshTokenGrant extends AbstractGrant
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $oldRefreshToken['user_id'], $scopes);
$refreshToken = $this->issueRefreshToken($accessToken);
// Send events to emitter
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
// Inject tokens into response
$responseType->setAccessToken($accessToken);
$responseType->setRefreshToken($refreshToken);

View File

@@ -20,12 +20,12 @@ interface ClientRepositoryInterface extends RepositoryInterface
* Get a client.
*
* @param string $clientIdentifier The client's identifier
* @param string $grantType The grant type used
* @param null|string $grantType The grant type used (if sent)
* @param null|string $clientSecret The client's secret (if sent)
* @param bool $mustValidateSecret If true the client must attempt to validate the secret if the client
* is confidential
*
* @return ClientEntityInterface
*/
public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true);
public function getClientEntity($clientIdentifier, $grantType = null, $clientSecret = null, $mustValidateSecret = true);
}

View File

@@ -18,6 +18,9 @@ class RequestEvent extends Event
const USER_AUTHENTICATION_FAILED = 'user.authentication.failed';
const REFRESH_TOKEN_CLIENT_FAILED = 'refresh_token.client.failed';
const REFRESH_TOKEN_ISSUED = 'refresh_token.issued';
const ACCESS_TOKEN_ISSUED = 'access_token.issued';
/**
* @var ServerRequestInterface
*/

View File

@@ -53,14 +53,14 @@ class AuthorizationRequest
/**
* The redirect URI used in the request
*
* @var string
* @var string|null
*/
protected $redirectUri;
/**
* The state parameter on the authorization request
*
* @var string
* @var string|null
*/
protected $state;
@@ -159,7 +159,7 @@ class AuthorizationRequest
}
/**
* @return string
* @return string|null
*/
public function getRedirectUri()
{
@@ -167,7 +167,7 @@ class AuthorizationRequest
}
/**
* @param string $redirectUri
* @param string|null $redirectUri
*/
public function setRedirectUri($redirectUri)
{
@@ -175,7 +175,7 @@ class AuthorizationRequest
}
/**
* @return string
* @return string|null
*/
public function getState()
{

View File

@@ -63,7 +63,9 @@ class ResourceServer
$this->authorizationValidator = new BearerTokenValidator($this->accessTokenRepository);
}
$this->authorizationValidator->setPublicKey($this->publicKey);
if ($this->authorizationValidator instanceof BearerTokenValidator === true) {
$this->authorizationValidator->setPublicKey($this->publicKey);
}
return $this->authorizationValidator;
}

View File

@@ -11,6 +11,7 @@
namespace League\OAuth2\Server\ResponseTypes;
use Defuse\Crypto\Key;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use Psr\Http\Message\ResponseInterface;
@@ -37,7 +38,7 @@ interface ResponseTypeInterface
/**
* Set the encryption key
*
* @param string|null $key
* @param string|Key|null $key
*/
public function setEncryptionKey($key = null);
}

View File

@@ -19,15 +19,14 @@ use LeagueTests\Stubs\ClientEntity;
use LeagueTests\Stubs\ScopeEntity;
use LeagueTests\Stubs\StubResponseType;
use LeagueTests\Stubs\UserEntity;
use Psr\Http\Message\ResponseInterface;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response;
use Zend\Diactoros\ServerRequest;
use Zend\Diactoros\ServerRequestFactory;
class AuthorizationServerTest extends TestCase
{
const DEFAULT_SCOPE = 'basic';
public function setUp()
@@ -35,6 +34,7 @@ class AuthorizationServerTest extends TestCase
// Make sure the keys have the correct permissions.
chmod(__DIR__ . '/Stubs/private.key', 0600);
chmod(__DIR__ . '/Stubs/public.key', 0600);
chmod(__DIR__ . '/Stubs/private.key.crlf', 0600);
}
public function testRespondToRequestInvalidGrantType()
@@ -197,16 +197,16 @@ class AuthorizationServerTest extends TestCase
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
$server = new AuthorizationServer(
$clientRepositoryMock,
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/Stubs/private.key',
'file://' . __DIR__ . '/Stubs/public.key'
);

View File

@@ -1,29 +0,0 @@
<?php
namespace LeagueTests\Utils;
use LeagueTests\Stubs\CryptTraitStub;
use PHPUnit\Framework\TestCase;
class CryptTraitTest extends TestCase
{
/**
* @var \LeagueTests\Stubs\CryptTraitStub
*/
protected $cryptStub;
public function setUp()
{
$this->cryptStub = new CryptTraitStub;
}
public function testEncryptDecrypt()
{
$payload = 'alex loves whisky';
$encrypted = $this->cryptStub->doEncrypt($payload);
$plainText = $this->cryptStub->doDecrypt($encrypted);
$this->assertNotEquals($payload, $encrypted);
$this->assertEquals($payload, $plainText);
}
}

View File

@@ -34,6 +34,10 @@ class AuthCodeGrantTest extends TestCase
*/
protected $cryptStub;
const CODE_VERIFIER = 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk';
const CODE_CHALLENGE = 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM';
public function setUp()
{
$this->cryptStub = new CryptTraitStub;
@@ -185,7 +189,7 @@ class AuthCodeGrantTest extends TestCase
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code_challenge' => str_repeat('A', 43),
'code_challenge' => self::CODE_CHALLENGE,
]
);
@@ -686,7 +690,7 @@ class AuthCodeGrantTest extends TestCase
'grant_type' => 'authorization_code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code_verifier' => 'foobar',
'code_verifier' => self::CODE_VERIFIER,
'code' => $this->cryptStub->doEncrypt(
json_encode(
[
@@ -696,7 +700,7 @@ class AuthCodeGrantTest extends TestCase
'user_id' => 123,
'scopes' => ['foo'],
'redirect_uri' => 'http://foo/bar',
'code_challenge' => 'foobar',
'code_challenge' => self::CODE_VERIFIER,
'code_challenge_method' => 'plain',
]
)
@@ -757,7 +761,7 @@ class AuthCodeGrantTest extends TestCase
'grant_type' => 'authorization_code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code_verifier' => 'foobar',
'code_verifier' => self::CODE_VERIFIER,
'code' => $this->cryptStub->doEncrypt(
json_encode(
[
@@ -767,7 +771,7 @@ class AuthCodeGrantTest extends TestCase
'user_id' => 123,
'scopes' => ['foo'],
'redirect_uri' => 'http://foo/bar',
'code_challenge' => hash('sha256', strtr(rtrim(base64_encode('foobar'), '='), '+/', '-_')),
'code_challenge' => self::CODE_CHALLENGE,
'code_challenge_method' => 'S256',
]
)
@@ -1200,7 +1204,7 @@ class AuthCodeGrantTest extends TestCase
'grant_type' => 'authorization_code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code_verifier' => 'nope',
'code_verifier' => self::CODE_VERIFIER,
'code' => $this->cryptStub->doEncrypt(
json_encode(
[
@@ -1294,7 +1298,151 @@ class AuthCodeGrantTest extends TestCase
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M'));
} catch (OAuthServerException $e) {
$this->assertEquals($e->getHint(), 'Failed to verify `code_verifier`.');
$this->assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.');
}
}
public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInvalidChars()
{
$client = new ClientEntity();
$client->setIdentifier('foo');
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
$grant->setClientRepository($clientRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$grant->setEncryptionKey($this->cryptStub->getKey());
$request = new ServerRequest(
[],
[],
null,
'POST',
'php://input',
[],
[],
[],
[
'grant_type' => 'authorization_code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'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' => 'http://foo/bar',
'code_challenge' => self::CODE_CHALLENGE,
'code_challenge_method' => 'S256',
]
)
),
]
);
try {
/* @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.');
}
}
public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInvalidLength()
{
$client = new ClientEntity();
$client->setIdentifier('foo');
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
$grant->setClientRepository($clientRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$grant->setEncryptionKey($this->cryptStub->getKey());
$request = new ServerRequest(
[],
[],
null,
'POST',
'php://input',
[],
[],
[],
[
'grant_type' => 'authorization_code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'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' => 'http://foo/bar',
'code_challenge' => 'R7T1y1HPNFvs1WDCrx4lfoBS6KD2c71pr8OHvULjvv8',
'code_challenge_method' => 'S256',
]
)
),
]
);
try {
/* @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.');
}
}
@@ -1667,47 +1815,4 @@ class AuthCodeGrantTest extends TestCase
$grant->completeAuthorizationRequest(new AuthorizationRequest());
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
* @expectedExceptionCode 5
*/
public function testValidateAuthorizationRequestFailsWithoutScope()
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
$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 AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$request = new ServerRequest(
[],
[],
null,
null,
'php://input',
[],
[],
[
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
]
);
$grant->validateAuthorizationRequest($request);
}
}

View File

@@ -58,40 +58,4 @@ class ClientCredentialsGrantTest extends TestCase
$this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken());
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
* @expectedExceptionCode 5
*/
public function testRespondToRequestFailsWithoutScope()
{
$client = new ClientEntity();
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$scope = new ScopeEntity();
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$grant = new ClientCredentialsGrant();
$grant->setClientRepository($clientRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withParsedBody(
[
'client_id' => 'foo',
'client_secret' => 'bar',
]
);
$responseType = new StubResponseType();
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
}
}

View File

@@ -412,42 +412,4 @@ class ImplicitGrantTest extends TestCase
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$grant->completeAuthorizationRequest(new AuthorizationRequest());
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
* @expectedExceptionCode 5
*/
public function testValidateAuthorizationRequestFailsWithoutScope()
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$grant->setClientRepository($clientRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$request = new ServerRequest(
[],
[],
null,
null,
'php://input',
$headers = [],
$cookies = [],
$queryParams = [
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
]
);
$grant->validateAuthorizationRequest($request);
}
}

View File

@@ -174,50 +174,4 @@ class PasswordGrantTest extends TestCase
$responseType = new StubResponseType();
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
* @expectedExceptionCode 5
*/
public function testRespondToRequestFailsWithoutScope()
{
$client = new ClientEntity();
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock();
$userEntity = new UserEntity();
$userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity);
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$scope = new ScopeEntity();
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
$grant->setClientRepository($clientRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withParsedBody(
[
'client_id' => 'foo',
'client_secret' => 'bar',
'username' => 'foo',
'password' => 'bar',
]
);
$responseType = new StubResponseType();
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types = 1);
namespace LeagueTests\PHPStan;
use League\OAuth2\Server\Grant\AbstractGrant;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\NullType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
final class AbstractGrantExtension implements DynamicMethodReturnTypeExtension
{
public function getClass(): string
{
return AbstractGrant::class;
}
public function isMethodSupported(MethodReflection $methodReflection): bool
{
return in_array($methodReflection->getName(), [
'getRequestParameter',
'getQueryStringParameter',
'getCookieParameter',
], true);
}
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
{
return TypeCombinator::union(...[
new StringType(),
isset($methodCall->args[2]) ? $scope->getType($methodCall->args[2]->value) : new NullType(),
]);
}
}

View File

@@ -20,9 +20,7 @@ class BearerResponseTypeTest extends TestCase
{
public function testGenerateHttpResponse()
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$responseType = new BearerTokenResponse($accessTokenRepositoryMock);
$responseType = new BearerTokenResponse();
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$responseType->setEncryptionKey(base64_encode(random_bytes(36)));
@@ -64,9 +62,7 @@ class BearerResponseTypeTest extends TestCase
public function testGenerateHttpResponseWithExtraParams()
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$responseType = new BearerTokenResponseWithParams($accessTokenRepositoryMock);
$responseType = new BearerTokenResponseWithParams();
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$responseType->setEncryptionKey(base64_encode(random_bytes(36)));
@@ -111,10 +107,7 @@ class BearerResponseTypeTest extends TestCase
public function testDetermineAccessTokenInHeaderValidToken()
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false);
$responseType = new BearerTokenResponse($accessTokenRepositoryMock);
$responseType = new BearerTokenResponse();
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$responseType->setEncryptionKey(base64_encode(random_bytes(36)));
@@ -158,9 +151,8 @@ class BearerResponseTypeTest extends TestCase
public function testDetermineAccessTokenInHeaderInvalidJWT()
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false);
$responseType = new BearerTokenResponse($accessTokenRepositoryMock);
$responseType = new BearerTokenResponse();
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$responseType->setEncryptionKey(base64_encode(random_bytes(36)));
@@ -247,9 +239,7 @@ class BearerResponseTypeTest extends TestCase
public function testDetermineAccessTokenInHeaderInvalidToken()
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$responseType = new BearerTokenResponse($accessTokenRepositoryMock);
$responseType = new BearerTokenResponse();
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$responseType->setEncryptionKey(base64_encode(random_bytes(36)));
@@ -273,9 +263,7 @@ class BearerResponseTypeTest extends TestCase
public function testDetermineMissingBearerInHeader()
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$responseType = new BearerTokenResponse($accessTokenRepositoryMock);
$responseType = new BearerTokenResponse();
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$responseType->setEncryptionKey(base64_encode(random_bytes(36)));

1
tests/Stubs/.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
private.key.crlf text eol=crlf

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAtHYxRBYATiiyDFs3pEhFg6Ei/UiQEmolTaQyQK810xHY23+X
4elLl6HP1J09mefmJ3ZdIgjIOS6rfK1BQnZIvI+IkoC7+qpD92y9f48iL0tCYKsn
i1LFFjP0bESTGDe7XANifQPkp9GvKgJbu7h1/ac8x4CBSU0ZjtEvinQRsdYil6OM
MXLWGozbBy13X8G+Ganv2i1aPZ2B25GyrH6lVIEwztGrSYxUrFVL+8dHhONf6PYX
19gjdzxkXCYQy2AGMc1FevZmnpIqDNQwX7CUUXQ4TDJmiP0aBEni094gUhnRFUr9
dmGpLQcCb2i0WMh2K+swFk3EutDAJ+73LKoZ3QIDAQABAoIBADo8Tge3xd9zGIoO
QbV9MRmaPW1ZJk0a/fDBRQpEwGzdvIqQ8VWQ8Lj9GdF18LQi9s3TT5i1FtAFNIfm
bUHiY/SdqSgF7SOmIIrPB5QLf6+dbM0/TmKSklFo8L6jnohZK9g0q2rGf9p8Ozem
TS4WB9WUS3PiD1a1T8Mb1Gisri0h7rvI4TIkrcx6lUUCgphCZd2TWUhmE3YmybOg
4h855W685g/ydzjwB+5Y6CS3V6a78Z5Gb4df3l0XfqCWh/xzuNs7nIpRv8CE0vRE
vq9j/cVyKkzMjiagteJaisTCBkDmtAi9dEVL8uaSDoTJq1g+VOGuJxHUm31Pavqr
3RwvXS0CgYEA74jUqmzxAwr/uBWquIkfMg+hsKjJe3gsSAJIAPzcA9OkzZd9w/1R
P8C92N2UaDbCW7ZEl7ZzS+IO6nA4OcR98j77/nBk6cYykyVRkSaj01epz3bRApxc
R18e49MBftSMnI5R7lIJO/UAIRfd0rntX4jkdVAdn9s/VOvG8w4KQXcCgYEAwN3W
b3azSNYlj4CW8+t6qS/3JQ/qpPgVuqkqP9dQXC9O6VlV03pJIwFk2Ldjd7/eXT+0
hFVB3O71iECfet/1UgustlgFp5I4ZrPmYF/J1nGpx1KIE8P4d0qC8lODtdnsGAcU
+/vBjXinX7pWgM8e6LAJzqNUq/xal/wNY325dEsCgYB7J0+n+/ECToJhhApNbHq0
g2LvcCh/Ka8iqsGYeGkqMoOWDKBlxvUiIRe6y1nFJvpQquqjUfP/fM+Ma3wM/2B9
zzJChEjuBK/2BYblaQdr3rN47i7R99BeBaLdIZywN9m/mFC5hkYnJHUXjqzG7j8E
El7bjgBdMx1hrQOR7ZMKSwKBgQC2SXXBiBlPwEdj6I/EH06h1hnrR63pGim/cN/j
0ye62WPmHW+HH888bLbaNgqnRgtvayS85rAHlzst+pZBVqfRUgN9nJhLl2IDgAlA
EYj9TBTBtXmz5MdUSHKXguO73yrMUvU8bOi1Q9I+IipcOGboWmoKikke/LbLa4lj
/ZJpHQKBgQCuDanU+AJKgUQkkC2gHwT8quxPoRcFFErHp3iaDAwd5XsZJG9FHQUP
RkPE+JkSaj65byFLhCPHUayfk4Y4udHEy4cXiv2SxZNK8q1HwuFEvb7uFprj0hNs
14qJunONVt/jzswdwO5kGVbpGlHl7U0JABnTJP71fW/rE5SH4zYxqg==
-----END RSA PRIVATE KEY-----

View File

@@ -17,7 +17,7 @@ class CryptKeyTest extends TestCase
public function testKeyCreation()
{
$keyFile = __DIR__ . '/Stubs/public.key';
$keyFile = __DIR__ . '/../Stubs/public.key';
$key = new CryptKey($keyFile, 'secret');
$this->assertEquals('file://' . $keyFile, $key->getKeyPath());
@@ -26,7 +26,15 @@ class CryptKeyTest extends TestCase
public function testKeyFileCreation()
{
$keyContent = file_get_contents(__DIR__ . '/Stubs/public.key');
$keyContent = file_get_contents(__DIR__ . '/../Stubs/public.key');
$key = new CryptKey($keyContent);
$this->assertEquals(
'file://' . sys_get_temp_dir() . '/' . sha1($keyContent) . '.key',
$key->getKeyPath()
);
$keyContent = file_get_contents(__DIR__ . '/../Stubs/private.key.crlf');
$key = new CryptKey($keyContent);
$this->assertEquals(

View File

@@ -0,0 +1,41 @@
<?php
namespace LeagueTests\Utils;
use Defuse\Crypto\Key;
use LeagueTests\Stubs\CryptTraitStub;
use PHPUnit\Framework\TestCase;
class CryptTraitTest extends TestCase
{
protected $cryptStub;
protected function setUp()
{
$this->cryptStub = new CryptTraitStub();
}
public function testEncryptDecryptWithPassword()
{
$this->cryptStub->setEncryptionKey(base64_encode(random_bytes(36)));
$this->encryptDecrypt();
}
public function testEncryptDecryptWithKey()
{
$this->cryptStub->setEncryptionKey(Key::createNewRandomKey());
$this->encryptDecrypt();
}
private function encryptDecrypt()
{
$payload = 'alex loves whisky';
$encrypted = $this->cryptStub->doEncrypt($payload);
$plainText = $this->cryptStub->doDecrypt($encrypted);
$this->assertNotEquals($payload, $encrypted);
$this->assertEquals($payload, $plainText);
}
}