mirror of
https://github.com/elyby/oauth2-server.git
synced 2025-05-31 14:12:07 +05:30
Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
138524984a | ||
|
8335935854 | ||
|
3bec591393 | ||
|
084b779cc6 | ||
|
491f3f0e95 |
19
.gitattributes
vendored
19
.gitattributes
vendored
@@ -1,14 +1,5 @@
|
||||
* text=auto
|
||||
|
||||
/examples export-ignore
|
||||
/tests export-ignore
|
||||
/.gitattributes export-ignore
|
||||
/.gitignore export-ignore
|
||||
/.travis.yml export-ignore
|
||||
.travis.yml export-ignore
|
||||
.scrutinizer.yml export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
/CHANGELOG.md export-ignore
|
||||
/CONTRIBUTING.md export-ignore
|
||||
/README.md export-ignore
|
||||
|
||||
tests/ export-ignore
|
||||
phpunit.xml export-ignore
|
||||
build.xml export-ignore
|
||||
test export-ignore
|
||||
.travis.yml export-ignore
|
19
.gitignore
vendored
19
.gitignore
vendored
@@ -1,8 +1,15 @@
|
||||
/vendor
|
||||
/composer.lock
|
||||
phpunit.xml
|
||||
.idea
|
||||
/examples/vendor
|
||||
examples/public.key
|
||||
examples/private.key
|
||||
build
|
||||
/build
|
||||
/docs
|
||||
/testing
|
||||
/examples/relational/vendor
|
||||
/examples/relational/config/oauth2.sqlite3
|
||||
/examples/nosql/vendor
|
||||
/examples/nosql/config/oauth2.sqlite3
|
||||
/examples/relational/composer.lock
|
||||
/tests/codecept/tests/_log
|
||||
oauth2-server.paw
|
||||
/output_*/
|
||||
/_site
|
||||
.idea
|
@@ -2,6 +2,7 @@ filter:
|
||||
excluded_paths:
|
||||
- tests/*
|
||||
- vendor/*
|
||||
- examples/*
|
||||
checks:
|
||||
php:
|
||||
code_rating: true
|
||||
|
52
.styleci.yml
52
.styleci.yml
@@ -1,52 +0,0 @@
|
||||
preset: psr2
|
||||
|
||||
enabled:
|
||||
- binary_operator_spaces
|
||||
- blank_line_before_return
|
||||
- concat_with_spaces
|
||||
- function_typehint_space
|
||||
- hash_to_slash_comment
|
||||
- include
|
||||
- lowercase_cast
|
||||
- method_separation
|
||||
- native_function_casing
|
||||
- no_blank_lines_after_class_opening
|
||||
- no_blank_lines_between_uses
|
||||
- no_duplicate_semicolons
|
||||
- no_leading_import_slash
|
||||
- no_leading_namespace_whitespace
|
||||
- no_multiline_whitespace_before_semicolons
|
||||
- no_php4_constructor
|
||||
- no_short_bool_cast
|
||||
- no_singleline_whitespace_before_semicolons
|
||||
- no_trailing_comma_in_singleline_array
|
||||
- no_unreachable_default_argument_value
|
||||
- no_unused_imports
|
||||
- no_whitespace_before_comma_in_array
|
||||
- ordered_imports
|
||||
- phpdoc_align
|
||||
- phpdoc_indent
|
||||
- phpdoc_inline_tag
|
||||
- phpdoc_no_access
|
||||
- phpdoc_no_simplified_null_return
|
||||
- phpdoc_property
|
||||
- phpdoc_scalar
|
||||
- phpdoc_separation
|
||||
- phpdoc_to_comment
|
||||
- phpdoc_trim
|
||||
- phpdoc_type_to_var
|
||||
- phpdoc_types
|
||||
- phpdoc_var_without_name
|
||||
- print_to_echo
|
||||
- short_array_syntax
|
||||
- short_scalar_cast
|
||||
- simplified_null_return
|
||||
- single_quote
|
||||
- spaces_cast
|
||||
- standardize_not_equal
|
||||
- ternary_operator_spaces
|
||||
- trailing_comma_in_multiline_array
|
||||
- trim_array_spaces
|
||||
- unary_operator_spaces
|
||||
- whitespace_after_comma_in_array
|
||||
- whitespacy_lines
|
52
.travis.yml
52
.travis.yml
@@ -1,32 +1,56 @@
|
||||
language: php
|
||||
|
||||
dist: trusty
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- vendor
|
||||
|
||||
env:
|
||||
- DEPENDENCIES=""
|
||||
- DEPENDENCIES="--prefer-lowest --prefer-stable"
|
||||
- vendor
|
||||
|
||||
php:
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
- 7.2
|
||||
- hhvm
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: 7.0
|
||||
fast_finish: true
|
||||
|
||||
install:
|
||||
- composer update --no-interaction --prefer-dist $DEPENDENCIES
|
||||
- travis_retry composer install --no-interaction --prefer-source
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit --coverage-clover=coverage.clover
|
||||
- vendor/bin/phpstan analyse -l 7 -c phpstan.neon src tests
|
||||
- mkdir -p build/logs
|
||||
- phpunit --coverage-text --verbose --coverage-clover=coverage.clover --coverage-html coverage
|
||||
|
||||
after_script:
|
||||
- wget https://scrutinizer-ci.com/ocular.phar
|
||||
- php ocular.phar code-coverage:upload --format=php-clover coverage.clover
|
||||
|
||||
- bash -c 'if [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi;
|
||||
- bash -c 'if [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi;
|
||||
- git config --global user.email "travis@travis-ci.org"
|
||||
- git config --global user.name "TravisCI"
|
||||
- cp -R coverage ${HOME}/coverage
|
||||
- cd ${HOME}
|
||||
- git clone --quiet --branch=gh-pages https://${GITHUBTOKEN}@github.com/thephpleague/oauth2-server.git gh-pages > /dev/null
|
||||
- cd gh-pages
|
||||
- mkdir ${TRAVIS_BRANCH}
|
||||
- cd ${TRAVIS_BRANCH}
|
||||
- cp -Rf $HOME/coverage/* .
|
||||
- git add -f .
|
||||
- git commit -m "Travis pushed coverage of ${TRAVIS_COMMIT}@${TRAVIS_BRANCH} to gh-pages"
|
||||
- git push -fq origin gh-pages > /dev/null
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
env:
|
||||
global:
|
||||
secure: "C4wD/BQefKSu9W594iyLp+IBCjlM8kKlmp+nXKXnZGi0L8IkV3m4mmNOb8PExxGMhZ3mlev5DnU4Uoh4oJaUxnkR1FpX4dSEpyzU3VknUzSE2yZOlL+bdCw3o85TGoCcp/+ReJCOw5sncxTskJKHlW1YMa33FznaXwLNoImpjTg="
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/7de0ca12596cd5268f30
|
||||
on_success: always # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: false # default: false
|
||||
|
506
CHANGELOG.md
506
CHANGELOG.md
@@ -1,443 +1,205 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
## 4.1.6 (released 2016-09-13)
|
||||
|
||||
## [Unreleased]
|
||||
* Less restrictive on Authorization header check (Issue #652)
|
||||
|
||||
## [7.1.1] - released 2018-05-21
|
||||
## 4.1.5 (released 2016-01-04)
|
||||
|
||||
### Fixed
|
||||
- No longer set a WWW-Authenticate header for invalid clients if the client did not send an Authorization header in the original request (PR #902)
|
||||
* Enable Symfony 3.0 support (#412)
|
||||
|
||||
## [7.1.0] - released 2018-04-22
|
||||
## 4.1.4 (released 2015-11-13)
|
||||
|
||||
### Changed
|
||||
- Changed hint for unsupportedGrantType exception so it no longer references the grant type parameter which isn't always expected (PR #893)
|
||||
- Upgrade PHPStan checks to level 7 (PR #856)
|
||||
* Fix for determining access token in header (Issue #328)
|
||||
* Refresh tokens are now returned for MAC responses (Issue #356)
|
||||
* Added integration list to readme (Issue #341)
|
||||
* Expose parameter passed to exceptions (Issue #345)
|
||||
* Removed duplicate routing setup code (Issue #346)
|
||||
* Docs fix (Issues #347, #360, #380)
|
||||
* Examples fix (Issues #348, #358)
|
||||
* Fix typo in docblock (Issue #352)
|
||||
* Improved timeouts for MAC tokens (Issue #364)
|
||||
* `hash_hmac()` should output raw binary data, not hexits (Issue #370)
|
||||
* Improved regex for matching all Base64 characters (Issue #371)
|
||||
* Fix incorrect signature parameter (Issue #372)
|
||||
* AuthCodeGrant and RefreshTokenGrant don't require client_secret (Issue #377)
|
||||
* Added priority argument to event listener (Issue #388)
|
||||
|
||||
### Added
|
||||
- Added event emitters for issued access and refresh tokens (PR #860)
|
||||
- Can now use Defuse\Crypto\Key for encryption/decryption of keys which is faster than the Cryto class (PR #812)
|
||||
## 4.1.3 (released 2015-03-22)
|
||||
|
||||
### Removed
|
||||
- Remove paragone/random_compat from dependencies
|
||||
* Docblock, namespace and inconsistency fixes (Issue #303)
|
||||
* Docblock type fix (Issue #310)
|
||||
* Example bug fix (Issue #300)
|
||||
* Updated league/event to ~2.1 (Issue #311)
|
||||
* Fixed missing session scope (Issue #319)
|
||||
* Updated interface docs (Issue #323)
|
||||
* `.travis.yml` updates
|
||||
|
||||
## [7.0.0] - released 2018-02-18
|
||||
## 4.1.2 (released 2015-01-01)
|
||||
|
||||
### Added
|
||||
- Use PHPStan for static analysis of code (PR #848)
|
||||
- Enforce stricter static analysis checks and upgrade library dependencies (PR #852)
|
||||
- Provide PHPStan coverage for tests and update PHPUnit (PR #849)
|
||||
- Get and set methods for OAuth Server Exception payloads. Allow implementer to specify the JSON encode options (PR #719)
|
||||
* Remove side-effects in hash_equals() implementation (Issue #290)
|
||||
|
||||
### Changed
|
||||
- ClientRepository interface will now accept null for the Grant type to improve extensibility options (PR #607)
|
||||
- Do not issue an error if key file permissions are 400 or 440 (PR #839)
|
||||
- Skip key file creation if the file already exists (PR #845)
|
||||
- Change changelog format and update readme
|
||||
## 4.1.1 (released 2014-12-31)
|
||||
|
||||
### Removed
|
||||
- Support for PHP 5.6
|
||||
- Support for version 5.x and 6.x of the library
|
||||
* Changed `symfony/http-foundation` dependency version to `~2.4` so package can be installed in Laravel `4.1.*`
|
||||
|
||||
### Fixed
|
||||
- PKCE implementation (PR #744)
|
||||
- Set correct redirect URI when validating scopes (PR #840)
|
||||
- S256 code challenege method (PR #842)
|
||||
- Accept RSA key with CRLF line endings (PR #805)
|
||||
## 4.1.0 (released 2014-12-27)
|
||||
|
||||
## [6.1.1] - 2017-12-23
|
||||
* Added MAC token support (Issue #158)
|
||||
* Fixed example init code (Issue #280)
|
||||
* Toggle refresh token rotation (Issue #286)
|
||||
* Docblock fixes
|
||||
|
||||
- Removed check on empty scopes
|
||||
## 4.0.5 (released 2014-12-15)
|
||||
|
||||
## [6.1.0] - 2017-12-23
|
||||
* Prevent duplicate session in auth code grant (Issue #282)
|
||||
|
||||
- Changed the token type issued by the Implicit Grant to be Bearer instead of bearer. (PR #724)
|
||||
- Replaced call to array_key_exists() with the faster isset() on the Implicit Grant. (PR #749)
|
||||
- Allow specification of query delimiter character in the Password Grant (PR #801)
|
||||
- Add Zend Diactoros library dependency to examples (PR #678)
|
||||
- Can set default scope for the authorization endpoint. If no scope is passed during an authorization request, the default scope will be used if set. If not, the server will issue an invalid scope exception (PR #811)
|
||||
- Added validation for redirect URIs on the authorization end point to ensure exactly one redirection URI has been passed (PR #573)
|
||||
## 4.0.4 (released 2014-12-03)
|
||||
|
||||
## [6.0.2] - 2017-08-03
|
||||
* Ensure refresh token hasn't expired (Issue #270)
|
||||
|
||||
- An invalid refresh token that can't be decrypted now returns a HTTP 401 error instead of HTTP 400 (Issue #759)
|
||||
- Removed chmod from CryptKey and add toggle to disable checking (Issue #776)
|
||||
- Fixes invalid code challenge method payload key name (Issue #777)
|
||||
## 4.0.3 (released 2014-12-02)
|
||||
|
||||
## [6.0.1] - 2017-07-19
|
||||
* Fix bad type hintings (Issue #267)
|
||||
* Do not forget to set the expire time (Issue #268)
|
||||
|
||||
To address feedback from the security release the following change has been made:
|
||||
## 4.0.2 (released 2014-11-21)
|
||||
|
||||
- If an RSA key cannot be chmod'ed to 600 then it will now throw a E_USER_NOTICE instead of an exception.
|
||||
* Improved interfaces (Issue #255)
|
||||
* Learnt how to spell delimiter and so `getScopeDelimiter()` and `setScopeDelimiter()` methods have been renamed
|
||||
* Docblock improvements (Issue #254)
|
||||
|
||||
## [6.0.0] - 2017-07-01
|
||||
## 4.0.1 (released 2014-11-09)
|
||||
|
||||
- Breaking change: The `AuthorizationServer` constructor now expects an encryption key string instead of a public key
|
||||
- Remove support for HHVM
|
||||
- Remove support for PHP 5.5
|
||||
* Alias the master branch in composer.json (Issue #243)
|
||||
* Numerous PHP CodeSniffer fixes (Issue #244)
|
||||
* .travis.yml update (Issue #245)
|
||||
* The getAccessToken method should return an AccessTokenEntity object instead of a string in ResourceServer.php (#246)
|
||||
|
||||
## [5.1.4] - 2017-07-01
|
||||
## 4.0.0 (released 2014-11-08)
|
||||
|
||||
- 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)
|
||||
* Complete rewrite
|
||||
* Check out the documentation - [http://oauth2.thephpleague.com](http://oauth2.thephpleague.com)
|
||||
|
||||
## [5.1.3] - 2016-10-12
|
||||
## 3.2.0 (released 2014-04-16)
|
||||
|
||||
- Fixed WWW-Authenticate header (Issue #669)
|
||||
- Increase the recommended RSA key length from 1024 to 2048 bits (Issue #668)
|
||||
* Added the ability to change the algorithm that is used to generate the token strings (Issue #151)
|
||||
|
||||
## [5.1.2] - 2016-09-19
|
||||
## 3.1.2 (released 2014-02-26)
|
||||
|
||||
- Fixed `finalizeScopes` call (Issue #650)
|
||||
* Support Authorization being an environment variable. [See more](http://fortrabbit.com/docs/essentials/quirks-and-constraints#authorization-header)
|
||||
|
||||
## [5.1.1] - 2016-07-26
|
||||
## 3.1.1 (released 2013-12-05)
|
||||
|
||||
- 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)
|
||||
* Normalize headers when `getallheaders()` is available (Issues #108 and #114)
|
||||
|
||||
## [5.1.0] - 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] - 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] - 2016-04-18
|
||||
## 3.1.0 (released 2013-12-05)
|
||||
|
||||
- `state` parameter is now correctly returned after implicit grant authorization
|
||||
- Small code and docblock improvements
|
||||
* No longer necessary to inject the authorisation server into a grant, the server will inject itself
|
||||
* Added test for 1419ba8cdcf18dd034c8db9f7de86a2594b68605
|
||||
|
||||
## [5.0.1] - 2016-04-18
|
||||
## 3.0.1 (released 2013-12-02)
|
||||
|
||||
- Fixes an issue (#550) whereby it was unclear whether or not to validate a client's secret during a request.
|
||||
* Forgot to tell TravisCI from testing PHP 5.3
|
||||
|
||||
## [5.0.0] - 2016-04-17
|
||||
## 3.0.0 (released 2013-12-02)
|
||||
|
||||
Version 5 is a complete code rewrite.
|
||||
* Fixed spelling of Implicit grant class (Issue #84)
|
||||
* Travis CI now tests for PHP 5.5
|
||||
* Fixes for checking headers for resource server (Issues #79 and #)
|
||||
* The word "bearer" now has a capital "B" in JSON output to match OAuth 2.0 spec
|
||||
* All grants no longer remove old sessions by default
|
||||
* All grants now support custom access token TTL (Issue #92)
|
||||
* All methods which didn't before return a value now return `$this` to support method chaining
|
||||
* Removed the build in DB providers - these will be put in their own repos to remove baggage in the main repository
|
||||
* Removed support for PHP 5.3 because this library now uses traits and will use other modern PHP features going forward
|
||||
* Moved some grant related functions into a trait to reduce duplicate code
|
||||
|
||||
- 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
|
||||
## 2.1.1 (released 2013-06-02)
|
||||
|
||||
## [5.0.0-RC2] - 2016-04-10
|
||||
* Added conditional `isValid()` flag to check for Authorization header only (thanks @alexmcroberts)
|
||||
* Fixed semantic meaning of `requireScopeParam()` and `requireStateParam()` by changing their default value to true
|
||||
* Updated some duff docblocks
|
||||
* Corrected array key call in Resource.php (Issue #63)
|
||||
|
||||
- Allow multiple client redirect URIs (Issue #511)
|
||||
- Remove unused mac token interface (Issue #503)
|
||||
- Handle RSA key passphrase (Issue #502)
|
||||
- Remove access token repository from response types (Issue #501)
|
||||
- Remove unnecessary methods from entity interfaces (Issue #490)
|
||||
- Ensure incoming JWT hasn't expired (Issue #509)
|
||||
- Fix client identifier passed where user identifier is expected (Issue #498)
|
||||
- Removed built-in entities; added traits to for quick re-use (Issue #504)
|
||||
- Redirect uri is required only if the "redirect_uri" parameter was included in the authorization request (Issue #514)
|
||||
- Removed templating for auth code and implicit grants (Issue #499)
|
||||
## 2.1 (released 2013-05-10)
|
||||
|
||||
## [5.0.0-RC1] - 2016-03-24
|
||||
* Moved zetacomponents/database to "suggest" in composer.json. If you rely on this feature you now need to include " zetacomponents/database" into "require" key in your own composer.json. (Issue #51)
|
||||
* New method in Refresh grant called `rotateRefreshTokens()`. Pass in `true` to issue a new refresh token each time an access token is refreshed. This parameter needs to be set to true in order to request reduced scopes with the new access token. (Issue #47)
|
||||
* Rename `key` column in oauth_scopes table to `scope` as `key` is a reserved SQL word. (Issue #45)
|
||||
* The `scope` parameter is no longer required by default as per the RFC. (Issue #43)
|
||||
* You can now set multiple default scopes by passing an array into `setDefaultScope()`. (Issue #42)
|
||||
* The password and client credentials grants now allow for multiple sessions per user. (Issue #32)
|
||||
* Scopes associated to authorization codes are not held in their own table (Issue #44)
|
||||
* Database schema updates.
|
||||
|
||||
Version 5 is a complete code rewrite.
|
||||
## 2.0.5 (released 2013-05-09)
|
||||
|
||||
- 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
|
||||
* Fixed `oauth_session_token_scopes` table primary key
|
||||
* Removed `DEFAULT ''` that has slipped into some tables
|
||||
* Fixed docblock for `SessionInterface::associateRefreshToken()`
|
||||
|
||||
## [4.1.5] - 2016-01-04
|
||||
## 2.0.4 (released 2013-05-09)
|
||||
|
||||
- Enable Symfony 3.0 support (#412)
|
||||
* Renamed primary key in oauth_client_endpoints table
|
||||
* Adding missing column to oauth_session_authcodes
|
||||
* SECURITY FIX: A refresh token should be bound to a client ID
|
||||
|
||||
## [4.1.4] - 2015-11-13
|
||||
## 2.0.3 (released 2013-05-08)
|
||||
|
||||
- Fix for determining access token in header (Issue #328)
|
||||
- Refresh tokens are now returned for MAC responses (Issue #356)
|
||||
- Added integration list to readme (Issue #341)
|
||||
- Expose parameter passed to exceptions (Issue #345)
|
||||
- Removed duplicate routing setup code (Issue #346)
|
||||
- Docs fix (Issues #347, #360, #380)
|
||||
- Examples fix (Issues #348, #358)
|
||||
- Fix typo in docblock (Issue #352)
|
||||
- Improved timeouts for MAC tokens (Issue #364)
|
||||
- `hash_hmac()` should output raw binary data, not hexits (Issue #370)
|
||||
- Improved regex for matching all Base64 characters (Issue #371)
|
||||
- Fix incorrect signature parameter (Issue #372)
|
||||
- AuthCodeGrant and RefreshTokenGrant don't require client_secret (Issue #377)
|
||||
- Added priority argument to event listener (Issue #388)
|
||||
* Fixed a link to code in composer.json
|
||||
|
||||
## [4.1.3] - 2015-03-22
|
||||
## 2.0.2 (released 2013-05-08)
|
||||
|
||||
- Docblock, namespace and inconsistency fixes (Issue #303)
|
||||
- Docblock type fix (Issue #310)
|
||||
- Example bug fix (Issue #300)
|
||||
- Updated league/event to ~2.1 (Issue #311)
|
||||
- Fixed missing session scope (Issue #319)
|
||||
- Updated interface docs (Issue #323)
|
||||
- `.travis.yml` updates
|
||||
* Updated README with wiki guides
|
||||
* Removed `null` as default parameters in some methods in the storage interfaces
|
||||
* Fixed license copyright
|
||||
|
||||
## [4.1.2] - 2015-01-01
|
||||
|
||||
- Remove side-effects in hash_equals() implementation (Issue #290)
|
||||
|
||||
## [4.1.1] - 2014-12-31
|
||||
|
||||
- Changed `symfony/http-foundation` dependency version to `~2.4` so package can be installed in Laravel `4.1.*`
|
||||
|
||||
## [4.1.0] - 2014-12-27
|
||||
|
||||
- Added MAC token support (Issue #158)
|
||||
- Fixed example init code (Issue #280)
|
||||
- Toggle refresh token rotation (Issue #286)
|
||||
- Docblock fixes
|
||||
|
||||
## [4.0.5] - 2014-12-15
|
||||
|
||||
- Prevent duplicate session in auth code grant (Issue #282)
|
||||
|
||||
## [4.0.4] - 2014-12-03
|
||||
|
||||
- Ensure refresh token hasn't expired (Issue #270)
|
||||
|
||||
## [4.0.3] - 2014-12-02
|
||||
|
||||
- Fix bad type hintings (Issue #267)
|
||||
- Do not forget to set the expire time (Issue #268)
|
||||
|
||||
## [4.0.2] - 2014-11-21
|
||||
|
||||
- Improved interfaces (Issue #255)
|
||||
- Learnt how to spell delimiter and so `getScopeDelimiter()` and `setScopeDelimiter()` methods have been renamed
|
||||
- Docblock improvements (Issue #254)
|
||||
|
||||
## [4.0.1] - 2014-11-09
|
||||
|
||||
- Alias the master branch in composer.json (Issue #243)
|
||||
- Numerous PHP CodeSniffer fixes (Issue #244)
|
||||
- .travis.yml update (Issue #245)
|
||||
- The getAccessToken method should return an AccessTokenEntity object instead of a string in ResourceServer.php (#246)
|
||||
|
||||
## [4.0.0] - 2014-11-08
|
||||
|
||||
- Complete rewrite
|
||||
- Check out the documentation - [http://oauth2.thephpleague.com](http://oauth2.thephpleague.com)
|
||||
|
||||
## [3.2.0] - 2014-04-16
|
||||
|
||||
- Added the ability to change the algorithm that is used to generate the token strings (Issue #151)
|
||||
|
||||
## [3.1.2] - 2014-02-26
|
||||
|
||||
- Support Authorization being an environment variable. [See more](http://fortrabbit.com/docs/essentials/quirks-and-constraints#authorization-header)
|
||||
|
||||
## [3.1.1] - 2013-12-05
|
||||
|
||||
- Normalize headers when `getallheaders()` is available (Issues #108 and #114)
|
||||
|
||||
## [3.1.0] - 2013-12-05
|
||||
|
||||
- No longer necessary to inject the authorisation server into a grant, the server will inject itself
|
||||
- Added test for 1419ba8cdcf18dd034c8db9f7de86a2594b68605
|
||||
|
||||
## [3.0.1] - 2013-12-02
|
||||
|
||||
- Forgot to tell TravisCI from testing PHP 5.3
|
||||
|
||||
## [3.0.0] - 2013-12-02
|
||||
|
||||
- Fixed spelling of Implicit grant class (Issue #84)
|
||||
- Travis CI now tests for PHP 5.5
|
||||
- Fixes for checking headers for resource server (Issues #79 and #)
|
||||
- The word "bearer" now has a capital "B" in JSON output to match OAuth 2.0 spec
|
||||
- All grants no longer remove old sessions by default
|
||||
- All grants now support custom access token TTL (Issue #92)
|
||||
- All methods which didn't before return a value now return `$this` to support method chaining
|
||||
- Removed the build in DB providers - these will be put in their own repos to remove baggage in the main repository
|
||||
- Removed support for PHP 5.3 because this library now uses traits and will use other modern PHP features going forward
|
||||
- Moved some grant related functions into a trait to reduce duplicate code
|
||||
|
||||
## [2.1.1] - 2013-06-02
|
||||
|
||||
- Added conditional `isValid()` flag to check for Authorization header only (thanks @alexmcroberts)
|
||||
- Fixed semantic meaning of `requireScopeParam()` and `requireStateParam()` by changing their default value to true
|
||||
- Updated some duff docblocks
|
||||
- Corrected array key call in Resource.php (Issue #63)
|
||||
|
||||
## [2.1.0] - 2013-05-10
|
||||
|
||||
- Moved zetacomponents/database to "suggest" in composer.json. If you rely on this feature you now need to include " zetacomponents/database" into "require" key in your own composer.json. (Issue #51)
|
||||
- New method in Refresh grant called `rotateRefreshTokens()`. Pass in `true` to issue a new refresh token each time an access token is refreshed. This parameter needs to be set to true in order to request reduced scopes with the new access token. (Issue #47)
|
||||
- Rename `key` column in oauth_scopes table to `scope` as `key` is a reserved SQL word. (Issue #45)
|
||||
- The `scope` parameter is no longer required by default as per the RFC. (Issue #43)
|
||||
- You can now set multiple default scopes by passing an array into `setDefaultScope()`. (Issue #42)
|
||||
- The password and client credentials grants now allow for multiple sessions per user. (Issue #32)
|
||||
- Scopes associated to authorization codes are not held in their own table (Issue #44)
|
||||
- Database schema updates.
|
||||
|
||||
## [2.0.5] - 2013-05-09
|
||||
|
||||
- Fixed `oauth_session_token_scopes` table primary key
|
||||
- Removed `DEFAULT ''` that has slipped into some tables
|
||||
- Fixed docblock for `SessionInterface::associateRefreshToken()`
|
||||
|
||||
## [2.0.4] - 2013-05-09
|
||||
|
||||
- Renamed primary key in oauth_client_endpoints table
|
||||
- Adding missing column to oauth_session_authcodes
|
||||
|
||||
### Security
|
||||
- A refresh token should be bound to a client ID
|
||||
|
||||
## [2.0.3] - 2013-05-08
|
||||
|
||||
- Fixed a link to code in composer.json
|
||||
|
||||
## [2.0.2] - 2013-05-08
|
||||
|
||||
- Updated README with wiki guides
|
||||
- Removed `null` as default parameters in some methods in the storage interfaces
|
||||
- Fixed license copyright
|
||||
|
||||
## [2.0.0] - 2013-05-08
|
||||
## 2.0.0 (released 2013-05-08)
|
||||
|
||||
**If you're upgrading from v1.0.8 there are lots of breaking changes**
|
||||
|
||||
- Rewrote the session storage interface from scratch so methods are more obvious
|
||||
- Included a PDO driver which implements the storage interfaces so the library is more "get up and go"
|
||||
- Further normalised the database structure so all sessions no longer contain infomation related to authorization grant (which may or may not be enabled)
|
||||
- A session can have multiple associated access tokens
|
||||
- Individual grants can have custom expire times for access tokens
|
||||
- Authorization codes now have a TTL of 10 minutes by default (can be manually set)
|
||||
- Refresh tokens now have a TTL of one week by default (can be manually set)
|
||||
- The client credentials grant will no longer gives out refresh tokens as per the specification
|
||||
* Rewrote the session storage interface from scratch so methods are more obvious
|
||||
* Included a PDO driver which implements the storage interfaces so the library is more "get up and go"
|
||||
* Further normalised the database structure so all sessions no longer contain infomation related to authorization grant (which may or may not be enabled)
|
||||
* A session can have multiple associated access tokens
|
||||
* Induvidual grants can have custom expire times for access tokens
|
||||
* Authorization codes now have a TTL of 10 minutes by default (can be manually set)
|
||||
* Refresh tokens now have a TTL of one week by default (can be manually set)
|
||||
* The client credentials grant will no longer gives out refresh tokens as per the specification
|
||||
|
||||
## [1.0.8] - 2013-03-18
|
||||
## 1.0.8 (released 2013-03-18)
|
||||
|
||||
- Fixed check for required state parameter
|
||||
- Fixed check that user's credentials are correct in Password grant
|
||||
* Fixed check for required state parameter
|
||||
* Fixed check that user's credentials are correct in Password grant
|
||||
|
||||
## [1.0.7] - 2013-03-04
|
||||
## 1.0.7 (released 2013-03-04)
|
||||
|
||||
- Added method `requireStateParam()`
|
||||
- Added method `requireScopeParam()`
|
||||
* Added method `requireStateParam()`
|
||||
* Added method `requireScopeParam()`
|
||||
|
||||
## [1.0.6] - 2013-02-22
|
||||
## 1.0.6 (released 2013-02-22)
|
||||
|
||||
- Added links to tutorials in the README
|
||||
- Added missing `state` parameter request to the `checkAuthoriseParams()` method.
|
||||
* Added links to tutorials in the README
|
||||
* Added missing `state` parameter request to the `checkAuthoriseParams()` method.
|
||||
|
||||
## [1.0.5] - 2013-02-21
|
||||
## 1.0.5 (released 2013-02-21)
|
||||
|
||||
- Fixed the SQL example for SessionInterface::getScopes()
|
||||
* Fixed the SQL example for SessionInterface::getScopes()
|
||||
|
||||
## [1.0.3] - 2013-02-20
|
||||
## 1.0.3 (released 2013-02-20)
|
||||
|
||||
- Changed all instances of the "authentication server" to "authorization server"
|
||||
* Changed all instances of the "authentication server" to "authorization server"
|
||||
|
||||
## [1.0.2] - 2013-02-20
|
||||
## 1.0.2 (released 2013-02-20)
|
||||
|
||||
- Fixed MySQL create table order
|
||||
- Fixed version number in composer.json
|
||||
* Fixed MySQL create table order
|
||||
* Fixed version number in composer.json
|
||||
|
||||
## [1.0.1] - 2013-02-19
|
||||
## 1.0.1 (released 2013-02-19)
|
||||
|
||||
- Updated AuthServer.php to use `self::getParam()`
|
||||
* Updated AuthServer.php to use `self::getParam()`
|
||||
|
||||
## 1.0.0 - 2013-02-15
|
||||
## 1.0.0 (released 2013-02-15)
|
||||
|
||||
- First major release
|
||||
|
||||
[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...HEAD
|
||||
[7.1.1]: https://github.com/thephpleague/oauth2-server/compare/7.1.0...7.1.1
|
||||
[7.1.0]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...7.1.0
|
||||
[7.0.0]: https://github.com/thephpleague/oauth2-server/compare/6.1.1...7.0.0
|
||||
[6.1.1]: https://github.com/thephpleague/oauth2-server/compare/6.0.0...6.1.1
|
||||
[6.1.0]: https://github.com/thephpleague/oauth2-server/compare/6.0.2...6.1.0
|
||||
[6.0.2]: https://github.com/thephpleague/oauth2-server/compare/6.0.1...6.0.2
|
||||
[6.0.1]: https://github.com/thephpleague/oauth2-server/compare/6.0.0...6.0.1
|
||||
[6.0.0]: https://github.com/thephpleague/oauth2-server/compare/5.1.4...6.0.0
|
||||
[5.1.4]: https://github.com/thephpleague/oauth2-server/compare/5.1.3...5.1.4
|
||||
[5.1.3]: https://github.com/thephpleague/oauth2-server/compare/5.1.2...5.1.3
|
||||
[5.1.2]: https://github.com/thephpleague/oauth2-server/compare/5.1.1...5.1.2
|
||||
[5.1.1]: https://github.com/thephpleague/oauth2-server/compare/5.1.0...5.1.1
|
||||
[5.1.0]: https://github.com/thephpleague/oauth2-server/compare/5.0.2...5.1.0
|
||||
[5.0.3]: https://github.com/thephpleague/oauth2-server/compare/5.0.3...5.0.2
|
||||
[5.0.2]: https://github.com/thephpleague/oauth2-server/compare/5.0.1...5.0.2
|
||||
[5.0.1]: https://github.com/thephpleague/oauth2-server/compare/5.0.0...5.0.1
|
||||
[5.0.0]: https://github.com/thephpleague/oauth2-server/compare/5.0.0-RC2...5.0.0
|
||||
[5.0.0-RC2]: https://github.com/thephpleague/oauth2-server/compare/5.0.0-RC1...5.0.0-RC2
|
||||
[5.0.0-RC1]: https://github.com/thephpleague/oauth2-server/compare/4.1.5...5.0.0-RC1
|
||||
[4.1.5]: https://github.com/thephpleague/oauth2-server/compare/4.1.4...4.1.5
|
||||
[4.1.4]: https://github.com/thephpleague/oauth2-server/compare/4.1.3...4.1.4
|
||||
[4.1.3]: https://github.com/thephpleague/oauth2-server/compare/4.1.2...4.1.3
|
||||
[4.1.2]: https://github.com/thephpleague/oauth2-server/compare/4.1.1...4.1.2
|
||||
[4.1.1]: https://github.com/thephpleague/oauth2-server/compare/4.0.0...4.1.1
|
||||
[4.1.0]: https://github.com/thephpleague/oauth2-server/compare/4.0.5...4.1.0
|
||||
[4.0.5]: https://github.com/thephpleague/oauth2-server/compare/4.0.4...4.0.5
|
||||
[4.0.4]: https://github.com/thephpleague/oauth2-server/compare/4.0.3...4.0.4
|
||||
[4.0.3]: https://github.com/thephpleague/oauth2-server/compare/4.0.2...4.0.3
|
||||
[4.0.2]: https://github.com/thephpleague/oauth2-server/compare/4.0.1...4.0.2
|
||||
[4.0.1]: https://github.com/thephpleague/oauth2-server/compare/4.0.0...4.0.1
|
||||
[4.0.0]: https://github.com/thephpleague/oauth2-server/compare/3.2.0...4.0.0
|
||||
[3.2.0]: https://github.com/thephpleague/oauth2-server/compare/3.1.2...3.2.0
|
||||
[3.1.2]: https://github.com/thephpleague/oauth2-server/compare/3.1.1...3.1.2
|
||||
[3.1.1]: https://github.com/thephpleague/oauth2-server/compare/3.1.0...3.1.1
|
||||
[3.1.0]: https://github.com/thephpleague/oauth2-server/compare/3.0.1...3.1.0
|
||||
[3.0.1]: https://github.com/thephpleague/oauth2-server/compare/3.0.0...3.0.1
|
||||
[3.0.0]: https://github.com/thephpleague/oauth2-server/compare/2.1.1...3.0.0
|
||||
[2.1.1]: https://github.com/thephpleague/oauth2-server/compare/2.1.0...2.1.1
|
||||
[2.1.0]: https://github.com/thephpleague/oauth2-server/compare/2.0.5...2.1.0
|
||||
[2.0.5]: https://github.com/thephpleague/oauth2-server/compare/2.0.4...2.0.5
|
||||
[2.0.4]: https://github.com/thephpleague/oauth2-server/compare/2.0.3...2.0.4
|
||||
[2.0.3]: https://github.com/thephpleague/oauth2-server/compare/2.0.2...2.0.3
|
||||
[2.0.2]: https://github.com/thephpleague/oauth2-server/compare/2.0.0...2.0.2
|
||||
[2.0.0]: https://github.com/thephpleague/oauth2-server/compare/1.0.8...2.0.0
|
||||
[1.0.8]: https://github.com/thephpleague/oauth2-server/compare/1.0.7...1.0.8
|
||||
[1.0.7]: https://github.com/thephpleague/oauth2-server/compare/1.0.6...1.0.7
|
||||
[1.0.6]: https://github.com/thephpleague/oauth2-server/compare/1.0.5...1.0.6
|
||||
[1.0.5]: https://github.com/thephpleague/oauth2-server/compare/1.0.3...1.0.5
|
||||
[1.0.3]: https://github.com/thephpleague/oauth2-server/compare/1.0.2...1.0.3
|
||||
[1.0.2]: https://github.com/thephpleague/oauth2-server/compare/1.0.1...1.0.2
|
||||
[1.0.1]: https://github.com/thephpleague/oauth2-server/compare/1.0.0...1.0.1
|
||||
* First major release
|
@@ -1,73 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
education, socio-economic status, nationality, personal appearance, race,
|
||||
religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at andrew@noexceptions.io. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
@@ -1,7 +1,7 @@
|
||||
Thanks for contributing to this project.
|
||||
|
||||
|
||||
**Please submit your pull request against the `master` branch only.**
|
||||
**Please submit your pull request against the `develop` branch only.**
|
||||
|
||||
|
||||
Please ensure that you run `phpunit` from the project root after you've made any changes.
|
||||
|
97
README.md
97
README.md
@@ -1,95 +1,68 @@
|
||||
# PHP OAuth 2.0 Server
|
||||
# PHP OAuth 2.0 Server by [@alexbilbie](https://twitter.com/alexbilbie)
|
||||
|
||||
[](https://github.com/thephpleague/oauth2-server/releases)
|
||||
[](LICENSE.md)
|
||||
[](https://travis-ci.org/thephpleague/oauth2-server)
|
||||
[](https://scrutinizer-ci.com/g/thephpleague/oauth2-server/code-structure)
|
||||
[](https://scrutinizer-ci.com/g/thephpleague/oauth2-server)
|
||||
[](https://packagist.org/packages/league/oauth2-server)
|
||||
[](https://github.com/phpstan/phpstan)
|
||||
[](https://packagist.org/packages/league/oauth2-server) [](https://gitter.im/thephpleague/oauth2-server?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
`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.
|
||||
|
||||
Out of the box it supports the following grants:
|
||||
A standards compliant [OAuth 2.0](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-v2/) authorization server and resource 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:
|
||||
|
||||
* Authorization code grant
|
||||
* Implicit grant
|
||||
* Client credentials grant
|
||||
* Resource owner password credentials grant
|
||||
* Refresh grant
|
||||
|
||||
The following RFCs are implemented:
|
||||
You can also define your own grants.
|
||||
|
||||
* [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)
|
||||
In addition it supports the following token types:
|
||||
|
||||
* Bearer tokens
|
||||
* MAC tokens
|
||||
* JSON web tokens (coming soon)
|
||||
|
||||
You can also create you own tokens.
|
||||
|
||||
This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](https://twitter.com/alexbilbie).
|
||||
|
||||
## Requirements
|
||||
|
||||
The following versions of PHP are supported:
|
||||
|
||||
* PHP 7.0
|
||||
* PHP 7.1
|
||||
* PHP 7.2
|
||||
|
||||
The `openssl` extension is also required.
|
||||
|
||||
All HTTP messages passed to the server should be [PSR-7 compliant](https://www.php-fig.org/psr/psr-7/). This ensures interoperability between other packages and frameworks.
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
composer require league/oauth2-server
|
||||
```
|
||||
* PHP 5.4
|
||||
* PHP 5.5
|
||||
* PHP 5.6
|
||||
* HHVM
|
||||
|
||||
## Documentation
|
||||
|
||||
The library documentation can be found at [https://oauth2.thephpleague.com](https://oauth2.thephpleague.com).
|
||||
You can contribute to the documentation in the [gh-pages branch](https://github.com/thephpleague/oauth2-server/tree/gh-pages/).
|
||||
This library has [full documentation](http://oauth2.thephpleague.com), powered by [Jekyll](http://jekyllrb.com/).
|
||||
|
||||
## Testing
|
||||
|
||||
The library uses [PHPUnit](https://phpunit.de/) for unit tests and [PHPStan](https://github.com/phpstan/phpstan) for static analysis of the code.
|
||||
|
||||
```
|
||||
vendor/bin/phpunit
|
||||
vendor/bin/phpstan analyse -l 7 -c phpstan.neon src tests
|
||||
```
|
||||
|
||||
## Continous Integration
|
||||
|
||||
We use [Travis CI](https://travis-ci.org/), [Scrutinizer](https://scrutinizer-ci.com/), and [StyleCI](https://styleci.io/) for continuous integration. Check out [our](https://github.com/thephpleague/oauth2-server/blob/master/.travis.yml) [configuration](https://github.com/thephpleague/oauth2-server/blob/master/.scrutinizer.yml) [files](https://github.com/thephpleague/oauth2-server/blob/master/.styleci.yml) if you'd like to know more.
|
||||
|
||||
## Community Integrations
|
||||
|
||||
* [Drupal](https://www.drupal.org/project/simple_oauth)
|
||||
* [Laravel Passport](https://github.com/laravel/passport)
|
||||
* [OAuth 2 Server for CakePHP 3](https://github.com/uafrica/oauth-server)
|
||||
Contribute to this documentation in the [gh-pages branch](https://github.com/thephpleague/oauth2-server/tree/gh-pages/).
|
||||
|
||||
## Changelog
|
||||
|
||||
See the [project changelog](https://github.com/thephpleague/oauth2-server/blob/master/CHANGELOG.md)
|
||||
[See the project releases page](https://github.com/thephpleague/oauth2-server/releases)
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are always welcome. Please see [CONTRIBUTING.md](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](https://github.com/thephpleague/oauth2-server/blob/master/CODE_OF_CONDUCT.md) for details.
|
||||
Please see [CONTRIBUTING](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) for details.
|
||||
|
||||
## Integration
|
||||
|
||||
- [CakePHP 3](https://github.com/uafrica/oauth-server)
|
||||
- [Laravel](https://github.com/lucadegasperi/oauth2-server-laravel)
|
||||
|
||||
## Support
|
||||
|
||||
Bugs and feature request are tracked on [GitHub](https://github.com/thephpleague/oauth2-server/issues).
|
||||
|
||||
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).
|
||||
Bugs and feature request are tracked on [GitHub](https://github.com/thephpleague/oauth2-server/issues)
|
||||
|
||||
## 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
|
||||
|
||||
@@ -97,14 +70,14 @@ This package is released under the MIT License. See the bundled [LICENSE](https:
|
||||
|
||||
## Credits
|
||||
|
||||
This code is principally developed and maintained by [Andy Millington](https://twitter.com/Sephster) and [Simon Hamp](https://twitter.com/simonhamp).
|
||||
This code is principally developed and maintained by [Alex Bilbie](https://twitter.com/alexbilbie).
|
||||
|
||||
Between 2012 and 2017 this library was developed and maintained by [Alex Bilbie](https://alexbilbie.com/).
|
||||
Special thanks to:
|
||||
|
||||
PHP OAuth 2.0 Server is one of many packages provided by The PHP League. To find out more, please visit [our website](https://thephpleague.com).
|
||||
|
||||
Special thanks to [all of these awesome contributors](https://github.com/thephpleague/oauth2-server/contributors).
|
||||
|
||||
Additional thanks go to the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source) for funding a security audit of this library.
|
||||
* [Dan Horrigan](https://github.com/dandoescode)
|
||||
* [Nick Jackson](https://github.com/jacksonj04)
|
||||
* [Michael Gooden](https://github.com/MichaelGooden)
|
||||
* [Phil Sturgeon](https://github.com/philsturgeon)
|
||||
* [and all the other contributors](https://github.com/thephpleague/oauth2-server/contributors)
|
||||
|
||||
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.
|
||||
|
120
composer.json
120
composer.json
@@ -1,65 +1,59 @@
|
||||
{
|
||||
"name": "league/oauth2-server",
|
||||
"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": "https://oauth2.thephpleague.com/",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=7.0.0",
|
||||
"ext-openssl": "*",
|
||||
"league/event": "^2.1",
|
||||
"lcobucci/jwt": "^3.2.2",
|
||||
"psr/http-message": "^1.0.1",
|
||||
"defuse/php-encryption": "^2.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.3 || ^7.0",
|
||||
"zendframework/zend-diactoros": "^1.3.2",
|
||||
"phpstan/phpstan": "^0.9.2",
|
||||
"phpstan/phpstan-phpunit": "^0.9.4",
|
||||
"phpstan/phpstan-strict-rules": "^0.9.0"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/oauth2-server.git"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"oauth",
|
||||
"oauth2",
|
||||
"oauth 2",
|
||||
"oauth 2.0",
|
||||
"server",
|
||||
"auth",
|
||||
"authorization",
|
||||
"authorisation",
|
||||
"authentication",
|
||||
"resource",
|
||||
"api",
|
||||
"auth",
|
||||
"protect",
|
||||
"secure"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alex Bilbie",
|
||||
"email": "hello@alexbilbie.com",
|
||||
"homepage": "http://www.alexbilbie.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"replace": {
|
||||
"lncd/oauth2": "*",
|
||||
"league/oauth2server": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\OAuth2\\Server\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"LeagueTests\\": "tests/"
|
||||
}
|
||||
}
|
||||
"name": "league/oauth2-server",
|
||||
"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/",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"symfony/http-foundation": "~2.4|~3.0",
|
||||
"league/event": "~2.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.3.*",
|
||||
"mockery/mockery": "0.9.*"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/oauth2-server.git"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"oauth",
|
||||
"oauth2",
|
||||
"oauth 2",
|
||||
"oauth 2.0",
|
||||
"server",
|
||||
"auth",
|
||||
"authorization",
|
||||
"authorisation",
|
||||
"authentication",
|
||||
"resource",
|
||||
"api",
|
||||
"auth",
|
||||
"protect",
|
||||
"secure"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alex Bilbie",
|
||||
"email": "hello@alexbilbie.com",
|
||||
"homepage": "http://www.alexbilbie.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"replace": {
|
||||
"lncd/oauth2": "*",
|
||||
"league/oauth2server": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\OAuth2\\Server\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"LeagueTests\\": "tests/unit/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,53 +0,0 @@
|
||||
# Example implementations
|
||||
|
||||
## Installation
|
||||
|
||||
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`
|
||||
|
||||
## Testing the client credentials grant example
|
||||
|
||||
Send the following cURL request:
|
||||
|
||||
```
|
||||
curl -X "POST" "http://localhost:4444/client_credentials.php/access_token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-H "Accept: 1.0" \
|
||||
--data-urlencode "grant_type=client_credentials" \
|
||||
--data-urlencode "client_id=myawesomeapp" \
|
||||
--data-urlencode "client_secret=abc123" \
|
||||
--data-urlencode "scope=basic email"
|
||||
```
|
||||
|
||||
## Testing the password grant example
|
||||
|
||||
Send the following cURL request:
|
||||
|
||||
```
|
||||
curl -X "POST" "http://localhost:4444/password.php/access_token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-H "Accept: 1.0" \
|
||||
--data-urlencode "grant_type=password" \
|
||||
--data-urlencode "client_id=myawesomeapp" \
|
||||
--data-urlencode "client_secret=abc123" \
|
||||
--data-urlencode "username=alex" \
|
||||
--data-urlencode "password=whisky" \
|
||||
--data-urlencode "scope=basic email"
|
||||
```
|
||||
|
||||
## Testing the refresh token grant example
|
||||
|
||||
Send the following cURL request. Replace `{{REFRESH_TOKEN}}` with a refresh token from another grant above:
|
||||
|
||||
```
|
||||
curl -X "POST" "http://localhost:4444/refresh_token.php/access_token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-H "Accept: 1.0" \
|
||||
--data-urlencode "grant_type=refresh_token" \
|
||||
--data-urlencode "client_id=myawesomeapp" \
|
||||
--data-urlencode "client_secret=abc123" \
|
||||
--data-urlencode "refresh_token={{REFRESH_TOKEN}}"
|
||||
```
|
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"require": {
|
||||
"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",
|
||||
"zendframework/zend-diactoros": "^1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"OAuth2ServerExamples\\": "src/",
|
||||
"League\\OAuth2\\Server\\": "../src/"
|
||||
}
|
||||
}
|
||||
}
|
523
examples/composer.lock
generated
523
examples/composer.lock
generated
@@ -1,523 +0,0 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "9813ed7c3b6dcf107f44df9392935b8f",
|
||||
"packages": [
|
||||
{
|
||||
"name": "container-interop/container-interop",
|
||||
"version": "1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/container-interop/container-interop.git",
|
||||
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"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": {
|
||||
"Interop\\Container\\": "src/Interop/Container/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
|
||||
"homepage": "https://github.com/container-interop/container-interop",
|
||||
"time": "2017-02-14T19:40:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/fast-route",
|
||||
"version": "v0.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/FastRoute.git",
|
||||
"reference": "31fa86924556b80735f98b294a7ffdfb26789f22"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/31fa86924556b80735f98b294a7ffdfb26789f22",
|
||||
"reference": "31fa86924556b80735f98b294a7ffdfb26789f22",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"FastRoute\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nikita Popov",
|
||||
"email": "nikic@php.net"
|
||||
}
|
||||
],
|
||||
"description": "Fast request router for PHP",
|
||||
"keywords": [
|
||||
"router",
|
||||
"routing"
|
||||
],
|
||||
"time": "2015-06-18T19:15:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pimple/pimple",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/silexphp/Pimple.git",
|
||||
"reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/a30f7d6e57565a2e1a316e1baf2a483f788b258a",
|
||||
"reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Pimple": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
}
|
||||
],
|
||||
"description": "Pimple, a simple Dependency Injection Container",
|
||||
"homepage": "http://pimple.sensiolabs.org",
|
||||
"keywords": [
|
||||
"container",
|
||||
"dependency injection"
|
||||
],
|
||||
"time": "2015-09-11T15:10:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/container.git",
|
||||
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"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": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
"homepage": "https://github.com/php-fig/http-message",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"time": "2016-08-06T14:39:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "slim/slim",
|
||||
"version": "3.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/slimphp/Slim.git",
|
||||
"reference": "3b06f0f2d84dabbe81b6cea46ace46a3e883253e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/slimphp/Slim/zipball/3b06f0f2d84dabbe81b6cea46ace46a3e883253e",
|
||||
"reference": "3b06f0f2d84dabbe81b6cea46ace46a3e883253e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"container-interop/container-interop": "^1.1",
|
||||
"nikic/fast-route": "^0.6",
|
||||
"php": ">=5.5.0",
|
||||
"pimple/pimple": "^3.0",
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Slim\\": "Slim"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Rob Allen",
|
||||
"email": "rob@akrabat.com",
|
||||
"homepage": "http://akrabat.com"
|
||||
},
|
||||
{
|
||||
"name": "Josh Lockhart",
|
||||
"email": "hello@joshlockhart.com",
|
||||
"homepage": "https://joshlockhart.com"
|
||||
},
|
||||
{
|
||||
"name": "Gabriel Manricks",
|
||||
"email": "gmanricks@me.com",
|
||||
"homepage": "http://gabrielmanricks.com"
|
||||
},
|
||||
{
|
||||
"name": "Andrew Smith",
|
||||
"email": "a.smith@silentworks.co.uk",
|
||||
"homepage": "http://silentworks.co.uk"
|
||||
}
|
||||
],
|
||||
"description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs",
|
||||
"homepage": "http://slimframework.com",
|
||||
"keywords": [
|
||||
"api",
|
||||
"framework",
|
||||
"micro",
|
||||
"router"
|
||||
],
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": []
|
||||
}
|
@@ -1,74 +0,0 @@
|
||||
<?php
|
||||
|
||||
use League\OAuth2\Server\ResourceServer;
|
||||
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
// 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(ResourceServer::class)
|
||||
)
|
||||
);
|
||||
|
||||
// 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',
|
||||
],
|
||||
];
|
||||
|
||||
$totalUsers = count($users);
|
||||
|
||||
// If the access token doesn't have the `basic` scope hide users' names
|
||||
if (in_array('basic', $request->getAttribute('oauth_scopes')) === false) {
|
||||
for ($i = 0; $i < $totalUsers; $i++) {
|
||||
unset($users[$i]['name']);
|
||||
}
|
||||
}
|
||||
|
||||
// If the access token doesn't have the `email` scope hide users' email addresses
|
||||
if (in_array('email', $request->getAttribute('oauth_scopes')) === false) {
|
||||
for ($i = 0; $i < $totalUsers; $i++) {
|
||||
unset($users[$i]['email']);
|
||||
}
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode($users));
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
);
|
||||
|
||||
$app->run();
|
@@ -1,107 +0,0 @@
|
||||
<?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 OAuth2ServerExamples\Entities\UserEntity;
|
||||
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\AuthCodeRepository;
|
||||
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\ScopeRepository;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
use Zend\Diactoros\Stream;
|
||||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
],
|
||||
AuthorizationServer::class => function () {
|
||||
// Init our repositories
|
||||
$clientRepository = new ClientRepository();
|
||||
$scopeRepository = new ScopeRepository();
|
||||
$accessTokenRepository = new AccessTokenRepository();
|
||||
$authCodeRepository = new AuthCodeRepository();
|
||||
$refreshTokenRepository = new RefreshTokenRepository();
|
||||
|
||||
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
|
||||
|
||||
// Setup the authorization server
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$accessTokenRepository,
|
||||
$scopeRepository,
|
||||
$privateKeyPath,
|
||||
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
|
||||
);
|
||||
|
||||
// Enable the authentication code grant on the server with a token TTL of 1 hour
|
||||
$server->enableGrantType(
|
||||
new AuthCodeGrant(
|
||||
$authCodeRepository,
|
||||
$refreshTokenRepository,
|
||||
new \DateInterval('PT10M')
|
||||
),
|
||||
new \DateInterval('PT1H')
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
$app->get('/authorize', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
|
||||
try {
|
||||
// Validate the HTTP request and return an AuthorizationRequest object.
|
||||
// The auth request object can be serialized into a user's session
|
||||
$authRequest = $server->validateAuthorizationRequest($request);
|
||||
|
||||
// Once the user has logged in set the user on the AuthorizationRequest
|
||||
$authRequest->setUser(new UserEntity());
|
||||
|
||||
// Once the user has approved or denied the client update the status
|
||||
// (true = approved, false = denied)
|
||||
$authRequest->setAuthorizationApproved(true);
|
||||
|
||||
// Return the HTTP redirect response
|
||||
return $server->completeAuthorizationRequest($authRequest, $response);
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
} catch (\Exception $exception) {
|
||||
$body = new Stream('php://temp', 'r+');
|
||||
$body->write($exception->getMessage());
|
||||
|
||||
return $response->withStatus(500)->withBody($body);
|
||||
}
|
||||
});
|
||||
|
||||
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
|
||||
try {
|
||||
return $server->respondToAccessTokenRequest($request, $response);
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
} catch (\Exception $exception) {
|
||||
$body = new Stream('php://temp', 'r+');
|
||||
$body->write($exception->getMessage());
|
||||
|
||||
return $response->withStatus(500)->withBody($body);
|
||||
}
|
||||
});
|
||||
|
||||
$app->run();
|
@@ -1,78 +0,0 @@
|
||||
<?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 OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||
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';
|
||||
|
||||
$app = new App([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
],
|
||||
AuthorizationServer::class => function () {
|
||||
// Init our repositories
|
||||
$clientRepository = new ClientRepository(); // instance of ClientRepositoryInterface
|
||||
$scopeRepository = new ScopeRepository(); // instance of ScopeRepositoryInterface
|
||||
$accessTokenRepository = new AccessTokenRepository(); // instance of AccessTokenRepositoryInterface
|
||||
|
||||
// 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
|
||||
|
||||
// Setup the authorization server
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$accessTokenRepository,
|
||||
$scopeRepository,
|
||||
$privateKey,
|
||||
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
|
||||
);
|
||||
|
||||
// Enable the client credentials grant on the server
|
||||
$server->enableGrantType(
|
||||
new \League\OAuth2\Server\Grant\ClientCredentialsGrant(),
|
||||
new \DateInterval('PT1H') // access tokens will expire after 1 hour
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
|
||||
/* @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());
|
||||
|
||||
return $response->withStatus(500)->withBody($body);
|
||||
}
|
||||
});
|
||||
|
||||
$app->run();
|
@@ -1,80 +0,0 @@
|
||||
<?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 OAuth2ServerExamples\Entities\UserEntity;
|
||||
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;
|
||||
use Zend\Diactoros\Stream;
|
||||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
],
|
||||
AuthorizationServer::class => function () {
|
||||
// Init our repositories
|
||||
$clientRepository = new ClientRepository();
|
||||
$scopeRepository = new ScopeRepository();
|
||||
$accessTokenRepository = new AccessTokenRepository();
|
||||
|
||||
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
|
||||
|
||||
// Setup the authorization server
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$accessTokenRepository,
|
||||
$scopeRepository,
|
||||
$privateKeyPath,
|
||||
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
|
||||
);
|
||||
|
||||
// Enable the implicit grant on the server with a token TTL of 1 hour
|
||||
$server->enableGrantType(new ImplicitGrant(new \DateInterval('PT1H')));
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
$app->get('/authorize', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
|
||||
try {
|
||||
// Validate the HTTP request and return an AuthorizationRequest object.
|
||||
// The auth request object can be serialized into a user's session
|
||||
$authRequest = $server->validateAuthorizationRequest($request);
|
||||
|
||||
// Once the user has logged in set the user on the AuthorizationRequest
|
||||
$authRequest->setUser(new UserEntity());
|
||||
|
||||
// Once the user has approved or denied the client update the status
|
||||
// (true = approved, false = denied)
|
||||
$authRequest->setAuthorizationApproved(true);
|
||||
|
||||
// Return the HTTP redirect response
|
||||
return $server->completeAuthorizationRequest($authRequest, $response);
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
} catch (\Exception $exception) {
|
||||
$body = new Stream('php://temp', 'r+');
|
||||
$body->write($exception->getMessage());
|
||||
|
||||
return $response->withStatus(500)->withBody($body);
|
||||
}
|
||||
});
|
||||
|
||||
$app->run();
|
@@ -1,109 +0,0 @@
|
||||
<?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\AuthorizationServerMiddleware;
|
||||
use League\OAuth2\Server\Middleware\ResourceServerMiddleware;
|
||||
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 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,
|
||||
],
|
||||
AuthorizationServer::class => function () {
|
||||
// Init our repositories
|
||||
$clientRepository = new ClientRepository();
|
||||
$accessTokenRepository = new AccessTokenRepository();
|
||||
$scopeRepository = new ScopeRepository();
|
||||
$authCodeRepository = new AuthCodeRepository();
|
||||
$refreshTokenRepository = new RefreshTokenRepository();
|
||||
|
||||
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
|
||||
|
||||
// Setup the authorization server
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$accessTokenRepository,
|
||||
$scopeRepository,
|
||||
$privateKeyPath,
|
||||
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
|
||||
);
|
||||
|
||||
// Enable the authentication code grant on the server with a token TTL of 1 hour
|
||||
$server->enableGrantType(
|
||||
new AuthCodeGrant(
|
||||
$authCodeRepository,
|
||||
$refreshTokenRepository,
|
||||
new \DateInterval('PT10M')
|
||||
),
|
||||
new \DateInterval('PT1H')
|
||||
);
|
||||
|
||||
// Enable the refresh token grant on the server with a token TTL of 1 month
|
||||
$server->enableGrantType(
|
||||
new RefreshTokenGrant($refreshTokenRepository),
|
||||
new \DateInterval('P1M')
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
ResourceServer::class => function () {
|
||||
$publicKeyPath = 'file://' . __DIR__ . '/../public.key';
|
||||
|
||||
$server = new ResourceServer(
|
||||
new AccessTokenRepository(),
|
||||
$publicKeyPath
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
// Access token issuer
|
||||
$app->post('/access_token', function () {
|
||||
})->add(new AuthorizationServerMiddleware($app->getContainer()->get(AuthorizationServer::class)));
|
||||
|
||||
// Secured API
|
||||
$app->group('/api', function () {
|
||||
$this->get('/user', function (ServerRequestInterface $request, ResponseInterface $response) {
|
||||
$params = [];
|
||||
|
||||
if (in_array('basic', $request->getAttribute('oauth_scopes', []))) {
|
||||
$params = [
|
||||
'id' => 1,
|
||||
'name' => 'Alex',
|
||||
'city' => 'London',
|
||||
];
|
||||
}
|
||||
|
||||
if (in_array('email', $request->getAttribute('oauth_scopes', []))) {
|
||||
$params['email'] = 'alex@example.com';
|
||||
}
|
||||
|
||||
$body = new Stream('php://temp', 'r+');
|
||||
$body->write(json_encode($params));
|
||||
|
||||
return $response->withBody($body);
|
||||
});
|
||||
})->add(new ResourceServerMiddleware($app->getContainer()->get(ResourceServer::class)));
|
||||
|
||||
$app->run();
|
@@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Grant\PasswordGrant;
|
||||
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||
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;
|
||||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
// Add the authorization server to the DI container
|
||||
AuthorizationServer::class => function () {
|
||||
|
||||
// Setup the authorization server
|
||||
$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
|
||||
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' // encryption key
|
||||
);
|
||||
|
||||
$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(
|
||||
$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\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
|
||||
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();
|
@@ -1,73 +0,0 @@
|
||||
<?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 OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\ScopeRepository;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
],
|
||||
AuthorizationServer::class => function () {
|
||||
// Init our repositories
|
||||
$clientRepository = new ClientRepository();
|
||||
$accessTokenRepository = new AccessTokenRepository();
|
||||
$scopeRepository = new ScopeRepository();
|
||||
$refreshTokenRepository = new RefreshTokenRepository();
|
||||
|
||||
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
|
||||
|
||||
// Setup the authorization server
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$accessTokenRepository,
|
||||
$scopeRepository,
|
||||
$privateKeyPath,
|
||||
'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
|
||||
|
||||
$server->enableGrantType(
|
||||
$grant,
|
||||
new \DateInterval('PT1H') // The new access token will expire after 1 hour
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
|
||||
try {
|
||||
return $server->respondToAccessTokenRequest($request, $response);
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
} catch (\Exception $exception) {
|
||||
$response->getBody()->write($exception->getMessage());
|
||||
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
$app->run();
|
25
examples/relational/Model/Users.php
Normal file
25
examples/relational/Model/Users.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace RelationalExample\Model;
|
||||
|
||||
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||
|
||||
class Users
|
||||
{
|
||||
public function get($username = null)
|
||||
{
|
||||
$query = Capsule::table('users')->select(['username', 'password', 'name', 'email', 'photo']);
|
||||
|
||||
if ($username !== null) {
|
||||
$query->where('username', '=', $username);
|
||||
}
|
||||
|
||||
$result = $query->get();
|
||||
|
||||
if (count($result) > 0) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
93
examples/relational/Storage/AccessTokenStorage.php
Normal file
93
examples/relational/Storage/AccessTokenStorage.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace RelationalExample\Storage;
|
||||
|
||||
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||
use League\OAuth2\Server\Entity\AccessTokenEntity;
|
||||
use League\OAuth2\Server\Entity\ScopeEntity;
|
||||
use League\OAuth2\Server\Storage\AbstractStorage;
|
||||
use League\OAuth2\Server\Storage\AccessTokenInterface;
|
||||
|
||||
class AccessTokenStorage extends AbstractStorage implements AccessTokenInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($token)
|
||||
{
|
||||
$result = Capsule::table('oauth_access_tokens')
|
||||
->where('access_token', $token)
|
||||
->get();
|
||||
|
||||
if (count($result) === 1) {
|
||||
$token = (new AccessTokenEntity($this->server))
|
||||
->setId($result[0]['access_token'])
|
||||
->setExpireTime($result[0]['expire_time']);
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getScopes(AccessTokenEntity $token)
|
||||
{
|
||||
$result = Capsule::table('oauth_access_token_scopes')
|
||||
->select(['oauth_scopes.id', 'oauth_scopes.description'])
|
||||
->join('oauth_scopes', 'oauth_access_token_scopes.scope', '=', 'oauth_scopes.id')
|
||||
->where('access_token', $token->getId())
|
||||
->get();
|
||||
|
||||
$response = [];
|
||||
|
||||
if (count($result) > 0) {
|
||||
foreach ($result as $row) {
|
||||
$scope = (new ScopeEntity($this->server))->hydrate([
|
||||
'id' => $row['id'],
|
||||
'description' => $row['description'],
|
||||
]);
|
||||
$response[] = $scope;
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create($token, $expireTime, $sessionId)
|
||||
{
|
||||
Capsule::table('oauth_access_tokens')
|
||||
->insert([
|
||||
'access_token' => $token,
|
||||
'session_id' => $sessionId,
|
||||
'expire_time' => $expireTime,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function associateScope(AccessTokenEntity $token, ScopeEntity $scope)
|
||||
{
|
||||
Capsule::table('oauth_access_token_scopes')
|
||||
->insert([
|
||||
'access_token' => $token->getId(),
|
||||
'scope' => $scope->getId(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete(AccessTokenEntity $token)
|
||||
{
|
||||
Capsule::table('oauth_access_tokens')
|
||||
->where('access_token', $token->getId())
|
||||
->delete();
|
||||
}
|
||||
}
|
93
examples/relational/Storage/AuthCodeStorage.php
Normal file
93
examples/relational/Storage/AuthCodeStorage.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace RelationalExample\Storage;
|
||||
|
||||
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||
use League\OAuth2\Server\Entity\AuthCodeEntity;
|
||||
use League\OAuth2\Server\Entity\ScopeEntity;
|
||||
use League\OAuth2\Server\Storage\AbstractStorage;
|
||||
use League\OAuth2\Server\Storage\AuthCodeInterface;
|
||||
|
||||
class AuthCodeStorage extends AbstractStorage implements AuthCodeInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($code)
|
||||
{
|
||||
$result = Capsule::table('oauth_auth_codes')
|
||||
->where('auth_code', $code)
|
||||
->where('expire_time', '>=', time())
|
||||
->get();
|
||||
|
||||
if (count($result) === 1) {
|
||||
$token = new AuthCodeEntity($this->server);
|
||||
$token->setId($result[0]['auth_code']);
|
||||
$token->setRedirectUri($result[0]['client_redirect_uri']);
|
||||
$token->setExpireTime($result[0]['expire_time']);
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public function create($token, $expireTime, $sessionId, $redirectUri)
|
||||
{
|
||||
Capsule::table('oauth_auth_codes')
|
||||
->insert([
|
||||
'auth_code' => $token,
|
||||
'client_redirect_uri' => $redirectUri,
|
||||
'session_id' => $sessionId,
|
||||
'expire_time' => $expireTime,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getScopes(AuthCodeEntity $token)
|
||||
{
|
||||
$result = Capsule::table('oauth_auth_code_scopes')
|
||||
->select(['oauth_scopes.id', 'oauth_scopes.description'])
|
||||
->join('oauth_scopes', 'oauth_auth_code_scopes.scope', '=', 'oauth_scopes.id')
|
||||
->where('auth_code', $token->getId())
|
||||
->get();
|
||||
|
||||
$response = [];
|
||||
|
||||
if (count($result) > 0) {
|
||||
foreach ($result as $row) {
|
||||
$scope = (new ScopeEntity($this->server))->hydrate([
|
||||
'id' => $row['id'],
|
||||
'description' => $row['description'],
|
||||
]);
|
||||
$response[] = $scope;
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function associateScope(AuthCodeEntity $token, ScopeEntity $scope)
|
||||
{
|
||||
Capsule::table('oauth_auth_code_scopes')
|
||||
->insert([
|
||||
'auth_code' => $token->getId(),
|
||||
'scope' => $scope->getId(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete(AuthCodeEntity $token)
|
||||
{
|
||||
Capsule::table('oauth_auth_codes')
|
||||
->where('auth_code', $token->getId())
|
||||
->delete();
|
||||
}
|
||||
}
|
70
examples/relational/Storage/ClientStorage.php
Normal file
70
examples/relational/Storage/ClientStorage.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace RelationalExample\Storage;
|
||||
|
||||
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||
use League\OAuth2\Server\Entity\ClientEntity;
|
||||
use League\OAuth2\Server\Entity\SessionEntity;
|
||||
use League\OAuth2\Server\Storage\AbstractStorage;
|
||||
use League\OAuth2\Server\Storage\ClientInterface;
|
||||
|
||||
class ClientStorage extends AbstractStorage implements ClientInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($clientId, $clientSecret = null, $redirectUri = null, $grantType = null)
|
||||
{
|
||||
$query = Capsule::table('oauth_clients')
|
||||
->select('oauth_clients.*')
|
||||
->where('oauth_clients.id', $clientId);
|
||||
|
||||
if ($clientSecret !== null) {
|
||||
$query->where('oauth_clients.secret', $clientSecret);
|
||||
}
|
||||
|
||||
if ($redirectUri) {
|
||||
$query->join('oauth_client_redirect_uris', 'oauth_clients.id', '=', 'oauth_client_redirect_uris.client_id')
|
||||
->select(['oauth_clients.*', 'oauth_client_redirect_uris.*'])
|
||||
->where('oauth_client_redirect_uris.redirect_uri', $redirectUri);
|
||||
}
|
||||
|
||||
$result = $query->get();
|
||||
|
||||
if (count($result) === 1) {
|
||||
$client = new ClientEntity($this->server);
|
||||
$client->hydrate([
|
||||
'id' => $result[0]['id'],
|
||||
'name' => $result[0]['name'],
|
||||
]);
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBySession(SessionEntity $session)
|
||||
{
|
||||
$result = Capsule::table('oauth_clients')
|
||||
->select(['oauth_clients.id', 'oauth_clients.name'])
|
||||
->join('oauth_sessions', 'oauth_clients.id', '=', 'oauth_sessions.client_id')
|
||||
->where('oauth_sessions.id', $session->getId())
|
||||
->get();
|
||||
|
||||
if (count($result) === 1) {
|
||||
$client = new ClientEntity($this->server);
|
||||
$client->hydrate([
|
||||
'id' => $result[0]['id'],
|
||||
'name' => $result[0]['name'],
|
||||
]);
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
55
examples/relational/Storage/RefreshTokenStorage.php
Normal file
55
examples/relational/Storage/RefreshTokenStorage.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace RelationalExample\Storage;
|
||||
|
||||
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||
use League\OAuth2\Server\Entity\RefreshTokenEntity;
|
||||
use League\OAuth2\Server\Storage\AbstractStorage;
|
||||
use League\OAuth2\Server\Storage\RefreshTokenInterface;
|
||||
|
||||
class RefreshTokenStorage extends AbstractStorage implements RefreshTokenInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($token)
|
||||
{
|
||||
$result = Capsule::table('oauth_refresh_tokens')
|
||||
->where('refresh_token', $token)
|
||||
->get();
|
||||
|
||||
if (count($result) === 1) {
|
||||
$token = (new RefreshTokenEntity($this->server))
|
||||
->setId($result[0]['refresh_token'])
|
||||
->setExpireTime($result[0]['expire_time'])
|
||||
->setAccessTokenId($result[0]['access_token']);
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create($token, $expireTime, $accessToken)
|
||||
{
|
||||
Capsule::table('oauth_refresh_tokens')
|
||||
->insert([
|
||||
'refresh_token' => $token,
|
||||
'access_token' => $accessToken,
|
||||
'expire_time' => $expireTime,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete(RefreshTokenEntity $token)
|
||||
{
|
||||
Capsule::table('oauth_refresh_tokens')
|
||||
->where('refresh_token', $token->getId())
|
||||
->delete();
|
||||
}
|
||||
}
|
30
examples/relational/Storage/ScopeStorage.php
Normal file
30
examples/relational/Storage/ScopeStorage.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace RelationalExample\Storage;
|
||||
|
||||
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||
use League\OAuth2\Server\Entity\ScopeEntity;
|
||||
use League\OAuth2\Server\Storage\AbstractStorage;
|
||||
use League\OAuth2\Server\Storage\ScopeInterface;
|
||||
|
||||
class ScopeStorage extends AbstractStorage implements ScopeInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($scope, $grantType = null, $clientId = null)
|
||||
{
|
||||
$result = Capsule::table('oauth_scopes')
|
||||
->where('id', $scope)
|
||||
->get();
|
||||
|
||||
if (count($result) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (new ScopeEntity($this->server))->hydrate([
|
||||
'id' => $result[0]['id'],
|
||||
'description' => $result[0]['description'],
|
||||
]);
|
||||
}
|
||||
}
|
109
examples/relational/Storage/SessionStorage.php
Normal file
109
examples/relational/Storage/SessionStorage.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace RelationalExample\Storage;
|
||||
|
||||
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||
use League\OAuth2\Server\Entity\AccessTokenEntity;
|
||||
use League\OAuth2\Server\Entity\AuthCodeEntity;
|
||||
use League\OAuth2\Server\Entity\ScopeEntity;
|
||||
use League\OAuth2\Server\Entity\SessionEntity;
|
||||
use League\OAuth2\Server\Storage\AbstractStorage;
|
||||
use League\OAuth2\Server\Storage\SessionInterface;
|
||||
|
||||
class SessionStorage extends AbstractStorage implements SessionInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getByAccessToken(AccessTokenEntity $accessToken)
|
||||
{
|
||||
$result = Capsule::table('oauth_sessions')
|
||||
->select(['oauth_sessions.id', 'oauth_sessions.owner_type', 'oauth_sessions.owner_id', 'oauth_sessions.client_id', 'oauth_sessions.client_redirect_uri'])
|
||||
->join('oauth_access_tokens', 'oauth_access_tokens.session_id', '=', 'oauth_sessions.id')
|
||||
->where('oauth_access_tokens.access_token', $accessToken->getId())
|
||||
->get();
|
||||
|
||||
if (count($result) === 1) {
|
||||
$session = new SessionEntity($this->server);
|
||||
$session->setId($result[0]['id']);
|
||||
$session->setOwner($result[0]['owner_type'], $result[0]['owner_id']);
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getByAuthCode(AuthCodeEntity $authCode)
|
||||
{
|
||||
$result = Capsule::table('oauth_sessions')
|
||||
->select(['oauth_sessions.id', 'oauth_sessions.owner_type', 'oauth_sessions.owner_id', 'oauth_sessions.client_id', 'oauth_sessions.client_redirect_uri'])
|
||||
->join('oauth_auth_codes', 'oauth_auth_codes.session_id', '=', 'oauth_sessions.id')
|
||||
->where('oauth_auth_codes.auth_code', $authCode->getId())
|
||||
->get();
|
||||
|
||||
if (count($result) === 1) {
|
||||
$session = new SessionEntity($this->server);
|
||||
$session->setId($result[0]['id']);
|
||||
$session->setOwner($result[0]['owner_type'], $result[0]['owner_id']);
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getScopes(SessionEntity $session)
|
||||
{
|
||||
$result = Capsule::table('oauth_sessions')
|
||||
->select('oauth_scopes.*')
|
||||
->join('oauth_session_scopes', 'oauth_sessions.id', '=', 'oauth_session_scopes.session_id')
|
||||
->join('oauth_scopes', 'oauth_scopes.id', '=', 'oauth_session_scopes.scope')
|
||||
->where('oauth_sessions.id', $session->getId())
|
||||
->get();
|
||||
|
||||
$scopes = [];
|
||||
|
||||
foreach ($result as $scope) {
|
||||
$scopes[] = (new ScopeEntity($this->server))->hydrate([
|
||||
'id' => $scope['id'],
|
||||
'description' => $scope['description'],
|
||||
]);
|
||||
}
|
||||
|
||||
return $scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create($ownerType, $ownerId, $clientId, $clientRedirectUri = null)
|
||||
{
|
||||
$id = Capsule::table('oauth_sessions')
|
||||
->insertGetId([
|
||||
'owner_type' => $ownerType,
|
||||
'owner_id' => $ownerId,
|
||||
'client_id' => $clientId,
|
||||
]);
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function associateScope(SessionEntity $session, ScopeEntity $scope)
|
||||
{
|
||||
Capsule::table('oauth_session_scopes')
|
||||
->insert([
|
||||
'session_id' => $session->getId(),
|
||||
'scope' => $scope->getId(),
|
||||
]);
|
||||
}
|
||||
}
|
130
examples/relational/api.php
Normal file
130
examples/relational/api.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
use League\OAuth2\Server\ResourceServer;
|
||||
use Orno\Http\Exception\NotFoundException;
|
||||
use Orno\Http\Request;
|
||||
use Orno\Http\Response;
|
||||
use RelationalExample\Model;
|
||||
use RelationalExample\Storage;
|
||||
|
||||
include __DIR__.'/vendor/autoload.php';
|
||||
|
||||
// Set up the OAuth 2.0 resource server
|
||||
$sessionStorage = new Storage\SessionStorage();
|
||||
$accessTokenStorage = new Storage\AccessTokenStorage();
|
||||
$clientStorage = new Storage\ClientStorage();
|
||||
$scopeStorage = new Storage\ScopeStorage();
|
||||
|
||||
$server = new ResourceServer(
|
||||
$sessionStorage,
|
||||
$accessTokenStorage,
|
||||
$clientStorage,
|
||||
$scopeStorage
|
||||
);
|
||||
|
||||
// Routing setup
|
||||
$request = (new Request())->createFromGlobals();
|
||||
$router = new \Orno\Route\RouteCollection();
|
||||
|
||||
// GET /tokeninfo
|
||||
$router->get('/tokeninfo', function (Request $request) use ($server) {
|
||||
|
||||
$accessToken = $server->getAccessToken();
|
||||
$session = $server->getSessionStorage()->getByAccessToken($accessToken);
|
||||
$token = [
|
||||
'owner_id' => $session->getOwnerId(),
|
||||
'owner_type' => $session->getOwnerType(),
|
||||
'access_token' => $accessToken,
|
||||
'client_id' => $session->getClient()->getId(),
|
||||
'scopes' => $accessToken->getScopes(),
|
||||
];
|
||||
|
||||
return new Response(json_encode($token));
|
||||
|
||||
});
|
||||
|
||||
// GET /users
|
||||
$router->get('/users', function (Request $request) use ($server) {
|
||||
|
||||
$results = (new Model\Users())->get();
|
||||
|
||||
$users = [];
|
||||
|
||||
foreach ($results as $result) {
|
||||
$user = [
|
||||
'username' => $result['username'],
|
||||
'name' => $result['name'],
|
||||
];
|
||||
|
||||
if ($server->getAccessToken()->hasScope('email')) {
|
||||
$user['email'] = $result['email'];
|
||||
}
|
||||
|
||||
if ($server->getAccessToken()->hasScope('photo')) {
|
||||
$user['photo'] = $result['photo'];
|
||||
}
|
||||
|
||||
$users[] = $user;
|
||||
}
|
||||
|
||||
return new Response(json_encode($users));
|
||||
});
|
||||
|
||||
// GET /users/{username}
|
||||
$router->get('/users/{username}', function (Request $request, Response $response, array $args) use ($server) {
|
||||
|
||||
$result = (new Model\Users())->get($args['username']);
|
||||
|
||||
if (count($result) === 0) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
$user = [
|
||||
'username' => $result[0]['username'],
|
||||
'name' => $result[0]['name'],
|
||||
];
|
||||
|
||||
if ($server->getAccessToken()->hasScope('email')) {
|
||||
$user['email'] = $result[0]['email'];
|
||||
}
|
||||
|
||||
if ($server->getAccessToken()->hasScope('photo')) {
|
||||
$user['photo'] = $result[0]['photo'];
|
||||
}
|
||||
|
||||
return new Response(json_encode($user));
|
||||
});
|
||||
|
||||
$dispatcher = $router->getDispatcher();
|
||||
|
||||
try {
|
||||
// Check that access token is present
|
||||
$server->isValidRequest(false);
|
||||
|
||||
// A successful response
|
||||
$response = $dispatcher->dispatch(
|
||||
$request->getMethod(),
|
||||
$request->getPathInfo()
|
||||
);
|
||||
} catch (\Orno\Http\Exception $e) {
|
||||
// A failed response
|
||||
$response = $e->getJsonResponse();
|
||||
$response->setContent(json_encode(['status_code' => $e->getStatusCode(), 'message' => $e->getMessage()]));
|
||||
} catch (\League\OAuth2\Server\Exception\OAuthException $e) {
|
||||
$response = new Response(json_encode([
|
||||
'error' => $e->errorType,
|
||||
'message' => $e->getMessage(),
|
||||
]), $e->httpStatusCode);
|
||||
|
||||
foreach ($e->getHttpHeaders() as $header) {
|
||||
$response->headers($header);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$response = new Orno\Http\Response();
|
||||
$response->setStatusCode(500);
|
||||
$response->setContent(json_encode(['status_code' => 500, 'message' => $e->getMessage()]));
|
||||
} finally {
|
||||
// Return the response
|
||||
$response->headers->set('Content-type', 'application/json');
|
||||
$response->send();
|
||||
}
|
117
examples/relational/authcode_grant.php
Normal file
117
examples/relational/authcode_grant.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
use Orno\Http\Request;
|
||||
use Orno\Http\Response;
|
||||
use RelationalExample\Storage;
|
||||
|
||||
include __DIR__.'/vendor/autoload.php';
|
||||
|
||||
// Routing setup
|
||||
$request = (new Request())->createFromGlobals();
|
||||
$router = new \Orno\Route\RouteCollection();
|
||||
$router->setStrategy(\Orno\Route\RouteStrategyInterface::RESTFUL_STRATEGY);
|
||||
|
||||
// Set up the OAuth 2.0 authorization server
|
||||
$server = new \League\OAuth2\Server\AuthorizationServer();
|
||||
$server->setSessionStorage(new Storage\SessionStorage());
|
||||
$server->setAccessTokenStorage(new Storage\AccessTokenStorage());
|
||||
$server->setRefreshTokenStorage(new Storage\RefreshTokenStorage());
|
||||
$server->setClientStorage(new Storage\ClientStorage());
|
||||
$server->setScopeStorage(new Storage\ScopeStorage());
|
||||
$server->setAuthCodeStorage(new Storage\AuthCodeStorage());
|
||||
|
||||
$authCodeGrant = new \League\OAuth2\Server\Grant\AuthCodeGrant();
|
||||
$server->addGrantType($authCodeGrant);
|
||||
|
||||
$refrehTokenGrant = new \League\OAuth2\Server\Grant\RefreshTokenGrant();
|
||||
$server->addGrantType($refrehTokenGrant);
|
||||
|
||||
// Routing setup
|
||||
$request = (new Request())->createFromGlobals();
|
||||
$router = new \Orno\Route\RouteCollection();
|
||||
|
||||
$router->get('/authorize', function (Request $request) use ($server) {
|
||||
|
||||
// First ensure the parameters in the query string are correct
|
||||
|
||||
try {
|
||||
$authParams = $server->getGrantType('authorization_code')->checkAuthorizeParams();
|
||||
} catch (\Exception $e) {
|
||||
return new Response(
|
||||
json_encode([
|
||||
'error' => $e->errorType,
|
||||
'message' => $e->getMessage(),
|
||||
]),
|
||||
$e->httpStatusCode,
|
||||
$e->getHttpHeaders()
|
||||
);
|
||||
}
|
||||
|
||||
// Normally at this point you would show the user a sign-in screen and ask them to authorize the requested scopes
|
||||
|
||||
// ...
|
||||
|
||||
// ...
|
||||
|
||||
// ...
|
||||
|
||||
// Create a new authorize request which will respond with a redirect URI that the user will be redirected to
|
||||
|
||||
$redirectUri = $server->getGrantType('authorization_code')->newAuthorizeRequest('user', 1, $authParams);
|
||||
|
||||
$response = new Response('', 200, [
|
||||
'Location' => $redirectUri
|
||||
]);
|
||||
|
||||
return $response;
|
||||
});
|
||||
|
||||
$router->post('/access_token', function (Request $request) use ($server) {
|
||||
|
||||
try {
|
||||
$response = $server->issueAccessToken();
|
||||
|
||||
return new Response(json_encode($response), 200);
|
||||
} catch (\Exception $e) {
|
||||
return new Response(
|
||||
json_encode([
|
||||
'error' => $e->errorType,
|
||||
'message' => $e->getMessage(),
|
||||
]),
|
||||
$e->httpStatusCode,
|
||||
$e->getHttpHeaders()
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$dispatcher = $router->getDispatcher();
|
||||
|
||||
try {
|
||||
// A successful response
|
||||
$response = $dispatcher->dispatch(
|
||||
$request->getMethod(),
|
||||
$request->getPathInfo()
|
||||
);
|
||||
} catch (\Orno\Http\Exception $e) {
|
||||
// A failed response
|
||||
$response = $e->getJsonResponse();
|
||||
$response->setContent(json_encode(['status_code' => $e->getStatusCode(), 'message' => $e->getMessage()]));
|
||||
} catch (\League\OAuth2\Server\Exception\OAuthException $e) {
|
||||
$response = new Response(json_encode([
|
||||
'error' => $e->errorType,
|
||||
'message' => $e->getMessage(),
|
||||
]), $e->httpStatusCode);
|
||||
|
||||
foreach ($e->getHttpHeaders() as $header) {
|
||||
$response->headers($header);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$response = new Orno\Http\Response();
|
||||
$response->setStatusCode(500);
|
||||
$response->setContent(json_encode(['status_code' => 500, 'message' => $e->getMessage()]));
|
||||
} finally {
|
||||
// Return the response
|
||||
$response->headers->set('Content-type', 'application/json');
|
||||
$response->send();
|
||||
}
|
17
examples/relational/composer.json
Normal file
17
examples/relational/composer.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"require": {
|
||||
"illuminate/database": "4.1.*",
|
||||
"orno/route": "1.*",
|
||||
"ircmaxell/password-compat": "1.0.2",
|
||||
"league/event": "0.2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\OAuth2\\Server\\": "../../src/",
|
||||
"RelationalExample\\": "."
|
||||
},
|
||||
"files": [
|
||||
"config/db.php"
|
||||
]
|
||||
}
|
||||
}
|
18
examples/relational/config/db.php
Normal file
18
examples/relational/config/db.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace RelationalExample\Config;
|
||||
|
||||
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||
|
||||
include __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
$capsule = new Capsule();
|
||||
|
||||
$capsule->addConnection([
|
||||
'driver' => 'sqlite',
|
||||
'database' => __DIR__.'/oauth2.sqlite3',
|
||||
'charset' => 'utf8',
|
||||
'collation' => 'utf8_unicode_ci',
|
||||
]);
|
||||
|
||||
$capsule->setAsGlobal();
|
249
examples/relational/config/init.php
Normal file
249
examples/relational/config/init.php
Normal file
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
namespace RelationalExample\Config;
|
||||
|
||||
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||
|
||||
include __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
@unlink(__DIR__.'/oauth2.sqlite3');
|
||||
touch(__DIR__.'/oauth2.sqlite3');
|
||||
|
||||
Capsule::statement('PRAGMA foreign_keys = ON');
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
print 'Creating users table'.PHP_EOL;
|
||||
|
||||
Capsule::schema()->create('users', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->string('username');
|
||||
$table->string('password');
|
||||
$table->string('name');
|
||||
$table->string('email');
|
||||
$table->string('photo');
|
||||
});
|
||||
|
||||
Capsule::table('users')->insert([
|
||||
'username' => 'alexbilbie',
|
||||
'password' => password_hash('whisky', PASSWORD_DEFAULT),
|
||||
'name' => 'Alex Bilbie',
|
||||
'email' => 'hello@alexbilbie.com',
|
||||
'photo' => 'https://s.gravatar.com/avatar/14902eb1dac66b8458ebbb481d80f0a3',
|
||||
]);
|
||||
|
||||
Capsule::table('users')->insert([
|
||||
'username' => 'philsturgeon',
|
||||
'password' => password_hash('cider', PASSWORD_DEFAULT),
|
||||
'name' => 'Phil Sturgeon',
|
||||
'email' => 'email@philsturgeon.co.uk',
|
||||
'photo' => 'https://s.gravatar.com/avatar/14df293d6c5cd6f05996dfc606a6a951',
|
||||
]);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
print 'Creating clients table'.PHP_EOL;
|
||||
|
||||
Capsule::schema()->create('oauth_clients', function ($table) {
|
||||
$table->string('id');
|
||||
$table->string('secret');
|
||||
$table->string('name');
|
||||
$table->primary('id');
|
||||
});
|
||||
|
||||
Capsule::table('oauth_clients')->insert([
|
||||
'id' => 'testclient',
|
||||
'secret' => 'secret',
|
||||
'name' => 'Test Client',
|
||||
]);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
print 'Creating client redirect uris table'.PHP_EOL;
|
||||
|
||||
Capsule::schema()->create('oauth_client_redirect_uris', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->string('client_id');
|
||||
$table->string('redirect_uri');
|
||||
});
|
||||
|
||||
Capsule::table('oauth_client_redirect_uris')->insert([
|
||||
'client_id' => 'testclient',
|
||||
'redirect_uri' => 'http://example.com/redirect',
|
||||
]);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
print 'Creating scopes table'.PHP_EOL;
|
||||
|
||||
Capsule::schema()->create('oauth_scopes', function ($table) {
|
||||
$table->string('id');
|
||||
$table->string('description');
|
||||
$table->primary('id');
|
||||
});
|
||||
|
||||
Capsule::table('oauth_scopes')->insert([
|
||||
'id' => 'basic',
|
||||
'description' => 'Basic details about your account',
|
||||
]);
|
||||
|
||||
Capsule::table('oauth_scopes')->insert([
|
||||
'id' => 'email',
|
||||
'description' => 'Your email address',
|
||||
]);
|
||||
|
||||
Capsule::table('oauth_scopes')->insert([
|
||||
'id' => 'photo',
|
||||
'description' => 'Your photo',
|
||||
]);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
print 'Creating sessions table'.PHP_EOL;
|
||||
|
||||
Capsule::schema()->create('oauth_sessions', function ($table) {
|
||||
$table->increments('id')->unsigned();
|
||||
$table->string('owner_type');
|
||||
$table->string('owner_id');
|
||||
$table->string('client_id');
|
||||
$table->string('client_redirect_uri')->nullable();
|
||||
|
||||
$table->foreign('client_id')->references('id')->on('oauth_clients')->onDelete('cascade');
|
||||
});
|
||||
|
||||
Capsule::table('oauth_sessions')->insert([
|
||||
'owner_type' => 'client',
|
||||
'owner_id' => 'testclient',
|
||||
'client_id' => 'testclient',
|
||||
]);
|
||||
|
||||
Capsule::table('oauth_sessions')->insert([
|
||||
'owner_type' => 'user',
|
||||
'owner_id' => '1',
|
||||
'client_id' => 'testclient',
|
||||
]);
|
||||
|
||||
Capsule::table('oauth_sessions')->insert([
|
||||
'owner_type' => 'user',
|
||||
'owner_id' => '2',
|
||||
'client_id' => 'testclient',
|
||||
]);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
print 'Creating access tokens table'.PHP_EOL;
|
||||
|
||||
Capsule::schema()->create('oauth_access_tokens', function ($table) {
|
||||
$table->string('access_token')->primary();
|
||||
$table->integer('session_id')->unsigned();
|
||||
$table->integer('expire_time');
|
||||
|
||||
$table->foreign('session_id')->references('id')->on('oauth_sessions')->onDelete('cascade');
|
||||
});
|
||||
|
||||
Capsule::table('oauth_access_tokens')->insert([
|
||||
'access_token' => 'iamgod',
|
||||
'session_id' => '1',
|
||||
'expire_time' => time() + 86400,
|
||||
]);
|
||||
|
||||
Capsule::table('oauth_access_tokens')->insert([
|
||||
'access_token' => 'iamalex',
|
||||
'session_id' => '2',
|
||||
'expire_time' => time() + 86400,
|
||||
]);
|
||||
|
||||
Capsule::table('oauth_access_tokens')->insert([
|
||||
'access_token' => 'iamphil',
|
||||
'session_id' => '3',
|
||||
'expire_time' => time() + 86400,
|
||||
]);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
print 'Creating refresh tokens table'.PHP_EOL;
|
||||
|
||||
Capsule::schema()->create('oauth_refresh_tokens', function ($table) {
|
||||
$table->string('refresh_token')->primary();
|
||||
$table->integer('expire_time');
|
||||
$table->string('access_token');
|
||||
|
||||
$table->foreign('access_token')->references('access_token')->on('oauth_access_tokens')->onDelete('cascade');
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
print 'Creating auth codes table'.PHP_EOL;
|
||||
|
||||
Capsule::schema()->create('oauth_auth_codes', function ($table) {
|
||||
$table->string('auth_code')->primary();
|
||||
$table->integer('session_id')->unsigned();
|
||||
$table->integer('expire_time');
|
||||
$table->string('client_redirect_uri');
|
||||
|
||||
$table->foreign('session_id')->references('id')->on('oauth_sessions')->onDelete('cascade');
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
print 'Creating oauth access token scopes table'.PHP_EOL;
|
||||
|
||||
Capsule::schema()->create('oauth_access_token_scopes', function ($table) {
|
||||
$table->increments('id')->unsigned();
|
||||
$table->string('access_token');
|
||||
$table->string('scope');
|
||||
|
||||
$table->foreign('access_token')->references('access_token')->on('oauth_access_tokens')->onDelete('cascade');
|
||||
$table->foreign('scope')->references('id')->on('oauth_scopes')->onDelete('cascade');
|
||||
});
|
||||
|
||||
Capsule::table('oauth_access_token_scopes')->insert([
|
||||
'access_token' => 'iamgod',
|
||||
'scope' => 'basic',
|
||||
]);
|
||||
|
||||
Capsule::table('oauth_access_token_scopes')->insert([
|
||||
'access_token' => 'iamgod',
|
||||
'scope' => 'email',
|
||||
]);
|
||||
|
||||
Capsule::table('oauth_access_token_scopes')->insert([
|
||||
'access_token' => 'iamgod',
|
||||
'scope' => 'photo',
|
||||
]);
|
||||
|
||||
Capsule::table('oauth_access_token_scopes')->insert([
|
||||
'access_token' => 'iamphil',
|
||||
'scope' => 'email',
|
||||
]);
|
||||
|
||||
Capsule::table('oauth_access_token_scopes')->insert([
|
||||
'access_token' => 'iamalex',
|
||||
'scope' => 'photo',
|
||||
]);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
print 'Creating oauth auth code scopes table'.PHP_EOL;
|
||||
|
||||
Capsule::schema()->create('oauth_auth_code_scopes', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->string('auth_code');
|
||||
$table->string('scope');
|
||||
|
||||
$table->foreign('auth_code')->references('auth_code')->on('oauth_auth_codes')->onDelete('cascade');
|
||||
$table->foreign('scope')->references('id')->on('oauth_scopes')->onDelete('cascade');
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
print 'Creating oauth session scopes table'.PHP_EOL;
|
||||
|
||||
Capsule::schema()->create('oauth_session_scopes', function ($table) {
|
||||
$table->increments('id')->unsigned();
|
||||
$table->integer('session_id')->unsigned();
|
||||
$table->string('scope');
|
||||
|
||||
$table->foreign('session_id')->references('id')->on('oauth_sessions')->onDelete('cascade');
|
||||
$table->foreign('scope')->references('id')->on('oauth_scopes')->onDelete('cascade');
|
||||
});
|
97
examples/relational/other_grants.php
Normal file
97
examples/relational/other_grants.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
use Orno\Http\Request;
|
||||
use Orno\Http\Response;
|
||||
use RelationalExample\Model;
|
||||
use RelationalExample\Storage;
|
||||
|
||||
include __DIR__.'/vendor/autoload.php';
|
||||
|
||||
// Routing setup
|
||||
$request = (new Request())->createFromGlobals();
|
||||
$router = new \Orno\Route\RouteCollection();
|
||||
$router->setStrategy(\Orno\Route\RouteStrategyInterface::RESTFUL_STRATEGY);
|
||||
|
||||
// Set up the OAuth 2.0 authorization server
|
||||
$server = new \League\OAuth2\Server\AuthorizationServer();
|
||||
$server->setSessionStorage(new Storage\SessionStorage());
|
||||
$server->setAccessTokenStorage(new Storage\AccessTokenStorage());
|
||||
$server->setRefreshTokenStorage(new Storage\RefreshTokenStorage());
|
||||
$server->setClientStorage(new Storage\ClientStorage());
|
||||
$server->setScopeStorage(new Storage\ScopeStorage());
|
||||
$server->setAuthCodeStorage(new Storage\AuthCodeStorage());
|
||||
|
||||
$clientCredentials = new \League\OAuth2\Server\Grant\ClientCredentialsGrant();
|
||||
$server->addGrantType($clientCredentials);
|
||||
|
||||
$passwordGrant = new \League\OAuth2\Server\Grant\PasswordGrant();
|
||||
$passwordGrant->setVerifyCredentialsCallback(function ($username, $password) {
|
||||
$result = (new Model\Users())->get($username);
|
||||
if (count($result) !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (password_verify($password, $result[0]['password'])) {
|
||||
return $username;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
$server->addGrantType($passwordGrant);
|
||||
|
||||
$refrehTokenGrant = new \League\OAuth2\Server\Grant\RefreshTokenGrant();
|
||||
$server->addGrantType($refrehTokenGrant);
|
||||
|
||||
// Routing setup
|
||||
$request = (new Request())->createFromGlobals();
|
||||
$router = new \Orno\Route\RouteCollection();
|
||||
|
||||
$router->post('/access_token', function (Request $request) use ($server) {
|
||||
|
||||
try {
|
||||
$response = $server->issueAccessToken();
|
||||
|
||||
return new Response(json_encode($response), 200);
|
||||
} catch (\Exception $e) {
|
||||
return new Response(
|
||||
json_encode([
|
||||
'error' => $e->errorType,
|
||||
'message' => $e->getMessage(),
|
||||
]),
|
||||
$e->httpStatusCode,
|
||||
$e->getHttpHeaders()
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$dispatcher = $router->getDispatcher();
|
||||
|
||||
try {
|
||||
// A successful response
|
||||
$response = $dispatcher->dispatch(
|
||||
$request->getMethod(),
|
||||
$request->getPathInfo()
|
||||
);
|
||||
} catch (\Orno\Http\Exception $e) {
|
||||
// A failed response
|
||||
$response = $e->getJsonResponse();
|
||||
$response->setContent(json_encode(['status_code' => $e->getStatusCode(), 'message' => $e->getMessage()]));
|
||||
} catch (\League\OAuth2\Server\Exception\OAuthException $e) {
|
||||
$response = new Response(json_encode([
|
||||
'error' => $e->errorType,
|
||||
'message' => $e->getMessage(),
|
||||
]), $e->httpStatusCode);
|
||||
|
||||
foreach ($e->getHttpHeaders() as $header) {
|
||||
$response->headers($header);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$response = new Orno\Http\Response();
|
||||
$response->setStatusCode(500);
|
||||
$response->setContent(json_encode(['status_code' => 500, 'message' => $e->getMessage()]));
|
||||
} finally {
|
||||
// Return the response
|
||||
$response->headers->set('Content-type', 'application/json');
|
||||
$response->send();
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\AccessTokenTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
|
||||
|
||||
class AccessTokenEntity implements AccessTokenEntityInterface
|
||||
{
|
||||
use AccessTokenTrait, TokenEntityTrait, EntityTrait;
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\AuthCodeTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
|
||||
|
||||
class AuthCodeEntity implements AuthCodeEntityInterface
|
||||
{
|
||||
use EntityTrait, TokenEntityTrait, AuthCodeTrait;
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\ClientTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
|
||||
class ClientEntity implements ClientEntityInterface
|
||||
{
|
||||
use EntityTrait, ClientTrait;
|
||||
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function setRedirectUri($uri)
|
||||
{
|
||||
$this->redirectUri = $uri;
|
||||
}
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\RefreshTokenTrait;
|
||||
|
||||
class RefreshTokenEntity implements RefreshTokenEntityInterface
|
||||
{
|
||||
use RefreshTokenTrait, EntityTrait;
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
|
||||
class ScopeEntity implements ScopeEntityInterface
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->getIdentifier();
|
||||
}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||
|
||||
class UserEntity implements UserEntityInterface
|
||||
{
|
||||
/**
|
||||
* Return the user's identifier.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
@@ -1,57 +0,0 @@
|
||||
<?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\AccessTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||
use OAuth2ServerExamples\Entities\AccessTokenEntity;
|
||||
|
||||
class AccessTokenRepository implements AccessTokenRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity)
|
||||
{
|
||||
// Some logic here to save the access token to a database
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function revokeAccessToken($tokenId)
|
||||
{
|
||||
// Some logic here to revoke the access token
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isAccessTokenRevoked($tokenId)
|
||||
{
|
||||
return false; // Access token hasn't been revoked
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null)
|
||||
{
|
||||
$accessToken = new AccessTokenEntity();
|
||||
$accessToken->setClient($clientEntity);
|
||||
foreach ($scopes as $scope) {
|
||||
$accessToken->addScope($scope);
|
||||
}
|
||||
$accessToken->setUserIdentifier($userIdentifier);
|
||||
|
||||
return $accessToken;
|
||||
}
|
||||
}
|
@@ -1,49 +0,0 @@
|
||||
<?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\AuthCodeEntityInterface;
|
||||
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
|
||||
use OAuth2ServerExamples\Entities\AuthCodeEntity;
|
||||
|
||||
class AuthCodeRepository implements AuthCodeRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity)
|
||||
{
|
||||
// Some logic to persist the auth code to a database
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function revokeAuthCode($codeId)
|
||||
{
|
||||
// Some logic to revoke the auth code in a database
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isAuthCodeRevoked($codeId)
|
||||
{
|
||||
return false; // The auth code has not been revoked
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNewAuthCode()
|
||||
{
|
||||
return new AuthCodeEntity();
|
||||
}
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
<?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\Repositories\ClientRepositoryInterface;
|
||||
use OAuth2ServerExamples\Entities\ClientEntity;
|
||||
|
||||
class ClientRepository implements ClientRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getClientEntity($clientIdentifier, $grantType = null, $clientSecret = null, $mustValidateSecret = true)
|
||||
{
|
||||
$clients = [
|
||||
'myawesomeapp' => [
|
||||
'secret' => password_hash('abc123', PASSWORD_BCRYPT),
|
||||
'name' => 'My Awesome App',
|
||||
'redirect_uri' => 'http://foo/bar',
|
||||
'is_confidential' => true,
|
||||
],
|
||||
];
|
||||
|
||||
// Check if client is registered
|
||||
if (array_key_exists($clientIdentifier, $clients) === false) {
|
||||
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']);
|
||||
$client->setRedirectUri($clients[$clientIdentifier]['redirect_uri']);
|
||||
|
||||
return $client;
|
||||
}
|
||||
}
|
@@ -1,49 +0,0 @@
|
||||
<?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\RefreshTokenEntityInterface;
|
||||
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||
use OAuth2ServerExamples\Entities\RefreshTokenEntity;
|
||||
|
||||
class RefreshTokenRepository implements RefreshTokenRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntityInterface)
|
||||
{
|
||||
// Some logic to persist the refresh token in a database
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function revokeRefreshToken($tokenId)
|
||||
{
|
||||
// Some logic to revoke the refresh token in a database
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRefreshTokenRevoked($tokenId)
|
||||
{
|
||||
return false; // The refresh token has not been revoked
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNewRefreshToken()
|
||||
{
|
||||
return new RefreshTokenEntity();
|
||||
}
|
||||
}
|
@@ -1,60 +0,0 @@
|
||||
<?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\ScopeRepositoryInterface;
|
||||
use OAuth2ServerExamples\Entities\ScopeEntity;
|
||||
|
||||
class ScopeRepository implements ScopeRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getScopeEntityByIdentifier($scopeIdentifier)
|
||||
{
|
||||
$scopes = [
|
||||
'basic' => [
|
||||
'description' => 'Basic details about you',
|
||||
],
|
||||
'email' => [
|
||||
'description' => 'Your email address',
|
||||
],
|
||||
];
|
||||
|
||||
if (array_key_exists($scopeIdentifier, $scopes) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope = new ScopeEntity();
|
||||
$scope->setIdentifier($scopeIdentifier);
|
||||
|
||||
return $scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function finalizeScopes(
|
||||
array $scopes,
|
||||
$grantType,
|
||||
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;
|
||||
}
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
<?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\UserEntity;
|
||||
|
||||
class UserRepository implements UserRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUserEntityByUserCredentials(
|
||||
$username,
|
||||
$password,
|
||||
$grantType,
|
||||
ClientEntityInterface $clientEntity
|
||||
) {
|
||||
if ($username === 'alex' && $password === 'whisky') {
|
||||
return new UserEntity();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
10
phpstan.neon
10
phpstan.neon
@@ -1,10 +0,0 @@
|
||||
includes:
|
||||
- vendor/phpstan/phpstan-phpunit/extension.neon
|
||||
- vendor/phpstan/phpstan-phpunit/rules.neon
|
||||
- vendor/phpstan/phpstan-phpunit/strictRules.neon
|
||||
- vendor/phpstan/phpstan-strict-rules/rules.neon
|
||||
services:
|
||||
-
|
||||
class: LeagueTests\PHPStan\AbstractGrantExtension
|
||||
tags:
|
||||
- phpstan.broker.dynamicMethodReturnTypeExtension
|
17
phpunit.xml
Normal file
17
phpunit.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit colors="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" stopOnError="true" stopOnFailure="true" stopOnIncomplete="false" stopOnSkipped="false" bootstrap="tests/unit/Bootstrap.php">
|
||||
<testsuites>
|
||||
<testsuite name="Tests">
|
||||
<directory>./tests/unit/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
<logging>
|
||||
<!-- <log type="coverage-text" target="php://stdout" title="thephpleague/oauth2-server" charset="UTF-8" yui="true" highlight="true" lowUpperBound="60" highLowerBound="90"/> -->
|
||||
<log type="coverage-html" target="build/coverage" title="thephpleague/oauth2-server" charset="UTF-8" yui="true" highlight="true" lowUpperBound="60" highLowerBound="90"/>
|
||||
</logging>
|
||||
</phpunit>
|
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit colors="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" stopOnError="true"
|
||||
stopOnFailure="true" stopOnIncomplete="false" stopOnSkipped="false" bootstrap="tests/Bootstrap.php">
|
||||
<testsuites>
|
||||
<testsuite name="Tests">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src</directory>
|
||||
<exclude>
|
||||
<directory suffix=".php">src/ResponseTypes/DefaultTemplates</directory>
|
||||
<directory suffix=".php">src/TemplateRenderer</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
<logging>
|
||||
<log type="coverage-text" target="php://stdout" title="thephpleague/oauth2-server" charset="UTF-8" yui="true"
|
||||
highlight="true" lowUpperBound="60" highLowerBound="90"/>
|
||||
<log type="coverage-html" target="build/coverage" title="thephpleague/oauth2-server" charset="UTF-8" yui="true"
|
||||
highlight="true" lowUpperBound="60" highLowerBound="90"/>
|
||||
</logging>
|
||||
</phpunit>
|
358
src/AbstractServer.php
Normal file
358
src/AbstractServer.php
Normal file
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Abstract Server
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\Emitter;
|
||||
use League\OAuth2\Server\Storage\AccessTokenInterface;
|
||||
use League\OAuth2\Server\Storage\AuthCodeInterface;
|
||||
use League\OAuth2\Server\Storage\ClientInterface;
|
||||
use League\OAuth2\Server\Storage\MacTokenInterface;
|
||||
use League\OAuth2\Server\Storage\RefreshTokenInterface;
|
||||
use League\OAuth2\Server\Storage\ScopeInterface;
|
||||
use League\OAuth2\Server\Storage\SessionInterface;
|
||||
use League\OAuth2\Server\TokenType\TokenTypeInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* OAuth 2.0 Resource Server
|
||||
*/
|
||||
abstract class AbstractServer
|
||||
{
|
||||
/**
|
||||
* The request object
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* Session storage
|
||||
*
|
||||
* @var \League\OAuth2\Server\Storage\SessionInterface
|
||||
*/
|
||||
protected $sessionStorage;
|
||||
|
||||
/**
|
||||
* Access token storage
|
||||
*
|
||||
* @var \League\OAuth2\Server\Storage\AccessTokenInterface
|
||||
*/
|
||||
protected $accessTokenStorage;
|
||||
|
||||
/**
|
||||
* Refresh token storage
|
||||
*
|
||||
* @var \League\OAuth2\Server\Storage\RefreshTokenInterface
|
||||
*/
|
||||
protected $refreshTokenStorage;
|
||||
|
||||
/**
|
||||
* Auth code storage
|
||||
*
|
||||
* @var \League\OAuth2\Server\Storage\AuthCodeInterface
|
||||
*/
|
||||
protected $authCodeStorage;
|
||||
|
||||
/**
|
||||
* Scope storage
|
||||
*
|
||||
* @var \League\OAuth2\Server\Storage\ScopeInterface
|
||||
*/
|
||||
protected $scopeStorage;
|
||||
|
||||
/**
|
||||
* Client storage
|
||||
*
|
||||
* @var \League\OAuth2\Server\Storage\ClientInterface
|
||||
*/
|
||||
protected $clientStorage;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\Storage\MacTokenInterface
|
||||
*/
|
||||
protected $macStorage;
|
||||
|
||||
/**
|
||||
* Token type
|
||||
*
|
||||
* @var \League\OAuth2\Server\TokenType\TokenTypeInterface
|
||||
*/
|
||||
protected $tokenType;
|
||||
|
||||
/**
|
||||
* Event emitter
|
||||
*
|
||||
* @var \League\Event\Emitter
|
||||
*/
|
||||
protected $eventEmitter;
|
||||
|
||||
/**
|
||||
* Abstract server constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->setEventEmitter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an event emitter
|
||||
*
|
||||
* @param object $emitter Event emitter object
|
||||
*/
|
||||
public function setEventEmitter($emitter = null)
|
||||
{
|
||||
if ($emitter === null) {
|
||||
$this->eventEmitter = new Emitter();
|
||||
} else {
|
||||
$this->eventEmitter = $emitter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event listener to the event emitter
|
||||
*
|
||||
* @param string $eventName Event name
|
||||
* @param callable $listener Callable function or method
|
||||
* @param int $priority Priority of event listener
|
||||
*/
|
||||
public function addEventListener($eventName, callable $listener, $priority = Emitter::P_NORMAL)
|
||||
{
|
||||
$this->eventEmitter->addListener($eventName, $listener, $priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the event emitter
|
||||
*
|
||||
* @return \League\Event\Emitter
|
||||
*/
|
||||
public function getEventEmitter()
|
||||
{
|
||||
return $this->eventEmitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Request Object
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request The Request Object
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setRequest($request)
|
||||
{
|
||||
$this->request = $request;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Request object. It will create one from the globals if one is not set.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Request
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
if ($this->request === null) {
|
||||
$this->request = Request::createFromGlobals();
|
||||
}
|
||||
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the client storage
|
||||
*
|
||||
* @param \League\OAuth2\Server\Storage\ClientInterface $storage
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setClientStorage(ClientInterface $storage)
|
||||
{
|
||||
$storage->setServer($this);
|
||||
$this->clientStorage = $storage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the session storage
|
||||
*
|
||||
* @param \League\OAuth2\Server\Storage\SessionInterface $storage
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setSessionStorage(SessionInterface $storage)
|
||||
{
|
||||
$storage->setServer($this);
|
||||
$this->sessionStorage = $storage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the access token storage
|
||||
*
|
||||
* @param \League\OAuth2\Server\Storage\AccessTokenInterface $storage
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setAccessTokenStorage(AccessTokenInterface $storage)
|
||||
{
|
||||
$storage->setServer($this);
|
||||
$this->accessTokenStorage = $storage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the refresh token storage
|
||||
*
|
||||
* @param \League\OAuth2\Server\Storage\RefreshTokenInterface $storage
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setRefreshTokenStorage(RefreshTokenInterface $storage)
|
||||
{
|
||||
$storage->setServer($this);
|
||||
$this->refreshTokenStorage = $storage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the auth code storage
|
||||
*
|
||||
* @param \League\OAuth2\Server\Storage\AuthCodeInterface $storage
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setAuthCodeStorage(AuthCodeInterface $storage)
|
||||
{
|
||||
$storage->setServer($this);
|
||||
$this->authCodeStorage = $storage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the scope storage
|
||||
*
|
||||
* @param \League\OAuth2\Server\Storage\ScopeInterface $storage
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setScopeStorage(ScopeInterface $storage)
|
||||
{
|
||||
$storage->setServer($this);
|
||||
$this->scopeStorage = $storage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the client storage
|
||||
*
|
||||
* @return \League\OAuth2\Server\Storage\ClientInterface
|
||||
*/
|
||||
public function getClientStorage()
|
||||
{
|
||||
return $this->clientStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the scope storage
|
||||
*
|
||||
* @return \League\OAuth2\Server\Storage\ScopeInterface
|
||||
*/
|
||||
public function getScopeStorage()
|
||||
{
|
||||
return $this->scopeStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the session storage
|
||||
*
|
||||
* @return \League\OAuth2\Server\Storage\SessionInterface
|
||||
*/
|
||||
public function getSessionStorage()
|
||||
{
|
||||
return $this->sessionStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the refresh token storage
|
||||
*
|
||||
* @return \League\OAuth2\Server\Storage\RefreshTokenInterface
|
||||
*/
|
||||
public function getRefreshTokenStorage()
|
||||
{
|
||||
return $this->refreshTokenStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the access token storage
|
||||
*
|
||||
* @return \League\OAuth2\Server\Storage\AccessTokenInterface
|
||||
*/
|
||||
public function getAccessTokenStorage()
|
||||
{
|
||||
return $this->accessTokenStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the auth code storage
|
||||
*
|
||||
* @return \League\OAuth2\Server\Storage\AuthCodeInterface
|
||||
*/
|
||||
public function getAuthCodeStorage()
|
||||
{
|
||||
return $this->authCodeStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the access token type
|
||||
*
|
||||
* @param TokenTypeInterface $tokenType The token type
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTokenType(TokenTypeInterface $tokenType)
|
||||
{
|
||||
$tokenType->setServer($this);
|
||||
$this->tokenType = $tokenType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the access token type
|
||||
*
|
||||
* @return TokenTypeInterface
|
||||
*/
|
||||
public function getTokenType()
|
||||
{
|
||||
return $this->tokenType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MacTokenInterface
|
||||
*/
|
||||
public function getMacStorage()
|
||||
{
|
||||
return $this->macStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MacTokenInterface $macStorage
|
||||
*/
|
||||
public function setMacStorage(MacTokenInterface $macStorage)
|
||||
{
|
||||
$this->macStorage = $macStorage;
|
||||
}
|
||||
}
|
@@ -1,228 +1,295 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Authorization Server
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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 Defuse\Crypto\Key;
|
||||
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\AbstractResponseType;
|
||||
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use League\OAuth2\Server\TokenType\Bearer;
|
||||
|
||||
class AuthorizationServer implements EmitterAwareInterface
|
||||
/**
|
||||
* OAuth 2.0 authorization server class
|
||||
*/
|
||||
class AuthorizationServer extends AbstractServer
|
||||
{
|
||||
use EmitterAwareTrait;
|
||||
|
||||
/**
|
||||
* @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|Key
|
||||
*/
|
||||
private $encryptionKey;
|
||||
|
||||
/**
|
||||
* The delimiter between scopes specified in the scope query string parameter
|
||||
* The OAuth 2 specification states it should be a space but most use a comma
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $defaultScope = '';
|
||||
protected $scopeDelimiter = ' ';
|
||||
|
||||
/**
|
||||
* New server instance.
|
||||
* The TTL (time to live) of an access token in seconds (default: 3600)
|
||||
*
|
||||
* @param ClientRepositoryInterface $clientRepository
|
||||
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||
* @param ScopeRepositoryInterface $scopeRepository
|
||||
* @param CryptKey|string $privateKey
|
||||
* @param string|Key $encryptionKey
|
||||
* @param null|ResponseTypeInterface $responseType
|
||||
* @var integer
|
||||
*/
|
||||
public function __construct(
|
||||
ClientRepositoryInterface $clientRepository,
|
||||
AccessTokenRepositoryInterface $accessTokenRepository,
|
||||
ScopeRepositoryInterface $scopeRepository,
|
||||
$privateKey,
|
||||
$encryptionKey,
|
||||
ResponseTypeInterface $responseType = null
|
||||
) {
|
||||
$this->clientRepository = $clientRepository;
|
||||
$this->accessTokenRepository = $accessTokenRepository;
|
||||
$this->scopeRepository = $scopeRepository;
|
||||
protected $accessTokenTTL = 3600;
|
||||
|
||||
if ($privateKey instanceof CryptKey === false) {
|
||||
$privateKey = new CryptKey($privateKey);
|
||||
}
|
||||
$this->privateKey = $privateKey;
|
||||
$this->encryptionKey = $encryptionKey;
|
||||
$this->responseType = $responseType;
|
||||
/**
|
||||
* The registered grant response types
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $responseTypes = [];
|
||||
|
||||
/**
|
||||
* The registered grant types
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $grantTypes = [];
|
||||
|
||||
/**
|
||||
* Require the "scope" parameter to be in checkAuthoriseParams()
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $requireScopeParam = false;
|
||||
|
||||
/**
|
||||
* Default scope(s) to be used if none is provided
|
||||
*
|
||||
* @var string|array
|
||||
*/
|
||||
protected $defaultScope;
|
||||
|
||||
/**
|
||||
* Require the "state" parameter to be in checkAuthoriseParams()
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $requireStateParam = false;
|
||||
|
||||
/**
|
||||
* Create a new OAuth2 authorization server
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// Set Bearer as the default token type
|
||||
$this->setTokenType(new Bearer());
|
||||
|
||||
parent::__construct();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a grant type on the server.
|
||||
* Enable support for a grant
|
||||
*
|
||||
* @param GrantTypeInterface $grantType
|
||||
* @param null|\DateInterval $accessTokenTTL
|
||||
* @param GrantTypeInterface $grantType A grant class which conforms to Interface/GrantTypeInterface
|
||||
* @param null|string $identifier An identifier for the grant (autodetected if not passed)
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function enableGrantType(GrantTypeInterface $grantType, \DateInterval $accessTokenTTL = null)
|
||||
public function addGrantType(GrantTypeInterface $grantType, $identifier = null)
|
||||
{
|
||||
if ($accessTokenTTL instanceof \DateInterval === false) {
|
||||
$accessTokenTTL = new \DateInterval('PT1H');
|
||||
if (is_null($identifier)) {
|
||||
$identifier = $grantType->getIdentifier();
|
||||
}
|
||||
|
||||
$grantType->setAccessTokenRepository($this->accessTokenRepository);
|
||||
$grantType->setClientRepository($this->clientRepository);
|
||||
$grantType->setScopeRepository($this->scopeRepository);
|
||||
$grantType->setDefaultScope($this->defaultScope);
|
||||
$grantType->setPrivateKey($this->privateKey);
|
||||
$grantType->setEmitter($this->getEmitter());
|
||||
$grantType->setEncryptionKey($this->encryptionKey);
|
||||
// Inject server into grant
|
||||
$grantType->setAuthorizationServer($this);
|
||||
|
||||
$this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType;
|
||||
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] = $accessTokenTTL;
|
||||
$this->grantTypes[$identifier] = $grantType;
|
||||
|
||||
if (!is_null($grantType->getResponseType())) {
|
||||
$this->responseTypes[] = $grantType->getResponseType();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an authorization request
|
||||
* Check if a grant type has been enabled
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param string $identifier The grant type identifier
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return AuthorizationRequest
|
||||
* @return boolean Returns "true" if enabled, "false" if not
|
||||
*/
|
||||
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
||||
public function hasGrantType($identifier)
|
||||
{
|
||||
foreach ($this->enabledGrantTypes as $grantType) {
|
||||
if ($grantType->canRespondToAuthorizationRequest($request)) {
|
||||
return $grantType->validateAuthorizationRequest($request);
|
||||
}
|
||||
}
|
||||
|
||||
throw OAuthServerException::unsupportedGrantType();
|
||||
return (array_key_exists($identifier, $this->grantTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete an authorization request
|
||||
* Returns response types
|
||||
*
|
||||
* @param AuthorizationRequest $authRequest
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @return array
|
||||
*/
|
||||
public function completeAuthorizationRequest(AuthorizationRequest $authRequest, ResponseInterface $response)
|
||||
public function getResponseTypes()
|
||||
{
|
||||
return $this->enabledGrantTypes[$authRequest->getGrantTypeId()]
|
||||
->completeAuthorizationRequest($authRequest)
|
||||
->generateHttpResponse($response);
|
||||
return $this->responseTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an access token response.
|
||||
* Require the "scope" parameter in checkAuthoriseParams()
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @param boolean $require
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @return self
|
||||
*/
|
||||
public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response)
|
||||
public function requireScopeParam($require = true)
|
||||
{
|
||||
foreach ($this->enabledGrantTypes as $grantType) {
|
||||
if (!$grantType->canRespondToAccessTokenRequest($request)) {
|
||||
continue;
|
||||
}
|
||||
$tokenResponse = $grantType->respondToAccessTokenRequest(
|
||||
$request,
|
||||
$this->getResponseType(),
|
||||
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()]
|
||||
);
|
||||
$this->requireScopeParam = $require;
|
||||
|
||||
if ($tokenResponse instanceof ResponseTypeInterface) {
|
||||
return $tokenResponse->generateHttpResponse($response);
|
||||
}
|
||||
}
|
||||
|
||||
throw OAuthServerException::unsupportedGrantType();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token type that grants will return in the HTTP response.
|
||||
* Is the scope parameter required?
|
||||
*
|
||||
* @return ResponseTypeInterface
|
||||
* @return bool
|
||||
*/
|
||||
protected function getResponseType()
|
||||
public function scopeParamRequired()
|
||||
{
|
||||
if ($this->responseType instanceof ResponseTypeInterface === false) {
|
||||
$this->responseType = new BearerTokenResponse();
|
||||
}
|
||||
|
||||
if ($this->responseType instanceof AbstractResponseType === true) {
|
||||
$this->responseType->setPrivateKey($this->privateKey);
|
||||
}
|
||||
$this->responseType->setEncryptionKey($this->encryptionKey);
|
||||
|
||||
return $this->responseType;
|
||||
return $this->requireScopeParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default scope for the authorization server.
|
||||
* Default scope to be used if none is provided and requireScopeParam() is false
|
||||
*
|
||||
* @param string $defaultScope
|
||||
* @param string $default Name of the default scope
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDefaultScope($defaultScope)
|
||||
public function setDefaultScope($default = null)
|
||||
{
|
||||
$this->defaultScope = $defaultScope;
|
||||
$this->defaultScope = $default;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default scope to be used if none is provided and requireScopeParam is false
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDefaultScope()
|
||||
{
|
||||
return $this->defaultScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require the "state" parameter in checkAuthoriseParams()
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function stateParamRequired()
|
||||
{
|
||||
return $this->requireStateParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require the "state" parameter in checkAuthoriseParams()
|
||||
*
|
||||
* @param boolean $require
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function requireStateParam($require = true)
|
||||
{
|
||||
$this->requireStateParam = $require;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scope delimiter
|
||||
*
|
||||
* @return string The scope delimiter (default: ",")
|
||||
*/
|
||||
public function getScopeDelimiter()
|
||||
{
|
||||
return $this->scopeDelimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the scope delimiter
|
||||
*
|
||||
* @param string $scopeDelimiter
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setScopeDelimiter($scopeDelimiter = ' ')
|
||||
{
|
||||
$this->scopeDelimiter = $scopeDelimiter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the TTL for an access token
|
||||
*
|
||||
* @return int The TTL
|
||||
*/
|
||||
public function getAccessTokenTTL()
|
||||
{
|
||||
return $this->accessTokenTTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the TTL for an access token
|
||||
*
|
||||
* @param int $accessTokenTTL The new TTL
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setAccessTokenTTL($accessTokenTTL = 3600)
|
||||
{
|
||||
$this->accessTokenTTL = $accessTokenTTL;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue an access token
|
||||
*
|
||||
* @return array Authorise request parameters
|
||||
*
|
||||
* @throws
|
||||
*/
|
||||
public function issueAccessToken()
|
||||
{
|
||||
$grantType = $this->getRequest()->request->get('grant_type');
|
||||
if (is_null($grantType)) {
|
||||
throw new Exception\InvalidRequestException('grant_type');
|
||||
}
|
||||
|
||||
// Ensure grant type is one that is recognised and is enabled
|
||||
if (!in_array($grantType, array_keys($this->grantTypes))) {
|
||||
throw new Exception\UnsupportedGrantTypeException($grantType);
|
||||
}
|
||||
|
||||
// Complete the flow
|
||||
return $this->getGrantType($grantType)->completeFlow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a grant type class
|
||||
*
|
||||
* @param string $grantType The grant type identifier
|
||||
*
|
||||
* @return Grant\GrantTypeInterface
|
||||
*
|
||||
* @throws
|
||||
*/
|
||||
public function getGrantType($grantType)
|
||||
{
|
||||
if (isset($this->grantTypes[$grantType])) {
|
||||
return $this->grantTypes[$grantType];
|
||||
}
|
||||
|
||||
throw new Exception\InvalidGrantException($grantType);
|
||||
}
|
||||
}
|
||||
|
@@ -1,25 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
interface AuthorizationValidatorInterface
|
||||
{
|
||||
/**
|
||||
* Determine the access token in the authorization header and append OAUth properties to the request
|
||||
* as attributes.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @return ServerRequestInterface
|
||||
*/
|
||||
public function validateAuthorization(ServerRequestInterface $request);
|
||||
}
|
@@ -1,99 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use Lcobucci\JWT\Parser;
|
||||
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||
use Lcobucci\JWT\ValidationData;
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
use League\OAuth2\Server\CryptTrait;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class BearerTokenValidator implements AuthorizationValidatorInterface
|
||||
{
|
||||
use CryptTrait;
|
||||
|
||||
/**
|
||||
* @var AccessTokenRepositoryInterface
|
||||
*/
|
||||
private $accessTokenRepository;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\CryptKey
|
||||
*/
|
||||
protected $publicKey;
|
||||
|
||||
/**
|
||||
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||
*/
|
||||
public function __construct(AccessTokenRepositoryInterface $accessTokenRepository)
|
||||
{
|
||||
$this->accessTokenRepository = $accessTokenRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the public key
|
||||
*
|
||||
* @param \League\OAuth2\Server\CryptKey $key
|
||||
*/
|
||||
public function setPublicKey(CryptKey $key)
|
||||
{
|
||||
$this->publicKey = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateAuthorization(ServerRequestInterface $request)
|
||||
{
|
||||
if ($request->hasHeader('authorization') === false) {
|
||||
throw OAuthServerException::accessDenied('Missing "Authorization" header');
|
||||
}
|
||||
|
||||
$header = $request->getHeader('authorization');
|
||||
$jwt = trim(preg_replace('/^(?:\s+)?Bearer\s/', '', $header[0]));
|
||||
|
||||
try {
|
||||
// Attempt to parse and validate the JWT
|
||||
$token = (new Parser())->parse($jwt);
|
||||
if ($token->verify(new Sha256(), $this->publicKey->getKeyPath()) === false) {
|
||||
throw OAuthServerException::accessDenied('Access token could not be verified');
|
||||
}
|
||||
|
||||
// Ensure access token hasn't expired
|
||||
$data = new ValidationData();
|
||||
$data->setCurrentTime(time());
|
||||
|
||||
if ($token->validate($data) === false) {
|
||||
throw OAuthServerException::accessDenied('Access token is invalid');
|
||||
}
|
||||
|
||||
// Check if token has been revoked
|
||||
if ($this->accessTokenRepository->isAccessTokenRevoked($token->getClaim('jti'))) {
|
||||
throw OAuthServerException::accessDenied('Access token has been revoked');
|
||||
}
|
||||
|
||||
// Return the request with additional attributes
|
||||
return $request
|
||||
->withAttribute('oauth_access_token_id', $token->getClaim('jti'))
|
||||
->withAttribute('oauth_client_id', $token->getClaim('aud'))
|
||||
->withAttribute('oauth_user_id', $token->getClaim('sub'))
|
||||
->withAttribute('oauth_scopes', $token->getClaim('scopes'));
|
||||
} 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');
|
||||
}
|
||||
}
|
||||
}
|
120
src/CryptKey.php
120
src/CryptKey.php
@@ -1,120 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Cryptography key holder.
|
||||
*
|
||||
* @author Julián Gutiérrez <juliangut@gmail.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server;
|
||||
|
||||
class CryptKey
|
||||
{
|
||||
const RSA_KEY_PATTERN =
|
||||
'/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----)\R.*(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)\R?$/s';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $keyPath;
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
protected $passPhrase;
|
||||
|
||||
/**
|
||||
* @param string $keyPath
|
||||
* @param null|string $passPhrase
|
||||
* @param bool $keyPermissionsCheck
|
||||
*/
|
||||
public function __construct($keyPath, $passPhrase = null, $keyPermissionsCheck = true)
|
||||
{
|
||||
if (preg_match(self::RSA_KEY_PATTERN, $keyPath)) {
|
||||
$keyPath = $this->saveKeyToFile($keyPath);
|
||||
}
|
||||
|
||||
if (strpos($keyPath, 'file://') !== 0) {
|
||||
$keyPath = 'file://' . $keyPath;
|
||||
}
|
||||
|
||||
if (!file_exists($keyPath) || !is_readable($keyPath)) {
|
||||
throw new \LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
|
||||
}
|
||||
|
||||
if ($keyPermissionsCheck === true) {
|
||||
// Verify the permissions of the key
|
||||
$keyPathPerms = decoct(fileperms($keyPath) & 0777);
|
||||
if (in_array($keyPathPerms, ['400', '440', '600', '660'], true) === false) {
|
||||
trigger_error(sprintf(
|
||||
'Key file "%s" permissions are not correct, recommend changing to 600 or 660 instead of %s',
|
||||
$keyPath,
|
||||
$keyPathPerms
|
||||
), E_USER_NOTICE);
|
||||
}
|
||||
}
|
||||
|
||||
$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)) {
|
||||
return 'file://' . $keyPath;
|
||||
}
|
||||
|
||||
if (!touch($keyPath)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new \RuntimeException(sprintf('"%s" key file could not be created', $keyPath));
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
if (file_put_contents($keyPath, $key) === false) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new \RuntimeException(sprintf('Unable to write key file to temporary directory "%s"', $tmpDir));
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
if (chmod($keyPath, 0600) === false) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new \RuntimeException(sprintf('The key file "%s" file mode could not be changed with chmod to 600', $keyPath));
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
return 'file://' . $keyPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve key path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getKeyPath()
|
||||
{
|
||||
return $this->keyPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve key pass phrase.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getPassPhrase()
|
||||
{
|
||||
return $this->passPhrase;
|
||||
}
|
||||
}
|
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Public/private key encryption.
|
||||
*
|
||||
* @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 Defuse\Crypto\Crypto;
|
||||
use Defuse\Crypto\Key;
|
||||
|
||||
trait CryptTrait
|
||||
{
|
||||
/**
|
||||
* @var string|Key
|
||||
*/
|
||||
protected $encryptionKey;
|
||||
|
||||
/**
|
||||
* Encrypt data with a private key.
|
||||
*
|
||||
* @param string $unencryptedData
|
||||
*
|
||||
* @throws \LogicException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function encrypt($unencryptedData)
|
||||
{
|
||||
try {
|
||||
if ($this->encryptionKey instanceof Key) {
|
||||
return Crypto::encrypt($unencryptedData, $this->encryptionKey);
|
||||
}
|
||||
|
||||
return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey);
|
||||
} catch (\Exception $e) {
|
||||
throw new \LogicException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt data with a public key.
|
||||
*
|
||||
* @param string $encryptedData
|
||||
*
|
||||
* @throws \LogicException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function decrypt($encryptedData)
|
||||
{
|
||||
try {
|
||||
if ($this->encryptionKey instanceof Key) {
|
||||
return Crypto::decrypt($encryptedData, $this->encryptionKey);
|
||||
}
|
||||
|
||||
return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey);
|
||||
} catch (\Exception $e) {
|
||||
throw new \LogicException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the encryption key
|
||||
*
|
||||
* @param string|Key $key
|
||||
*/
|
||||
public function setEncryptionKey($key = null)
|
||||
{
|
||||
$this->encryptionKey = $key;
|
||||
}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use Lcobucci\JWT\Token;
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
|
||||
interface AccessTokenEntityInterface extends TokenInterface
|
||||
{
|
||||
/**
|
||||
* Generate a JWT from the access token
|
||||
*
|
||||
* @param CryptKey $privateKey
|
||||
*
|
||||
* @return Token
|
||||
*/
|
||||
public function convertToJWT(CryptKey $privateKey);
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
<?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;
|
||||
|
||||
interface AuthCodeEntityInterface extends TokenInterface
|
||||
{
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRedirectUri();
|
||||
|
||||
/**
|
||||
* @param string $uri
|
||||
*/
|
||||
public function setRedirectUri($uri);
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
<?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;
|
||||
|
||||
interface ClientEntityInterface
|
||||
{
|
||||
/**
|
||||
* Get the client's identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier();
|
||||
|
||||
/**
|
||||
* Get the client's name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Returns the registered redirect URI (as a string).
|
||||
*
|
||||
* Alternatively return an indexed array of redirect URIs.
|
||||
*
|
||||
* @return string|string[]
|
||||
*/
|
||||
public function getRedirectUri();
|
||||
}
|
@@ -1,55 +0,0 @@
|
||||
<?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;
|
||||
|
||||
interface RefreshTokenEntityInterface
|
||||
{
|
||||
/**
|
||||
* Get the token's identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier();
|
||||
|
||||
/**
|
||||
* Set the token's identifier.
|
||||
*
|
||||
* @param mixed $identifier
|
||||
*/
|
||||
public function setIdentifier($identifier);
|
||||
|
||||
/**
|
||||
* Get the token's expiry date time.
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getExpiryDateTime();
|
||||
|
||||
/**
|
||||
* Set the date time when the token expires.
|
||||
*
|
||||
* @param \DateTime $dateTime
|
||||
*/
|
||||
public function setExpiryDateTime(\DateTime $dateTime);
|
||||
|
||||
/**
|
||||
* Set the access token that the refresh token was associated with.
|
||||
*
|
||||
* @param AccessTokenEntityInterface $accessToken
|
||||
*/
|
||||
public function setAccessToken(AccessTokenEntityInterface $accessToken);
|
||||
|
||||
/**
|
||||
* Get the access token that the refresh token was originally associated with.
|
||||
*
|
||||
* @return AccessTokenEntityInterface
|
||||
*/
|
||||
public function getAccessToken();
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
<?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;
|
||||
|
||||
interface ScopeEntityInterface extends \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* Get the scope's identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier();
|
||||
}
|
@@ -1,83 +0,0 @@
|
||||
<?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;
|
||||
|
||||
interface TokenInterface
|
||||
{
|
||||
/**
|
||||
* Get the token's identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier();
|
||||
|
||||
/**
|
||||
* Set the token's identifier.
|
||||
*
|
||||
* @param mixed $identifier
|
||||
*/
|
||||
public function setIdentifier($identifier);
|
||||
|
||||
/**
|
||||
* Get the token's expiry date time.
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getExpiryDateTime();
|
||||
|
||||
/**
|
||||
* Set the date time when the token expires.
|
||||
*
|
||||
* @param \DateTime $dateTime
|
||||
*/
|
||||
public function setExpiryDateTime(\DateTime $dateTime);
|
||||
|
||||
/**
|
||||
* Set the identifier of the user associated with the token.
|
||||
*
|
||||
* @param string|int|null $identifier The identifier of the user
|
||||
*/
|
||||
public function setUserIdentifier($identifier);
|
||||
|
||||
/**
|
||||
* Get the token user's identifier.
|
||||
*
|
||||
* @return string|int|null
|
||||
*/
|
||||
public function getUserIdentifier();
|
||||
|
||||
/**
|
||||
* Get the client that the token was issued to.
|
||||
*
|
||||
* @return ClientEntityInterface
|
||||
*/
|
||||
public function getClient();
|
||||
|
||||
/**
|
||||
* Set the client that the token was issued to.
|
||||
*
|
||||
* @param ClientEntityInterface $client
|
||||
*/
|
||||
public function setClient(ClientEntityInterface $client);
|
||||
|
||||
/**
|
||||
* Associate a scope with the token.
|
||||
*
|
||||
* @param ScopeEntityInterface $scope
|
||||
*/
|
||||
public function addScope(ScopeEntityInterface $scope);
|
||||
|
||||
/**
|
||||
* Return an array of scopes associated with the token.
|
||||
*
|
||||
* @return ScopeEntityInterface[]
|
||||
*/
|
||||
public function getScopes();
|
||||
}
|
@@ -1,62 +0,0 @@
|
||||
<?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 Lcobucci\JWT\Builder;
|
||||
use Lcobucci\JWT\Signer\Key;
|
||||
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||
use Lcobucci\JWT\Token;
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
|
||||
trait AccessTokenTrait
|
||||
{
|
||||
/**
|
||||
* Generate a JWT from the access token
|
||||
*
|
||||
* @param CryptKey $privateKey
|
||||
*
|
||||
* @return Token
|
||||
*/
|
||||
public function convertToJWT(CryptKey $privateKey)
|
||||
{
|
||||
return (new Builder())
|
||||
->setAudience($this->getClient()->getIdentifier())
|
||||
->setId($this->getIdentifier(), true)
|
||||
->setIssuedAt(time())
|
||||
->setNotBefore(time())
|
||||
->setExpiration($this->getExpiryDateTime()->getTimestamp())
|
||||
->setSubject($this->getUserIdentifier())
|
||||
->set('scopes', $this->getScopes())
|
||||
->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();
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
<?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 AuthCodeTrait
|
||||
{
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
protected $redirectUri;
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRedirectUri()
|
||||
{
|
||||
return $this->redirectUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uri
|
||||
*/
|
||||
public function setRedirectUri($uri)
|
||||
{
|
||||
$this->redirectUri = $uri;
|
||||
}
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Get the client's name.
|
||||
*
|
||||
* @return string
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the registered redirect URI (as a string).
|
||||
*
|
||||
* Alternatively return an indexed array of redirect URIs.
|
||||
*
|
||||
* @return string|string[]
|
||||
*/
|
||||
public function getRedirectUri()
|
||||
{
|
||||
return $this->redirectUri;
|
||||
}
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
<?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 EntityTrait
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $identifier;
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $identifier
|
||||
*/
|
||||
public function setIdentifier($identifier)
|
||||
{
|
||||
$this->identifier = $identifier;
|
||||
}
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
<?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 League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
|
||||
trait RefreshTokenTrait
|
||||
{
|
||||
/**
|
||||
* @var AccessTokenEntityInterface
|
||||
*/
|
||||
protected $accessToken;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $expiryDateTime;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setAccessToken(AccessTokenEntityInterface $accessToken)
|
||||
{
|
||||
$this->accessToken = $accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAccessToken()
|
||||
{
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token's expiry date time.
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getExpiryDateTime()
|
||||
{
|
||||
return $this->expiryDateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the date time when the token expires.
|
||||
*
|
||||
* @param \DateTime $dateTime
|
||||
*/
|
||||
public function setExpiryDateTime(\DateTime $dateTime)
|
||||
{
|
||||
$this->expiryDateTime = $dateTime;
|
||||
}
|
||||
}
|
@@ -1,116 +0,0 @@
|
||||
<?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 League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
|
||||
trait TokenEntityTrait
|
||||
{
|
||||
/**
|
||||
* @var ScopeEntityInterface[]
|
||||
*/
|
||||
protected $scopes = [];
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $expiryDateTime;
|
||||
|
||||
/**
|
||||
* @var string|int|null
|
||||
*/
|
||||
protected $userIdentifier;
|
||||
|
||||
/**
|
||||
* @var ClientEntityInterface
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* Associate a scope with the token.
|
||||
*
|
||||
* @param ScopeEntityInterface $scope
|
||||
*/
|
||||
public function addScope(ScopeEntityInterface $scope)
|
||||
{
|
||||
$this->scopes[$scope->getIdentifier()] = $scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of scopes associated with the token.
|
||||
*
|
||||
* @return ScopeEntityInterface[]
|
||||
*/
|
||||
public function getScopes()
|
||||
{
|
||||
return array_values($this->scopes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token's expiry date time.
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getExpiryDateTime()
|
||||
{
|
||||
return $this->expiryDateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the date time when the token expires.
|
||||
*
|
||||
* @param \DateTime $dateTime
|
||||
*/
|
||||
public function setExpiryDateTime(\DateTime $dateTime)
|
||||
{
|
||||
$this->expiryDateTime = $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the identifier of the user associated with the token.
|
||||
*
|
||||
* @param string|int|null $identifier The identifier of the user
|
||||
*/
|
||||
public function setUserIdentifier($identifier)
|
||||
{
|
||||
$this->userIdentifier = $identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token user's identifier.
|
||||
*
|
||||
* @return string|int|null
|
||||
*/
|
||||
public function getUserIdentifier()
|
||||
{
|
||||
return $this->userIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client that the token was issued to.
|
||||
*
|
||||
* @return ClientEntityInterface
|
||||
*/
|
||||
public function getClient()
|
||||
{
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the client that the token was issued to.
|
||||
*
|
||||
* @param ClientEntityInterface $client
|
||||
*/
|
||||
public function setClient(ClientEntityInterface $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
<?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;
|
||||
|
||||
interface UserEntityInterface
|
||||
{
|
||||
/**
|
||||
* Return the user's identifier.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getIdentifier();
|
||||
}
|
209
src/Entity/AbstractTokenEntity.php
Normal file
209
src/Entity/AbstractTokenEntity.php
Normal file
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Abstract token
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\Entity;
|
||||
|
||||
use League\OAuth2\Server\AbstractServer;
|
||||
use League\OAuth2\Server\Util\SecureKey;
|
||||
|
||||
/**
|
||||
* Abstract token class
|
||||
*/
|
||||
abstract class AbstractTokenEntity
|
||||
{
|
||||
/**
|
||||
* Token identifier
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* Associated session
|
||||
*
|
||||
* @var \League\OAuth2\Server\Entity\SessionEntity
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
/**
|
||||
* Session scopes
|
||||
*
|
||||
* @var \League\OAuth2\Server\Entity\ScopeEntity[]
|
||||
*/
|
||||
protected $scopes;
|
||||
|
||||
/**
|
||||
* Token expire time
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $expireTime = 0;
|
||||
|
||||
/**
|
||||
* Authorization or resource server
|
||||
*
|
||||
* @var \League\OAuth2\Server\AbstractServer
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param \League\OAuth2\Server\AbstractServer $server
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function __construct(AbstractServer $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set session
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entity\SessionEntity $session
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setSession(SessionEntity $session)
|
||||
{
|
||||
$this->session = $session;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expire time of the token
|
||||
*
|
||||
* @param integer $expireTime Unix time stamp
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setExpireTime($expireTime)
|
||||
{
|
||||
$this->expireTime = $expireTime;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return token expire time
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getExpireTime()
|
||||
{
|
||||
return $this->expireTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the token expired?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isExpired()
|
||||
{
|
||||
return ((time() - $this->expireTime) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set token ID
|
||||
*
|
||||
* @param string $id Token ID
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setId($id = null)
|
||||
{
|
||||
$this->id = ($id !== null) ? $id : SecureKey::generate();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token ID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a scope
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entity\ScopeEntity $scope
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function associateScope(ScopeEntity $scope)
|
||||
{
|
||||
if (!isset($this->scopes[$scope->getId()])) {
|
||||
$this->scopes[$scope->getId()] = $scope;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the local scopes array
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entity\ScopeEntity[]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function formatScopes($unformatted = [])
|
||||
{
|
||||
if (is_null($unformatted)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$scopes = [];
|
||||
foreach ($unformatted as $scope) {
|
||||
if ($scope instanceof ScopeEntity) {
|
||||
$scopes[$scope->getId()] = $scope;
|
||||
}
|
||||
}
|
||||
|
||||
return $scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token as a string if the object is cast as a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
if ($this->id === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expire the token
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function expire();
|
||||
|
||||
/**
|
||||
* Save the token
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function save();
|
||||
}
|
93
src/Entity/AccessTokenEntity.php
Normal file
93
src/Entity/AccessTokenEntity.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Access token entity
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\Entity;
|
||||
|
||||
/**
|
||||
* Access token entity class
|
||||
*/
|
||||
class AccessTokenEntity extends AbstractTokenEntity
|
||||
{
|
||||
/**
|
||||
* Get session
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entity\SessionEntity
|
||||
*/
|
||||
public function getSession()
|
||||
{
|
||||
if ($this->session instanceof SessionEntity) {
|
||||
return $this->session;
|
||||
}
|
||||
|
||||
$this->session = $this->server->getSessionStorage()->getByAccessToken($this);
|
||||
|
||||
return $this->session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if access token has an associated scope
|
||||
*
|
||||
* @param string $scope Scope to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasScope($scope)
|
||||
{
|
||||
if ($this->scopes === null) {
|
||||
$this->getScopes();
|
||||
}
|
||||
|
||||
return isset($this->scopes[$scope]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all scopes associated with the access token
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entity\ScopeEntity[]
|
||||
*/
|
||||
public function getScopes()
|
||||
{
|
||||
if ($this->scopes === null) {
|
||||
$this->scopes = $this->formatScopes(
|
||||
$this->server->getAccessTokenStorage()->getScopes($this)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$this->server->getAccessTokenStorage()->create(
|
||||
$this->getId(),
|
||||
$this->getExpireTime(),
|
||||
$this->getSession()->getId()
|
||||
);
|
||||
|
||||
// Associate the scope with the token
|
||||
foreach ($this->getScopes() as $scope) {
|
||||
$this->server->getAccessTokenStorage()->associateScope($this, $scope);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function expire()
|
||||
{
|
||||
$this->server->getAccessTokenStorage()->delete($this);
|
||||
}
|
||||
}
|
128
src/Entity/AuthCodeEntity.php
Normal file
128
src/Entity/AuthCodeEntity.php
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Auth code entity
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\Entity;
|
||||
|
||||
/**
|
||||
* Auth Code entity class
|
||||
*/
|
||||
class AuthCodeEntity extends AbstractTokenEntity
|
||||
{
|
||||
/**
|
||||
* Redirect URI
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectUri = '';
|
||||
|
||||
/**
|
||||
* Set the redirect URI for the authorization request
|
||||
*
|
||||
* @param string $redirectUri
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setRedirectUri($redirectUri)
|
||||
{
|
||||
$this->redirectUri = $redirectUri;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the redirect URI
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRedirectUri()
|
||||
{
|
||||
return $this->redirectUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a redirect URI
|
||||
*
|
||||
* @param string $state The state parameter if set by the client
|
||||
* @param string $queryDelimeter The query delimiter ('?' for auth code grant, '#' for implicit grant)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateRedirectUri($state = null, $queryDelimeter = '?')
|
||||
{
|
||||
$uri = $this->getRedirectUri();
|
||||
$uri .= (strstr($this->getRedirectUri(), $queryDelimeter) === false) ? $queryDelimeter : '&';
|
||||
|
||||
return $uri.http_build_query([
|
||||
'code' => $this->getId(),
|
||||
'state' => $state,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get session
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entity\SessionEntity
|
||||
*/
|
||||
public function getSession()
|
||||
{
|
||||
if ($this->session instanceof SessionEntity) {
|
||||
return $this->session;
|
||||
}
|
||||
|
||||
$this->session = $this->server->getSessionStorage()->getByAuthCode($this);
|
||||
|
||||
return $this->session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all scopes associated with the session
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entity\ScopeEntity[]
|
||||
*/
|
||||
public function getScopes()
|
||||
{
|
||||
if ($this->scopes === null) {
|
||||
$this->scopes = $this->formatScopes(
|
||||
$this->server->getAuthCodeStorage()->getScopes($this)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$this->server->getAuthCodeStorage()->create(
|
||||
$this->getId(),
|
||||
$this->getExpireTime(),
|
||||
$this->getSession()->getId(),
|
||||
$this->getRedirectUri()
|
||||
);
|
||||
|
||||
// Associate the scope with the token
|
||||
foreach ($this->getScopes() as $scope) {
|
||||
$this->server->getAuthCodeStorage()->associateScope($this, $scope);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function expire()
|
||||
{
|
||||
$this->server->getAuthCodeStorage()->delete($this);
|
||||
}
|
||||
}
|
111
src/Entity/ClientEntity.php
Normal file
111
src/Entity/ClientEntity.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Client entity
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\Entity;
|
||||
|
||||
use League\OAuth2\Server\AbstractServer;
|
||||
|
||||
/**
|
||||
* Client entity class
|
||||
*/
|
||||
class ClientEntity
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* Client identifier
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id = null;
|
||||
|
||||
/**
|
||||
* Client secret
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $secret = null;
|
||||
|
||||
/**
|
||||
* Client name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = null;
|
||||
|
||||
/**
|
||||
* Client redirect URI
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectUri = null;
|
||||
|
||||
/**
|
||||
* Authorization or resource server
|
||||
*
|
||||
* @var \League\OAuth2\Server\AbstractServer
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param \League\OAuth2\Server\AbstractServer $server
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function __construct(AbstractServer $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the client identifier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the client secret
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSecret()
|
||||
{
|
||||
return $this->secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returnt the client redirect URI
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRedirectUri()
|
||||
{
|
||||
return $this->redirectUri;
|
||||
}
|
||||
}
|
33
src/Entity/EntityTrait.php
Normal file
33
src/Entity/EntityTrait.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Entity trait
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\Entity;
|
||||
|
||||
trait EntityTrait
|
||||
{
|
||||
/**
|
||||
* Hydrate an entity with properites
|
||||
*
|
||||
* @param array $properties
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function hydrate(array $properties)
|
||||
{
|
||||
foreach ($properties as $prop => $val) {
|
||||
if (property_exists($this, $prop)) {
|
||||
$this->{$prop} = $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
94
src/Entity/RefreshTokenEntity.php
Normal file
94
src/Entity/RefreshTokenEntity.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Refresh token entity
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\Entity;
|
||||
|
||||
/**
|
||||
* Refresh token entity class
|
||||
*/
|
||||
class RefreshTokenEntity extends AbstractTokenEntity
|
||||
{
|
||||
/**
|
||||
* Access token associated to refresh token
|
||||
*
|
||||
* @var \League\OAuth2\Server\Entity\AccessTokenEntity
|
||||
*/
|
||||
protected $accessTokenEntity;
|
||||
|
||||
/**
|
||||
* Id of the access token
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $accessTokenId;
|
||||
|
||||
/**
|
||||
* Set the ID of the associated access token
|
||||
*
|
||||
* @param string $accessTokenId
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setAccessTokenId($accessTokenId)
|
||||
{
|
||||
$this->accessTokenId = $accessTokenId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate an access token
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entity\AccessTokenEntity $accessTokenEntity
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setAccessToken(AccessTokenEntity $accessTokenEntity)
|
||||
{
|
||||
$this->accessTokenEntity = $accessTokenEntity;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return access token
|
||||
*
|
||||
* @return AccessTokenEntity
|
||||
*/
|
||||
public function getAccessToken()
|
||||
{
|
||||
if (! $this->accessTokenEntity instanceof AccessTokenEntity) {
|
||||
$this->accessTokenEntity = $this->server->getAccessTokenStorage()->get($this->accessTokenId);
|
||||
}
|
||||
|
||||
return $this->accessTokenEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$this->server->getRefreshTokenStorage()->create(
|
||||
$this->getId(),
|
||||
$this->getExpireTime(),
|
||||
$this->getAccessToken()->getId()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function expire()
|
||||
{
|
||||
$this->server->getRefreshTokenStorage()->delete($this);
|
||||
}
|
||||
}
|
90
src/Entity/ScopeEntity.php
Normal file
90
src/Entity/ScopeEntity.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 scope entity
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\Entity;
|
||||
|
||||
use League\OAuth2\Server\AbstractServer;
|
||||
|
||||
/**
|
||||
* Scope entity class
|
||||
*/
|
||||
class ScopeEntity implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* Scope identifier
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* Scope description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* Authorization or resource server
|
||||
*
|
||||
* @var \League\OAuth2\Server\AbstractServer
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param \League\OAuth2\Server\AbstractServer $server
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function __construct(AbstractServer $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the scope identifer
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the scope's description
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON object when entity is passed into json_encode
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'description' => $this->getDescription()
|
||||
];
|
||||
}
|
||||
}
|
308
src/Entity/SessionEntity.php
Normal file
308
src/Entity/SessionEntity.php
Normal file
@@ -0,0 +1,308 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 session entity
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\Entity;
|
||||
|
||||
use League\OAuth2\Server\AbstractServer;
|
||||
use League\OAuth2\Server\Event\SessionOwnerEvent;
|
||||
|
||||
/**
|
||||
* Session entity grant
|
||||
*/
|
||||
class SessionEntity
|
||||
{
|
||||
/**
|
||||
* Session identifier
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* Client identifier
|
||||
*
|
||||
* @var \League\OAuth2\Server\Entity\ClientEntity
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* Session owner identifier
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $ownerId;
|
||||
|
||||
/**
|
||||
* Session owner type (e.g. "user")
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $ownerType;
|
||||
|
||||
/**
|
||||
* Auth code
|
||||
*
|
||||
* @var \League\OAuth2\Server\Entity\AuthCodeEntity
|
||||
*/
|
||||
protected $authCode;
|
||||
|
||||
/**
|
||||
* Access token
|
||||
*
|
||||
* @var \League\OAuth2\Server\Entity\AccessTokenEntity
|
||||
*/
|
||||
protected $accessToken;
|
||||
|
||||
/**
|
||||
* Refresh token
|
||||
*
|
||||
* @var \League\OAuth2\Server\Entity\RefreshTokenEntity
|
||||
*/
|
||||
protected $refreshToken;
|
||||
|
||||
/**
|
||||
* Session scopes
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\ParameterBag
|
||||
*/
|
||||
protected $scopes;
|
||||
|
||||
/**
|
||||
* Authorization or resource server
|
||||
*
|
||||
* @var \League\OAuth2\Server\AuthorizationServer|\League\OAuth2\Server\ResourceServer
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param \League\OAuth2\Server\AbstractServer $server
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function __construct(AbstractServer $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the session identifier
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the session identifier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a scope
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entity\ScopeEntity $scope
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function associateScope(ScopeEntity $scope)
|
||||
{
|
||||
if (!isset($this->scopes[$scope->getId()])) {
|
||||
$this->scopes[$scope->getId()] = $scope;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if access token has an associated scope
|
||||
*
|
||||
* @param string $scope Scope to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasScope($scope)
|
||||
{
|
||||
if ($this->scopes === null) {
|
||||
$this->getScopes();
|
||||
}
|
||||
|
||||
return isset($this->scopes[$scope]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all scopes associated with the session
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entity\ScopeEntity[]
|
||||
*/
|
||||
public function getScopes()
|
||||
{
|
||||
if ($this->scopes === null) {
|
||||
$this->scopes = $this->formatScopes($this->server->getSessionStorage()->getScopes($this));
|
||||
}
|
||||
|
||||
return $this->scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the local scopes array
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entity\Scope[]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function formatScopes($unformatted = [])
|
||||
{
|
||||
$scopes = [];
|
||||
if (is_array($unformatted)) {
|
||||
foreach ($unformatted as $scope) {
|
||||
if ($scope instanceof ScopeEntity) {
|
||||
$scopes[$scope->getId()] = $scope;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate an access token with the session
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entity\AccessTokenEntity $accessToken
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function associateAccessToken(AccessTokenEntity $accessToken)
|
||||
{
|
||||
$this->accessToken = $accessToken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a refresh token with the session
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entity\RefreshTokenEntity $refreshToken
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function associateRefreshToken(RefreshTokenEntity $refreshToken)
|
||||
{
|
||||
$this->refreshToken = $refreshToken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a client with the session
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entity\ClientEntity $client The client
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function associateClient(ClientEntity $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the session client
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entity\ClientEntity
|
||||
*/
|
||||
public function getClient()
|
||||
{
|
||||
if ($this->client instanceof ClientEntity) {
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
$this->client = $this->server->getClientStorage()->getBySession($this);
|
||||
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the session owner
|
||||
*
|
||||
* @param string $type The type of the owner (e.g. user, app)
|
||||
* @param string $id The identifier of the owner
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setOwner($type, $id)
|
||||
{
|
||||
$this->ownerType = $type;
|
||||
$this->ownerId = $id;
|
||||
|
||||
$this->server->getEventEmitter()->emit(new SessionOwnerEvent($this));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return session owner identifier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOwnerId()
|
||||
{
|
||||
return $this->ownerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return session owner type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOwnerType()
|
||||
{
|
||||
return $this->ownerType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the session
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
// Save the session and get an identifier
|
||||
$id = $this->server->getSessionStorage()->create(
|
||||
$this->getOwnerType(),
|
||||
$this->getOwnerId(),
|
||||
$this->getClient()->getId(),
|
||||
$this->getClient()->getRedirectUri()
|
||||
);
|
||||
|
||||
$this->setId($id);
|
||||
|
||||
// Associate the scope with the session
|
||||
foreach ($this->getScopes() as $scope) {
|
||||
$this->server->getSessionStorage()->associateScope($this, $scope);
|
||||
}
|
||||
}
|
||||
}
|
55
src/Event/ClientAuthenticationFailedEvent.php
Normal file
55
src/Event/ClientAuthenticationFailedEvent.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 client authentication failed event
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\Event;
|
||||
|
||||
use League\Event\AbstractEvent;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class ClientAuthenticationFailedEvent extends AbstractEvent
|
||||
{
|
||||
/**
|
||||
* Request
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\Request
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* Init the event with a request
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the event
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'error.auth.client';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return request
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Request
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
}
|
55
src/Event/SessionOwnerEvent.php
Normal file
55
src/Event/SessionOwnerEvent.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 session owner event
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\Event;
|
||||
|
||||
use League\Event\AbstractEvent;
|
||||
use League\OAuth2\Server\Entity\SessionEntity;
|
||||
|
||||
class SessionOwnerEvent extends AbstractEvent
|
||||
{
|
||||
/**
|
||||
* Session entity
|
||||
*
|
||||
* @var \League\OAuth2\Server\Entity\SessionEntity
|
||||
*/
|
||||
private $session;
|
||||
|
||||
/**
|
||||
* Init the event with a session
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entity\SessionEntity $session
|
||||
*/
|
||||
public function __construct(SessionEntity $session)
|
||||
{
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the event
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'session.owner';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return session
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entity\SessionEntity
|
||||
*/
|
||||
public function getSession()
|
||||
{
|
||||
return $this->session;
|
||||
}
|
||||
}
|
55
src/Event/UserAuthenticationFailedEvent.php
Normal file
55
src/Event/UserAuthenticationFailedEvent.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 user authentication failed event
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\Event;
|
||||
|
||||
use League\Event\AbstractEvent;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class UserAuthenticationFailedEvent extends AbstractEvent
|
||||
{
|
||||
/**
|
||||
* Request
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\Request
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* Init the event with a request
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the event
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'error.auth.user';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return request
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Request
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
}
|
36
src/Exception/AccessDeniedException.php
Normal file
36
src/Exception/AccessDeniedException.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Access Denied Exception
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Exception class
|
||||
*/
|
||||
class AccessDeniedException extends OAuthException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $httpStatusCode = 401;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $errorType = 'access_denied';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('The resource owner or authorization server denied the request.');
|
||||
}
|
||||
}
|
36
src/Exception/InvalidClientException.php
Normal file
36
src/Exception/InvalidClientException.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Invalid Client Exception
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Exception class
|
||||
*/
|
||||
class InvalidClientException extends OAuthException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $httpStatusCode = 401;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $errorType = 'invalid_client';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('Client authentication failed.');
|
||||
}
|
||||
}
|
36
src/Exception/InvalidCredentialsException.php
Normal file
36
src/Exception/InvalidCredentialsException.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Invalid Credentials Exception
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Exception class
|
||||
*/
|
||||
class InvalidCredentialsException extends OAuthException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $httpStatusCode = 401;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $errorType = 'invalid_credentials';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('The user credentials were incorrect.');
|
||||
}
|
||||
}
|
43
src/Exception/InvalidGrantException.php
Normal file
43
src/Exception/InvalidGrantException.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Invalid Grant Exception
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Exception class
|
||||
*/
|
||||
class InvalidGrantException extends OAuthException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $httpStatusCode = 400;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $errorType = 'invalid_grant';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
||||
public function __construct($parameter)
|
||||
{
|
||||
$this->parameter = $parameter;
|
||||
parent::__construct(
|
||||
sprintf(
|
||||
'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. Check the "%s" parameter.',
|
||||
$parameter
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
36
src/Exception/InvalidRefreshException.php
Normal file
36
src/Exception/InvalidRefreshException.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Invalid Refresh Exception
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Exception class
|
||||
*/
|
||||
class InvalidRefreshException extends OAuthException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $httpStatusCode = 400;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $errorType = 'invalid_request';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('The refresh token is invalid.');
|
||||
}
|
||||
}
|
45
src/Exception/InvalidRequestException.php
Normal file
45
src/Exception/InvalidRequestException.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Invalid Request Exception
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Exception class
|
||||
*/
|
||||
class InvalidRequestException extends OAuthException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $httpStatusCode = 400;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $errorType = 'invalid_request';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
||||
public function __construct($parameter, $redirectUri = null)
|
||||
{
|
||||
$this->parameter = $parameter;
|
||||
parent::__construct(
|
||||
sprintf(
|
||||
'The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the "%s" parameter.',
|
||||
$parameter
|
||||
)
|
||||
);
|
||||
|
||||
$this->redirectUri = $redirectUri;
|
||||
}
|
||||
}
|
45
src/Exception/InvalidScopeException.php
Normal file
45
src/Exception/InvalidScopeException.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Invalid Scope Exception
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Exception class
|
||||
*/
|
||||
class InvalidScopeException extends OAuthException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $httpStatusCode = 400;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $errorType = 'invalid_scope';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
||||
public function __construct($parameter, $redirectUri = null)
|
||||
{
|
||||
$this->parameter = $parameter;
|
||||
parent::__construct(
|
||||
sprintf(
|
||||
'The requested scope is invalid, unknown, or malformed. Check the "%s" scope.',
|
||||
$parameter
|
||||
)
|
||||
);
|
||||
|
||||
$this->redirectUri = $redirectUri;
|
||||
}
|
||||
}
|
145
src/Exception/OAuthException.php
Normal file
145
src/Exception/OAuthException.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Base Exception
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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;
|
||||
|
||||
use League\OAuth2\Server\Util\RedirectUri;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Exception class
|
||||
*/
|
||||
class OAuthException extends \Exception
|
||||
{
|
||||
/**
|
||||
* The HTTP status code for this exception that should be sent in the response
|
||||
*/
|
||||
public $httpStatusCode = 400;
|
||||
|
||||
/**
|
||||
* Redirect URI if the server should redirect back to the client
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $redirectUri = null;
|
||||
|
||||
/**
|
||||
* The exception type
|
||||
*/
|
||||
public $errorType = '';
|
||||
|
||||
/**
|
||||
* Parameter eventually passed to Exception
|
||||
*/
|
||||
public $parameter = '';
|
||||
|
||||
/**
|
||||
* Throw a new exception
|
||||
*
|
||||
* @param string $msg Exception Message
|
||||
*/
|
||||
public function __construct($msg = 'An error occured')
|
||||
{
|
||||
parent::__construct($msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the server redirect back to the client?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldRedirect()
|
||||
{
|
||||
return is_null($this->redirectUri) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return redirect URI if set
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRedirectUri()
|
||||
{
|
||||
return RedirectUri::make(
|
||||
$this->redirectUri,
|
||||
[
|
||||
'error' => $this->errorType,
|
||||
'message' => $this->getMessage(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return parameter if set
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getParameter()
|
||||
{
|
||||
return $this->parameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all headers that have to be send with the error response
|
||||
*
|
||||
* @return array Array with header values
|
||||
*/
|
||||
public function getHttpHeaders()
|
||||
{
|
||||
$headers = [];
|
||||
switch ($this->httpStatusCode) {
|
||||
case 401:
|
||||
$headers[] = 'HTTP/1.1 401 Unauthorized';
|
||||
break;
|
||||
case 500:
|
||||
$headers[] = 'HTTP/1.1 500 Internal Server Error';
|
||||
break;
|
||||
case 501:
|
||||
$headers[] = 'HTTP/1.1 501 Not Implemented';
|
||||
break;
|
||||
case 400:
|
||||
default:
|
||||
$headers[] = 'HTTP/1.1 400 Bad Request';
|
||||
break;
|
||||
}
|
||||
|
||||
// Add "WWW-Authenticate" header
|
||||
//
|
||||
// RFC 6749, section 5.2.:
|
||||
// "If the client attempted to authenticate via the 'Authorization'
|
||||
// request header field, the authorization server MUST
|
||||
// respond with an HTTP 401 (Unauthorized) status code and
|
||||
// include the "WWW-Authenticate" response header field
|
||||
// matching the authentication scheme used by the client.
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($this->errorType === 'invalid_client') {
|
||||
$authScheme = null;
|
||||
$request = new Request();
|
||||
if ($request->getUser() !== null) {
|
||||
$authScheme = 'Basic';
|
||||
} else {
|
||||
$authHeader = $request->headers->get('Authorization');
|
||||
if ($authHeader !== null) {
|
||||
if (strpos($authHeader, 'Bearer') === 0) {
|
||||
$authScheme = 'Bearer';
|
||||
} elseif (strpos($authHeader, 'Basic') === 0) {
|
||||
$authScheme = 'Basic';
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($authScheme !== null) {
|
||||
$headers[] = 'WWW-Authenticate: '.$authScheme.' realm=""';
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
return $headers;
|
||||
}
|
||||
}
|
@@ -1,323 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class OAuthServerException extends \Exception
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $httpStatusCode;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $errorType;
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
private $hint;
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
private $redirectUri;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $payload;
|
||||
|
||||
/**
|
||||
* Throw a new exception.
|
||||
*
|
||||
* @param string $message Error message
|
||||
* @param int $code Error code
|
||||
* @param string $errorType Error type
|
||||
* @param int $httpStatusCode HTTP status code to send (default = 400)
|
||||
* @param null|string $hint A helper hint
|
||||
* @param null|string $redirectUri A HTTP URI to redirect the user back to
|
||||
*/
|
||||
public function __construct($message, $code, $errorType, $httpStatusCode = 400, $hint = null, $redirectUri = null)
|
||||
{
|
||||
parent::__construct($message, $code);
|
||||
$this->httpStatusCode = $httpStatusCode;
|
||||
$this->errorType = $errorType;
|
||||
$this->hint = $hint;
|
||||
$this->redirectUri = $redirectUri;
|
||||
$this->payload = [
|
||||
'error' => $errorType,
|
||||
'message' => $message,
|
||||
];
|
||||
if ($hint !== null) {
|
||||
$this->payload['hint'] = $hint;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current payload.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPayload()
|
||||
{
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current payload.
|
||||
*
|
||||
* @param array $payload
|
||||
*/
|
||||
public function setPayload(array $payload)
|
||||
{
|
||||
$this->payload = $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsupported grant type error.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function unsupportedGrantType()
|
||||
{
|
||||
$errorMessage = 'The authorization grant type is not supported by the authorization server.';
|
||||
$hint = 'Check that all required parameters have been provided';
|
||||
|
||||
return new static($errorMessage, 2, 'unsupported_grant_type', 400, $hint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid request error.
|
||||
*
|
||||
* @param string $parameter The invalid parameter
|
||||
* @param null|string $hint
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function invalidRequest($parameter, $hint = null)
|
||||
{
|
||||
$errorMessage = 'The request is missing a required parameter, includes an invalid parameter value, ' .
|
||||
'includes a parameter more than once, or is otherwise malformed.';
|
||||
$hint = ($hint === null) ? sprintf('Check the `%s` parameter', $parameter) : $hint;
|
||||
|
||||
return new static($errorMessage, 3, 'invalid_request', 400, $hint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid client error.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function invalidClient()
|
||||
{
|
||||
$errorMessage = 'Client authentication failed';
|
||||
|
||||
return new static($errorMessage, 4, 'invalid_client', 401);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid scope error.
|
||||
*
|
||||
* @param string $scope The bad scope
|
||||
* @param null|string $redirectUri A HTTP URI to redirect the user back to
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function invalidScope($scope, $redirectUri = null)
|
||||
{
|
||||
$errorMessage = 'The requested scope is invalid, unknown, or malformed';
|
||||
|
||||
if (empty($scope)) {
|
||||
$hint = 'Specify a scope in the request or set a default scope';
|
||||
} else {
|
||||
$hint = sprintf(
|
||||
'Check the `%s` scope',
|
||||
htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false)
|
||||
);
|
||||
}
|
||||
|
||||
return new static($errorMessage, 5, 'invalid_scope', 400, $hint, $redirectUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid credentials error.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function invalidCredentials()
|
||||
{
|
||||
return new static('The user credentials were incorrect.', 6, 'invalid_credentials', 401);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server error.
|
||||
*
|
||||
* @param string $hint
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function serverError($hint)
|
||||
{
|
||||
return new static(
|
||||
'The authorization server encountered an unexpected condition which prevented it from fulfilling'
|
||||
. ' the request: ' . $hint,
|
||||
7,
|
||||
'server_error',
|
||||
500
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid refresh token.
|
||||
*
|
||||
* @param null|string $hint
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function invalidRefreshToken($hint = null)
|
||||
{
|
||||
return new static('The refresh token is invalid.', 8, 'invalid_request', 401, $hint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access denied.
|
||||
*
|
||||
* @param null|string $hint
|
||||
* @param null|string $redirectUri
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function accessDenied($hint = null, $redirectUri = null)
|
||||
{
|
||||
return new static(
|
||||
'The resource owner or authorization server denied the request.',
|
||||
9,
|
||||
'access_denied',
|
||||
401,
|
||||
$hint,
|
||||
$redirectUri
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function getErrorType()
|
||||
{
|
||||
return $this->errorType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a HTTP response.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
* @param bool $useFragment True if errors should be in the URI fragment instead of query string
|
||||
* @param int $jsonOptions options passed to json_encode
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function generateHttpResponse(ResponseInterface $response, $useFragment = false, $jsonOptions = 0)
|
||||
{
|
||||
$headers = $this->getHttpHeaders();
|
||||
|
||||
$payload = $this->getPayload();
|
||||
|
||||
if ($this->redirectUri !== null) {
|
||||
if ($useFragment === true) {
|
||||
$this->redirectUri .= (strstr($this->redirectUri, '#') === false) ? '#' : '&';
|
||||
} else {
|
||||
$this->redirectUri .= (strstr($this->redirectUri, '?') === false) ? '?' : '&';
|
||||
}
|
||||
|
||||
return $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload));
|
||||
}
|
||||
|
||||
foreach ($headers as $header => $content) {
|
||||
$response = $response->withHeader($header, $content);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode($payload, $jsonOptions));
|
||||
|
||||
return $response->withStatus($this->getHttpStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all headers that have to be send with the error response.
|
||||
*
|
||||
* @return array Array with header values
|
||||
*/
|
||||
public function getHttpHeaders()
|
||||
{
|
||||
$headers = [
|
||||
'Content-type' => 'application/json',
|
||||
];
|
||||
|
||||
// Add "WWW-Authenticate" header
|
||||
//
|
||||
// RFC 6749, section 5.2.:
|
||||
// "If the client attempted to authenticate via the 'Authorization'
|
||||
// request header field, the authorization server MUST
|
||||
// respond with an HTTP 401 (Unauthorized) status code and
|
||||
// include the "WWW-Authenticate" response header field
|
||||
// matching the authentication scheme used by the client.
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($this->errorType === 'invalid_client' && array_key_exists('HTTP_AUTHORIZATION', $_SERVER) !== false) {
|
||||
$authScheme = strpos($_SERVER['HTTP_AUTHORIZATION'], 'Bearer') === 0 ? 'Bearer' : 'Basic';
|
||||
|
||||
$headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"';
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP status code to send when the exceptions is output.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHttpStatusCode()
|
||||
{
|
||||
return $this->httpStatusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|string
|
||||
*/
|
||||
public function getHint()
|
||||
{
|
||||
return $this->hint;
|
||||
}
|
||||
}
|
39
src/Exception/ServerErrorException.php
Normal file
39
src/Exception/ServerErrorException.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Server Error Exception
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Exception class
|
||||
*/
|
||||
class ServerErrorException extends OAuthException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $httpStatusCode = 500;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $errorType = 'server_error';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($parameter = null)
|
||||
{
|
||||
$this->parameter = $parameter;
|
||||
$parameter = is_null($parameter) ? 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.' : $parameter;
|
||||
parent::__construct($parameter);
|
||||
|
||||
}
|
||||
}
|
36
src/Exception/UnauthorizedClientException.php
Normal file
36
src/Exception/UnauthorizedClientException.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Unauthorized Client Exception
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Exception class
|
||||
*/
|
||||
class UnauthorizedClientException extends OAuthException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $httpStatusCode = 400;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $errorType = 'unauthorized_client';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('The client is not authorized to request an access token using this method.');
|
||||
}
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
43
src/Exception/UnsupportedGrantTypeException.php
Normal file
43
src/Exception/UnsupportedGrantTypeException.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Invalid Request Exception
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Exception class
|
||||
*/
|
||||
class UnsupportedGrantTypeException extends OAuthException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $httpStatusCode = 400;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $errorType = 'unsupported_grant_type';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
||||
public function __construct($parameter)
|
||||
{
|
||||
$this->parameter = $parameter;
|
||||
parent::__construct(
|
||||
sprintf(
|
||||
'The authorization grant type "%s" is not supported by the authorization server.',
|
||||
$parameter
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
38
src/Exception/UnsupportedResponseTypeException.php
Normal file
38
src/Exception/UnsupportedResponseTypeException.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Unsupported Response Type Exception
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Exception class
|
||||
*/
|
||||
class UnsupportedResponseTypeException extends OAuthException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $httpStatusCode = 400;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $errorType = 'unsupported_response_type';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($parameter, $redirectUri = null)
|
||||
{
|
||||
$this->parameter = $parameter;
|
||||
parent::__construct('The authorization server does not support obtaining an access token using this method.');
|
||||
$this->redirectUri = $redirectUri;
|
||||
}
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Abstract authorization grant.
|
||||
*
|
||||
* @author Julián Gutiérrez <juliangut@gmail.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
abstract class AbstractAuthorizeGrant extends AbstractGrant
|
||||
{
|
||||
/**
|
||||
* @param string $uri
|
||||
* @param array $params
|
||||
* @param string $queryDelimiter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function makeRedirectUri($uri, $params = [], $queryDelimiter = '?')
|
||||
{
|
||||
$uri .= (strstr($uri, $queryDelimiter) === false) ? $queryDelimiter : '&';
|
||||
|
||||
return $uri . http_build_query($params);
|
||||
}
|
||||
}
|
@@ -1,524 +1,197 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Abstract grant.
|
||||
* OAuth 2.0 Abstract grant
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\Event\EmitterAwareTrait;
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
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;
|
||||
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
|
||||
use League\OAuth2\Server\RequestEvent;
|
||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
use League\OAuth2\Server\Entity\ClientEntity;
|
||||
use League\OAuth2\Server\Entity\ScopeEntity;
|
||||
use League\OAuth2\Server\Exception;
|
||||
|
||||
/**
|
||||
* Abstract grant class.
|
||||
* Abstract grant class
|
||||
*/
|
||||
abstract class AbstractGrant implements GrantTypeInterface
|
||||
{
|
||||
use EmitterAwareTrait, CryptTrait;
|
||||
|
||||
const SCOPE_DELIMITER_STRING = ' ';
|
||||
|
||||
const MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS = 10;
|
||||
/**
|
||||
* Grant identifier
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $identifier = '';
|
||||
|
||||
/**
|
||||
* @var ClientRepositoryInterface
|
||||
* Response type
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $clientRepository;
|
||||
protected $responseType;
|
||||
|
||||
/**
|
||||
* @var AccessTokenRepositoryInterface
|
||||
* Callback to authenticate a user's name and password
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
protected $accessTokenRepository;
|
||||
protected $callback;
|
||||
|
||||
/**
|
||||
* @var ScopeRepositoryInterface
|
||||
* AuthServer instance
|
||||
*
|
||||
* @var \League\OAuth2\Server\AuthorizationServer
|
||||
*/
|
||||
protected $scopeRepository;
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* @var AuthCodeRepositoryInterface
|
||||
* Access token expires in override
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $authCodeRepository;
|
||||
protected $accessTokenTTL;
|
||||
|
||||
/**
|
||||
* @var RefreshTokenRepositoryInterface
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $refreshTokenRepository;
|
||||
|
||||
/**
|
||||
* @var UserRepositoryInterface
|
||||
*/
|
||||
protected $userRepository;
|
||||
|
||||
/**
|
||||
* @var \DateInterval
|
||||
*/
|
||||
protected $refreshTokenTTL;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\CryptKey
|
||||
*/
|
||||
protected $privateKey;
|
||||
|
||||
/**
|
||||
* @string
|
||||
*/
|
||||
protected $defaultScope;
|
||||
|
||||
/**
|
||||
* @param ClientRepositoryInterface $clientRepository
|
||||
*/
|
||||
public function setClientRepository(ClientRepositoryInterface $clientRepository)
|
||||
public function getIdentifier()
|
||||
{
|
||||
$this->clientRepository = $clientRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||
*/
|
||||
public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository)
|
||||
{
|
||||
$this->accessTokenRepository = $accessTokenRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ScopeRepositoryInterface $scopeRepository
|
||||
*/
|
||||
public function setScopeRepository(ScopeRepositoryInterface $scopeRepository)
|
||||
{
|
||||
$this->scopeRepository = $scopeRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
*/
|
||||
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
|
||||
{
|
||||
$this->refreshTokenRepository = $refreshTokenRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AuthCodeRepositoryInterface $authCodeRepository
|
||||
*/
|
||||
public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository)
|
||||
{
|
||||
$this->authCodeRepository = $authCodeRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UserRepositoryInterface $userRepository
|
||||
*/
|
||||
public function setUserRepository(UserRepositoryInterface $userRepository)
|
||||
{
|
||||
$this->userRepository = $userRepository;
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL)
|
||||
public function setIdentifier($identifier)
|
||||
{
|
||||
$this->refreshTokenTTL = $refreshTokenTTL;
|
||||
$this->identifier = $identifier;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the private key
|
||||
*
|
||||
* @param \League\OAuth2\Server\CryptKey $key
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setPrivateKey(CryptKey $key)
|
||||
public function getResponseType()
|
||||
{
|
||||
$this->privateKey = $key;
|
||||
return $this->responseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $scope
|
||||
* Get the TTL for an access token
|
||||
*
|
||||
* @return int The TTL
|
||||
*/
|
||||
public function setDefaultScope($scope)
|
||||
public function getAccessTokenTTL()
|
||||
{
|
||||
$this->defaultScope = $scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the client.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return ClientEntityInterface
|
||||
*/
|
||||
protected function validateClient(ServerRequestInterface $request)
|
||||
{
|
||||
list($basicAuthUser, $basicAuthPassword) = $this->getBasicAuthCredentials($request);
|
||||
|
||||
$clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser);
|
||||
if (is_null($clientId)) {
|
||||
throw OAuthServerException::invalidRequest('client_id');
|
||||
if ($this->accessTokenTTL) {
|
||||
return $this->accessTokenTTL;
|
||||
}
|
||||
|
||||
// If the client is confidential require the client secret
|
||||
$clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword);
|
||||
return $this->server->getAccessTokenTTL();
|
||||
}
|
||||
|
||||
$client = $this->clientRepository->getClientEntity(
|
||||
$clientId,
|
||||
$this->getIdentifier(),
|
||||
$clientSecret,
|
||||
true
|
||||
);
|
||||
/**
|
||||
* Override the default access token expire time
|
||||
*
|
||||
* @param int $accessTokenTTL
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setAccessTokenTTL($accessTokenTTL)
|
||||
{
|
||||
$this->accessTokenTTL = $accessTokenTTL;
|
||||
|
||||
if ($client instanceof ClientEntityInterface === false) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
// If a redirect URI is provided ensure it matches what is pre-registered
|
||||
$redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
|
||||
if ($redirectUri !== null) {
|
||||
if (
|
||||
is_string($client->getRedirectUri())
|
||||
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
} elseif (
|
||||
is_array($client->getRedirectUri())
|
||||
&& in_array($redirectUri, $client->getRedirectUri(), true) === false
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setAuthorizationServer(AuthorizationServer $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of scopes, validate them and return an array of Scope entities
|
||||
*
|
||||
* @param string $scopeParam A string of scopes (e.g. "profile email birthday")
|
||||
* @param \League\OAuth2\Server\Entity\ClientEntity $client Client entity
|
||||
* @param string|null $redirectUri The redirect URI to return the user to
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entity\ScopeEntity[]
|
||||
*
|
||||
* @throws \League\OAuth2\Server\Exception\InvalidScopeException If scope is invalid, or no scopes passed when required
|
||||
* @throws
|
||||
*/
|
||||
public function validateScopes($scopeParam = '', ClientEntity $client, $redirectUri = null)
|
||||
{
|
||||
$scopesList = explode($this->server->getScopeDelimiter(), $scopeParam);
|
||||
|
||||
for ($i = 0; $i < count($scopesList); $i++) {
|
||||
$scopesList[$i] = trim($scopesList[$i]);
|
||||
if ($scopesList[$i] === '') {
|
||||
unset($scopesList[$i]); // Remove any junk scopes
|
||||
}
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
if (
|
||||
$this->server->scopeParamRequired() === true
|
||||
&& $this->server->getDefaultScope() === null
|
||||
&& count($scopesList) === 0
|
||||
) {
|
||||
throw new Exception\InvalidRequestException('scope');
|
||||
} elseif (count($scopesList) === 0 && $this->server->getDefaultScope() !== null) {
|
||||
if (is_array($this->server->getDefaultScope())) {
|
||||
$scopesList = $this->server->getDefaultScope();
|
||||
} else {
|
||||
$scopesList = [0 => $this->server->getDefaultScope()];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate scopes in the request.
|
||||
*
|
||||
* @param string $scopes
|
||||
* @param string $redirectUri
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return ScopeEntityInterface[]
|
||||
*/
|
||||
public function validateScopes($scopes, $redirectUri = null)
|
||||
{
|
||||
$scopesList = array_filter(explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), function ($scope) {
|
||||
return !empty($scope);
|
||||
});
|
||||
|
||||
$validScopes = [];
|
||||
$scopes = [];
|
||||
|
||||
foreach ($scopesList as $scopeItem) {
|
||||
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem);
|
||||
$scope = $this->server->getScopeStorage()->get(
|
||||
$scopeItem,
|
||||
$this->getIdentifier(),
|
||||
$client->getId()
|
||||
);
|
||||
|
||||
if ($scope instanceof ScopeEntityInterface === false) {
|
||||
throw OAuthServerException::invalidScope($scopeItem, $redirectUri);
|
||||
if (($scope instanceof ScopeEntity) === false) {
|
||||
throw new Exception\InvalidScopeException($scopeItem, $redirectUri);
|
||||
}
|
||||
|
||||
$validScopes[] = $scope;
|
||||
$scopes[$scope->getId()] = $scope;
|
||||
}
|
||||
|
||||
return $validScopes;
|
||||
return $scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve request parameter.
|
||||
* Format the local scopes array
|
||||
*
|
||||
* @param string $parameter
|
||||
* @param ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
* @param \League\OAuth2\Server\Entity\ScopeEntity[]
|
||||
*
|
||||
* @return null|string
|
||||
* @return array
|
||||
*/
|
||||
protected function getRequestParameter($parameter, ServerRequestInterface $request, $default = null)
|
||||
protected function formatScopes($unformated = [])
|
||||
{
|
||||
$requestParameters = (array) $request->getParsedBody();
|
||||
|
||||
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 ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function getQueryStringParameter($parameter, ServerRequestInterface $request, $default = null)
|
||||
{
|
||||
return isset($request->getQueryParams()[$parameter]) ? $request->getQueryParams()[$parameter] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve cookie parameter.
|
||||
*
|
||||
* @param string $parameter
|
||||
* @param ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function getCookieParameter($parameter, ServerRequestInterface $request, $default = null)
|
||||
{
|
||||
return isset($request->getCookieParams()[$parameter]) ? $request->getCookieParams()[$parameter] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve server parameter.
|
||||
*
|
||||
* @param string $parameter
|
||||
* @param ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function getServerParameter($parameter, ServerRequestInterface $request, $default = null)
|
||||
{
|
||||
return isset($request->getServerParams()[$parameter]) ? $request->getServerParams()[$parameter] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue an access token.
|
||||
*
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
* @param ClientEntityInterface $client
|
||||
* @param string|null $userIdentifier
|
||||
* @param ScopeEntityInterface[] $scopes
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||
*
|
||||
* @return AccessTokenEntityInterface
|
||||
*/
|
||||
protected function issueAccessToken(
|
||||
\DateInterval $accessTokenTTL,
|
||||
ClientEntityInterface $client,
|
||||
$userIdentifier,
|
||||
array $scopes = []
|
||||
) {
|
||||
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
||||
|
||||
$accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier);
|
||||
$accessToken->setClient($client);
|
||||
$accessToken->setUserIdentifier($userIdentifier);
|
||||
$accessToken->setExpiryDateTime((new \DateTime())->add($accessTokenTTL));
|
||||
|
||||
foreach ($scopes as $scope) {
|
||||
$accessToken->addScope($scope);
|
||||
}
|
||||
|
||||
while ($maxGenerationAttempts-- > 0) {
|
||||
$accessToken->setIdentifier($this->generateUniqueIdentifier());
|
||||
try {
|
||||
$this->accessTokenRepository->persistNewAccessToken($accessToken);
|
||||
|
||||
return $accessToken;
|
||||
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
|
||||
if ($maxGenerationAttempts === 0) {
|
||||
throw $e;
|
||||
}
|
||||
$scopes = [];
|
||||
foreach ($unformated as $scope) {
|
||||
if ($scope instanceof ScopeEntity) {
|
||||
$scopes[$scope->getId()] = $scope;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue an auth code.
|
||||
*
|
||||
* @param \DateInterval $authCodeTTL
|
||||
* @param ClientEntityInterface $client
|
||||
* @param string $userIdentifier
|
||||
* @param string|null $redirectUri
|
||||
* @param ScopeEntityInterface[] $scopes
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||
*
|
||||
* @return AuthCodeEntityInterface
|
||||
*/
|
||||
protected function issueAuthCode(
|
||||
\DateInterval $authCodeTTL,
|
||||
ClientEntityInterface $client,
|
||||
$userIdentifier,
|
||||
$redirectUri,
|
||||
array $scopes = []
|
||||
) {
|
||||
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
||||
|
||||
$authCode = $this->authCodeRepository->getNewAuthCode();
|
||||
$authCode->setExpiryDateTime((new \DateTime())->add($authCodeTTL));
|
||||
$authCode->setClient($client);
|
||||
$authCode->setUserIdentifier($userIdentifier);
|
||||
|
||||
if ($redirectUri !== null) {
|
||||
$authCode->setRedirectUri($redirectUri);
|
||||
}
|
||||
|
||||
foreach ($scopes as $scope) {
|
||||
$authCode->addScope($scope);
|
||||
}
|
||||
|
||||
while ($maxGenerationAttempts-- > 0) {
|
||||
$authCode->setIdentifier($this->generateUniqueIdentifier());
|
||||
try {
|
||||
$this->authCodeRepository->persistNewAuthCode($authCode);
|
||||
|
||||
return $authCode;
|
||||
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
|
||||
if ($maxGenerationAttempts === 0) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccessTokenEntityInterface $accessToken
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||
*
|
||||
* @return RefreshTokenEntityInterface
|
||||
*/
|
||||
protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
|
||||
{
|
||||
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
||||
|
||||
$refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
|
||||
$refreshToken->setExpiryDateTime((new \DateTime())->add($this->refreshTokenTTL));
|
||||
$refreshToken->setAccessToken($accessToken);
|
||||
|
||||
while ($maxGenerationAttempts-- > 0) {
|
||||
$refreshToken->setIdentifier($this->generateUniqueIdentifier());
|
||||
try {
|
||||
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
|
||||
|
||||
return $refreshToken;
|
||||
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
|
||||
if ($maxGenerationAttempts === 0) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new unique identifier.
|
||||
*
|
||||
* @param int $length
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateUniqueIdentifier($length = 40)
|
||||
{
|
||||
try {
|
||||
return bin2hex(random_bytes($length));
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (\TypeError $e) {
|
||||
throw OAuthServerException::serverError('An unexpected error has occurred');
|
||||
} catch (\Error $e) {
|
||||
throw OAuthServerException::serverError('An unexpected error has occurred');
|
||||
} catch (\Exception $e) {
|
||||
// If you get this message, the CSPRNG failed hard.
|
||||
throw OAuthServerException::serverError('Could not generate a random string');
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canRespondToAccessTokenRequest(ServerRequestInterface $request)
|
||||
{
|
||||
$requestParameters = (array) $request->getParsedBody();
|
||||
|
||||
return (
|
||||
array_key_exists('grant_type', $requestParameters)
|
||||
&& $requestParameters['grant_type'] === $this->getIdentifier()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
||||
{
|
||||
throw new \LogicException('This grant cannot validate an authorization request');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
||||
{
|
||||
throw new \LogicException('This grant cannot complete an authorization request');
|
||||
return $scopes;
|
||||
}
|
||||
}
|
||||
|
@@ -1,382 +1,303 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Auth code grant
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\ScopeEntityInterface;
|
||||
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||
use League\OAuth2\Server\RequestEvent;
|
||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use League\OAuth2\Server\Entity\AccessTokenEntity;
|
||||
use League\OAuth2\Server\Entity\AuthCodeEntity;
|
||||
use League\OAuth2\Server\Entity\ClientEntity;
|
||||
use League\OAuth2\Server\Entity\RefreshTokenEntity;
|
||||
use League\OAuth2\Server\Entity\SessionEntity;
|
||||
use League\OAuth2\Server\Event;
|
||||
use League\OAuth2\Server\Exception;
|
||||
use League\OAuth2\Server\Util\SecureKey;
|
||||
|
||||
class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
/**
|
||||
* Auth code grant class
|
||||
*/
|
||||
class AuthCodeGrant extends AbstractGrant
|
||||
{
|
||||
/**
|
||||
* @var \DateInterval
|
||||
*/
|
||||
private $authCodeTTL;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $enableCodeExchangeProof = false;
|
||||
|
||||
/**
|
||||
* @param AuthCodeRepositoryInterface $authCodeRepository
|
||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
* @param \DateInterval $authCodeTTL
|
||||
*/
|
||||
public function __construct(
|
||||
AuthCodeRepositoryInterface $authCodeRepository,
|
||||
RefreshTokenRepositoryInterface $refreshTokenRepository,
|
||||
\DateInterval $authCodeTTL
|
||||
) {
|
||||
$this->setAuthCodeRepository($authCodeRepository);
|
||||
$this->setRefreshTokenRepository($refreshTokenRepository);
|
||||
$this->authCodeTTL = $authCodeTTL;
|
||||
$this->refreshTokenTTL = new \DateInterval('P1M');
|
||||
}
|
||||
|
||||
public function enableCodeExchangeProof()
|
||||
{
|
||||
$this->enableCodeExchangeProof = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to an access token request.
|
||||
* Grant identifier
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseTypeInterface $responseType
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return ResponseTypeInterface
|
||||
* @var string
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
\DateInterval $accessTokenTTL
|
||||
) {
|
||||
// Validate request
|
||||
$client = $this->validateClient($request);
|
||||
$encryptedAuthCode = $this->getRequestParameter('code', $request, null);
|
||||
protected $identifier = 'authorization_code';
|
||||
|
||||
if ($encryptedAuthCode === null) {
|
||||
throw OAuthServerException::invalidRequest('code');
|
||||
}
|
||||
/**
|
||||
* Response type
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $responseType = 'code';
|
||||
|
||||
// Validate the authorization code
|
||||
try {
|
||||
$authCodePayload = json_decode($this->decrypt($encryptedAuthCode));
|
||||
if (time() > $authCodePayload->expire_time) {
|
||||
throw OAuthServerException::invalidRequest('code', 'Authorization code has expired');
|
||||
}
|
||||
/**
|
||||
* AuthServer instance
|
||||
*
|
||||
* @var \League\OAuth2\Server\AuthorizationServer
|
||||
*/
|
||||
protected $server = null;
|
||||
|
||||
if ($this->authCodeRepository->isAuthCodeRevoked($authCodePayload->auth_code_id) === true) {
|
||||
throw OAuthServerException::invalidRequest('code', 'Authorization code has been revoked');
|
||||
}
|
||||
/**
|
||||
* Access token expires in override
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $accessTokenTTL = null;
|
||||
|
||||
if ($authCodePayload->client_id !== $client->getIdentifier()) {
|
||||
throw OAuthServerException::invalidRequest('code', 'Authorization code was not issued to this client');
|
||||
}
|
||||
/**
|
||||
* The TTL of the auth token
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $authTokenTTL = 600;
|
||||
|
||||
// The redirect URI is required in this request
|
||||
$redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
|
||||
if (empty($authCodePayload->redirect_uri) === false && $redirectUri === null) {
|
||||
throw OAuthServerException::invalidRequest('redirect_uri');
|
||||
}
|
||||
/**
|
||||
* Whether to require the client secret when
|
||||
* completing the flow.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $requireClientSecret = true;
|
||||
|
||||
if ($authCodePayload->redirect_uri !== $redirectUri) {
|
||||
throw OAuthServerException::invalidRequest('redirect_uri', 'Invalid redirect URI');
|
||||
}
|
||||
|
||||
$scopes = [];
|
||||
foreach ($authCodePayload->scopes as $scopeId) {
|
||||
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId);
|
||||
|
||||
if ($scope instanceof ScopeEntityInterface === false) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw OAuthServerException::invalidScope($scopeId);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$scopes[] = $scope;
|
||||
}
|
||||
|
||||
// Finalize the requested scopes
|
||||
$scopes = $this->scopeRepository->finalizeScopes(
|
||||
$scopes,
|
||||
$this->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');
|
||||
}
|
||||
|
||||
// Validate code_verifier according to RFC-7636
|
||||
// @see: https://tools.ietf.org/html/rfc7636#section-4.1
|
||||
if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeVerifier) !== 1) {
|
||||
throw OAuthServerException::invalidRequest(
|
||||
'code_verifier',
|
||||
'Code Verifier must follow the specifications of RFC-7636.'
|
||||
);
|
||||
}
|
||||
|
||||
switch ($authCodePayload->code_challenge_method) {
|
||||
case 'plain':
|
||||
if (hash_equals($codeVerifier, $authCodePayload->code_challenge) === false) {
|
||||
throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.');
|
||||
}
|
||||
|
||||
break;
|
||||
case 'S256':
|
||||
if (
|
||||
hash_equals(
|
||||
strtr(rtrim(base64_encode(hash('sha256', $codeVerifier, true)), '='), '+/', '-_'),
|
||||
$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);
|
||||
|
||||
// Send events to emitter
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
||||
|
||||
// Inject tokens into response type
|
||||
$responseType->setAccessToken($accessToken);
|
||||
$responseType->setRefreshToken($refreshToken);
|
||||
|
||||
// Revoke used auth code
|
||||
$this->authCodeRepository->revokeAuthCode($authCodePayload->auth_code_id);
|
||||
|
||||
return $responseType;
|
||||
/**
|
||||
* Override the default access token expire time
|
||||
*
|
||||
* @param int $authTokenTTL
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setAuthTokenTTL($authTokenTTL)
|
||||
{
|
||||
$this->authTokenTTL = $authTokenTTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the grant identifier that can be used in matching up requests.
|
||||
*
|
||||
* @return string
|
||||
* @param bool $required True to require client secret during access
|
||||
* token request. False if not. Default = true
|
||||
*/
|
||||
public function getIdentifier()
|
||||
public function setRequireClientSecret($required)
|
||||
{
|
||||
return 'authorization_code';
|
||||
$this->requireClientSecret = $required;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* True if client secret is required during
|
||||
* access token request. False if it isn't.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
|
||||
public function shouldRequireClientSecret()
|
||||
{
|
||||
return (
|
||||
array_key_exists('response_type', $request->getQueryParams())
|
||||
&& $request->getQueryParams()['response_type'] === 'code'
|
||||
&& isset($request->getQueryParams()['client_id'])
|
||||
);
|
||||
return $this->requireClientSecret;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* Check authorize parameters
|
||||
*
|
||||
* @return array Authorize request parameters
|
||||
*
|
||||
* @throws
|
||||
*/
|
||||
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
||||
public function checkAuthorizeParams()
|
||||
{
|
||||
$clientId = $this->getQueryStringParameter(
|
||||
'client_id',
|
||||
$request,
|
||||
$this->getServerParameter('PHP_AUTH_USER', $request)
|
||||
);
|
||||
|
||||
// Get required params
|
||||
$clientId = $this->server->getRequest()->query->get('client_id', null);
|
||||
if (is_null($clientId)) {
|
||||
throw OAuthServerException::invalidRequest('client_id');
|
||||
throw new Exception\InvalidRequestException('client_id');
|
||||
}
|
||||
|
||||
$client = $this->clientRepository->getClientEntity(
|
||||
$redirectUri = $this->server->getRequest()->query->get('redirect_uri', null);
|
||||
if (is_null($redirectUri)) {
|
||||
throw new Exception\InvalidRequestException('redirect_uri');
|
||||
}
|
||||
|
||||
// Validate client ID and redirect URI
|
||||
$client = $this->server->getClientStorage()->get(
|
||||
$clientId,
|
||||
$this->getIdentifier(),
|
||||
null,
|
||||
false
|
||||
$redirectUri,
|
||||
$this->getIdentifier()
|
||||
);
|
||||
|
||||
if ($client instanceof ClientEntityInterface === false) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
if (($client instanceof ClientEntity) === false) {
|
||||
$this->server->getEventEmitter()->emit(new Event\ClientAuthenticationFailedEvent($this->server->getRequest()));
|
||||
throw new Exception\InvalidClientException();
|
||||
}
|
||||
|
||||
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
|
||||
|
||||
if ($redirectUri !== null) {
|
||||
if (
|
||||
is_string($client->getRedirectUri())
|
||||
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
} elseif (
|
||||
is_array($client->getRedirectUri())
|
||||
&& in_array($redirectUri, $client->getRedirectUri(), true) === false
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
} elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1
|
||||
|| empty($client->getRedirectUri())) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
} else {
|
||||
$redirectUri = is_array($client->getRedirectUri())
|
||||
? $client->getRedirectUri()[0]
|
||||
: $client->getRedirectUri();
|
||||
$state = $this->server->getRequest()->query->get('state', null);
|
||||
if ($this->server->stateParamRequired() === true && is_null($state)) {
|
||||
throw new Exception\InvalidRequestException('state', $redirectUri);
|
||||
}
|
||||
|
||||
$scopes = $this->validateScopes(
|
||||
$this->getQueryStringParameter('scope', $request, $this->defaultScope),
|
||||
$redirectUri
|
||||
);
|
||||
|
||||
$stateParameter = $this->getQueryStringParameter('state', $request);
|
||||
|
||||
$authorizationRequest = new AuthorizationRequest();
|
||||
$authorizationRequest->setGrantTypeId($this->getIdentifier());
|
||||
$authorizationRequest->setClient($client);
|
||||
$authorizationRequest->setRedirectUri($redirectUri);
|
||||
|
||||
if ($stateParameter !== null) {
|
||||
$authorizationRequest->setState($stateParameter);
|
||||
$responseType = $this->server->getRequest()->query->get('response_type', null);
|
||||
if (is_null($responseType)) {
|
||||
throw new Exception\InvalidRequestException('response_type', $redirectUri);
|
||||
}
|
||||
|
||||
$authorizationRequest->setScopes($scopes);
|
||||
|
||||
if ($this->enableCodeExchangeProof === true) {
|
||||
$codeChallenge = $this->getQueryStringParameter('code_challenge', $request);
|
||||
if ($codeChallenge === null) {
|
||||
throw OAuthServerException::invalidRequest('code_challenge');
|
||||
}
|
||||
|
||||
$codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain');
|
||||
|
||||
if (in_array($codeChallengeMethod, ['plain', 'S256'], true) === false) {
|
||||
throw OAuthServerException::invalidRequest(
|
||||
'code_challenge_method',
|
||||
'Code challenge method must be `plain` or `S256`'
|
||||
);
|
||||
}
|
||||
|
||||
// Validate code_challenge according to RFC-7636
|
||||
// @see: https://tools.ietf.org/html/rfc7636#section-4.2
|
||||
if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeChallenge) !== 1) {
|
||||
throw OAuthServerException::invalidRequest(
|
||||
'code_challenged',
|
||||
'Code challenge must follow the specifications of RFC-7636.'
|
||||
);
|
||||
}
|
||||
|
||||
$authorizationRequest->setCodeChallenge($codeChallenge);
|
||||
$authorizationRequest->setCodeChallengeMethod($codeChallengeMethod);
|
||||
// Ensure response type is one that is recognised
|
||||
if (!in_array($responseType, $this->server->getResponseTypes())) {
|
||||
throw new Exception\UnsupportedResponseTypeException($responseType, $redirectUri);
|
||||
}
|
||||
|
||||
return $authorizationRequest;
|
||||
// Validate any scopes that are in the request
|
||||
$scopeParam = $this->server->getRequest()->query->get('scope', '');
|
||||
$scopes = $this->validateScopes($scopeParam, $client, $redirectUri);
|
||||
|
||||
return [
|
||||
'client' => $client,
|
||||
'redirect_uri' => $redirectUri,
|
||||
'state' => $state,
|
||||
'response_type' => $responseType,
|
||||
'scopes' => $scopes
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* Parse a new authorize request
|
||||
*
|
||||
* @param string $type The session owner's type
|
||||
* @param string $typeId The session owner's ID
|
||||
* @param array $authParams The authorize request $_GET parameters
|
||||
*
|
||||
* @return string An authorisation code
|
||||
*/
|
||||
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
||||
public function newAuthorizeRequest($type, $typeId, $authParams = [])
|
||||
{
|
||||
if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) {
|
||||
throw new \LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest');
|
||||
// Create a new session
|
||||
$session = new SessionEntity($this->server);
|
||||
$session->setOwner($type, $typeId);
|
||||
$session->associateClient($authParams['client']);
|
||||
|
||||
// Create a new auth code
|
||||
$authCode = new AuthCodeEntity($this->server);
|
||||
$authCode->setId(SecureKey::generate());
|
||||
$authCode->setRedirectUri($authParams['redirect_uri']);
|
||||
$authCode->setExpireTime(time() + $this->authTokenTTL);
|
||||
|
||||
foreach ($authParams['scopes'] as $scope) {
|
||||
$authCode->associateScope($scope);
|
||||
$session->associateScope($scope);
|
||||
}
|
||||
|
||||
$finalRedirectUri = ($authorizationRequest->getRedirectUri() === null)
|
||||
? is_array($authorizationRequest->getClient()->getRedirectUri())
|
||||
? $authorizationRequest->getClient()->getRedirectUri()[0]
|
||||
: $authorizationRequest->getClient()->getRedirectUri()
|
||||
: $authorizationRequest->getRedirectUri();
|
||||
$session->save();
|
||||
$authCode->setSession($session);
|
||||
$authCode->save();
|
||||
|
||||
// The user approved the client, redirect them back with an auth code
|
||||
if ($authorizationRequest->isAuthorizationApproved() === true) {
|
||||
$authCode = $this->issueAuthCode(
|
||||
$this->authCodeTTL,
|
||||
$authorizationRequest->getClient(),
|
||||
$authorizationRequest->getUser()->getIdentifier(),
|
||||
$authorizationRequest->getRedirectUri(),
|
||||
$authorizationRequest->getScopes()
|
||||
);
|
||||
|
||||
$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(),
|
||||
];
|
||||
|
||||
$response = new RedirectResponse();
|
||||
$response->setRedirectUri(
|
||||
$this->makeRedirectUri(
|
||||
$finalRedirectUri,
|
||||
[
|
||||
'code' => $this->encrypt(
|
||||
json_encode(
|
||||
$payload
|
||||
)
|
||||
),
|
||||
'state' => $authorizationRequest->getState(),
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
// The user denied the client, redirect them back with an error
|
||||
throw OAuthServerException::accessDenied(
|
||||
'The user denied the request',
|
||||
$this->makeRedirectUri(
|
||||
$finalRedirectUri,
|
||||
[
|
||||
'state' => $authorizationRequest->getState(),
|
||||
]
|
||||
)
|
||||
);
|
||||
return $authCode->generateRedirectUri($authParams['state']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the auth code grant
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws
|
||||
*/
|
||||
public function completeFlow()
|
||||
{
|
||||
// Get the required params
|
||||
$clientId = $this->server->getRequest()->request->get('client_id', $this->server->getRequest()->getUser());
|
||||
if (is_null($clientId)) {
|
||||
throw new Exception\InvalidRequestException('client_id');
|
||||
}
|
||||
|
||||
$clientSecret = $this->server->getRequest()->request->get('client_secret',
|
||||
$this->server->getRequest()->getPassword());
|
||||
if ($this->shouldRequireClientSecret() && is_null($clientSecret)) {
|
||||
throw new Exception\InvalidRequestException('client_secret');
|
||||
}
|
||||
|
||||
$redirectUri = $this->server->getRequest()->request->get('redirect_uri', null);
|
||||
if (is_null($redirectUri)) {
|
||||
throw new Exception\InvalidRequestException('redirect_uri');
|
||||
}
|
||||
|
||||
// Validate client ID and client secret
|
||||
$client = $this->server->getClientStorage()->get(
|
||||
$clientId,
|
||||
$clientSecret,
|
||||
$redirectUri,
|
||||
$this->getIdentifier()
|
||||
);
|
||||
|
||||
if (($client instanceof ClientEntity) === false) {
|
||||
$this->server->getEventEmitter()->emit(new Event\ClientAuthenticationFailedEvent($this->server->getRequest()));
|
||||
throw new Exception\InvalidClientException();
|
||||
}
|
||||
|
||||
// Validate the auth code
|
||||
$authCode = $this->server->getRequest()->request->get('code', null);
|
||||
if (is_null($authCode)) {
|
||||
throw new Exception\InvalidRequestException('code');
|
||||
}
|
||||
|
||||
$code = $this->server->getAuthCodeStorage()->get($authCode);
|
||||
if (($code instanceof AuthCodeEntity) === false) {
|
||||
throw new Exception\InvalidRequestException('code');
|
||||
}
|
||||
|
||||
// Ensure the auth code hasn't expired
|
||||
if ($code->isExpired() === true) {
|
||||
throw new Exception\InvalidRequestException('code');
|
||||
}
|
||||
|
||||
// Check redirect URI presented matches redirect URI originally used in authorize request
|
||||
if ($code->getRedirectUri() !== $redirectUri) {
|
||||
throw new Exception\InvalidRequestException('redirect_uri');
|
||||
}
|
||||
|
||||
$session = $code->getSession();
|
||||
$session->associateClient($client);
|
||||
|
||||
$authCodeScopes = $code->getScopes();
|
||||
|
||||
// Generate the access token
|
||||
$accessToken = new AccessTokenEntity($this->server);
|
||||
$accessToken->setId(SecureKey::generate());
|
||||
$accessToken->setExpireTime($this->getAccessTokenTTL() + time());
|
||||
|
||||
foreach ($authCodeScopes as $authCodeScope) {
|
||||
$session->associateScope($authCodeScope);
|
||||
}
|
||||
|
||||
foreach ($session->getScopes() as $scope) {
|
||||
$accessToken->associateScope($scope);
|
||||
}
|
||||
|
||||
$this->server->getTokenType()->setSession($session);
|
||||
$this->server->getTokenType()->setParam('access_token', $accessToken->getId());
|
||||
$this->server->getTokenType()->setParam('expires_in', $this->getAccessTokenTTL());
|
||||
|
||||
// Associate a refresh token if set
|
||||
if ($this->server->hasGrantType('refresh_token')) {
|
||||
$refreshToken = new RefreshTokenEntity($this->server);
|
||||
$refreshToken->setId(SecureKey::generate());
|
||||
$refreshToken->setExpireTime($this->server->getGrantType('refresh_token')->getRefreshTokenTTL() + time());
|
||||
$this->server->getTokenType()->setParam('refresh_token', $refreshToken->getId());
|
||||
}
|
||||
|
||||
// Expire the auth code
|
||||
$code->expire();
|
||||
|
||||
// Save all the things
|
||||
$accessToken->setSession($session);
|
||||
$accessToken->save();
|
||||
|
||||
if (isset($refreshToken) && $this->server->hasGrantType('refresh_token')) {
|
||||
$refreshToken->setAccessToken($accessToken);
|
||||
$refreshToken->save();
|
||||
}
|
||||
|
||||
return $this->server->getTokenType()->generateResponse();
|
||||
}
|
||||
}
|
@@ -1,57 +1,122 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Client credentials grant.
|
||||
* OAuth 2.0 Client credentials grant
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\RequestEvent;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use League\OAuth2\Server\Entity\AccessTokenEntity;
|
||||
use League\OAuth2\Server\Entity\ClientEntity;
|
||||
use League\OAuth2\Server\Entity\SessionEntity;
|
||||
use League\OAuth2\Server\Event;
|
||||
use League\OAuth2\Server\Exception;
|
||||
use League\OAuth2\Server\Util\SecureKey;
|
||||
|
||||
/**
|
||||
* Client credentials grant class.
|
||||
* Client credentials grant class
|
||||
*/
|
||||
class ClientCredentialsGrant extends AbstractGrant
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* Grant identifier
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
\DateInterval $accessTokenTTL
|
||||
) {
|
||||
// Validate request
|
||||
$client = $this->validateClient($request);
|
||||
$scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope));
|
||||
|
||||
// Finalize the requested scopes
|
||||
$finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client);
|
||||
|
||||
// Issue and persist access token
|
||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, null, $finalizedScopes);
|
||||
|
||||
// Send event to emitter
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
||||
|
||||
// Inject access token into response type
|
||||
$responseType->setAccessToken($accessToken);
|
||||
|
||||
return $responseType;
|
||||
}
|
||||
protected $identifier = 'client_credentials';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* Response type
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public function getIdentifier()
|
||||
protected $responseType = null;
|
||||
|
||||
/**
|
||||
* AuthServer instance
|
||||
*
|
||||
* @var \League\OAuth2\Server\AuthorizationServer
|
||||
*/
|
||||
protected $server = null;
|
||||
|
||||
/**
|
||||
* Access token expires in override
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $accessTokenTTL = null;
|
||||
|
||||
/**
|
||||
* Complete the client credentials grant
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws
|
||||
*/
|
||||
public function completeFlow()
|
||||
{
|
||||
return 'client_credentials';
|
||||
// Get the required params
|
||||
$clientId = $this->server->getRequest()->request->get('client_id', $this->server->getRequest()->getUser());
|
||||
if (is_null($clientId)) {
|
||||
throw new Exception\InvalidRequestException('client_id');
|
||||
}
|
||||
|
||||
$clientSecret = $this->server->getRequest()->request->get('client_secret',
|
||||
$this->server->getRequest()->getPassword());
|
||||
if (is_null($clientSecret)) {
|
||||
throw new Exception\InvalidRequestException('client_secret');
|
||||
}
|
||||
|
||||
// Validate client ID and client secret
|
||||
$client = $this->server->getClientStorage()->get(
|
||||
$clientId,
|
||||
$clientSecret,
|
||||
null,
|
||||
$this->getIdentifier()
|
||||
);
|
||||
|
||||
if (($client instanceof ClientEntity) === false) {
|
||||
$this->server->getEventEmitter()->emit(new Event\ClientAuthenticationFailedEvent($this->server->getRequest()));
|
||||
throw new Exception\InvalidClientException();
|
||||
}
|
||||
|
||||
// Validate any scopes that are in the request
|
||||
$scopeParam = $this->server->getRequest()->request->get('scope', '');
|
||||
$scopes = $this->validateScopes($scopeParam, $client);
|
||||
|
||||
// Create a new session
|
||||
$session = new SessionEntity($this->server);
|
||||
$session->setOwner('client', $client->getId());
|
||||
$session->associateClient($client);
|
||||
|
||||
// Generate an access token
|
||||
$accessToken = new AccessTokenEntity($this->server);
|
||||
$accessToken->setId(SecureKey::generate());
|
||||
$accessToken->setExpireTime($this->getAccessTokenTTL() + time());
|
||||
|
||||
// Associate scopes with the session and access token
|
||||
foreach ($scopes as $scope) {
|
||||
$session->associateScope($scope);
|
||||
}
|
||||
|
||||
foreach ($session->getScopes() as $scope) {
|
||||
$accessToken->associateScope($scope);
|
||||
}
|
||||
|
||||
// Save everything
|
||||
$session->save();
|
||||
$accessToken->setSession($session);
|
||||
$accessToken->save();
|
||||
|
||||
$this->server->getTokenType()->setSession($session);
|
||||
$this->server->getTokenType()->setParam('access_token', $accessToken->getId());
|
||||
$this->server->getTokenType()->setParam('expires_in', $this->getAccessTokenTTL());
|
||||
|
||||
return $this->server->getTokenType()->generateResponse();
|
||||
}
|
||||
}
|
||||
|
@@ -1,143 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Grant type interface.
|
||||
* OAuth 2.0 Grant type interface
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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 Defuse\Crypto\Key;
|
||||
use League\Event\EmitterAwareInterface;
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
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\ResponseTypeInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
|
||||
/**
|
||||
* Grant type interface.
|
||||
* Grant type interface
|
||||
*/
|
||||
interface GrantTypeInterface extends EmitterAwareInterface
|
||||
interface GrantTypeInterface
|
||||
{
|
||||
/**
|
||||
* Set refresh token TTL.
|
||||
*
|
||||
* @param \DateInterval $refreshTokenTTL
|
||||
*/
|
||||
public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL);
|
||||
|
||||
/**
|
||||
* Return the grant identifier that can be used in matching up requests.
|
||||
* Return the identifier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier();
|
||||
|
||||
/**
|
||||
* Respond to an incoming request.
|
||||
* Return the identifier
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseTypeInterface $responseType
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
* @param string $identifier
|
||||
*
|
||||
* @return ResponseTypeInterface
|
||||
* @return self
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
\DateInterval $accessTokenTTL
|
||||
);
|
||||
public function setIdentifier($identifier);
|
||||
|
||||
/**
|
||||
* The grant type should return true if it is able to response to an authorization request
|
||||
* Return the response type
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @return bool
|
||||
* @return string
|
||||
*/
|
||||
public function canRespondToAuthorizationRequest(ServerRequestInterface $request);
|
||||
public function getResponseType();
|
||||
|
||||
/**
|
||||
* If the grant can respond to an authorization request this method should be called to validate the parameters of
|
||||
* the request.
|
||||
* Inject the authorization server into the grant
|
||||
*
|
||||
* 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 \League\OAuth2\Server\AuthorizationServer $server The authorization server instance
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @return AuthorizationRequest
|
||||
* @return self
|
||||
*/
|
||||
public function validateAuthorizationRequest(ServerRequestInterface $request);
|
||||
public function setAuthorizationServer(AuthorizationServer $server);
|
||||
|
||||
/**
|
||||
* Once a user has authenticated and authorized the client the grant can complete the authorization request.
|
||||
* 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.
|
||||
* Complete the grant flow
|
||||
*
|
||||
* @param AuthorizationRequest $authorizationRequest
|
||||
*
|
||||
* @return ResponseTypeInterface
|
||||
* @return array
|
||||
*/
|
||||
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest);
|
||||
|
||||
/**
|
||||
* The grant type should return true if it is able to respond to this request.
|
||||
*
|
||||
* For example most grant types will check that the $_POST['grant_type'] property matches it's identifier property.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canRespondToAccessTokenRequest(ServerRequestInterface $request);
|
||||
|
||||
/**
|
||||
* Set the client repository.
|
||||
*
|
||||
* @param ClientRepositoryInterface $clientRepository
|
||||
*/
|
||||
public function setClientRepository(ClientRepositoryInterface $clientRepository);
|
||||
|
||||
/**
|
||||
* Set the access token repository.
|
||||
*
|
||||
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||
*/
|
||||
public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository);
|
||||
|
||||
/**
|
||||
* Set the scope repository.
|
||||
*
|
||||
* @param ScopeRepositoryInterface $scopeRepository
|
||||
*/
|
||||
public function setScopeRepository(ScopeRepositoryInterface $scopeRepository);
|
||||
|
||||
/**
|
||||
* Set the default scope.
|
||||
*
|
||||
* @param string $scope
|
||||
*/
|
||||
public function setDefaultScope($scope);
|
||||
|
||||
/**
|
||||
* Set the path to the private key.
|
||||
*
|
||||
* @param CryptKey $privateKey
|
||||
*/
|
||||
public function setPrivateKey(CryptKey $privateKey);
|
||||
|
||||
/**
|
||||
* Set the encryption key
|
||||
*
|
||||
* @param string|Key|null $key
|
||||
*/
|
||||
public function setEncryptionKey($key = null);
|
||||
public function completeFlow();
|
||||
}
|
||||
|
@@ -1,242 +0,0 @@
|
||||
<?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;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
{
|
||||
/**
|
||||
* @var \DateInterval
|
||||
*/
|
||||
private $accessTokenTTL;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $queryDelimiter;
|
||||
|
||||
/**
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
* @param string $queryDelimiter
|
||||
*/
|
||||
public function __construct(\DateInterval $accessTokenTTL, $queryDelimiter = '#')
|
||||
{
|
||||
$this->accessTokenTTL = $accessTokenTTL;
|
||||
$this->queryDelimiter = $queryDelimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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}
|
||||
*/
|
||||
public function canRespondToAccessTokenRequest(ServerRequestInterface $request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the grant identifier that can be used in matching up requests.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return 'implicit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to an incoming request.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseTypeInterface $responseType
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
*
|
||||
* @return ResponseTypeInterface
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
\DateInterval $accessTokenTTL
|
||||
) {
|
||||
throw new \LogicException('This grant does not used this method');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
|
||||
{
|
||||
return (
|
||||
isset($request->getQueryParams()['response_type'])
|
||||
&& $request->getQueryParams()['response_type'] === 'token'
|
||||
&& isset($request->getQueryParams()['client_id'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
||||
{
|
||||
$clientId = $this->getQueryStringParameter(
|
||||
'client_id',
|
||||
$request,
|
||||
$this->getServerParameter('PHP_AUTH_USER', $request)
|
||||
);
|
||||
if (is_null($clientId)) {
|
||||
throw OAuthServerException::invalidRequest('client_id');
|
||||
}
|
||||
|
||||
$client = $this->clientRepository->getClientEntity(
|
||||
$clientId,
|
||||
$this->getIdentifier(),
|
||||
null,
|
||||
false
|
||||
);
|
||||
|
||||
if ($client instanceof ClientEntityInterface === false) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
|
||||
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
|
||||
if ($redirectUri !== null) {
|
||||
if (
|
||||
is_string($client->getRedirectUri())
|
||||
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
} elseif (
|
||||
is_array($client->getRedirectUri())
|
||||
&& in_array($redirectUri, $client->getRedirectUri(), true) === false
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
} elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1
|
||||
|| empty($client->getRedirectUri())) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
} else {
|
||||
$redirectUri = is_array($client->getRedirectUri())
|
||||
? $client->getRedirectUri()[0]
|
||||
: $client->getRedirectUri();
|
||||
}
|
||||
|
||||
$scopes = $this->validateScopes(
|
||||
$this->getQueryStringParameter('scope', $request, $this->defaultScope),
|
||||
$redirectUri
|
||||
);
|
||||
|
||||
// Finalize the requested scopes
|
||||
$finalizedScopes = $this->scopeRepository->finalizeScopes(
|
||||
$scopes,
|
||||
$this->getIdentifier(),
|
||||
$client
|
||||
);
|
||||
|
||||
$stateParameter = $this->getQueryStringParameter('state', $request);
|
||||
|
||||
$authorizationRequest = new AuthorizationRequest();
|
||||
$authorizationRequest->setGrantTypeId($this->getIdentifier());
|
||||
$authorizationRequest->setClient($client);
|
||||
$authorizationRequest->setRedirectUri($redirectUri);
|
||||
|
||||
if ($stateParameter !== null) {
|
||||
$authorizationRequest->setState($stateParameter);
|
||||
}
|
||||
|
||||
$authorizationRequest->setScopes($finalizedScopes);
|
||||
|
||||
return $authorizationRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
||||
{
|
||||
if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) {
|
||||
throw new \LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest');
|
||||
}
|
||||
|
||||
$finalRedirectUri = ($authorizationRequest->getRedirectUri() === null)
|
||||
? is_array($authorizationRequest->getClient()->getRedirectUri())
|
||||
? $authorizationRequest->getClient()->getRedirectUri()[0]
|
||||
: $authorizationRequest->getClient()->getRedirectUri()
|
||||
: $authorizationRequest->getRedirectUri();
|
||||
|
||||
// The user approved the client, redirect them back with an access token
|
||||
if ($authorizationRequest->isAuthorizationApproved() === true) {
|
||||
$accessToken = $this->issueAccessToken(
|
||||
$this->accessTokenTTL,
|
||||
$authorizationRequest->getClient(),
|
||||
$authorizationRequest->getUser()->getIdentifier(),
|
||||
$authorizationRequest->getScopes()
|
||||
);
|
||||
|
||||
$response = new RedirectResponse();
|
||||
$response->setRedirectUri(
|
||||
$this->makeRedirectUri(
|
||||
$finalRedirectUri,
|
||||
[
|
||||
'access_token' => (string) $accessToken->convertToJWT($this->privateKey),
|
||||
'token_type' => 'Bearer',
|
||||
'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(),
|
||||
'state' => $authorizationRequest->getState(),
|
||||
],
|
||||
$this->queryDelimiter
|
||||
)
|
||||
);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
// The user denied the client, redirect them back with an error
|
||||
throw OAuthServerException::accessDenied(
|
||||
'The user denied the request',
|
||||
$this->makeRedirectUri(
|
||||
$finalRedirectUri,
|
||||
[
|
||||
'state' => $authorizationRequest->getState(),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,115 +1,182 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Password grant.
|
||||
* OAuth 2.0 Password grant
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @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\Repositories\UserRepositoryInterface;
|
||||
use League\OAuth2\Server\RequestEvent;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use League\OAuth2\Server\Entity\AccessTokenEntity;
|
||||
use League\OAuth2\Server\Entity\ClientEntity;
|
||||
use League\OAuth2\Server\Entity\RefreshTokenEntity;
|
||||
use League\OAuth2\Server\Entity\SessionEntity;
|
||||
use League\OAuth2\Server\Event;
|
||||
use League\OAuth2\Server\Exception;
|
||||
use League\OAuth2\Server\Util\SecureKey;
|
||||
|
||||
/**
|
||||
* Password grant class.
|
||||
* Password grant class
|
||||
*/
|
||||
class PasswordGrant extends AbstractGrant
|
||||
{
|
||||
/**
|
||||
* @param UserRepositoryInterface $userRepository
|
||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
* Grant identifier
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public function __construct(
|
||||
UserRepositoryInterface $userRepository,
|
||||
RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
) {
|
||||
$this->setUserRepository($userRepository);
|
||||
$this->setRefreshTokenRepository($refreshTokenRepository);
|
||||
|
||||
$this->refreshTokenTTL = new \DateInterval('P1M');
|
||||
}
|
||||
protected $identifier = 'password';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* Response type
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
\DateInterval $accessTokenTTL
|
||||
) {
|
||||
// Validate request
|
||||
$client = $this->validateClient($request);
|
||||
$scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope));
|
||||
$user = $this->validateUser($request, $client);
|
||||
|
||||
// Finalize the requested scopes
|
||||
$finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier());
|
||||
|
||||
// Issue and persist new tokens
|
||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes);
|
||||
$refreshToken = $this->issueRefreshToken($accessToken);
|
||||
|
||||
// Send events to emitter
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
||||
|
||||
// Inject tokens into response
|
||||
$responseType->setAccessToken($accessToken);
|
||||
$responseType->setRefreshToken($refreshToken);
|
||||
|
||||
return $responseType;
|
||||
}
|
||||
protected $responseType;
|
||||
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ClientEntityInterface $client
|
||||
* Callback to authenticate a user's name and password
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return UserEntityInterface
|
||||
* @var callable
|
||||
*/
|
||||
protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client)
|
||||
protected $callback;
|
||||
|
||||
/**
|
||||
* Access token expires in override
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $accessTokenTTL;
|
||||
|
||||
/**
|
||||
* Set the callback to verify a user's username and password
|
||||
*
|
||||
* @param callable $callback The callback function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setVerifyCredentialsCallback(callable $callback)
|
||||
{
|
||||
$username = $this->getRequestParameter('username', $request);
|
||||
if (is_null($username)) {
|
||||
throw OAuthServerException::invalidRequest('username');
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the callback function
|
||||
*
|
||||
* @return callable
|
||||
*
|
||||
* @throws
|
||||
*/
|
||||
protected function getVerifyCredentialsCallback()
|
||||
{
|
||||
if (is_null($this->callback) || !is_callable($this->callback)) {
|
||||
throw new Exception\ServerErrorException('Null or non-callable callback set on Password grant');
|
||||
}
|
||||
|
||||
$password = $this->getRequestParameter('password', $request);
|
||||
if (is_null($password)) {
|
||||
throw OAuthServerException::invalidRequest('password');
|
||||
return $this->callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the password grant
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws
|
||||
*/
|
||||
public function completeFlow()
|
||||
{
|
||||
// Get the required params
|
||||
$clientId = $this->server->getRequest()->request->get('client_id', $this->server->getRequest()->getUser());
|
||||
if (is_null($clientId)) {
|
||||
throw new Exception\InvalidRequestException('client_id');
|
||||
}
|
||||
|
||||
$user = $this->userRepository->getUserEntityByUserCredentials(
|
||||
$username,
|
||||
$password,
|
||||
$this->getIdentifier(),
|
||||
$client
|
||||
$clientSecret = $this->server->getRequest()->request->get('client_secret',
|
||||
$this->server->getRequest()->getPassword());
|
||||
if (is_null($clientSecret)) {
|
||||
throw new Exception\InvalidRequestException('client_secret');
|
||||
}
|
||||
|
||||
// Validate client ID and client secret
|
||||
$client = $this->server->getClientStorage()->get(
|
||||
$clientId,
|
||||
$clientSecret,
|
||||
null,
|
||||
$this->getIdentifier()
|
||||
);
|
||||
if ($user instanceof UserEntityInterface === false) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
|
||||
|
||||
throw OAuthServerException::invalidCredentials();
|
||||
if (($client instanceof ClientEntity) === false) {
|
||||
$this->server->getEventEmitter()->emit(new Event\ClientAuthenticationFailedEvent($this->server->getRequest()));
|
||||
throw new Exception\InvalidClientException();
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
$username = $this->server->getRequest()->request->get('username', null);
|
||||
if (is_null($username)) {
|
||||
throw new Exception\InvalidRequestException('username');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return 'password';
|
||||
$password = $this->server->getRequest()->request->get('password', null);
|
||||
if (is_null($password)) {
|
||||
throw new Exception\InvalidRequestException('password');
|
||||
}
|
||||
|
||||
// Check if user's username and password are correct
|
||||
$userId = call_user_func($this->getVerifyCredentialsCallback(), $username, $password);
|
||||
|
||||
if ($userId === false) {
|
||||
$this->server->getEventEmitter()->emit(new Event\UserAuthenticationFailedEvent($this->server->getRequest()));
|
||||
throw new Exception\InvalidCredentialsException();
|
||||
}
|
||||
|
||||
// Validate any scopes that are in the request
|
||||
$scopeParam = $this->server->getRequest()->request->get('scope', '');
|
||||
$scopes = $this->validateScopes($scopeParam, $client);
|
||||
|
||||
// Create a new session
|
||||
$session = new SessionEntity($this->server);
|
||||
$session->setOwner('user', $userId);
|
||||
$session->associateClient($client);
|
||||
|
||||
// Generate an access token
|
||||
$accessToken = new AccessTokenEntity($this->server);
|
||||
$accessToken->setId(SecureKey::generate());
|
||||
$accessToken->setExpireTime($this->getAccessTokenTTL() + time());
|
||||
|
||||
// Associate scopes with the session and access token
|
||||
foreach ($scopes as $scope) {
|
||||
$session->associateScope($scope);
|
||||
}
|
||||
|
||||
foreach ($session->getScopes() as $scope) {
|
||||
$accessToken->associateScope($scope);
|
||||
}
|
||||
|
||||
$this->server->getTokenType()->setSession($session);
|
||||
$this->server->getTokenType()->setParam('access_token', $accessToken->getId());
|
||||
$this->server->getTokenType()->setParam('expires_in', $this->getAccessTokenTTL());
|
||||
|
||||
// Associate a refresh token if set
|
||||
if ($this->server->hasGrantType('refresh_token')) {
|
||||
$refreshToken = new RefreshTokenEntity($this->server);
|
||||
$refreshToken->setId(SecureKey::generate());
|
||||
$refreshToken->setExpireTime($this->server->getGrantType('refresh_token')->getRefreshTokenTTL() + time());
|
||||
$this->server->getTokenType()->setParam('refresh_token', $refreshToken->getId());
|
||||
}
|
||||
|
||||
// Save everything
|
||||
$session->save();
|
||||
$accessToken->setSession($session);
|
||||
$accessToken->save();
|
||||
|
||||
if ($this->server->hasGrantType('refresh_token')) {
|
||||
$refreshToken->setAccessToken($accessToken);
|
||||
$refreshToken->save();
|
||||
}
|
||||
|
||||
return $this->server->getTokenType()->generateResponse();
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user