Compare commits

...

205 Commits

Author SHA1 Message Date
Alex Bilbie
26889abdd3 5.1.4 not 5.1.14 2017-07-01 18:37:54 +01:00
Alex Bilbie
0f19a6f41c Removed HHVM from .travis.yml 2017-07-01 18:34:53 +01:00
Alex Bilbie
4e996ab3f1 Updated README 2017-07-01 18:34:32 +01:00
Alex Bilbie
55f93f9400 Merge pull request #752 from thephpleague/analysis-qBDGNm
Apply fixes from StyleCI
2017-07-01 17:20:19 +01:00
Alex Bilbie
aee1779432 Apply fixes from StyleCI 2017-07-01 16:19:23 +00:00
Alex Bilbie
09c167ac43 Updated changelog and readme 2017-07-01 17:17:55 +01:00
Alex Bilbie
765a01021b Updated error message 2017-07-01 16:45:29 +01:00
Alex Bilbie
0706d66c76 Don’t pad and shuffle the payload if an encryption key has been set 2017-07-01 16:45:29 +01:00
Alex Bilbie
e123fe82d0 Ignore error_log messages in code coverage 2017-07-01 16:45:29 +01:00
Alex Bilbie
107cfc3678 Updated examples 2017-07-01 16:45:29 +01:00
Alex Bilbie
1954120c3d Use catch all exception 2017-07-01 16:45:29 +01:00
Alex Bilbie
dd5eee150d Ensure response type also has access to the encryption key 2017-07-01 16:45:29 +01:00
Alex Bilbie
76c1349181 Updated random_compat version 2017-07-01 16:45:29 +01:00
Alex Bilbie
1af4012df4 New property on AuthorizationServer to receive an encryption key which is used for future encryption/decryption instead of keybased encryption/decryption 2017-07-01 16:45:29 +01:00
Alex Bilbie
4a717104fa Shuffle the contents of the authorization code payload 2017-07-01 16:45:29 +01:00
Alex Bilbie
63530443fe Better error checking when saving a temporary key to ensure file was written successfully and the server is the exclusive mode 2017-07-01 16:44:57 +01:00
Alex Bilbie
2f8de3d230 Ensure the server is the exclusive owner of the key 2017-07-01 16:44:51 +01:00
Alex Bilbie
57d199b889 Stricter validation of code challenge value to match RFC 7636 requirements 2017-07-01 16:44:43 +01:00
Alex Bilbie
6bdd108145 Escape scope parameter to reduce pontential XSS vector 2017-07-01 16:43:31 +01:00
Alex Bilbie
bf7084a147 Merge pull request #709 from toby-griffiths/fix-refresh-token-ttl
Corrected DateInterval from 1 min to 1 month
2017-03-02 14:06:27 +00:00
Toby Griffiths
13c608b849 Corrected DateInterval from 1 min to 1 month 2017-03-01 13:08:42 +00:00
Alex Bilbie
ded7c1ed47 Mentioned PHP 7.1 support 2017-02-02 17:29:06 +00:00
Alex Bilbie
0da70c916a Merge pull request #690 from Jalle19/patch-1
Fix typo in the first README sentence
2016-12-23 07:42:23 +00:00
Sam Stenvall
90cb1bf012 Fix typo in the first README sentence 2016-12-23 00:30:54 +02:00
Alex Bilbie
b32204bd91 Merge pull request #682 from wilsonge/patch-1
Fix middleware example fatal error
2016-11-08 13:18:13 +00:00
George Wilson
518c1fcec5 Fix middleware example fatal error 2016-11-08 12:27:49 +00:00
Alex Bilbie
6946592553 Merge pull request #671 from duncan3dc/patch-1
[Travis] Test on PHP 7.1
2016-10-16 16:58:15 +01:00
Craig Duncan
25580b98b7 [Travis] Test on PHP 7.1 2016-10-16 16:48:44 +01:00
Alex Bilbie
f78dc2eca0 Updated README 2016-10-12 15:08:15 +01:00
Alex Bilbie
105b3116dc Merge pull request #669 from jeremykendall/fix/www-authenticate-header
Fix WWW-Authenticate entry in $headers array
2016-10-12 15:05:19 +01:00
jeremykendall
01677a564e Fix WWW-Authenticate entry in $headers array
In this context the header name should be the array key and the header
value the array value.
2016-10-11 22:27:24 -05:00
Alex Bilbie
4c4b0633b1 Merge pull request #668 from er0k/increase-ssl-key-length
Increase the recommended RSA key length from 1024 to 2048 bits
2016-10-11 14:27:16 +01:00
er0k
c4a75b2880 Increase the recommended RSA key length from 1024 to 2048 bits 2016-10-11 09:24:27 -04:00
Alex Bilbie
e091d48127 Changelog bump 2016-09-19 10:23:42 +01:00
Alex Bilbie
a798cfdc5d Merge pull request #656 from thephpleague/issue-650-fix
Fix for #650
2016-09-19 10:19:05 +01:00
Alex Bilbie
56e8d374fb Fix broken tests 2016-09-19 10:06:00 +01:00
Alex Bilbie
b1bfff7325 Don't pass in user because we don't know who user is 2016-09-19 10:05:55 +01:00
Alex Bilbie
32cde01ab2 Merge pull request #657 from thephpleague/analysis-86wPg4
Applied fixes from StyleCI
2016-09-13 15:19:56 +01:00
Alex Bilbie
11ccc305d0 Applied fixes from StyleCI 2016-09-13 14:17:09 +00:00
Alex Bilbie
d7df2f7e24 Fix for #650 2016-09-13 15:16:58 +01:00
Alex Bilbie
b8b92e5925 Changelog update 2016-07-26 15:42:03 -04:00
Alex Bilbie
0ebdcd2ab8 Merge pull request #614 from lookyman/better-tests
Improved tests
2016-07-25 12:17:28 -04:00
Alex Bilbie
9dee08ba3d Merge pull request #625 from juliangut/key-file
Key file auto-generation from string
2016-07-19 17:24:12 +01:00
Julián Gutiérrez
065ef5db99 CryptKey tests 2016-07-19 17:15:36 +02:00
Julián Gutiérrez
039537ebe2 touch! 2016-07-19 15:06:32 +02:00
Julián Gutiérrez
d8930af5ee key file auto-generation from string 2016-07-19 15:01:31 +02:00
Alex Bilbie
ada8d20be6 Merge pull request #624 from iansltx/bearer-token-response-params
Allow easy addition of custom fields to Bearer token response
2016-07-16 16:38:23 +01:00
Ian Littman
090c01d3d1 Allow easy addition of custom fields to Bearer token response 2016-07-16 10:27:33 -05:00
Alex Bilbie
4b6ba5859c Merge pull request #621 from pounard/master
while(array_shift()) makes the AuthorizationServer class configuratio…
2016-07-13 11:02:26 +01:00
Pierre Rineau
57323f38f7 while(array_shift()) makes the AuthorizationServer class configuration mutable 2016-07-13 12:03:05 +02:00
Alex Bilbie
46cd448a47 Merge pull request #616 from lookyman/phpdoc
Updated PHPDoc
2016-07-10 09:32:13 +01:00
Lukáš Unger
c874c59b9c Explicitly compare to false when checking not instanceof 2016-07-09 12:09:21 +02:00
Lukáš Unger
c3a4670c11 Updated PHPDoc 2016-07-09 02:01:53 +02:00
Lukáš Unger
17b6e2a207 tests: Fix missing redirect uri test, add redirect uri mismatch test 2016-07-08 16:04:14 +02:00
Lukáš Unger
54422a244f tests: AuthCodeGrantTest additional tests 2016-07-08 15:31:29 +02:00
Lukáš Unger
9899aa1f99 tests: ImplicitGrantTest additional tests 2016-07-08 15:30:59 +02:00
Lukáš Unger
32efd091a1 tests: use MockBuilder everywhere 2016-07-08 15:29:21 +02:00
Alex Bilbie
68e4b1d390 Updated changelog 2016-06-28 09:03:41 +01:00
Alex Bilbie
5ee1583c5b Ensure state is in access denied redirect. Fixes #597 2016-06-28 09:03:01 +01:00
Alex Bilbie
66de05a395 Merge pull request #605 from jfilla/master
Added catch Runtime exception when parsing JWT string
2016-06-28 08:49:29 +01:00
Alex Bilbie
df20da1235 Merge pull request #601 from zerkms/ISSUE-596_UNIQUE_ACCESS_TOKEN
Added a check for unique access token constraint violation
2016-06-28 08:48:38 +01:00
Alex Bilbie
7321622104 Merge pull request #606 from GrahamCampbell/patch-2
Allow random compat 2.x
2016-06-28 08:46:57 +01:00
Graham Campbell
84187041bd Allow random compat 2.x 2016-06-27 19:31:35 +01:00
Jakub Filla
9eccc40eb6 Added catch Runtime exception when parsing JWT string 2016-06-22 12:38:03 +02:00
Alex Bilbie
8b865cc523 Merge pull request #604 from iansltx/http-basic-from-header
Look at Authorization header directly for HTTP Basic auth checks
2016-06-22 08:42:30 +01:00
Ian Littman
9775c0076b Look at Authorization header directly for HTTP Basic auth check
Should allow for better compatibility with server implementations that aren't sitting on top of a standard SAPI (e.g. persistent web servers building a PSR-7 compatible request from a socket-received message).

One catch here is that I've seen Apache hijack the HTTP Authorization header in the past, though that would probably impact the other aspects of the server just as much as it would this, so I think that risk is manageable.

Added tests to cover all paths through the new code, so the AbstractGrant type still has 100% coverage :)

Did notice that, as of the latest versions of PHPUnit, the mock creation method is deprecated. Maybe that needs to be updated? Haven't checked to see whether the replacements are PHPUnit 4.8 compatible though, so maybe they need to stay in order to test on older PHP versions?
2016-06-21 21:08:38 -05:00
Ivan Kurnosov
b68ef973df Added a check for unique access token constraint violation 2016-06-20 20:19:03 +12:00
Alex Bilbie
c6e5f12a7c Merge pull request #600 from zerkms/ISSUE-598_REDUNDANT_IS_EXPIRED
Removed isExpired() from interfaces and traits
2016-06-17 09:14:38 +01:00
Ivan Kurnosov
6b88cbeb13 Removed isExpired() from interfaces and traits 2016-06-17 19:50:04 +12:00
Alex Bilbie
64a0fcb3a6 Updated examples. Fixes #589 2016-06-02 09:35:27 +01:00
Alex Bilbie
78dbb267ed Merge pull request #578 from juliangut/master
unify middleware exception responses
2016-05-12 09:53:42 +01:00
Julián Gutiérrez
22e6a350dd unify middleware exception responses 2016-05-11 14:13:58 +02:00
Alex Bilbie
c0936cc320 Updated commercial support statement 2016-05-10 13:23:56 +01:00
Alex Bilbie
bb82651bec First commit of update changelog 2016-05-10 08:10:50 +01:00
Alex Bilbie
599c9aba75 Added indigophp/hash-compat to suggest and require dev for PHP 5.5 support 2016-05-06 15:23:57 +01:00
Alex Bilbie
4c6c189dff Added a list of supported RFCs 2016-05-06 15:23:25 +01:00
Alex Bilbie
8e8aed1a50 Implemented RFC7636. Fixes #574 2016-05-06 15:23:16 +01:00
Alex Bilbie
4a4f4fe2d7 Added commercial support section to README 2016-05-04 09:17:38 +01:00
Alex Bilbie
6b18a9441a Updated changelog 2016-05-04 09:13:20 +01:00
Alex Bilbie
44ff7b33a1 Merge branch 'master' of github.com:thephpleague/oauth2-server 2016-05-04 09:10:11 +01:00
Alex Bilbie
db055f790d Revert "Remove redundant parameters in example" #553
This reverts commit 9a93dca05c.
2016-05-04 09:10:05 +01:00
Alex Bilbie
d1bc4848c8 Revert "Remove redundant parameters in example"
This reverts commit 9a93dca05c.
2016-05-04 09:07:50 +01:00
Alex Bilbie
cf63403585 Merge branch 'master' of github.com:thephpleague/oauth2-server 2016-05-04 08:56:04 +01:00
Alex Bilbie
cdf43e498e Use constant for event name instead of explicit string. Fixes #563 2016-05-04 08:55:57 +01:00
Alex Bilbie
a12fc98b0d Merge pull request #569 from ismailbaskin/patch-2
Correct wrong phpdoc
2016-05-04 08:45:58 +01:00
Alex Bilbie
019d285235 Merge pull request #570 from Themodem/master
Fixed typo in exception string
2016-05-04 08:45:37 +01:00
Lee
0bb968f413 Fixed typo in exception string 2016-05-04 15:13:48 +08:00
ismail BASKIN
88b19ad2d0 Correct wrong phpdoc 2016-05-04 00:54:36 +03:00
Alex Bilbie
6856699cab Merge pull request #564 from ismailbaskin/patch-1
Remove unused request property
2016-04-30 12:16:12 +01:00
ismail BASKIN
72cd9a62e1 Remove unused request property 2016-04-30 05:08:28 +03:00
Alex Bilbie
acf262f879 Merge pull request #553 from markinjapan/patch-1
Remove redundant parameters in getNewToken()
2016-04-27 20:58:29 +01:00
Alex Bilbie
5241309bdb Fixes #560 2016-04-27 20:53:12 +01:00
Mark
9a93dca05c Remove redundant parameters in example 2016-04-20 16:52:54 +09:00
Mark
a6b7a5cedc Remove use of redundant parameters 2016-04-20 16:52:36 +09:00
Mark
78b6bddc4d Remove redundant parameters 2016-04-20 16:29:37 +09:00
Alex Bilbie
14b6761c0f Changelog update 2016-04-19 10:28:20 +01:00
Alex Bilbie
7c61922f07 Merge pull request #551 from ivyhjk/patch-1
wrong comment "month"
2016-04-19 09:53:17 +01:00
ivyhjk
20535ad95b wrong comment "month" 2016-04-18 18:08:27 -03:00
Alex Bilbie
e885114714 Improved examples 2016-04-18 12:23:21 +01:00
Alex Bilbie
f80d0d39a4 Updated .scrutenizer.yml 2016-04-18 12:23:13 +01:00
Alex Bilbie
7bfd5b7d0d Added abstract methods for required methods 2016-04-18 12:22:15 +01:00
Alex Bilbie
143a2e32f7 Client may return an array of redirect URIs 2016-04-18 12:21:42 +01:00
Alex Bilbie
8f418cff08 Added missing state parameter in redirect response 2016-04-18 12:19:54 +01:00
Alex Bilbie
fcec1f3442 Cody tidy 2016-04-18 12:19:36 +01:00
Alex Bilbie
46e7eef14e Client could potentially return an array of redirect URIs 2016-04-18 12:12:36 +01:00
Alex Bilbie
51f44fdf17 Code tidy 2016-04-18 12:12:06 +01:00
Alex Bilbie
f8b2e80ef3 Removed unnecessary parameter usage 2016-04-18 12:10:57 +01:00
Alex Bilbie
7045785d89 Spelling fix 2016-04-18 08:41:00 +01:00
Alex Bilbie
301ddc53c7 Updated changelog 2016-04-18 08:40:34 +01:00
Alex Bilbie
2a6f900323 Updated examples 2016-04-18 08:32:58 +01:00
Alex Bilbie
fb8f47e868 Added $mustValidateSecret parameter to ClientRepositoryInterface:: getClientEntity(). Fixes #550 2016-04-18 08:32:49 +01:00
Alex Bilbie
5b192b3548 Updated README 2016-04-17 13:32:20 +01:00
Alex Bilbie
bf55ce1f73 Merge branch 'V5-WIP'
Conflicts:
	.travis.yml
	CHANGELOG.md
	composer.json
	examples/relational/Storage/AccessTokenStorage.php
	examples/relational/api.php
	src/AbstractServer.php
	src/AuthorizationServer.php
	src/Entity/AuthCodeEntity.php
	src/Exception/InvalidGrantException.php
	src/Exception/InvalidRequestException.php
	src/Exception/InvalidScopeException.php
	src/Exception/OAuthException.php
	src/Exception/ServerErrorException.php
	src/Exception/UnsupportedGrantTypeException.php
	src/Exception/UnsupportedResponseTypeException.php
	src/Grant/AuthCodeGrant.php
	src/Grant/RefreshTokenGrant.php
	src/ResourceServer.php
	src/Storage/AccessTokenInterface.php
	src/Storage/AuthCodeInterface.php
	src/Storage/ClientInterface.php
	src/Storage/RefreshTokenInterface.php
	src/Storage/ScopeInterface.php
	src/Storage/SessionInterface.php
	src/TokenType/Bearer.php
	src/TokenType/MAC.php
	tests/unit/Grant/RefreshTokenGrantTest.php
	tests/unit/TokenType/MacTest.php
2016-04-17 13:21:22 +01:00
Alex Bilbie
4942585f4f Updated changelog 2016-04-17 13:18:12 +01:00
Alex Bilbie
1575128162 Merge pull request #549 from thephpleague/analysis-8L3Emw
Applied fixes from StyleCI
2016-04-17 13:07:29 +01:00
Alex Bilbie
78c2067698 Merge pull request #548 from thephpleague/analysis-z9mQxo
Applied fixes from StyleCI
2016-04-17 13:07:15 +01:00
Alex Bilbie
f765f134c9 Applied fixes from StyleCI 2016-04-17 08:07:03 -04:00
Alex Bilbie
257318e524 Merge pull request #547 from lookyman/scope-fixes
Fix scope loading in grants
2016-04-17 13:06:57 +01:00
Alex Bilbie
77737e7894 Applied fixes from StyleCI 2016-04-17 08:06:17 -04:00
Alex Bilbie
f007e25070 Added copyright docblocks 2016-04-17 13:06:05 +01:00
Alex Bilbie
25c2e9b31b Code tidy client_credentials 2016-04-17 13:00:49 +01:00
Alex Bilbie
6ed9cbf701 Class rename fixes 2016-04-17 12:54:49 +01:00
Alex Bilbie
7c35778316 Added tests for resource server middleware 2016-04-17 12:54:39 +01:00
Alex Bilbie
f6f39698d9 Renamed Server to AuthorizationServer 2016-04-17 12:54:25 +01:00
Lukáš Unger
3904767873 Fix scope loading in grants 2016-04-17 13:50:56 +02:00
Alex Bilbie
c3a7c418da Updated composer.json 2016-04-17 12:43:25 +01:00
Alex Bilbie
af5a06098b Removed --no-dev statement 2016-04-17 12:43:13 +01:00
Alex Bilbie
6205611a71 Removed unused methods 2016-04-17 12:42:42 +01:00
Alex Bilbie
9f3648039b Use resource server instead 2016-04-17 12:41:28 +01:00
Alex Bilbie
08c356a1e1 Added ResourceServer class 2016-04-17 12:33:29 +01:00
Alex Bilbie
70e32ce9bf Updated changelog 2016-04-17 12:23:38 +01:00
Alex Bilbie
94a1c18fa9 Implict grant does not return return refresh tokens 2016-04-17 12:12:49 +01:00
Alex Bilbie
88b01b792a Clarified example 2016-04-12 20:23:05 +01:00
Alex Bilbie
0178a837d4 Merge pull request #538 from lucadegasperi/patch-4
Update AbstractGrant.php
2016-04-11 15:28:26 +01:00
Alex Bilbie
2025bd6a30 Merge pull request #540 from vinkla/patch-2
Allow phpunit 5.0
2016-04-11 15:28:13 +01:00
Alex Bilbie
e7f18911f3 Merge pull request #539 from vinkla/patch-1
Add 5.5.9 to travis
2016-04-11 15:27:47 +01:00
Vincent Klaiber
8e8ac35dcb Allow phpunit 5.0
We can allow phpunit 5.0
2016-04-11 16:23:24 +02:00
Vincent Klaiber
16ed4ea51c Add 5.5.9 to travis
We should test against 5.5.9 as well since that is the lowest requirement.
2016-04-11 16:22:20 +02:00
Luca Degasperi
de635f826f Update AbstractGrant.php
The hint is not necessary since it gets created by the exception with the parameter.
2016-04-11 15:59:47 +02:00
Alex Bilbie
3e8577f889 Merge pull request #536 from Bobselp/V5-WIP
less verbose exceptions for RefreshTokenGrant
2016-04-11 08:24:31 +01:00
Alex Bilbie
525b9b3d3e Merge pull request #537 from ivyhjk/V5-WIP
Update refresh token expire_time
2016-04-11 08:24:07 +01:00
ivyhjk
f7413c2f15 Update BearerTokenResponse.php 2016-04-10 19:05:32 -03:00
Bobselp
6e583fdf8a less verbose exceptions for RefreshTokenGrant
For the LogicException you could also use `throw OAuthServerException::invalidRequest('refresh_token', 'Cannot decrypt the authorization code');`, to get the exact same error AuthCodeGrant-php throws if decryption of `code` fails there.
The second error hint provides information which doesn't help users of the API, although it is next to impossible to trigger this error due to the encryption.
2016-04-10 22:19:42 +02:00
Alex Bilbie
1de13cf892 Removed .travis.yml after_build stuff 2016-03-24 19:30:14 +00:00
Alex Bilbie
1ad44d1ce0 Merge pull request #483 from mikicaivosevic/master
Bug fix
2016-03-23 11:27:41 +00:00
Mikica Ivosevic
b480373249 bug fix 2016-03-23 12:08:45 +01:00
Alex Bilbie
3ad97b4ef4 Merge pull request #434 from bitExpert/fix/getScopeDelimiterDockblock
Docblock stated that "," is the default scope delimiter but it is " ".
2016-01-20 11:33:36 +00:00
Stephan Hochdörfer
0490736861 Docblock stated that "," is the default scope delimiter but it is " ". 2016-01-18 22:37:39 +01:00
Alex Bilbie
abaf399f5f Merge pull request #429 from Bobselp/master
Add MAC Authentication to OAuthException->getHttpHeaders
2016-01-17 14:57:45 +00:00
Bobselp
55c8df8312 fix for thephpleague/oauth2-server#389 2016-01-17 15:50:34 +01:00
Alex Bilbie
c5db707e69 Updated changelog 2016-01-04 19:56:12 +00:00
Alex Bilbie
ed7f78179a Merge pull request #412 from derrabus/symfony-3
Allow Symfony 3.0
2015-12-20 20:38:02 +00:00
Alexander M. Turek
6e92239dd7 Allow Symfony 3.0. 2015-12-11 15:24:13 +01:00
Alex Bilbie
f5d731def9 Updated changelog 2015-11-13 17:52:27 +00:00
Alex Bilbie
03815cec6d Merge pull request #388 from m4tthumphrey/master
Added priority argument
2015-10-13 11:21:52 +01:00
Matt Humphrey
c71dc47459 Added priority argument 2015-10-13 11:16:49 +01:00
Alex Bilbie
3bcd8fc3f8 Removed runs and increased timeout 2015-09-26 11:26:17 +01:00
Alex Bilbie
db6d4e0dc6 Only send data to Scrutinizer only for PHP 5.6 2015-09-26 11:25:26 +01:00
Alex Bilbie
f19189a999 Merge pull request #345 from mpipet/master
Expose parameter passed to exceptions
2015-09-04 08:38:35 +01:00
Alex Bilbie
ec9c91cc11 Update .scrutinizer.yml 2015-09-04 08:37:12 +01:00
Alex Bilbie
c3457107ee Merge pull request #370 from michaelhogg/fix-bug-hmac-encoding
Fix bug: hash_hmac() should output raw binary data, not hexits
2015-09-04 08:36:33 +01:00
Alex Bilbie
a9f61fd3ed Merge pull request #377 from starJammer/master
AuthCodeGrant and RefreshTokenGrant don't require client_secret
2015-09-04 08:29:39 +01:00
Alex Bilbie
b78d8ca1d8 Merge pull request #364 from apollopy/master
Too idealistic. Should allow the client and server have some time difference.
2015-09-04 08:28:14 +01:00
Jerry Saravia
8f82e8ef86 Added test for setRequireClientSecret 2015-09-03 23:16:09 -04:00
Jerry Saravia
d88e01c7dd Making client secret optional during refresh and access token requsets. 2015-09-03 22:50:35 -04:00
Michael Hogg
d21374fb0b Merge remote-tracking branch 'thephpleague/master' into fix-bug-hmac-encoding 2015-09-02 09:50:46 +01:00
Alex Bilbie
31e5f4d33c Merge pull request #368 from apollopy/mac_token_only_header
Mac token only get to header
2015-09-01 14:33:58 +01:00
Alex Bilbie
a773405adf Merge pull request #369 from joaopramos/mac-refresh-tokens
Mac refresh tokens
2015-09-01 14:32:45 +01:00
Alex Bilbie
ccc845b195 Merge pull request #371 from michaelhogg/fix-bug-base64-regex
Fix bug: regex doesn't match all Base64 characters
2015-09-01 14:30:38 +01:00
Alex Bilbie
21cd917892 Merge pull request #372 from michaelhogg/fix-bug-request-uri
Fix bug: incorrect signature parameter
2015-09-01 14:29:41 +01:00
Michael Hogg
a2c418ee07 Fix bug: incorrect signature parameter 2015-08-28 16:41:12 +01:00
Michael Hogg
b220368583 Fix bug: regex doesn't match all Base64 characters 2015-08-28 14:01:22 +01:00
Michael Hogg
2d26c38d6c Update unit test: testDetermineAccessTokenInHeaderValid() 2015-08-28 13:11:20 +01:00
Michael Hogg
eeaa68400f Fix bug: hash_hmac() should output raw binary data, not hexits 2015-08-28 12:46:53 +01:00
joao
56c73d2427 ISSUE #356: added the refresh token to the mac token type response 2015-08-28 10:40:13 +00:00
joao
f632fcc997 ISSUE #356: added the refresh token to the mac token type response 2015-08-28 10:38:45 +00:00
ApolloPY
618d84ddcf Mac token only get to header 2015-08-22 01:47:59 +08:00
apollopy
ace42e89e0 change to 300 seconds 2015-08-21 20:02:42 +08:00
ApolloPY
c496df98e4 Too idealistic. Should allow the client and server have some time difference. 2015-08-21 17:17:51 +08:00
Alex Bilbie
2496653968 Merge pull request #342 from gaomd/master
Fix #328, strict check Bearer token
2015-08-21 09:00:02 +01:00
Alex Bilbie
abf66ef9c8 Merge pull request #346 from Korri/Korri-patch-removed-duplicate-routing
Removed duplicate routing setup code
2015-08-21 08:59:35 +01:00
Alex Bilbie
4b9ec488f4 Merge pull request #352 from daveblake/master
Fix typo in docblock
2015-07-13 21:55:43 +01:00
DavidBlake
726d879607 Fix typo in docblock 2015-06-18 13:27:58 +01:00
Mathieu Pipet
b256195421 Expose parameter passed to exceptions 2015-06-09 17:42:25 +02:00
Mathieu Pipet
c84ea1ea62 Expose parameter passed to exceptions 2015-06-09 17:30:13 +02:00
Hugo Vacher
16685ccde4 Removed duplicate routing setup code 2015-06-08 15:50:55 -04:00
Mengdi Gao
7934c7bb53 Fix #328, strict check Bearer token 2015-06-01 21:36:44 +08:00
Alex Bilbie
c174b6fc65 Merge pull request #341 from thephpleague/philsturgeon-patch-1
Added integration list to readme
2015-05-20 13:22:42 +01:00
Phil Sturgeon
75ced70248 Added integration list to readme
I figure it's a good idea to let people know where they can find their bridge packages to save em looking or building their own framework specific nonsense.
2015-05-17 13:33:50 -04:00
Alex Bilbie
5b7fdaeece Merge pull request #330 from jakeasmith/patch-1
Just a typo fix
2015-04-16 17:48:00 +01:00
Jake A. Smith
430a752315 Just a typo fix 2015-04-16 10:41:37 -05:00
Alex Bilbie
810544ec0a Changelog update 2015-03-22 23:32:45 +00:00
Alex Bilbie
34a6b66b8c More .travis.yml updates 2015-03-22 23:19:36 +00:00
Alex Bilbie
61738a7fe2 Added fast_finish: true to .travis.yml 2015-03-22 23:13:41 +00:00
Alex Bilbie
51184259d1 Merge pull request #323 from rdohms/interface-docs
Updated Interface Docs
2015-03-20 11:43:47 +00:00
rdohms
b21de11429 Updated Interface Docs
Made phpdocs match expectations like null when not found and using array notation for indicating array of <object>
2015-03-20 11:33:03 +01:00
Alex Bilbie
cf6e86c9d4 Merge pull request #319 from Fuxy22/patch-1
Fixed missing session scope
2015-03-13 11:03:05 +00:00
Alex Bilbie
f6fdbc7142 Added PHP 7.0 testing 2015-03-03 22:00:47 +00:00
Norbert Fuksz
7f7f45662a Fixed missing session scope
Close #297
2015-03-02 17:47:48 +00:00
Alex Bilbie
f92a68cc72 Merge branch 'master' of github.com:thephpleague/oauth2-server 2015-02-22 19:47:44 +00:00
Alex Bilbie
295d8ffa24 Updated league/event to ~2.1. Fixes #311 2015-02-22 19:47:27 +00:00
Alex Bilbie
3d08140651 Merge pull request #300 from vvllaadd/patch-2
Probable bug
2015-02-22 19:45:14 +00:00
Alex Bilbie
ec8a8393ee Merge pull request #310 from ismailbaskin/master
typo
2015-02-10 10:03:53 +00:00
Ismail BASKIN
3869b8f406 typo 2015-02-10 10:28:57 +02:00
Vlad
d43391564c Probable bug
AccessTokenStorage::delete should delete the token, not the scope associated with the token
2015-01-15 14:20:54 +01:00
83 changed files with 3327 additions and 1163 deletions

View File

@@ -2,7 +2,6 @@ filter:
excluded_paths:
- tests/*
- vendor/*
- examples/*
checks:
php:
code_rating: true
@@ -21,8 +20,7 @@ checks:
fix_doc_comments: true
tools:
external_code_coverage:
timeout: 600
runs: 3
timeout: 1800
php_code_coverage: false
php_code_sniffer:
config:
@@ -34,4 +32,4 @@ tools:
excluded_dirs: [vendor, tests, examples]
php_cpd:
enabled: true
excluded_dirs: [vendor, tests, examples]
excluded_dirs: [vendor, tests, examples]

View File

@@ -7,10 +7,11 @@ cache:
- vendor
php:
- 5.5.9
- 5.5
- 5.6
- 7.0
- hhvm
- 7.1
install:
- travis_retry composer install --no-interaction --prefer-source
@@ -21,4 +22,3 @@ script:
branches:
only:
- master
- V5-WIP

View File

@@ -1,6 +1,101 @@
# Changelog
## 5.0.0-RC2 (released 2016-04-XX)
## 5.1.4 (released 2017-07-01)
* 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.3 (released 2016-10-12)
* Fixed WWW-Authenticate header (Issue #669)
* Increase the recommended RSA key length from 1024 to 2048 bits (Issue #668)
## 5.1.2 (released 2016-09-19)
* Fixed `finalizeScopes` call (Issue #650)
## 5.1.1 (released 2016-07-26)
* 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.0 (released 2016-06-28)
* 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.0.3 (released 2016-05-04)
* 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.2 (released 2016-04-18)
* `state` parameter is now correctly returned after implicit grant authorization
* Small code and docblock improvements
## 5.0.1 (released 2016-04-18)
* Fixes an issue (#550) whereby it was unclear whether or not to validate a client's secret during a request.
## 5.0.0 (released 2016-04-17)
Version 5 is a complete code rewrite.
* JWT support
* PSR-7 support
* Improved exception errors
* Replace all occurrences of the term "Storage" with "Repository"
* Simplify repositories
* Entities conform to interfaces and use traits
* Auth code grant updated
* Allow support for public clients
* Add support for #439
* Client credentials grant updated
* Password grant updated
* Allow support for public clients
* Refresh token grant updated
* Implement Implicit grant
* Bearer token output type
* Remove MAC token output type
* Authorization server rewrite
* Resource server class moved to PSR-7 middleware
* Tests
* Much much better documentation
Changes since RC2:
* Renamed Server class to AuthorizationServer
* Added ResourceServer class
* Run unit tests again PHP 5.5.9 as it's the minimum supported version
* Enable PHPUnit 5.0 support
* Improved examples and documentation
* Make it clearer that the implicit grant doesn't support refresh tokens
* Improved refresh token validation errors
* Fixed refresh token expiry date
## 5.0.0-RC2 (released 2016-04-10)
Changes since RC1:
* Allow multiple client redirect URIs (Issue #511)
* Remove unused mac token interface (Issue #503)

View File

@@ -1,5 +1,11 @@
# 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.4` (backwards compatible) or `6.x` (one tiny breaking change) to fix some potential security vulnerabilities
### :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)
@@ -7,7 +13,7 @@
[![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)
`league/oauth2-server` is a a standards compliant implementation of an [OAuth 2.0](https://tools.ietf.org/html/rfc6749) authorization server written in PHP which makes working with OAuth 2.0 trivial. You can easily configure an OAuth 2.0 server to protect your API with access tokens, or allow clients to request new access tokens and refresh them.
`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:
@@ -17,6 +23,13 @@ It supports out of the box the following grants:
* Resource owner password credentials grant
* Refresh grant
The following RFCs are implemented:
* [RFC6749 "OAuth 2.0"](https://tools.ietf.org/html/rfc6749)
* [RFC6750 " The OAuth 2.0 Authorization Framework: Bearer Token Usage"](https://tools.ietf.org/html/rfc6750)
* [RFC7519 "JSON Web Token (JWT)"](https://tools.ietf.org/html/rfc7519)
* [RFC7636 "Proof Key for Code Exchange by OAuth Public Clients"](https://tools.ietf.org/html/rfc7636)
This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](https://twitter.com/alexbilbie).
## Requirements
@@ -26,6 +39,7 @@ The following versions of PHP are supported:
* PHP 5.5 (>=5.5.9)
* PHP 5.6
* PHP 7.0
* PHP 7.1
* HHVM
The `openssl` extension is also required.
@@ -49,9 +63,13 @@ 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.
## Commercial Support
If you would like help implementing this library into your existing platform, or would be interested in OAuth advice or training for you and your team please get in touch with [Glynde Labs](https://glyndelabs.com).
## Security
If you discover any security related issues, please email hello@alexbilbie.com instead of using the issue tracker.
If you discover any security related issues, please email `hello@alexbilbie.com` instead of using the issue tracker.
## License
@@ -61,6 +79,8 @@ This package is released under the MIT License. See the bundled [LICENSE](https:
This code is principally developed and maintained by [Alex Bilbie](https://twitter.com/alexbilbie).
Special thanks to [all of these awesome contributors](https://github.com/thephpleague/oauth2-server/contributors)
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.
The initial code was developed as part of the [Linkey](http://linkey.blogs.lincoln.ac.uk) project which was funded by [JISC](http://jisc.ac.uk) under the Access and Identity Management programme.

View File

@@ -8,12 +8,14 @@
"ext-openssl": "*",
"league/event": "^2.1",
"lcobucci/jwt": "^3.1",
"paragonie/random_compat": "^1.1",
"psr/http-message": "^1.0"
"paragonie/random_compat": "^2.0",
"psr/http-message": "^1.0",
"defuse/php-encryption": "^2.1"
},
"require-dev": {
"phpunit/phpunit": "^4.8",
"zendframework/zend-diactoros": "^1.0"
"phpunit/phpunit": "^4.8 || ^5.0",
"zendframework/zend-diactoros": "^1.0",
"indigophp/hash-compat": "^1.1"
},
"repositories": [
{
@@ -63,5 +65,8 @@
"branch-alias": {
"dev-V5-WIP": "5.0-dev"
}
},
"suggest": {
"indigophp/hash-compat": "Polyfill for hash_equals function for PHP 5.5"
}
}

View File

@@ -2,8 +2,8 @@
## Installation
0. Run `composer install --no-dev` in this directory to install dependencies
0. Create a private key `openssl genrsa -out private.key 1024`
0. Run `composer install` in this directory to install dependencies
0. Create a private key `openssl genrsa -out private.key 2048`
0. Create a public key `openssl rsa -in private.key -pubout > public.key`
0. `cd` into the public directory
0. Start a PHP server `php -S localhost:4444`

View File

@@ -1,17 +1,18 @@
{
"repositories": [
{
"type": "path",
"url": ".."
}
],
"require": {
"slim/slim": "3.0.*",
"league/oauth2-server": "dev-V5-WIP"
"slim/slim": "3.0.*"
},
"require-dev": {
"league/event": "^2.1",
"lcobucci/jwt": "^3.1",
"paragonie/random_compat": "^2.0",
"psr/http-message": "^1.0",
"defuse/php-encryption": "^2.1"
},
"autoload": {
"psr-4": {
"OAuth2ServerExamples\\": "src/"
"OAuth2ServerExamples\\": "src/",
"League\\OAuth2\\Server\\": "../src/"
}
}
}

598
examples/composer.lock generated
View File

@@ -4,23 +4,25 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "ec2df46ff78a00052e1a241ce5988d81",
"content-hash": "46a1c1e42c62d3e3645279fc54ae95b7",
"content-hash": "9813ed7c3b6dcf107f44df9392935b8f",
"packages": [
{
"name": "container-interop/container-interop",
"version": "1.1.0",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/container-interop/container-interop.git",
"reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e"
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e",
"reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e",
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"shasum": ""
},
"require": {
"psr/container": "^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
@@ -32,244 +34,8 @@
"MIT"
],
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
"time": "2014-12-30 15:22:37"
},
{
"name": "lcobucci/jwt",
"version": "3.1.1",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/jwt.git",
"reference": "afea8e682e911a21574fd8519321b32522fa25b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/afea8e682e911a21574fd8519321b32522fa25b5",
"reference": "afea8e682e911a21574fd8519321b32522fa25b5",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"php": ">=5.5"
},
"require-dev": {
"mdanter/ecc": "~0.3",
"mikey179/vfsstream": "~1.5",
"phpmd/phpmd": "~2.2",
"phpunit/php-invoker": "~1.1",
"phpunit/phpunit": "~4.5",
"squizlabs/php_codesniffer": "~2.3"
},
"suggest": {
"mdanter/ecc": "Required to use Elliptic Curves based algorithms."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Lcobucci\\JWT\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Luís Otávio Cobucci Oblonczyk",
"email": "lcobucci@gmail.com",
"role": "Developer"
}
],
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
"keywords": [
"JWS",
"jwt"
],
"time": "2016-03-24 22:46:13"
},
{
"name": "league/event",
"version": "2.1.2",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/event.git",
"reference": "e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/event/zipball/e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd",
"reference": "e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"henrikbjorn/phpspec-code-coverage": "~1.0.1",
"phpspec/phpspec": "~2.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
}
},
"autoload": {
"psr-4": {
"League\\Event\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frank de Jonge",
"email": "info@frenky.net"
}
],
"description": "Event package",
"keywords": [
"emitter",
"event",
"listener"
],
"time": "2015-05-21 12:24:47"
},
{
"name": "league/oauth2-server",
"version": "dev-V5-authorization-request-flow",
"dist": {
"type": "path",
"url": "../",
"reference": "5410a42bb66f5a61969c3ec1a8e7ab30d225875c",
"shasum": null
},
"require": {
"lcobucci/jwt": "^3.1",
"league/event": "^2.1",
"paragonie/random_compat": "^1.1",
"php": ">=5.5.9",
"psr/http-message": "^1.0"
},
"replace": {
"league/oauth2server": "*",
"lncd/oauth2": "*"
},
"require-dev": {
"league/plates": "^3.1",
"phpunit/phpunit": "^4.8",
"zendframework/zend-diactoros": "^1.0"
},
"suggest": {
"league/plates": "Used for parsing authorization code templates",
"mustache/mustache": "Used for parsing authorization code templates",
"smarty/smarty": "Used for parsing authorization code templates",
"twig/twig": "Used for parsing authorization code templates"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-V5-WIP": "5.0-dev"
}
},
"autoload": {
"psr-4": {
"League\\OAuth2\\Server\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"LeagueTests\\": "tests/"
}
},
"license": [
"MIT"
],
"authors": [
{
"name": "Alex Bilbie",
"email": "hello@alexbilbie.com",
"homepage": "http://www.alexbilbie.com",
"role": "Developer"
}
],
"description": "A lightweight and powerful OAuth 2.0 authorization and resource server library with support for all the core specification grants. This library will allow you to secure your API with OAuth and allow your applications users to approve apps that want to access their data from your API.",
"homepage": "http://oauth2.thephpleague.com/",
"keywords": [
"api",
"auth",
"auth",
"authentication",
"authorisation",
"authorization",
"oauth",
"oauth 2",
"oauth 2.0",
"oauth2",
"protect",
"resource",
"secure",
"server"
]
},
{
"name": "league/plates",
"version": "3.1.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/plates.git",
"reference": "2d8569e9f140a70d6a05db38006926f7547cb802"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/plates/zipball/2d8569e9f140a70d6a05db38006926f7547cb802",
"reference": "2d8569e9f140a70d6a05db38006926f7547cb802",
"shasum": ""
},
"require-dev": {
"mikey179/vfsstream": "~1.4.0",
"phpunit/phpunit": "~4.0",
"squizlabs/php_codesniffer": "~1.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"League\\Plates\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jonathan Reinink",
"email": "jonathan@reinink.ca",
"role": "Developer"
}
],
"description": "Plates, the native PHP template system that's fast, easy to use and easy to extend.",
"homepage": "http://platesphp.com",
"keywords": [
"league",
"package",
"templates",
"templating",
"views"
],
"time": "2015-07-09 02:14:40"
"homepage": "https://github.com/container-interop/container-interop",
"time": "2017-02-14T19:40:03+00:00"
},
{
"name": "nikic/fast-route",
@@ -312,55 +78,7 @@
"router",
"routing"
],
"time": "2015-06-18 19:15:47"
},
{
"name": "paragonie/random_compat",
"version": "v1.4.1",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "c7e26a21ba357863de030f0b9e701c7d04593774"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/c7e26a21ba357863de030f0b9e701c7d04593774",
"reference": "c7e26a21ba357863de030f0b9e701c7d04593774",
"shasum": ""
},
"require": {
"php": ">=5.2.0"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"type": "library",
"autoload": {
"files": [
"lib/random.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"pseudorandom",
"random"
],
"time": "2016-03-18 20:34:03"
"time": "2015-06-18T19:15:47+00:00"
},
{
"name": "pimple/pimple",
@@ -406,20 +124,69 @@
"container",
"dependency injection"
],
"time": "2015-09-11 15:10:35"
"time": "2015-09-11T15:10:35+00:00"
},
{
"name": "psr/http-message",
"version": "1.0",
"name": "psr/container",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298"
"url": "https://github.com/php-fig/container.git",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
"reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"time": "2017-02-14T16:28:37+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
@@ -447,6 +214,7 @@
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
@@ -455,7 +223,7 @@
"request",
"response"
],
"time": "2015-05-04 20:22:00"
"time": "2016-08-06T14:39:51+00:00"
},
{
"name": "slim/slim",
@@ -521,15 +289,233 @@
"micro",
"router"
],
"time": "2015-12-07 14:11:09"
"time": "2015-12-07T14:11:09+00:00"
}
],
"packages-dev": [
{
"name": "defuse/php-encryption",
"version": "v2.1.0",
"source": {
"type": "git",
"url": "https://github.com/defuse/php-encryption.git",
"reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/defuse/php-encryption/zipball/5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689",
"reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"paragonie/random_compat": "~2.0",
"php": ">=5.4.0"
},
"require-dev": {
"nikic/php-parser": "^2.0|^3.0",
"phpunit/phpunit": "^4|^5"
},
"bin": [
"bin/generate-defuse-key"
],
"type": "library",
"autoload": {
"psr-4": {
"Defuse\\Crypto\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Hornby",
"email": "taylor@defuse.ca",
"homepage": "https://defuse.ca/"
},
{
"name": "Scott Arciszewski",
"email": "info@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "Secure PHP Encryption Library",
"keywords": [
"aes",
"authenticated encryption",
"cipher",
"crypto",
"cryptography",
"encrypt",
"encryption",
"openssl",
"security",
"symmetric key cryptography"
],
"time": "2017-05-18T21:28:48+00:00"
},
{
"name": "lcobucci/jwt",
"version": "3.2.1",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/jwt.git",
"reference": "ddce703826f9c5229781933b1a39069e38e6a0f3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/ddce703826f9c5229781933b1a39069e38e6a0f3",
"reference": "ddce703826f9c5229781933b1a39069e38e6a0f3",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"php": ">=5.5"
},
"require-dev": {
"mdanter/ecc": "~0.3.1",
"mikey179/vfsstream": "~1.5",
"phpmd/phpmd": "~2.2",
"phpunit/php-invoker": "~1.1",
"phpunit/phpunit": "~4.5",
"squizlabs/php_codesniffer": "~2.3"
},
"suggest": {
"mdanter/ecc": "Required to use Elliptic Curves based algorithms."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Lcobucci\\JWT\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Luís Otávio Cobucci Oblonczyk",
"email": "lcobucci@gmail.com",
"role": "Developer"
}
],
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
"keywords": [
"JWS",
"jwt"
],
"time": "2016-10-31T20:09:32+00:00"
},
{
"name": "league/event",
"version": "2.1.2",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/event.git",
"reference": "e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/event/zipball/e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd",
"reference": "e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"henrikbjorn/phpspec-code-coverage": "~1.0.1",
"phpspec/phpspec": "~2.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
}
},
"autoload": {
"psr-4": {
"League\\Event\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frank de Jonge",
"email": "info@frenky.net"
}
],
"description": "Event package",
"keywords": [
"emitter",
"event",
"listener"
],
"time": "2015-05-21T12:24:47+00:00"
},
{
"name": "paragonie/random_compat",
"version": "v2.0.10",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d",
"reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d",
"shasum": ""
},
"require": {
"php": ">=5.2.0"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"type": "library",
"autoload": {
"files": [
"lib/random.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"pseudorandom",
"random"
],
"time": "2017-03-13T16:27:32+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"league/oauth2-server": 20
},
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],

View File

@@ -1,9 +1,7 @@
<?php
use League\OAuth2\Server\Server;
use League\OAuth2\Server\ResourceServer;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\ClientRepository;
use OAuth2ServerExamples\Repositories\ScopeRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
@@ -11,64 +9,64 @@ use Slim\App;
include __DIR__ . '/../vendor/autoload.php';
$app = new App([
'settings' => [
'displayErrorDetails' => true,
],
Server::class => function () {
// Setup the authorization server
$server = new Server(
new ClientRepository(),
new AccessTokenRepository(),
new ScopeRepository(),
'file://' . __DIR__ . '/../private.key',
'file://' . __DIR__ . '/../public.key'
// Add the resource server to the DI container
ResourceServer::class => function () {
$server = new ResourceServer(
new AccessTokenRepository(), // instance of AccessTokenRepositoryInterface
'file://' . __DIR__ . '/../public.key' // the authorization server's public key
);
return $server;
},
]);
// Add the resource server middleware which will intercept and validate requests
$app->add(
new \League\OAuth2\Server\Middleware\ResourceServerMiddleware(
$app->getContainer()->get(Server::class)
$app->getContainer()->get(ResourceServer::class)
)
);
$app->get('/users', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
// An example endpoint secured with OAuth 2.0
$app->get(
'/users',
function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
$users = [
[
'id' => 123,
'name' => 'Alex',
'email' => 'alex@thephpleague.com',
],
[
'id' => 124,
'name' => 'Frank',
'email' => 'frank@thephpleague.com',
],
[
'id' => 125,
'name' => 'Phil',
'email' => 'phil@thephpleague.com',
],
];
$users = [
[
'id' => 123,
'name' => 'Alex',
'email' => 'alex@thephpleague.com',
],
[
'id' => 124,
'name' => 'Frank',
'email' => 'frank@thephpleague.com',
],
[
'id' => 125,
'name' => 'Phil',
'email' => 'phil@thephpleague.com',
],
];
if (in_array('basic', $request->getAttribute('oauth_scopes')) === false) {
for ($i = 0; $i < count($users); $i++) {
unset($users[$i]['name']);
// If the access token doesn't have the `basic` scope hide users' names
if (in_array('basic', $request->getAttribute('oauth_scopes')) === false) {
for ($i = 0; $i < count($users); $i++) {
unset($users[$i]['name']);
}
}
}
if (in_array('email', $request->getAttribute('oauth_scopes')) === false) {
for ($i = 0; $i < count($users); $i++) {
unset($users[$i]['email']);
// 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++) {
unset($users[$i]['email']);
}
}
$response->getBody()->write(json_encode($users));
return $response->withStatus(200);
}
$response->getBody()->write(json_encode($users));
return $response->withStatus(200);
});
);
$app->run();

View File

@@ -1,9 +1,15 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\AuthCodeGrant;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
use League\OAuth2\Server\Server;
use OAuth2ServerExamples\Entities\UserEntity;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\AuthCodeRepository;
@@ -21,7 +27,7 @@ $app = new App([
'settings' => [
'displayErrorDetails' => true,
],
Server::class => function () {
AuthorizationServer::class => function () {
// Init our repositories
$clientRepository = new ClientRepository();
$scopeRepository = new ScopeRepository();
@@ -33,13 +39,14 @@ $app = new App([
$publicKeyPath = 'file://' . __DIR__ . '/../public.key';
// Setup the authorization server
$server = new Server(
$server = new AuthorizationServer(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKeyPath,
$publicKeyPath
);
$server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen');
// Enable the authentication code grant on the server with a token TTL of 1 hour
$server->enableGrantType(
@@ -56,8 +63,8 @@ $app = new App([
]);
$app->get('/authorize', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
/* @var \League\OAuth2\Server\Server $server */
$server = $app->getContainer()->get(Server::class);
/* @var \League\OAuth2\Server\AuthorizationServer $server */
$server = $app->getContainer()->get(AuthorizationServer::class);
try {
// Validate the HTTP request and return an AuthorizationRequest object.
@@ -84,8 +91,8 @@ $app->get('/authorize', function (ServerRequestInterface $request, ResponseInter
});
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
/* @var \League\OAuth2\Server\Server $server */
$server = $app->getContainer()->get(Server::class);
/* @var \League\OAuth2\Server\AuthorizationServer $server */
$server = $app->getContainer()->get(AuthorizationServer::class);
try {
return $server->respondToAccessTokenRequest($request, $response);

View File

@@ -1,8 +1,14 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
use League\OAuth2\Server\Server;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\ClientRepository;
use OAuth2ServerExamples\Repositories\ScopeRepository;
@@ -14,31 +20,34 @@ use Zend\Diactoros\Stream;
include __DIR__ . '/../vendor/autoload.php';
$app = new App([
'settings' => [
'settings' => [
'displayErrorDetails' => true,
],
Server::class => function () {
AuthorizationServer::class => function () {
// Init our repositories
$clientRepository = new ClientRepository();
$accessTokenRepository = new AccessTokenRepository();
$scopeRepository = new ScopeRepository();
$clientRepository = new ClientRepository(); // instance of ClientRepositoryInterface
$scopeRepository = new ScopeRepository(); // instance of ScopeRepositoryInterface
$accessTokenRepository = new AccessTokenRepository(); // instance of AccessTokenRepositoryInterface
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
$publicKeyPath = 'file://' . __DIR__ . '/../public.key';
// Path to public and private keys
$privateKey = 'file://' . __DIR__ . '/../private.key';
//$privateKey = new CryptKey('file://path/to/private.key', 'passphrase'); // if private key has a pass phrase
$publicKey = 'file://' . __DIR__ . '/../public.key';
// Setup the authorization server
$server = new Server(
$server = new AuthorizationServer(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKeyPath,
$publicKeyPath
$privateKey,
$publicKey
);
$server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen');
// Enable the client credentials grant on the server with a token TTL of 1 hour
// Enable the client credentials grant on the server
$server->enableGrantType(
new ClientCredentialsGrant(),
new \DateInterval('PT1H')
new \League\OAuth2\Server\Grant\ClientCredentialsGrant(),
new \DateInterval('PT1H') // access tokens will expire after 1 hour
);
return $server;
@@ -46,14 +55,21 @@ $app = new App([
]);
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
/* @var \League\OAuth2\Server\Server $server */
$server = $app->getContainer()->get(Server::class);
/* @var \League\OAuth2\Server\AuthorizationServer $server */
$server = $app->getContainer()->get(AuthorizationServer::class);
try {
// Try to respond to the request
return $server->respondToAccessTokenRequest($request, $response);
} catch (OAuthServerException $exception) {
// All instances of OAuthServerException can be formatted into a HTTP response
return $exception->generateHttpResponse($response);
} catch (\Exception $exception) {
// Unknown exception
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());

View File

@@ -1,8 +1,15 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\ImplicitGrant;
use League\OAuth2\Server\Server;
use OAuth2ServerExamples\Entities\UserEntity;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\ClientRepository;
@@ -18,7 +25,7 @@ $app = new App([
'settings' => [
'displayErrorDetails' => true,
],
Server::class => function () {
AuthorizationServer::class => function () {
// Init our repositories
$clientRepository = new ClientRepository();
$scopeRepository = new ScopeRepository();
@@ -28,13 +35,14 @@ $app = new App([
$publicKeyPath = 'file://' . __DIR__ . '/../public.key';
// Setup the authorization server
$server = new Server(
$server = new AuthorizationServer(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKeyPath,
$publicKeyPath
);
$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')));
@@ -44,8 +52,8 @@ $app = new App([
]);
$app->get('/authorize', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
/* @var \League\OAuth2\Server\Server $server */
$server = $app->getContainer()->get(Server::class);
/* @var \League\OAuth2\Server\AuthorizationServer $server */
$server = $app->getContainer()->get(AuthorizationServer::class);
try {
// Validate the HTTP request and return an AuthorizationRequest object.

View File

@@ -1,16 +1,23 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Grant\AuthCodeGrant;
use League\OAuth2\Server\Grant\RefreshTokenGrant;
use League\OAuth2\Server\Middleware\AuthenticationServerMiddleware;
use League\OAuth2\Server\Middleware\AuthorizationServerMiddleware;
use League\OAuth2\Server\Middleware\ResourceServerMiddleware;
use League\OAuth2\Server\Server;
use League\OAuth2\Server\ResourceServer;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\AuthCodeRepository;
use OAuth2ServerExamples\Repositories\ClientRepository;
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
use OAuth2ServerExamples\Repositories\ScopeRepository;
use OAuth2ServerExamples\Repositories\UserRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
@@ -19,36 +26,35 @@ use Zend\Diactoros\Stream;
include __DIR__ . '/../vendor/autoload.php';
$app = new App([
'settings' => [
'settings' => [
'displayErrorDetails' => true,
],
Server::class => function () {
AuthorizationServer::class => function () {
// Init our repositories
$clientRepository = new ClientRepository();
$accessTokenRepository = new AccessTokenRepository();
$scopeRepository = new ScopeRepository();
$authCodeRepository = new AuthCodeRepository();
$refreshTokenRepository = new RefreshTokenRepository();
$userRepository = new UserRepository();
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
$publicKeyPath = 'file://' . __DIR__ . '/../public.key';
// Setup the authorization server
$server = new Server(
$server = new AuthorizationServer(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKeyPath,
$publicKeyPath
);
$server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen');
// Enable the authentication code grant on the server with a token TTL of 1 hour
$server->enableGrantType(
new AuthCodeGrant(
$authCodeRepository,
$refreshTokenRepository,
$userRepository,
new \DateInterval('PT10M')
),
new \DateInterval('PT1H')
@@ -57,7 +63,17 @@ $app = new App([
// Enable the refresh token grant on the server with a token TTL of 1 month
$server->enableGrantType(
new RefreshTokenGrant($refreshTokenRepository),
new \DateInterval('PT1M')
new \DateInterval('P1M')
);
return $server;
},
ResourceServer::class => function () {
$publicKeyPath = 'file://' . __DIR__ . '/../public.key';
$server = new ResourceServer(
new AccessTokenRepository(),
$publicKeyPath
);
return $server;
@@ -66,7 +82,7 @@ $app = new App([
// Access token issuer
$app->post('/access_token', function () {
})->add(new AuthenticationServerMiddleware($app->getContainer()->get(Server::class)));
})->add(new AuthorizationServerMiddleware($app->getContainer()->get(AuthorizationServer::class)));
// Secured API
$app->group('/api', function () {
@@ -90,6 +106,6 @@ $app->group('/api', function () {
return $response->withBody($body);
});
})->add(new ResourceServerMiddleware($app->getContainer()->get(Server::class)));
})->add(new ResourceServerMiddleware($app->getContainer()->get(ResourceServer::class)));
$app->run();

View File

@@ -1,8 +1,8 @@
<?php
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\PasswordGrant;
use League\OAuth2\Server\Server;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\ClientRepository;
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
@@ -11,58 +11,63 @@ use OAuth2ServerExamples\Repositories\UserRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
use Zend\Diactoros\Stream;
include __DIR__ . '/../vendor/autoload.php';
$app = new App([
'settings' => [
'displayErrorDetails' => true,
],
Server::class => function () {
// Init our repositories
$clientRepository = new ClientRepository();
$accessTokenRepository = new AccessTokenRepository();
$scopeRepository = new ScopeRepository();
$userRepository = new UserRepository();
$refreshTokenRepository = new RefreshTokenRepository();
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
$publicKeyPath = 'file://' . __DIR__ . '/../public.key';
// Add the authorization server to the DI container
AuthorizationServer::class => function () {
// Setup the authorization server
$server = new Server(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKeyPath,
$publicKeyPath
$server = new AuthorizationServer(
new ClientRepository(), // instance of ClientRepositoryInterface
new AccessTokenRepository(), // instance of AccessTokenRepositoryInterface
new ScopeRepository(), // instance of ScopeRepositoryInterface
'file://' . __DIR__ . '/../private.key', // path to private key
'file://' . __DIR__ . '/../public.key' // path to public key
);
$server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen');
$grant = new PasswordGrant(
new UserRepository(), // instance of UserRepositoryInterface
new RefreshTokenRepository() // instance of RefreshTokenRepositoryInterface
);
$grant->setRefreshTokenTTL(new \DateInterval('P1M')); // refresh tokens will expire after 1 month
// Enable the password grant on the server with a token TTL of 1 hour
$server->enableGrantType(
new PasswordGrant($userRepository, $refreshTokenRepository),
new \DateInterval('PT1H')
$grant,
new \DateInterval('PT1H') // access tokens will expire after 1 hour
);
return $server;
},
]);
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
/* @var \League\OAuth2\Server\Server $server */
$server = $app->getContainer()->get(Server::class);
$app->post(
'/access_token',
function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
try {
return $server->respondToAccessTokenRequest($request, $response);
} catch (OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
} catch (\Exception $exception) {
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
/* @var \League\OAuth2\Server\AuthorizationServer $server */
$server = $app->getContainer()->get(AuthorizationServer::class);
return $response->withStatus(500)->withBody($body);
try {
// Try to respond to the access token request
return $server->respondToAccessTokenRequest($request, $response);
} catch (OAuthServerException $exception) {
// All instances of OAuthServerException can be converted to a PSR-7 response
return $exception->generateHttpResponse($response);
} catch (\Exception $exception) {
// Catch unexpected exceptions
$body = $response->getBody();
$body->write($exception->getMessage());
return $response->withStatus(500)->withBody($body);
}
}
});
);
$app->run();

View File

@@ -1,8 +1,15 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\RefreshTokenGrant;
use League\OAuth2\Server\Server;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\ClientRepository;
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
@@ -10,7 +17,6 @@ use OAuth2ServerExamples\Repositories\ScopeRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
use Zend\Diactoros\Stream;
include __DIR__ . '/../vendor/autoload.php';
@@ -18,7 +24,7 @@ $app = new App([
'settings' => [
'displayErrorDetails' => true,
],
Server::class => function () {
AuthorizationServer::class => function () {
// Init our repositories
$clientRepository = new ClientRepository();
$accessTokenRepository = new AccessTokenRepository();
@@ -29,18 +35,22 @@ $app = new App([
$publicKeyPath = 'file://' . __DIR__ . '/../public.key';
// Setup the authorization server
$server = new Server(
$server = new AuthorizationServer(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKeyPath,
$publicKeyPath
);
$server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen');
// Enable the refresh token grant on the server
$grant = new RefreshTokenGrant($refreshTokenRepository);
$grant->setRefreshTokenTTL(new \DateInterval('P1M')); // The refresh token will expire in 1 month
// Enable the refresh token grant on the server with a token TTL of 1 hour
$server->enableGrantType(
new RefreshTokenGrant($refreshTokenRepository),
new \DateInterval('PT1H')
$grant,
new \DateInterval('PT1H') // The new access token will expire after 1 hour
);
return $server;
@@ -48,18 +58,17 @@ $app = new App([
]);
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
/* @var \League\OAuth2\Server\Server $server */
$server = $app->getContainer()->get(Server::class);
/* @var \League\OAuth2\Server\AuthorizationServer $server */
$server = $app->getContainer()->get(AuthorizationServer::class);
try {
return $server->respondToAccessTokenRequest($request, $response);
} catch (OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
} catch (\Exception $exception) {
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
$response->getBody()->write($exception->getMessage());
return $response->withStatus(500)->withBody($body);
return $response->withStatus(500);
}
});

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace OAuth2ServerExamples\Entities;

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace OAuth2ServerExamples\Entities;

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace OAuth2ServerExamples\Entities;

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace OAuth2ServerExamples\Entities;

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace OAuth2ServerExamples\Entities;

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace OAuth2ServerExamples\Entities;

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace OAuth2ServerExamples\Repositories;

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace OAuth2ServerExamples\Repositories;

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace OAuth2ServerExamples\Repositories;
@@ -10,13 +17,14 @@ class ClientRepository implements ClientRepositoryInterface
/**
* {@inheritdoc}
*/
public function getClientEntity($clientIdentifier, $clientSecret = null, $redirectUri = null, $grantType = null)
public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true)
{
$clients = [
'myawesomeapp' => [
'secret' => password_hash('abc123', PASSWORD_BCRYPT),
'name' => 'My Awesome App',
'redirect_uri' => 'http://foo/bar',
'secret' => password_hash('abc123', PASSWORD_BCRYPT),
'name' => 'My Awesome App',
'redirect_uri' => 'http://foo/bar',
'is_confidential' => true,
],
];
@@ -25,6 +33,14 @@ class ClientRepository implements ClientRepositoryInterface
return;
}
if (
$mustValidateSecret === true
&& $clients[$clientIdentifier]['is_confidential'] === true
&& password_verify($clientSecret, $clients[$clientIdentifier]['secret']) === false
) {
return;
}
$client = new ClientEntity();
$client->setIdentifier($clientIdentifier);
$client->setName($clients[$clientIdentifier]['name']);

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace OAuth2ServerExamples\Repositories;

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace OAuth2ServerExamples\Repositories;
@@ -41,6 +48,13 @@ class ScopeRepository implements ScopeRepositoryInterface
ClientEntityInterface $clientEntity,
$userIdentifier = null
) {
// Example of programatically modifying the final scope of the access token
if ((int) $userIdentifier === 1) {
$scope = new ScopeEntity();
$scope->setIdentifier('email');
$scopes[] = $scope;
}
return $scopes;
}
}

View File

@@ -1,10 +1,16 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace OAuth2ServerExamples\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
use OAuth2ServerExamples\Entities\ScopeEntity;
use OAuth2ServerExamples\Entities\UserEntity;
class UserRepository implements UserRepositoryInterface
@@ -19,10 +25,6 @@ class UserRepository implements UserRepositoryInterface
ClientEntityInterface $clientEntity
) {
if ($username === 'alex' && $password === 'whisky') {
$scope = new ScopeEntity();
$scope->setIdentifier('email');
$scopes[] = $scope;
return new UserEntity();
}

237
src/AuthorizationServer.php Normal file
View File

@@ -0,0 +1,237 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server;
use League\Event\EmitterAwareInterface;
use League\Event\EmitterAwareTrait;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\GrantTypeInterface;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class AuthorizationServer implements EmitterAwareInterface
{
use EmitterAwareTrait;
const ENCRYPTION_KEY_ERROR = 'You must set the encryption key going forward to improve the security of this library - see this page for more information https://oauth2.thephpleague.com/v5-security-improvements/';
/**
* @var GrantTypeInterface[]
*/
protected $enabledGrantTypes = [];
/**
* @var \DateInterval[]
*/
protected $grantTypeAccessTokenTTL = [];
/**
* @var CryptKey
*/
protected $privateKey;
/**
* @var CryptKey
*/
protected $publicKey;
/**
* @var null|ResponseTypeInterface
*/
protected $responseType;
/**
* @var ClientRepositoryInterface
*/
private $clientRepository;
/**
* @var AccessTokenRepositoryInterface
*/
private $accessTokenRepository;
/**
* @var ScopeRepositoryInterface
*/
private $scopeRepository;
/**
* @var string
*/
private $encryptionKey;
/**
* New server instance.
*
* @param ClientRepositoryInterface $clientRepository
* @param AccessTokenRepositoryInterface $accessTokenRepository
* @param ScopeRepositoryInterface $scopeRepository
* @param CryptKey|string $privateKey
* @param CryptKey|string $publicKey
* @param null|ResponseTypeInterface $responseType
*/
public function __construct(
ClientRepositoryInterface $clientRepository,
AccessTokenRepositoryInterface $accessTokenRepository,
ScopeRepositoryInterface $scopeRepository,
$privateKey,
$publicKey,
ResponseTypeInterface $responseType = null
) {
$this->clientRepository = $clientRepository;
$this->accessTokenRepository = $accessTokenRepository;
$this->scopeRepository = $scopeRepository;
if ($privateKey instanceof CryptKey === false) {
$privateKey = new CryptKey($privateKey);
}
$this->privateKey = $privateKey;
if ($publicKey instanceof CryptKey === false) {
$publicKey = new CryptKey($publicKey);
}
$this->publicKey = $publicKey;
$this->responseType = $responseType;
}
/**
* Set the encryption key
*
* @param string $key
*/
public function setEncryptionKey($key)
{
$this->encryptionKey = $key;
}
/**
* Enable a grant type on the server.
*
* @param GrantTypeInterface $grantType
* @param null|\DateInterval $accessTokenTTL
*/
public function enableGrantType(GrantTypeInterface $grantType, \DateInterval $accessTokenTTL = null)
{
if ($accessTokenTTL instanceof \DateInterval === false) {
$accessTokenTTL = new \DateInterval('PT1H');
}
$grantType->setAccessTokenRepository($this->accessTokenRepository);
$grantType->setClientRepository($this->clientRepository);
$grantType->setScopeRepository($this->scopeRepository);
$grantType->setPrivateKey($this->privateKey);
$grantType->setPublicKey($this->publicKey);
$grantType->setEmitter($this->getEmitter());
if ($this->encryptionKey === null) {
// @codeCoverageIgnoreStart
error_log(self::ENCRYPTION_KEY_ERROR);
// @codeCoverageIgnoreEnd
}
$grantType->setEncryptionKey($this->encryptionKey);
$this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType;
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] = $accessTokenTTL;
}
/**
* Validate an authorization request
*
* @param ServerRequestInterface $request
*
* @throws OAuthServerException
*
* @return AuthorizationRequest
*/
public function validateAuthorizationRequest(ServerRequestInterface $request)
{
if ($this->encryptionKey === null) {
// @codeCoverageIgnoreStart
error_log(self::ENCRYPTION_KEY_ERROR);
// @codeCoverageIgnoreEnd
}
foreach ($this->enabledGrantTypes as $grantType) {
if ($grantType->canRespondToAuthorizationRequest($request)) {
return $grantType->validateAuthorizationRequest($request);
}
}
throw OAuthServerException::unsupportedGrantType();
}
/**
* Complete an authorization request
*
* @param AuthorizationRequest $authRequest
* @param ResponseInterface $response
*
* @return ResponseInterface
*/
public function completeAuthorizationRequest(AuthorizationRequest $authRequest, ResponseInterface $response)
{
return $this->enabledGrantTypes[$authRequest->getGrantTypeId()]
->completeAuthorizationRequest($authRequest)
->generateHttpResponse($response);
}
/**
* Return an access token response.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
*
* @throws OAuthServerException
*
* @return ResponseInterface
*/
public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response)
{
foreach ($this->enabledGrantTypes as $grantType) {
if ($grantType->canRespondToAccessTokenRequest($request)) {
$tokenResponse = $grantType->respondToAccessTokenRequest(
$request,
$this->getResponseType(),
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()]
);
if ($tokenResponse instanceof ResponseTypeInterface) {
return $tokenResponse->generateHttpResponse($response);
}
}
}
throw OAuthServerException::unsupportedGrantType();
}
/**
* Get the token type that grants will return in the HTTP response.
*
* @return ResponseTypeInterface
*/
protected function getResponseType()
{
if ($this->responseType instanceof ResponseTypeInterface === false) {
$this->responseType = new BearerTokenResponse();
}
$this->responseType->setPrivateKey($this->privateKey);
$this->responseType->setEncryptionKey($this->encryptionKey);
return $this->responseType;
}
}

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\AuthorizationValidators;

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\AuthorizationValidators;
@@ -15,14 +22,12 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
use CryptTrait;
/**
* @var \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface
* @var AccessTokenRepositoryInterface
*/
private $accessTokenRepository;
/**
* BearerTokenValidator constructor.
*
* @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository
* @param AccessTokenRepositoryInterface $accessTokenRepository
*/
public function __construct(AccessTokenRepositoryInterface $accessTokenRepository)
{
@@ -70,6 +75,9 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
} catch (\InvalidArgumentException $exception) {
// JWT couldn't be parsed so return the request as is
throw OAuthServerException::accessDenied($exception->getMessage());
} catch (\RuntimeException $exception) {
//JWR couldn't be parsed so return the request as is
throw OAuthServerException::accessDenied('Error while decoding to JSON');
}
}
}

View File

@@ -8,17 +8,21 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server;
class CryptKey
{
const RSA_KEY_PATTERN =
'/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----\n)(.|\n)+(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)$/';
/**
* @var string
*/
protected $keyPath;
/**
* @var string
* @var null|string
*/
protected $passPhrase;
@@ -28,6 +32,10 @@ class CryptKey
*/
public function __construct($keyPath, $passPhrase = null)
{
if (preg_match(self::RSA_KEY_PATTERN, $keyPath)) {
$keyPath = $this->saveKeyToFile($keyPath);
}
if (strpos($keyPath, 'file://') !== 0) {
$keyPath = 'file://' . $keyPath;
}
@@ -36,10 +44,60 @@ class CryptKey
throw new \LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
}
// Verify the permissions of the key
$keyPathPerms = decoct(fileperms($keyPath) & 0777);
if ($keyPathPerms !== '600') {
// Attempt to correct the permissions
if (chmod($keyPath, 0600) === false) {
// @codeCoverageIgnoreStart
throw new \LogicException(
sprintf(
'Key file "%s" permissions are not correct, should be 600 instead of %s, unable to automatically resolve the issue',
$keyPath,
$keyPathPerms
)
);
// @codeCoverageIgnoreEnd
}
}
$this->keyPath = $keyPath;
$this->passPhrase = $passPhrase;
}
/**
* @param string $key
*
* @throws \RuntimeException
*
* @return string
*/
private function saveKeyToFile($key)
{
$tmpDir = sys_get_temp_dir();
$keyPath = $tmpDir . '/' . sha1($key) . '.key';
if (!file_exists($keyPath) && !touch($keyPath)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('"%s" key file could not be created', $keyPath);
// @codeCoverageIgnoreEnd
}
if (file_put_contents($keyPath, $key) === false) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('Unable to write key file to temporary directory "%s"', $tmpDir);
// @codeCoverageIgnoreEnd
}
if (chmod($keyPath, 0600) === false) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('The key file "%s" file mode could not be changed with chmod to 600', $keyPath);
// @codeCoverageIgnoreEnd
}
return 'file://' . $keyPath;
}
/**
* Retrieve key path.
*

View File

@@ -8,24 +8,32 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server;
use Defuse\Crypto\Crypto;
trait CryptTrait
{
/**
* @var \League\OAuth2\Server\CryptKey
* @var CryptKey
*/
protected $privateKey;
/**
* @var \League\OAuth2\Server\CryptKey
* @var CryptKey
*/
protected $publicKey;
/**
* @var string
*/
protected $encryptionKey;
/**
* Set path to private key.
*
* @param \League\OAuth2\Server\CryptKey $privateKey
* @param CryptKey $privateKey
*/
public function setPrivateKey(CryptKey $privateKey)
{
@@ -35,7 +43,7 @@ trait CryptTrait
/**
* Set path to public key.
*
* @param \League\OAuth2\Server\CryptKey $publicKey
* @param CryptKey $publicKey
*/
public function setPublicKey(CryptKey $publicKey)
{
@@ -47,10 +55,16 @@ trait CryptTrait
*
* @param string $unencryptedData
*
* @throws \LogicException
*
* @return string
*/
protected function encrypt($unencryptedData)
{
if ($this->encryptionKey !== null) {
return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey);
}
$privateKey = openssl_pkey_get_private($this->privateKey->getKeyPath(), $this->privateKey->getPassPhrase());
$privateKeyDetails = @openssl_pkey_get_details($privateKey);
if ($privateKeyDetails === null) {
@@ -88,6 +102,10 @@ trait CryptTrait
*/
protected function decrypt($encryptedData)
{
if ($this->encryptionKey !== null) {
return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey);
}
$publicKey = openssl_pkey_get_public($this->publicKey->getKeyPath());
$publicKeyDetails = @openssl_pkey_get_details($publicKey);
if ($publicKeyDetails === null) {
@@ -115,4 +133,14 @@ trait CryptTrait
return $output;
}
/**
* Set the encryption key
*
* @param string $key
*/
public function setEncryptionKey($key = null)
{
$this->encryptionKey = $key;
}
}

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Entities;
@@ -9,7 +16,7 @@ interface AccessTokenEntityInterface extends TokenInterface
/**
* Generate a JWT from the access token
*
* @param \League\OAuth2\Server\CryptKey $privateKey
* @param CryptKey $privateKey
*
* @return string
*/

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Entities;

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Entities;

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Entities;
@@ -35,21 +42,14 @@ interface RefreshTokenEntityInterface
/**
* Set the access token that the refresh token was associated with.
*
* @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken
* @param AccessTokenEntityInterface $accessToken
*/
public function setAccessToken(AccessTokenEntityInterface $accessToken);
/**
* Get the access token that the refresh token was originally associated with.
*
* @return \League\OAuth2\Server\Entities\AccessTokenEntityInterface
* @return AccessTokenEntityInterface
*/
public function getAccessToken();
/**
* Has the token expired?
*
* @return bool
*/
public function isExpired();
}

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Entities;

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Entities;
@@ -56,14 +63,14 @@ interface TokenInterface
/**
* Set the client that the token was issued to.
*
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
* @param ClientEntityInterface $client
*/
public function setClient(ClientEntityInterface $client);
/**
* Associate a scope with the token.
*
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface $scope
* @param ScopeEntityInterface $scope
*/
public function addScope(ScopeEntityInterface $scope);
@@ -73,11 +80,4 @@ interface TokenInterface
* @return ScopeEntityInterface[]
*/
public function getScopes();
/**
* Has the token expired?
*
* @return bool
*/
public function isExpired();
}

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Entities\Traits;
@@ -6,13 +13,15 @@ use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
trait AccessTokenTrait
{
/**
* Generate a JWT from the access token
*
* @param \League\OAuth2\Server\CryptKey $privateKey
* @param CryptKey $privateKey
*
* @return string
*/
@@ -29,4 +38,24 @@ trait AccessTokenTrait
->sign(new Sha256(), new Key($privateKey->getKeyPath(), $privateKey->getPassPhrase()))
->getToken();
}
/**
* @return ClientEntityInterface
*/
abstract public function getClient();
/**
* @return \DateTime
*/
abstract public function getExpiryDateTime();
/**
* @return string|int
*/
abstract public function getUserIdentifier();
/**
* @return ScopeEntityInterface[]
*/
abstract public function getScopes();
}

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Entities\Traits;

View File

@@ -1,11 +1,24 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Entities\Traits;
trait ClientTrait
{
/**
* @var string
*/
protected $name;
/**
* @var string|string[]
*/
protected $redirectUri;
/**

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Entities\Traits;

View File

@@ -1,8 +1,14 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Entities\Traits;
use DateTime;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
trait RefreshTokenTrait
@@ -13,7 +19,7 @@ trait RefreshTokenTrait
protected $accessToken;
/**
* @var DateTime
* @var \DateTime
*/
protected $expiryDateTime;
@@ -36,7 +42,7 @@ trait RefreshTokenTrait
/**
* Get the token's expiry date time.
*
* @return DateTime
* @return \DateTime
*/
public function getExpiryDateTime()
{
@@ -46,20 +52,10 @@ trait RefreshTokenTrait
/**
* Set the date time when the token expires.
*
* @param DateTime $dateTime
* @param \DateTime $dateTime
*/
public function setExpiryDateTime(DateTime $dateTime)
public function setExpiryDateTime(\DateTime $dateTime)
{
$this->expiryDateTime = $dateTime;
}
/**
* Has the token expired?
*
* @return bool
*/
public function isExpired()
{
return (new DateTime()) > $this->getExpiryDateTime();
}
}

View File

@@ -1,8 +1,14 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Entities\Traits;
use DateTime;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
@@ -14,7 +20,7 @@ trait TokenEntityTrait
protected $scopes = [];
/**
* @var DateTime
* @var \DateTime
*/
protected $expiryDateTime;
@@ -31,7 +37,7 @@ trait TokenEntityTrait
/**
* Associate a scope with the token.
*
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface $scope
* @param ScopeEntityInterface $scope
*/
public function addScope(ScopeEntityInterface $scope)
{
@@ -51,7 +57,7 @@ trait TokenEntityTrait
/**
* Get the token's expiry date time.
*
* @return DateTime
* @return \DateTime
*/
public function getExpiryDateTime()
{
@@ -61,9 +67,9 @@ trait TokenEntityTrait
/**
* Set the date time when the token expires.
*
* @param DateTime $dateTime
* @param \DateTime $dateTime
*/
public function setExpiryDateTime(DateTime $dateTime)
public function setExpiryDateTime(\DateTime $dateTime)
{
$this->expiryDateTime = $dateTime;
}
@@ -101,20 +107,10 @@ trait TokenEntityTrait
/**
* Set the client that the token was issued to.
*
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
* @param ClientEntityInterface $client
*/
public function setClient(ClientEntityInterface $client)
{
$this->client = $client;
}
/**
* Has the token expired?
*
* @return bool
*/
public function isExpired()
{
return (new DateTime()) > $this->getExpiryDateTime();
}
}

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Entities;

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Exception;
@@ -62,7 +69,7 @@ class OAuthServerException extends \Exception
* Invalid request error.
*
* @param string $parameter The invalid parameter
* @param string|null $hint
* @param null|string $hint
*
* @return static
*/
@@ -98,7 +105,10 @@ class OAuthServerException extends \Exception
public static function invalidScope($scope, $redirectUri = null)
{
$errorMessage = 'The requested scope is invalid, unknown, or malformed';
$hint = sprintf('Check the `%s` scope', $scope);
$hint = sprintf(
'Check the `%s` scope',
htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false)
);
return new static($errorMessage, 5, 'invalid_scope', 400, $hint, $redirectUri);
}
@@ -136,7 +146,7 @@ class OAuthServerException extends \Exception
/**
* Invalid refresh token.
*
* @param string|null $hint
* @param null|string $hint
*
* @return static
*/
@@ -148,8 +158,8 @@ class OAuthServerException extends \Exception
/**
* Access denied.
*
* @param string|null $hint
* @param string|null $redirectUri
* @param null|string $hint
* @param null|string $redirectUri
*
* @return static
*/
@@ -165,6 +175,26 @@ class OAuthServerException extends \Exception
);
}
/**
* Invalid grant.
*
* @param string $hint
*
* @return static
*/
public static function invalidGrant($hint = '')
{
return new static(
'The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token '
. 'is invalid, expired, revoked, does not match the redirection URI used in the authorization request, '
. 'or was issued to another client.',
10,
'invalid_grant',
400,
$hint
);
}
/**
* @return string
*/
@@ -176,11 +206,10 @@ class OAuthServerException extends \Exception
/**
* Generate a HTTP response.
*
* @param \Psr\Http\Message\ResponseInterface $response
* @param bool $useFragment True if errors should be in the URI fragment instead of
* query string
* @param ResponseInterface $response
* @param bool $useFragment True if errors should be in the URI fragment instead of query string
*
* @return \Psr\Http\Message\ResponseInterface
* @return ResponseInterface
*/
public function generateHttpResponse(ResponseInterface $response, $useFragment = false)
{
@@ -241,7 +270,7 @@ class OAuthServerException extends \Exception
) {
$authScheme = 'Bearer';
}
$headers[] = 'WWW-Authenticate: ' . $authScheme . ' realm="OAuth"';
$headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"';
}
// @codeCoverageIgnoreEnd
return $headers;

View File

@@ -0,0 +1,20 @@
<?php
/**
* @author Ivan Kurnosov <zerkms@zerkms.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Exception;
class UniqueTokenIdentifierConstraintViolationException extends OAuthServerException
{
public static function create()
{
$errorMessage = 'Could not create unique access token identifier';
return new static($errorMessage, 100, 'access_token_duplicate', 500);
}
}

View File

@@ -13,9 +13,12 @@ namespace League\OAuth2\Server\Grant;
use League\Event\EmitterAwareTrait;
use League\OAuth2\Server\CryptTrait;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
@@ -35,10 +38,7 @@ abstract class AbstractGrant implements GrantTypeInterface
const SCOPE_DELIMITER_STRING = ' ';
/**
* @var ServerRequestInterface
*/
protected $request;
const MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS = 10;
/**
* @var ClientRepositoryInterface
@@ -56,17 +56,17 @@ abstract class AbstractGrant implements GrantTypeInterface
protected $scopeRepository;
/**
* @var \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface
* @var AuthCodeRepositoryInterface
*/
protected $authCodeRepository;
/**
* @var \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface
* @var RefreshTokenRepositoryInterface
*/
protected $refreshTokenRepository;
/**
* @var \League\OAuth2\Server\Repositories\UserRepositoryInterface
* @var UserRepositoryInterface
*/
protected $userRepository;
@@ -100,7 +100,7 @@ abstract class AbstractGrant implements GrantTypeInterface
}
/**
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
*/
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
{
@@ -108,7 +108,7 @@ abstract class AbstractGrant implements GrantTypeInterface
}
/**
* @param \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface $authCodeRepository
* @param AuthCodeRepositoryInterface $authCodeRepository
*/
public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository)
{
@@ -116,7 +116,7 @@ abstract class AbstractGrant implements GrantTypeInterface
}
/**
* @param \League\OAuth2\Server\Repositories\UserRepositoryInterface $userRepository
* @param UserRepositoryInterface $userRepository
*/
public function setUserRepository(UserRepositoryInterface $userRepository)
{
@@ -134,38 +134,33 @@ abstract class AbstractGrant implements GrantTypeInterface
/**
* Validate the client.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param ServerRequestInterface $request
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return \League\OAuth2\Server\Entities\ClientEntityInterface
* @return ClientEntityInterface
*/
protected function validateClient(ServerRequestInterface $request)
{
$clientId = $this->getRequestParameter(
'client_id',
$request,
$this->getServerParameter('PHP_AUTH_USER', $request)
);
list($basicAuthUser, $basicAuthPassword) = $this->getBasicAuthCredentials($request);
$clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser);
if (is_null($clientId)) {
throw OAuthServerException::invalidRequest('client_id', '`%s` parameter is missing');
throw OAuthServerException::invalidRequest('client_id');
}
// If the client is confidential require the client secret
$clientSecret = $this->getRequestParameter(
'client_secret',
$request,
$this->getServerParameter('PHP_AUTH_PW', $request)
);
$clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword);
$client = $this->clientRepository->getClientEntity(
$clientId,
$this->getIdentifier(),
$clientSecret
$clientSecret,
true
);
if (!$client instanceof ClientEntityInterface) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
if ($client instanceof ClientEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
@@ -176,13 +171,13 @@ abstract class AbstractGrant implements GrantTypeInterface
is_string($client->getRedirectUri())
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
} elseif (
is_array($client->getRedirectUri())
&& in_array($redirectUri, $client->getRedirectUri()) === false
) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
}
@@ -196,9 +191,9 @@ abstract class AbstractGrant implements GrantTypeInterface
* @param string $scopes
* @param string $redirectUri
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return \League\OAuth2\Server\Entities\ScopeEntityInterface[]
* @return ScopeEntityInterface[]
*/
public function validateScopes(
$scopes,
@@ -215,7 +210,7 @@ abstract class AbstractGrant implements GrantTypeInterface
foreach ($scopesList as $scopeItem) {
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem);
if (($scope instanceof ScopeEntityInterface) === false) {
if ($scope instanceof ScopeEntityInterface === false) {
throw OAuthServerException::invalidScope($scopeItem, $redirectUri);
}
@@ -228,9 +223,9 @@ abstract class AbstractGrant implements GrantTypeInterface
/**
* Retrieve request parameter.
*
* @param string $parameter
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param mixed $default
* @param string $parameter
* @param ServerRequestInterface $request
* @param mixed $default
*
* @return null|string
*/
@@ -241,12 +236,45 @@ abstract class AbstractGrant implements GrantTypeInterface
return isset($requestParameters[$parameter]) ? $requestParameters[$parameter] : $default;
}
/**
* Retrieve HTTP Basic Auth credentials with the Authorization header
* of a request. First index of the returned array is the username,
* second is the password (so list() will work). If the header does
* not exist, or is otherwise an invalid HTTP Basic header, return
* [null, null].
*
* @param ServerRequestInterface $request
*
* @return string[]|null[]
*/
protected function getBasicAuthCredentials(ServerRequestInterface $request)
{
if (!$request->hasHeader('Authorization')) {
return [null, null];
}
$header = $request->getHeader('Authorization')[0];
if (strpos($header, 'Basic ') !== 0) {
return [null, null];
}
if (!($decoded = base64_decode(substr($header, 6)))) {
return [null, null];
}
if (strpos($decoded, ':') === false) {
return [null, null]; // HTTP Basic header without colon isn't valid
}
return explode(':', $decoded, 2);
}
/**
* Retrieve query string parameter.
*
* @param string $parameter
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param mixed $default
* @param string $parameter
* @param ServerRequestInterface $request
* @param mixed $default
*
* @return null|string
*/
@@ -258,9 +286,9 @@ abstract class AbstractGrant implements GrantTypeInterface
/**
* Retrieve cookie parameter.
*
* @param string $parameter
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param mixed $default
* @param string $parameter
* @param ServerRequestInterface $request
* @param mixed $default
*
* @return null|string
*/
@@ -272,9 +300,9 @@ abstract class AbstractGrant implements GrantTypeInterface
/**
* Retrieve server parameter.
*
* @param string $parameter
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param mixed $default
* @param string $parameter
* @param ServerRequestInterface $request
* @param mixed $default
*
* @return null|string
*/
@@ -286,12 +314,15 @@ abstract class AbstractGrant implements GrantTypeInterface
/**
* Issue an access token.
*
* @param \DateInterval $accessTokenTTL
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
* @param string $userIdentifier
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
* @param \DateInterval $accessTokenTTL
* @param ClientEntityInterface $client
* @param string $userIdentifier
* @param ScopeEntityInterface[] $scopes
*
* @return \League\OAuth2\Server\Entities\AccessTokenEntityInterface
* @throws OAuthServerException
* @throws UniqueTokenIdentifierConstraintViolationException
*
* @return AccessTokenEntityInterface
*/
protected function issueAccessToken(
\DateInterval $accessTokenTTL,
@@ -299,31 +330,44 @@ abstract class AbstractGrant implements GrantTypeInterface
$userIdentifier,
array $scopes = []
) {
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
$accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier);
$accessToken->setClient($client);
$accessToken->setUserIdentifier($userIdentifier);
$accessToken->setIdentifier($this->generateUniqueIdentifier());
$accessToken->setExpiryDateTime((new \DateTime())->add($accessTokenTTL));
foreach ($scopes as $scope) {
$accessToken->addScope($scope);
}
$this->accessTokenRepository->persistNewAccessToken($accessToken);
while ($maxGenerationAttempts-- > 0) {
$accessToken->setIdentifier($this->generateUniqueIdentifier());
try {
$this->accessTokenRepository->persistNewAccessToken($accessToken);
return $accessToken;
return $accessToken;
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
if ($maxGenerationAttempts === 0) {
throw $e;
}
}
}
}
/**
* Issue an auth code.
*
* @param \DateInterval $authCodeTTL
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
* @param string $userIdentifier
* @param string $redirectUri
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
* @param \DateInterval $authCodeTTL
* @param ClientEntityInterface $client
* @param string $userIdentifier
* @param string $redirectUri
* @param ScopeEntityInterface[] $scopes
*
* @return \League\OAuth2\Server\Entities\AuthCodeEntityInterface
* @throws OAuthServerException
* @throws UniqueTokenIdentifierConstraintViolationException
*
* @return AuthCodeEntityInterface
*/
protected function issueAuthCode(
\DateInterval $authCodeTTL,
@@ -332,8 +376,9 @@ abstract class AbstractGrant implements GrantTypeInterface
$redirectUri,
array $scopes = []
) {
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
$authCode = $this->authCodeRepository->getNewAuthCode();
$authCode->setIdentifier($this->generateUniqueIdentifier());
$authCode->setExpiryDateTime((new \DateTime())->add($authCodeTTL));
$authCode->setClient($client);
$authCode->setUserIdentifier($userIdentifier);
@@ -343,26 +388,48 @@ abstract class AbstractGrant implements GrantTypeInterface
$authCode->addScope($scope);
}
$this->authCodeRepository->persistNewAuthCode($authCode);
while ($maxGenerationAttempts-- > 0) {
$authCode->setIdentifier($this->generateUniqueIdentifier());
try {
$this->authCodeRepository->persistNewAuthCode($authCode);
return $authCode;
return $authCode;
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
if ($maxGenerationAttempts === 0) {
throw $e;
}
}
}
}
/**
* @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken
* @param AccessTokenEntityInterface $accessToken
*
* @return \League\OAuth2\Server\Entities\RefreshTokenEntityInterface
* @throws OAuthServerException
* @throws UniqueTokenIdentifierConstraintViolationException
*
* @return RefreshTokenEntityInterface
*/
protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
{
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
$refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
$refreshToken->setIdentifier($this->generateUniqueIdentifier());
$refreshToken->setExpiryDateTime((new \DateTime())->add($this->refreshTokenTTL));
$refreshToken->setAccessToken($accessToken);
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
while ($maxGenerationAttempts-- > 0) {
$refreshToken->setIdentifier($this->generateUniqueIdentifier());
try {
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
return $refreshToken;
return $refreshToken;
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
if ($maxGenerationAttempts === 0) {
throw $e;
}
}
}
}
/**
@@ -370,7 +437,7 @@ abstract class AbstractGrant implements GrantTypeInterface
*
* @param int $length
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return string
*/

View File

@@ -1,9 +1,16 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Grant;
use DateInterval;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Entities\UserEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
@@ -22,9 +29,14 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
private $authCodeTTL;
/**
* @param \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface $authCodeRepository
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
* @param \DateInterval $authCodeTTL
* @var bool
*/
private $enableCodeExchangeProof = false;
/**
* @param AuthCodeRepositoryInterface $authCodeRepository
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
* @param \DateInterval $authCodeTTL
*/
public function __construct(
AuthCodeRepositoryInterface $authCodeRepository,
@@ -37,21 +49,26 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
$this->refreshTokenTTL = new \DateInterval('P1M');
}
public function enableCodeExchangeProof()
{
$this->enableCodeExchangeProof = true;
}
/**
* Respond to an access token request.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType
* @param \DateInterval $accessTokenTTL
* @param ServerRequestInterface $request
* @param ResponseTypeInterface $responseType
* @param \DateInterval $accessTokenTTL
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
* @return ResponseTypeInterface
*/
public function respondToAccessTokenRequest(
ServerRequestInterface $request,
ResponseTypeInterface $responseType,
DateInterval $accessTokenTTL
\DateInterval $accessTokenTTL
) {
// Validate request
$client = $this->validateClient($request);
@@ -90,7 +107,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
foreach ($authCodePayload->scopes as $scopeId) {
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId);
if ($scope === false) {
if ($scope instanceof ScopeEntityInterface === false) {
// @codeCoverageIgnoreStart
throw OAuthServerException::invalidScope($scopeId);
// @codeCoverageIgnoreEnd
@@ -100,11 +117,52 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
}
// Finalize the requested scopes
$scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $authCodePayload->user_id);
$scopes = $this->scopeRepository->finalizeScopes(
$scopes,
$this->getIdentifier(),
$client,
$authCodePayload->user_id
);
} catch (\LogicException $e) {
throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code');
}
// Validate code challenge
if ($this->enableCodeExchangeProof === true) {
$codeVerifier = $this->getRequestParameter('code_verifier', $request, null);
if ($codeVerifier === null) {
throw OAuthServerException::invalidRequest('code_verifier');
}
switch ($authCodePayload->code_challenge_method) {
case 'plain':
if (hash_equals($codeVerifier, $authCodePayload->code_challenge) === false) {
throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.');
}
break;
case 'S256':
if (
hash_equals(
urlencode(base64_encode(hash('sha256', $codeVerifier))),
$authCodePayload->code_challenge
) === false
) {
throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.');
}
// @codeCoverageIgnoreStart
break;
default:
throw OAuthServerException::serverError(
sprintf(
'Unsupported code challenge method `%s`',
$authCodePayload->code_challenge_method
)
);
// @codeCoverageIgnoreEnd
}
}
// Issue and persist access + refresh tokens
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $authCodePayload->user_id, $scopes);
$refreshToken = $this->issueRefreshToken($accessToken);
@@ -157,11 +215,13 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
$client = $this->clientRepository->getClientEntity(
$clientId,
$this->getIdentifier()
$this->getIdentifier(),
null,
false
);
if ($client instanceof ClientEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
@@ -171,20 +231,22 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
is_string($client->getRedirectUri())
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
} elseif (
is_array($client->getRedirectUri())
&& in_array($redirectUri, $client->getRedirectUri()) === false
) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
}
$scopes = $this->validateScopes(
$this->getQueryStringParameter('scope', $request),
$client->getRedirectUri()
is_array($client->getRedirectUri())
? $client->getRedirectUri()[0]
: $client->getRedirectUri()
);
$stateParameter = $this->getQueryStringParameter('state', $request);
@@ -196,6 +258,31 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
$authorizationRequest->setState($stateParameter);
$authorizationRequest->setScopes($scopes);
if ($this->enableCodeExchangeProof === true) {
$codeChallenge = $this->getQueryStringParameter('code_challenge', $request);
if ($codeChallenge === null) {
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) {
throw OAuthServerException::invalidRequest(
'code_challenge_method',
'Code challenge method must be `plain` or `S256`'
);
}
$authorizationRequest->setCodeChallenge($codeChallenge);
$authorizationRequest->setCodeChallengeMethod($codeChallengeMethod);
}
return $authorizationRequest;
}
@@ -224,25 +311,43 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
$authorizationRequest->getScopes()
);
$redirectPayload['code'] = $this->encrypt(
json_encode(
[
'client_id' => $authCode->getClient()->getIdentifier(),
'redirect_uri' => $authCode->getRedirectUri(),
'auth_code_id' => $authCode->getIdentifier(),
'scopes' => $authCode->getScopes(),
'user_id' => $authCode->getUserIdentifier(),
'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'),
]
)
);
$redirectPayload['state'] = $authorizationRequest->getState();
$payload = [
'client_id' => $authCode->getClient()->getIdentifier(),
'redirect_uri' => $authCode->getRedirectUri(),
'auth_code_id' => $authCode->getIdentifier(),
'scopes' => $authCode->getScopes(),
'user_id' => $authCode->getUserIdentifier(),
'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'),
'code_challenge' => $authorizationRequest->getCodeChallenge(),
'code_challenge_method ' => $authorizationRequest->getCodeChallengeMethod(),
];
if ($this->encryptionKey === null) {
// Add padding to vary the length of the payload
$payload['_padding'] = base64_encode(random_bytes(mt_rand(8, 256)));
// Shuffle the payload so that the structure is no longer know and obvious
$keys = array_keys($payload);
shuffle($keys);
$shuffledPayload = [];
foreach ($keys as $key) {
$shuffledPayload[$key] = $payload[$key];
}
} else {
$shuffledPayload = $payload;
}
$response = new RedirectResponse();
$response->setRedirectUri(
$this->makeRedirectUri(
$finalRedirectUri,
$redirectPayload
[
'code' => $this->encrypt(
json_encode(
$shuffledPayload
)
),
'state' => $authorizationRequest->getState(),
]
)
);
@@ -252,7 +357,12 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
// The user denied the client, redirect them back with an error
throw OAuthServerException::accessDenied(
'The user denied the request',
$finalRedirectUri
$this->makeRedirectUri(
$finalRedirectUri,
[
'state' => $authorizationRequest->getState(),
]
)
);
}
}

View File

@@ -8,6 +8,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Grant;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;

View File

@@ -8,6 +8,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Grant;
use League\Event\EmitterAwareInterface;
@@ -41,11 +42,11 @@ interface GrantTypeInterface extends EmitterAwareInterface
/**
* Respond to an incoming request.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType
* @param \DateInterval $accessTokenTTL
* @param ServerRequestInterface $request
* @param ResponseTypeInterface $responseType
* @param \DateInterval $accessTokenTTL
*
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
* @return ResponseTypeInterface
*/
public function respondToAccessTokenRequest(
ServerRequestInterface $request,
@@ -56,7 +57,7 @@ interface GrantTypeInterface extends EmitterAwareInterface
/**
* The grant type should return true if it is able to response to an authorization request
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param ServerRequestInterface $request
*
* @return bool
*/
@@ -69,7 +70,7 @@ interface GrantTypeInterface extends EmitterAwareInterface
* If the validation is successful an AuthorizationRequest object will be returned. This object can be safely
* serialized in a user's session, and can be used during user authentication and authorization.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param ServerRequestInterface $request
*
* @return AuthorizationRequest
*/
@@ -80,9 +81,9 @@ interface GrantTypeInterface extends EmitterAwareInterface
* The AuthorizationRequest object's $userId property must be set to the authenticated user and the
* $authorizationApproved property must reflect their desire to authorize or deny the client.
*
* @param \League\OAuth2\Server\RequestTypes\AuthorizationRequest $authorizationRequest
* @param AuthorizationRequest $authorizationRequest
*
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
* @return ResponseTypeInterface
*/
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest);
@@ -91,7 +92,7 @@ interface GrantTypeInterface extends EmitterAwareInterface
*
* For example most grant types will check that the $_POST['grant_type'] property matches it's identifier property.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param ServerRequestInterface $request
*
* @return bool
*/
@@ -100,35 +101,42 @@ interface GrantTypeInterface extends EmitterAwareInterface
/**
* Set the client repository.
*
* @param \League\OAuth2\Server\Repositories\ClientRepositoryInterface $clientRepository
* @param ClientRepositoryInterface $clientRepository
*/
public function setClientRepository(ClientRepositoryInterface $clientRepository);
/**
* Set the access token repository.
*
* @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository
* @param AccessTokenRepositoryInterface $accessTokenRepository
*/
public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository);
/**
* Set the scope repository.
*
* @param \League\OAuth2\Server\Repositories\ScopeRepositoryInterface $scopeRepository
* @param ScopeRepositoryInterface $scopeRepository
*/
public function setScopeRepository(ScopeRepositoryInterface $scopeRepository);
/**
* Set the path to the private key.
*
* @param \League\OAuth2\Server\CryptKey $privateKey
* @param CryptKey $privateKey
*/
public function setPrivateKey(CryptKey $privateKey);
/**
* Set the path to the public key.
*
* @param \League\OAuth2\Server\CryptKey $publicKey
* @param CryptKey $publicKey
*/
public function setPublicKey(CryptKey $publicKey);
/**
* Set the encryption key
*
* @param string|null $key
*/
public function setEncryptionKey($key = null);
}

View File

@@ -1,10 +1,18 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Grant;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\UserEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\RequestEvent;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
@@ -23,10 +31,29 @@ class ImplicitGrant extends AbstractAuthorizeGrant
*/
public function __construct(\DateInterval $accessTokenTTL)
{
$this->refreshTokenTTL = new \DateInterval('P1M');
$this->accessTokenTTL = $accessTokenTTL;
}
/**
* @param \DateInterval $refreshTokenTTL
*
* @throw \LogicException
*/
public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL)
{
throw new \LogicException('The Implicit Grant does not return refresh tokens');
}
/**
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
*
* @throw \LogicException
*/
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
{
throw new \LogicException('The Implicit Grant does not return refresh tokens');
}
/**
* {@inheritdoc}
*/
@@ -48,11 +75,11 @@ class ImplicitGrant extends AbstractAuthorizeGrant
/**
* Respond to an incoming request.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType
* @param \DateInterval $accessTokenTTL
* @param ServerRequestInterface $request
* @param ResponseTypeInterface $responseType
* @param \DateInterval $accessTokenTTL
*
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
* @return ResponseTypeInterface
*/
public function respondToAccessTokenRequest(
ServerRequestInterface $request,
@@ -90,11 +117,13 @@ class ImplicitGrant extends AbstractAuthorizeGrant
$client = $this->clientRepository->getClientEntity(
$clientId,
$this->getIdentifier()
$this->getIdentifier(),
null,
false
);
if ($client instanceof ClientEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
@@ -104,20 +133,29 @@ class ImplicitGrant extends AbstractAuthorizeGrant
is_string($client->getRedirectUri())
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
} elseif (
is_array($client->getRedirectUri())
&& in_array($redirectUri, $client->getRedirectUri()) === false
) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
}
$scopes = $this->validateScopes(
$this->getQueryStringParameter('scope', $request),
$client->getRedirectUri()
is_array($client->getRedirectUri())
? $client->getRedirectUri()[0]
: $client->getRedirectUri()
);
// Finalize the requested scopes
$scopes = $this->scopeRepository->finalizeScopes(
$scopes,
$this->getIdentifier(),
$client
);
$stateParameter = $this->getQueryStringParameter('state', $request);
@@ -156,15 +194,16 @@ class ImplicitGrant extends AbstractAuthorizeGrant
$authorizationRequest->getScopes()
);
$redirectPayload['access_token'] = (string) $accessToken->convertToJWT($this->privateKey);
$redirectPayload['token_type'] = 'bearer';
$redirectPayload['expires_in'] = $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp();
$response = new RedirectResponse();
$response->setRedirectUri(
$this->makeRedirectUri(
$finalRedirectUri,
$redirectPayload,
[
'access_token' => (string) $accessToken->convertToJWT($this->privateKey),
'token_type' => 'bearer',
'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(),
'state' => $authorizationRequest->getState(),
],
'#'
)
);
@@ -175,7 +214,12 @@ class ImplicitGrant extends AbstractAuthorizeGrant
// The user denied the client, redirect them back with an error
throw OAuthServerException::accessDenied(
'The user denied the request',
$finalRedirectUri
$this->makeRedirectUri(
$finalRedirectUri,
[
'state' => $authorizationRequest->getState(),
]
)
);
}
}

View File

@@ -8,6 +8,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Grant;
use League\OAuth2\Server\Entities\ClientEntityInterface;
@@ -25,8 +26,8 @@ use Psr\Http\Message\ServerRequestInterface;
class PasswordGrant extends AbstractGrant
{
/**
* @param \League\OAuth2\Server\Repositories\UserRepositoryInterface $userRepository
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
* @param UserRepositoryInterface $userRepository
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
*/
public function __construct(
UserRepositoryInterface $userRepository,
@@ -66,23 +67,23 @@ class PasswordGrant extends AbstractGrant
}
/**
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
* @param ServerRequestInterface $request
* @param ClientEntityInterface $client
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return \League\OAuth2\Server\Entities\UserEntityInterface
* @return UserEntityInterface
*/
protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client)
{
$username = $this->getRequestParameter('username', $request);
if (is_null($username)) {
throw OAuthServerException::invalidRequest('username', '`%s` parameter is missing');
throw OAuthServerException::invalidRequest('username');
}
$password = $this->getRequestParameter('password', $request);
if (is_null($password)) {
throw OAuthServerException::invalidRequest('password', '`%s` parameter is missing');
throw OAuthServerException::invalidRequest('password');
}
$user = $this->userRepository->getUserEntityByUserCredentials(
@@ -91,8 +92,8 @@ class PasswordGrant extends AbstractGrant
$this->getIdentifier(),
$client
);
if (!$user instanceof UserEntityInterface) {
$this->getEmitter()->emit(new RequestEvent('user.authentication.failed', $request));
if ($user instanceof UserEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidCredentials();
}

View File

@@ -8,8 +8,10 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
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;
@@ -22,7 +24,7 @@ use Psr\Http\Message\ServerRequestInterface;
class RefreshTokenGrant extends AbstractGrant
{
/**
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
*/
public function __construct(RefreshTokenRepositoryInterface $refreshTokenRepository)
{
@@ -49,7 +51,7 @@ class RefreshTokenGrant extends AbstractGrant
$scopes = array_map(function ($scopeId) use ($client) {
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId);
if (!$scope) {
if ($scope instanceof ScopeEntityInterface === false) {
// @codeCoverageIgnoreStart
throw OAuthServerException::invalidScope($scopeId);
// @codeCoverageIgnoreEnd
@@ -83,10 +85,10 @@ class RefreshTokenGrant extends AbstractGrant
}
/**
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param string $clientId
* @param ServerRequestInterface $request
* @param string $clientId
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return array
*/
@@ -100,18 +102,14 @@ class RefreshTokenGrant extends AbstractGrant
// Validate refresh token
try {
$refreshToken = $this->decrypt($encryptedRefreshToken);
} catch (\LogicException $e) {
throw OAuthServerException::invalidRefreshToken('Cannot parse refresh token: ' . $e->getMessage());
} catch (\Exception $e) {
throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token');
}
$refreshTokenData = json_decode($refreshToken, true);
if ($refreshTokenData['client_id'] !== $clientId) {
$this->getEmitter()->emit(new RequestEvent('refresh_token.client.failed', $request));
throw OAuthServerException::invalidRefreshToken(
'Token is not linked to client,' .
' got: ' . $clientId .
' expected: ' . $refreshTokenData['client_id']
);
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_CLIENT_FAILED, $request));
throw OAuthServerException::invalidRefreshToken('Token is not linked to client');
}
if ($refreshTokenData['expire_time'] < time()) {

View File

@@ -1,35 +1,40 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Middleware;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Server;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class AuthenticationServerMiddleware
class AuthorizationServerMiddleware
{
/**
* @var \League\OAuth2\Server\Server
* @var AuthorizationServer
*/
private $server;
/**
* AuthenticationServerMiddleware constructor.
*
* @param \League\OAuth2\Server\Server $server
* @param AuthorizationServer $server
*/
public function __construct(Server $server)
public function __construct(AuthorizationServer $server)
{
$this->server = $server;
}
/**
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \Psr\Http\Message\ResponseInterface $response
* @param callable $next
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return \Psr\Http\Message\ResponseInterface
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
@@ -39,9 +44,8 @@ class AuthenticationServerMiddleware
return $exception->generateHttpResponse($response);
// @codeCoverageIgnoreStart
} catch (\Exception $exception) {
$response->getBody()->write($exception->getMessage());
return $response->withStatus(500);
return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
->generateHttpResponse($response);
// @codeCoverageIgnoreEnd
}

View File

@@ -1,33 +1,38 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Middleware;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Server;
use League\OAuth2\Server\ResourceServer;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class ResourceServerMiddleware
{
/**
* @var \League\OAuth2\Server\Server
* @var ResourceServer
*/
private $server;
/**
* ResourceServerMiddleware constructor.
*
* @param \League\OAuth2\Server\Server $server
* @param ResourceServer $server
*/
public function __construct(Server $server)
public function __construct(ResourceServer $server)
{
$this->server = $server;
}
/**
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \Psr\Http\Message\ResponseInterface $response
* @param callable $next
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return \Psr\Http\Message\ResponseInterface
*/
@@ -39,9 +44,8 @@ class ResourceServerMiddleware
return $exception->generateHttpResponse($response);
// @codeCoverageIgnoreStart
} catch (\Exception $exception) {
$response->getBody()->write($exception->getMessage());
return $response->withStatus(500);
return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
->generateHttpResponse($response);
// @codeCoverageIgnoreEnd
}

View File

@@ -1,17 +1,17 @@
<?php
/**
* OAuth 2.0 Access token storage interface.
*
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
/**
* Access token interface.
@@ -21,9 +21,9 @@ interface AccessTokenRepositoryInterface extends RepositoryInterface
/**
* Create a new access token
*
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
* @param mixed $userIdentifier
* @param ClientEntityInterface $clientEntity
* @param ScopeEntityInterface[] $scopes
* @param mixed $userIdentifier
*
* @return AccessTokenEntityInterface
*/
@@ -32,7 +32,7 @@ interface AccessTokenRepositoryInterface extends RepositoryInterface
/**
* Persists a new access token to permanent storage.
*
* @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessTokenEntity
* @param AccessTokenEntityInterface $accessTokenEntity
*/
public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity);

View File

@@ -1,13 +1,12 @@
<?php
/**
* OAuth 2.0 Auth code storage interface.
*
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
@@ -20,14 +19,14 @@ interface AuthCodeRepositoryInterface extends RepositoryInterface
/**
* Creates a new AuthCode
*
* @return \League\OAuth2\Server\Entities\AuthCodeEntityInterface
* @return AuthCodeEntityInterface
*/
public function getNewAuthCode();
/**
* Persists a new auth code to permanent storage.
*
* @param \League\OAuth2\Server\Entities\AuthCodeEntityInterface $authCodeEntity
* @param AuthCodeEntityInterface $authCodeEntity
*/
public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity);

View File

@@ -1,15 +1,16 @@
<?php
/**
* OAuth 2.0 Client storage interface.
*
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
/**
* Client storage interface.
*/
@@ -18,11 +19,13 @@ interface ClientRepositoryInterface extends RepositoryInterface
/**
* Get a client.
*
* @param string $clientIdentifier The client's identifier
* @param string $grantType The grant type used
* @param null|string $clientSecret The client's secret (if sent)
* @param string $clientIdentifier The client's identifier
* @param string $grantType The grant type used
* @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 \League\OAuth2\Server\Entities\ClientEntityInterface
* @return ClientEntityInterface
*/
public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null);
public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true);
}

View File

@@ -1,13 +1,12 @@
<?php
/**
* OAuth 2.0 Refresh token storage interface.
*
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
@@ -27,7 +26,7 @@ interface RefreshTokenRepositoryInterface extends RepositoryInterface
/**
* Create a new refresh token_name.
*
* @param \League\OAuth2\Server\Entities\RefreshTokenEntityInterface $refreshTokenEntity
* @param RefreshTokenEntityInterface $refreshTokenEntity
*/
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity);

View File

@@ -1,13 +1,12 @@
<?php
/**
* OAuth 2.0 Repository interface.
*
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Repositories;
/**

View File

@@ -1,13 +1,12 @@
<?php
/**
* OAuth 2.0 Scope storage interface.
*
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
@@ -23,7 +22,7 @@ interface ScopeRepositoryInterface extends RepositoryInterface
*
* @param string $identifier The scope identifier
*
* @return \League\OAuth2\Server\Entities\ScopeEntityInterface
* @return ScopeEntityInterface
*/
public function getScopeEntityByIdentifier($identifier);
@@ -31,12 +30,12 @@ interface ScopeRepositoryInterface extends RepositoryInterface
* Given a client, grant type and optional user identifier validate the set of scopes requested are valid and optionally
* append additional scopes or remove requested scopes.
*
* @param ScopeEntityInterface[] $scopes
* @param string $grantType
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity
* @param null|string $userIdentifier
* @param ScopeEntityInterface[] $scopes
* @param string $grantType
* @param ClientEntityInterface $clientEntity
* @param null|string $userIdentifier
*
* @return \League\OAuth2\Server\Entities\ScopeEntityInterface[]
* @return ScopeEntityInterface[]
*/
public function finalizeScopes(
array $scopes,

View File

@@ -1,20 +1,28 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\UserEntityInterface;
interface UserRepositoryInterface extends RepositoryInterface
{
/**
* Get a user entity.
*
* @param string $username
* @param string $password
* @param string $grantType The grant type used
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity
* @param string $username
* @param string $password
* @param string $grantType The grant type used
* @param ClientEntityInterface $clientEntity
*
* @return \League\OAuth2\Server\Entities\UserEntityInterface
* @return UserEntityInterface
*/
public function getUserEntityByUserCredentials(
$username,

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server;
@@ -7,16 +14,20 @@ use Psr\Http\Message\ServerRequestInterface;
class RequestEvent extends Event
{
const CLIENT_AUTHENTICATION_FAILED = 'client.authentication.failed';
const USER_AUTHENTICATION_FAILED = 'user.authentication.failed';
const REFRESH_TOKEN_CLIENT_FAILED = 'refresh_token.client.failed';
/**
* @var \Psr\Http\Message\ServerRequestInterface
* @var ServerRequestInterface
*/
private $request;
/**
* RequestEvent constructor.
*
* @param string $name
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param string $name
* @param ServerRequestInterface $request
*/
public function __construct($name, ServerRequestInterface $request)
{

View File

@@ -1,4 +1,11 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\RequestTypes;
@@ -57,6 +64,20 @@ class AuthorizationRequest
*/
protected $state;
/**
* The code challenge (if provided)
*
* @var string
*/
protected $codeChallenge;
/**
* The code challenge method (if provided)
*
* @var string
*/
protected $codeChallengeMethod;
/**
* @return string
*/
@@ -106,7 +127,7 @@ class AuthorizationRequest
}
/**
* @return \League\OAuth2\Server\Entities\ScopeEntityInterface[]
* @return ScopeEntityInterface[]
*/
public function getScopes()
{
@@ -114,9 +135,9 @@ class AuthorizationRequest
}
/**
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
* @param ScopeEntityInterface[] $scopes
*/
public function setScopes($scopes)
public function setScopes(array $scopes)
{
$this->scopes = $scopes;
}
@@ -168,4 +189,36 @@ class AuthorizationRequest
{
$this->state = $state;
}
/**
* @return string
*/
public function getCodeChallenge()
{
return $this->codeChallenge;
}
/**
* @param string $codeChallenge
*/
public function setCodeChallenge($codeChallenge)
{
$this->codeChallenge = $codeChallenge;
}
/**
* @return string
*/
public function getCodeChallengeMethod()
{
return $this->codeChallengeMethod;
}
/**
* @param string $codeChallengeMethod
*/
public function setCodeChallengeMethod($codeChallengeMethod)
{
$this->codeChallengeMethod = $codeChallengeMethod;
}
}

84
src/ResourceServer.php Normal file
View File

@@ -0,0 +1,84 @@
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server;
use League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface;
use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use Psr\Http\Message\ServerRequestInterface;
class ResourceServer
{
/**
* @var AccessTokenRepositoryInterface
*/
private $accessTokenRepository;
/**
* @var CryptKey
*/
private $publicKey;
/**
* @var null|AuthorizationValidatorInterface
*/
private $authorizationValidator;
/**
* New server instance.
*
* @param AccessTokenRepositoryInterface $accessTokenRepository
* @param CryptKey|string $publicKey
* @param null|AuthorizationValidatorInterface $authorizationValidator
*/
public function __construct(
AccessTokenRepositoryInterface $accessTokenRepository,
$publicKey,
AuthorizationValidatorInterface $authorizationValidator = null
) {
$this->accessTokenRepository = $accessTokenRepository;
if ($publicKey instanceof CryptKey === false) {
$publicKey = new CryptKey($publicKey);
}
$this->publicKey = $publicKey;
$this->authorizationValidator = $authorizationValidator;
}
/**
* @return AuthorizationValidatorInterface
*/
protected function getAuthorizationValidator()
{
if ($this->authorizationValidator instanceof AuthorizationValidatorInterface === false) {
$this->authorizationValidator = new BearerTokenValidator($this->accessTokenRepository);
}
$this->authorizationValidator->setPublicKey($this->publicKey);
return $this->authorizationValidator;
}
/**
* Determine the access token validity.
*
* @param ServerRequestInterface $request
*
* @throws OAuthServerException
*
* @return ServerRequestInterface
*/
public function validateAuthenticatedRequest(ServerRequestInterface $request)
{
return $this->getAuthorizationValidator()->validateAuthorization($request);
}
}

View File

@@ -8,6 +8,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\ResponseTypes;
use League\OAuth2\Server\CryptTrait;
@@ -19,12 +20,12 @@ abstract class AbstractResponseType implements ResponseTypeInterface
use CryptTrait;
/**
* @var \League\OAuth2\Server\Entities\AccessTokenEntityInterface
* @var AccessTokenEntityInterface
*/
protected $accessToken;
/**
* @var \League\OAuth2\Server\Entities\RefreshTokenEntityInterface
* @var RefreshTokenEntityInterface
*/
protected $refreshToken;

View File

@@ -1,6 +1,6 @@
<?php
/**
* OAuth 2.0 Bearer Token Type.
* OAuth 2.0 Bearer Token Response.
*
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
@@ -8,8 +8,10 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\ResponseTypes;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use Psr\Http\Message\ResponseInterface;
@@ -39,7 +41,7 @@ class BearerTokenResponse extends AbstractResponseType
'access_token_id' => $this->accessToken->getIdentifier(),
'scopes' => $this->accessToken->getScopes(),
'user_id' => $this->accessToken->getUserIdentifier(),
'expire_time' => $expireDateTime,
'expire_time' => $this->refreshToken->getExpiryDateTime()->getTimestamp(),
]
)
);
@@ -47,6 +49,8 @@ class BearerTokenResponse extends AbstractResponseType
$responseParams['refresh_token'] = $refreshToken;
}
$responseParams = array_merge($this->getExtraParams($this->accessToken), $responseParams);
$response = $response
->withStatus(200)
->withHeader('pragma', 'no-cache')
@@ -57,4 +61,18 @@ class BearerTokenResponse extends AbstractResponseType
return $response;
}
/**
* Add custom fields to your Bearer Token response here, then override
* AuthorizationServer::getResponseType() to pull in your version of
* this class rather than the default.
*
* @param AccessTokenEntityInterface $accessToken
*
* @return array
*/
protected function getExtraParams(AccessTokenEntityInterface $accessToken)
{
return [];
}
}

View File

@@ -1,4 +1,13 @@
<?php
/**
* OAuth 2.0 Redirect Response.
*
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\ResponseTypes;

View File

@@ -8,6 +8,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\ResponseTypes;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
@@ -17,12 +18,12 @@ use Psr\Http\Message\ResponseInterface;
interface ResponseTypeInterface
{
/**
* @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken
* @param AccessTokenEntityInterface $accessToken
*/
public function setAccessToken(AccessTokenEntityInterface $accessToken);
/**
* @param \League\OAuth2\Server\Entities\RefreshTokenEntityInterface $refreshToken
* @param RefreshTokenEntityInterface $refreshToken
*/
public function setRefreshToken(RefreshTokenEntityInterface $refreshToken);
@@ -32,4 +33,11 @@ interface ResponseTypeInterface
* @return ResponseInterface
*/
public function generateHttpResponse(ResponseInterface $response);
/**
* Set the encryption key
*
* @param string|null $key
*/
public function setEncryptionKey($key = null);
}

View File

@@ -1,245 +0,0 @@
<?php
namespace League\OAuth2\Server;
use DateInterval;
use League\Event\EmitterAwareInterface;
use League\Event\EmitterAwareTrait;
use League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface;
use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\GrantTypeInterface;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class Server implements EmitterAwareInterface
{
use EmitterAwareTrait;
/**
* @var \League\OAuth2\Server\Grant\GrantTypeInterface[]
*/
protected $enabledGrantTypes = [];
/**
* @var \DateInterval[]
*/
protected $grantTypeAccessTokenTTL = [];
/**
* @var \League\OAuth2\Server\CryptKey
*/
protected $privateKey;
/**
* @var \League\OAuth2\Server\CryptKey
*/
protected $publicKey;
/**
* @var ResponseTypeInterface
*/
protected $responseType;
/**
* @var \League\OAuth2\Server\Repositories\ClientRepositoryInterface
*/
private $clientRepository;
/**
* @var \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface
*/
private $accessTokenRepository;
/**
* @var \League\OAuth2\Server\Repositories\ScopeRepositoryInterface
*/
private $scopeRepository;
/**
* @var \League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface
*/
private $authorizationValidator;
/**
* New server instance.
*
* @param \League\OAuth2\Server\Repositories\ClientRepositoryInterface $clientRepository
* @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository
* @param \League\OAuth2\Server\Repositories\ScopeRepositoryInterface $scopeRepository
* @param \League\OAuth2\Server\CryptKey|string $privateKey
* @param \League\OAuth2\Server\CryptKey|string $publicKey
* @param null|\League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType
* @param null|\League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface $authorizationValidator
*/
public function __construct(
ClientRepositoryInterface $clientRepository,
AccessTokenRepositoryInterface $accessTokenRepository,
ScopeRepositoryInterface $scopeRepository,
$privateKey,
$publicKey,
ResponseTypeInterface $responseType = null,
AuthorizationValidatorInterface $authorizationValidator = null
) {
$this->clientRepository = $clientRepository;
$this->accessTokenRepository = $accessTokenRepository;
$this->scopeRepository = $scopeRepository;
if (!$privateKey instanceof CryptKey) {
$privateKey = new CryptKey($privateKey);
}
$this->privateKey = $privateKey;
if (!$publicKey instanceof CryptKey) {
$publicKey = new CryptKey($publicKey);
}
$this->publicKey = $publicKey;
$this->responseType = $responseType;
$this->authorizationValidator = $authorizationValidator;
}
/**
* Enable a grant type on the server.
*
* @param \League\OAuth2\Server\Grant\GrantTypeInterface $grantType
* @param \DateInterval $accessTokenTTL
*/
public function enableGrantType(GrantTypeInterface $grantType, DateInterval $accessTokenTTL = null)
{
if ($accessTokenTTL instanceof DateInterval === false) {
$accessTokenTTL = new \DateInterval('PT1H');
}
$grantType->setAccessTokenRepository($this->accessTokenRepository);
$grantType->setClientRepository($this->clientRepository);
$grantType->setScopeRepository($this->scopeRepository);
$grantType->setPrivateKey($this->privateKey);
$grantType->setPublicKey($this->publicKey);
$grantType->setEmitter($this->getEmitter());
$this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType;
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] = $accessTokenTTL;
}
/**
* Validate an authorization request
*
* @param \Psr\Http\Message\ServerRequestInterface $request
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
*
* @return \League\OAuth2\Server\RequestTypes\AuthorizationRequest|null
*/
public function validateAuthorizationRequest(ServerRequestInterface $request)
{
$authRequest = null;
$enabledGrantTypes = $this->enabledGrantTypes;
while ($authRequest === null && $grantType = array_shift($enabledGrantTypes)) {
/** @var \League\OAuth2\Server\Grant\GrantTypeInterface $grantType */
if ($grantType->canRespondToAuthorizationRequest($request)) {
$authRequest = $grantType->validateAuthorizationRequest($request);
return $authRequest;
}
}
throw OAuthServerException::unsupportedGrantType();
}
/**
* Complete an authorization request
*
* @param \League\OAuth2\Server\RequestTypes\AuthorizationRequest $authRequest
* @param \Psr\Http\Message\ResponseInterface $response
*
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
*/
public function completeAuthorizationRequest(AuthorizationRequest $authRequest, ResponseInterface $response)
{
return $this->enabledGrantTypes[$authRequest->getGrantTypeId()]
->completeAuthorizationRequest($authRequest)
->generateHttpResponse($response);
}
/**
* Return an access token response.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \Psr\Http\Message\ResponseInterface $response
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
*
* @return \Psr\Http\Message\ResponseInterface
*/
public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response)
{
$tokenResponse = null;
while ($tokenResponse === null && $grantType = array_shift($this->enabledGrantTypes)) {
/** @var \League\OAuth2\Server\Grant\GrantTypeInterface $grantType */
if ($grantType->canRespondToAccessTokenRequest($request)) {
$tokenResponse = $grantType->respondToAccessTokenRequest(
$request,
$this->getResponseType(),
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()]
);
}
}
if ($tokenResponse instanceof ResponseTypeInterface) {
return $tokenResponse->generateHttpResponse($response);
}
throw OAuthServerException::unsupportedGrantType();
}
/**
* Determine the access token validity.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
*
* @return \Psr\Http\Message\ServerRequestInterface
*/
public function validateAuthenticatedRequest(ServerRequestInterface $request)
{
return $this->getAuthorizationValidator()->validateAuthorization($request);
}
/**
* Get the token type that grants will return in the HTTP response.
*
* @return ResponseTypeInterface
*/
protected function getResponseType()
{
if (!$this->responseType instanceof ResponseTypeInterface) {
$this->responseType = new BearerTokenResponse($this->accessTokenRepository);
}
$this->responseType->setPrivateKey($this->privateKey);
return $this->responseType;
}
/**
* @return \League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface
*/
protected function getAuthorizationValidator()
{
if (!$this->authorizationValidator instanceof AuthorizationValidatorInterface) {
$this->authorizationValidator = new BearerTokenValidator($this->accessTokenRepository);
}
$this->authorizationValidator->setPublicKey($this->publicKey);
return $this->authorizationValidator;
}
}

View File

@@ -2,6 +2,7 @@
namespace LeagueTests;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\AuthCodeGrant;
@@ -13,7 +14,6 @@ use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
use League\OAuth2\Server\Server;
use LeagueTests\Stubs\AccessTokenEntity;
use LeagueTests\Stubs\AuthCodeEntity;
use LeagueTests\Stubs\ClientEntity;
@@ -24,18 +24,19 @@ use Zend\Diactoros\Response;
use Zend\Diactoros\ServerRequest;
use Zend\Diactoros\ServerRequestFactory;
class ServerTest extends \PHPUnit_Framework_TestCase
class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
{
public function testRespondToRequestInvalidGrantType()
{
$server = new Server(
$this->getMock(ClientRepositoryInterface::class),
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
$server = new AuthorizationServer(
$this->getMockBuilder(ClientRepositoryInterface::class)->getMock(),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/Stubs/private.key',
'file://' . __DIR__ . '/Stubs/public.key',
new StubResponseType()
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M'));
@@ -49,16 +50,16 @@ class ServerTest extends \PHPUnit_Framework_TestCase
public function testRespondToRequest()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepository->method('getClientEntity')->willReturn(new ClientEntity());
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$accessTokenRepositoryMock = $this->getMock(AccessTokenRepositoryInterface::class);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$server = new Server(
$server = new AuthorizationServer(
$clientRepository,
$accessTokenRepositoryMock,
$scopeRepositoryMock,
@@ -66,6 +67,7 @@ class ServerTest extends \PHPUnit_Framework_TestCase
'file://' . __DIR__ . '/Stubs/public.key',
new StubResponseType()
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M'));
@@ -78,15 +80,16 @@ class ServerTest extends \PHPUnit_Framework_TestCase
public function testGetResponseType()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$server = new Server(
$server = new AuthorizationServer(
$clientRepository,
$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'
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$abstractGrantReflection = new \ReflectionClass($server);
$method = $abstractGrantReflection->getMethod('getResponseType');
@@ -95,43 +98,25 @@ class ServerTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($method->invoke($server) instanceof BearerTokenResponse);
}
public function testValidateAuthenticatedRequest()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$server = new Server(
$clientRepository,
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
'file://' . __DIR__ . '/Stubs/private.key',
'file://' . __DIR__ . '/Stubs/public.key'
);
try {
$server->validateAuthenticatedRequest(ServerRequestFactory::fromGlobals());
} catch (OAuthServerException $e) {
$this->assertEquals('Missing "Authorization" header', $e->getHint());
}
}
public function testCompleteAuthorizationRequest()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$server = new Server(
$server = new AuthorizationServer(
$clientRepository,
$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'
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock();
$authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
$grant = new AuthCodeGrant(
$authCodeRepository,
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
@@ -158,19 +143,20 @@ class ServerTest extends \PHPUnit_Framework_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 Server(
$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'
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$server->enableGrantType($grant);
$request = new ServerRequest(
@@ -196,13 +182,14 @@ class ServerTest extends \PHPUnit_Framework_TestCase
*/
public function testValidateAuthorizationRequestUnregistered()
{
$server = new Server(
$this->getMock(ClientRepositoryInterface::class),
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
$server = new AuthorizationServer(
$this->getMockBuilder(ClientRepositoryInterface::class)->getMock(),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/Stubs/private.key',
'file://' . __DIR__ . '/Stubs/public.key'
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$request = new ServerRequest(
[],

36
tests/CryptKeyTest.php Normal file
View File

@@ -0,0 +1,36 @@
<?php
namespace LeagueTests\Utils;
use League\OAuth2\Server\CryptKey;
class CryptKeyTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException \LogicException
*/
public function testNoFile()
{
new CryptKey('undefined file');
}
public function testKeyCreation()
{
$keyFile = __DIR__ . '/Stubs/public.key';
$key = new CryptKey($keyFile, 'secret');
$this->assertEquals('file://' . $keyFile, $key->getKeyPath());
$this->assertEquals('secret', $key->getPassPhrase());
}
public function testKeyFileCreation()
{
$keyContent = file_get_contents(__DIR__ . '/Stubs/public.key');
$key = new CryptKey($keyContent);
$this->assertEquals(
'file://' . sys_get_temp_dir() . '/' . sha1($keyContent) . '.key',
$key->getKeyPath()
);
}
}

View File

@@ -32,6 +32,76 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
$grantMock->setEmitter(new Emitter());
}
public function testHttpBasicWithPassword()
{
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withHeader('Authorization', 'Basic ' . base64_encode('Open:Sesame'));
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
$this->assertSame(['Open', 'Sesame'], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
public function testHttpBasicNoPassword()
{
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withHeader('Authorization', 'Basic ' . base64_encode('Open:'));
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
$this->assertSame(['Open', ''], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
public function testHttpBasicNotBasic()
{
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withHeader('Authorization', 'Foo ' . base64_encode('Open:Sesame'));
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
$this->assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
public function testHttpBasicNotBase64()
{
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withHeader('Authorization', 'Basic ||');
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
$this->assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
public function testHttpBasicNoColon()
{
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withHeader('Authorization', 'Basic ' . base64_encode('OpenSesame'));
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
$this->assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
public function testValidateClientPublic()
{
$client = new ClientEntity();
@@ -256,7 +326,7 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
public function testIssueRefreshToken()
{
$refreshTokenRepoMock = $this->getMock(RefreshTokenRepositoryInterface::class);
$refreshTokenRepoMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepoMock
->expects($this->once())
->method('getNewRefreshToken')
@@ -275,13 +345,12 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
/** @var RefreshTokenEntityInterface $refreshToken */
$refreshToken = $issueRefreshTokenMethod->invoke($grantMock, $accessToken);
$this->assertTrue($refreshToken instanceof RefreshTokenEntityInterface);
$this->assertFalse($refreshToken->isExpired());
$this->assertEquals($accessToken, $refreshToken->getAccessToken());
}
public function testIssueAccessToken()
{
$accessTokenRepoMock = $this->getMock(AccessTokenRepositoryInterface::class);
$accessTokenRepoMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepoMock->method('getNewToken')->willReturn(new AccessTokenEntity());
/** @var AbstractGrant $grantMock */
@@ -301,12 +370,11 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
[new ScopeEntity()]
);
$this->assertTrue($accessToken instanceof AccessTokenEntityInterface);
$this->assertFalse($accessToken->isExpired());
}
public function testIssueAuthCode()
{
$authCodeRepoMock = $this->getMock(AuthCodeRepositoryInterface::class);
$authCodeRepoMock = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock();
$authCodeRepoMock->expects($this->once())->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
/** @var AbstractGrant $grantMock */

File diff suppressed because it is too large Load Diff

View File

@@ -3,14 +3,19 @@
namespace LeagueTests\Grant;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
use League\OAuth2\Server\Grant\ImplicitGrant;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
use LeagueTests\Stubs\AccessTokenEntity;
use LeagueTests\Stubs\ClientEntity;
use LeagueTests\Stubs\CryptTraitStub;
use LeagueTests\Stubs\ScopeEntity;
use LeagueTests\Stubs\StubResponseType;
use LeagueTests\Stubs\UserEntity;
use Zend\Diactoros\ServerRequest;
@@ -83,8 +88,14 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase
$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(
[],
@@ -111,8 +122,14 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase
$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(
[],
@@ -295,4 +312,104 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase
$grant->completeAuthorizationRequest($authRequest);
}
public function testAccessTokenRepositoryUniqueConstraintCheck()
{
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
$authRequest->setClient(new ClientEntity());
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
/** @var AccessTokenRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject $accessTokenRepositoryMock */
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->expects($this->at(0))->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
$accessTokenRepositoryMock->expects($this->at(1))->method('persistNewAccessToken')->willReturnSelf();
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse);
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
* @expectedExceptionCode 7
*/
public function testAccessTokenRepositoryFailToPersist()
{
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
$authRequest->setClient(new ClientEntity());
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
/** @var AccessTokenRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject $accessTokenRepositoryMock */
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willThrowException(OAuthServerException::serverError('something bad happened'));
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->completeAuthorizationRequest($authRequest);
}
/**
* @expectedException \League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException
* @expectedExceptionCode 100
*/
public function testAccessTokenRepositoryFailToPersistUniqueNoInfiniteLoop()
{
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
$authRequest->setClient(new ClientEntity());
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
/** @var AccessTokenRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject $accessTokenRepositoryMock */
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->completeAuthorizationRequest($authRequest);
}
/**
* @expectedException \LogicException
*/
public function testSetRefreshTokenTTL()
{
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$grant->setRefreshTokenTTL(new \DateInterval('PT10M'));
}
/**
* @expectedException \LogicException
*/
public function testSetRefreshTokenRepository()
{
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
}
/**
* @expectedException \LogicException
*/
public function testCompleteAuthorizationRequestNoUser()
{
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$grant->completeAuthorizationRequest(new AuthorizationRequest());
}
}

View File

@@ -21,8 +21,8 @@ class PasswordGrantTest extends \PHPUnit_Framework_TestCase
{
public function testGetIdentifier()
{
$userRepositoryMock = $this->getMock(UserRepositoryInterface::class);
$refreshTokenRepositoryMock = $this->getMock(RefreshTokenRepositoryInterface::class);
$userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
$this->assertEquals('password', $grant->getIdentifier());

View File

@@ -32,7 +32,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase
public function testGetIdentifier()
{
$refreshTokenRepositoryMock = $this->getMock(RefreshTokenRepositoryInterface::class);
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
$this->assertEquals('refresh_token', $grant->getIdentifier());

View File

@@ -2,33 +2,33 @@
namespace LeagueTests\Middleware;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
use League\OAuth2\Server\Middleware\AuthenticationServerMiddleware;
use League\OAuth2\Server\Middleware\AuthorizationServerMiddleware;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use League\OAuth2\Server\Server;
use LeagueTests\Stubs\AccessTokenEntity;
use LeagueTests\Stubs\ClientEntity;
use LeagueTests\Stubs\StubResponseType;
use Zend\Diactoros\Response;
use Zend\Diactoros\ServerRequestFactory;
class AuthenticationServerMiddlewareTest extends \PHPUnit_Framework_TestCase
class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase
{
public function testValidResponse()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepository->method('getClientEntity')->willReturn(new ClientEntity());
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$accessRepositoryMock = $this->getMock(AccessTokenRepositoryInterface::class);
$accessRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$server = new Server(
$server = new AuthorizationServer(
$clientRepository,
$accessRepositoryMock,
$scopeRepositoryMock,
@@ -36,6 +36,7 @@ class AuthenticationServerMiddlewareTest extends \PHPUnit_Framework_TestCase
'file://' . __DIR__ . '/../Stubs/public.key',
new StubResponseType()
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$server->enableGrantType(new ClientCredentialsGrant());
@@ -45,7 +46,7 @@ class AuthenticationServerMiddlewareTest extends \PHPUnit_Framework_TestCase
$request = ServerRequestFactory::fromGlobals();
$middleware = new AuthenticationServerMiddleware($server);
$middleware = new AuthorizationServerMiddleware($server);
$response = $middleware->__invoke(
$request,
new Response(),
@@ -58,17 +59,18 @@ class AuthenticationServerMiddlewareTest extends \PHPUnit_Framework_TestCase
public function testOAuthErrorResponse()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepository->method('getClientEntity')->willReturn(null);
$server = new Server(
$server = new AuthorizationServer(
$clientRepository,
$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',
new StubResponseType()
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M'));
@@ -78,7 +80,7 @@ class AuthenticationServerMiddlewareTest extends \PHPUnit_Framework_TestCase
$request = ServerRequestFactory::fromGlobals();
$middleware = new AuthenticationServerMiddleware($server);
$middleware = new AuthorizationServerMiddleware($server);
$response = $middleware->__invoke(
$request,

View File

@@ -5,12 +5,9 @@ namespace LeagueTests\Middleware;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\Middleware\ResourceServerMiddleware;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use League\OAuth2\Server\Server;
use League\OAuth2\Server\ResourceServer;
use LeagueTests\Stubs\AccessTokenEntity;
use LeagueTests\Stubs\ClientEntity;
use LeagueTests\Stubs\StubResponseType;
use Zend\Diactoros\Response;
use Zend\Diactoros\ServerRequest;
@@ -18,15 +15,9 @@ class ResourceServerMiddlewareTest extends \PHPUnit_Framework_TestCase
{
public function testValidResponse()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$server = new Server(
$clientRepository,
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
'file://' . __DIR__ . '/../Stubs/private.key',
'file://' . __DIR__ . '/../Stubs/public.key',
new StubResponseType()
$server = new ResourceServer(
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/../Stubs/public.key'
);
$client = new ClientEntity();
@@ -59,15 +50,9 @@ class ResourceServerMiddlewareTest extends \PHPUnit_Framework_TestCase
public function testValidResponseExpiredToken()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$server = new Server(
$clientRepository,
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
'file://' . __DIR__ . '/../Stubs/private.key',
'file://' . __DIR__ . '/../Stubs/public.key',
new StubResponseType()
$server = new ResourceServer(
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/../Stubs/public.key'
);
$client = new ClientEntity();
@@ -100,15 +85,9 @@ class ResourceServerMiddlewareTest extends \PHPUnit_Framework_TestCase
public function testErrorResponse()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$server = new Server(
$clientRepository,
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
'file://' . __DIR__ . '/../Stubs/private.key',
'file://' . __DIR__ . '/../Stubs/public.key',
new StubResponseType()
$server = new ResourceServer(
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/../Stubs/public.key'
);
$request = new ServerRequest();

View File

@@ -0,0 +1,26 @@
<?php
namespace LeagueTests;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\ResourceServer;
use Zend\Diactoros\ServerRequestFactory;
class ResourceServerTest extends \PHPUnit_Framework_TestCase
{
public function testValidateAuthenticatedRequest()
{
$server = new ResourceServer(
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/Stubs/public.key'
);
try {
$server->validateAuthenticatedRequest(ServerRequestFactory::fromGlobals());
} catch (OAuthServerException $e) {
$this->assertEquals('Missing "Authorization" header', $e->getHint());
}
}
}

View File

@@ -61,6 +61,53 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase
$this->assertTrue(isset($json->refresh_token));
}
public function testGenerateHttpResponseWithExtraParams()
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$responseType = new BearerTokenResponseWithParams($accessTokenRepositoryMock);
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$client = new ClientEntity();
$client->setIdentifier('clientName');
$scope = new ScopeEntity();
$scope->setIdentifier('basic');
$accessToken = new AccessTokenEntity();
$accessToken->setIdentifier('abcdef');
$accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H')));
$accessToken->setClient($client);
$accessToken->addScope($scope);
$refreshToken = new RefreshTokenEntity();
$refreshToken->setIdentifier('abcdef');
$refreshToken->setAccessToken($accessToken);
$refreshToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H')));
$responseType->setAccessToken($accessToken);
$responseType->setRefreshToken($refreshToken);
$response = $responseType->generateHttpResponse(new Response());
$this->assertTrue($response instanceof ResponseInterface);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals('no-cache', $response->getHeader('pragma')[0]);
$this->assertEquals('no-store', $response->getHeader('cache-control')[0]);
$this->assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]);
$response->getBody()->rewind();
$json = json_decode($response->getBody()->getContents());
$this->assertEquals('Bearer', $json->token_type);
$this->assertTrue(isset($json->expires_in));
$this->assertTrue(isset($json->access_token));
$this->assertTrue(isset($json->refresh_token));
$this->assertTrue(isset($json->foo));
$this->assertEquals('bar', $json->foo);
}
public function testDetermineAccessTokenInHeaderValidToken()
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
@@ -226,4 +273,31 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase
);
}
}
public function testDetermineMissingBearerInHeader()
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$responseType = new BearerTokenResponse($accessTokenRepositoryMock);
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock);
$authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$request = new ServerRequest();
$request = $request->withHeader('authorization', 'Bearer blah.blah.blah');
try {
$authorizationValidator->validateAuthorization($request);
} catch (OAuthServerException $e) {
$this->assertEquals(
'Error while decoding to JSON',
$e->getHint()
);
}
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace LeagueTests\ResponseTypes;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
class BearerTokenResponseWithParams extends BearerTokenResponse
{
protected function getExtraParams(AccessTokenEntityInterface $accessToken)
{
return ['foo' => 'bar', 'token_type' => 'Should not overwrite'];
}
}