Showing preview only (604K chars total). Download the full file or copy to clipboard to get everything.
Repository: thephpleague/oauth2-server
Branch: master
Commit: 948b278fc873
Files: 143
Total size: 564.1 KB
Directory structure:
gitextract_nrzlm6b4/
├── .gitattributes
├── .github/
│ ├── FUNDING.yml
│ ├── dependabot.yml
│ └── workflows/
│ ├── backwards-compatibility.yml
│ ├── coding-standards.yml
│ ├── static-analysis.yml
│ └── tests.yml
├── .gitignore
├── .scrutinizer.yml
├── .styleci.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── composer.json
├── examples/
│ ├── README.md
│ ├── composer.json
│ ├── public/
│ │ ├── api.php
│ │ ├── auth_code.php
│ │ ├── client_credentials.php
│ │ ├── device_code.php
│ │ ├── implicit.php
│ │ ├── middleware_use.php
│ │ ├── password.php
│ │ └── refresh_token.php
│ └── src/
│ ├── Entities/
│ │ ├── AccessTokenEntity.php
│ │ ├── AuthCodeEntity.php
│ │ ├── ClientEntity.php
│ │ ├── DeviceCodeEntity.php
│ │ ├── RefreshTokenEntity.php
│ │ ├── ScopeEntity.php
│ │ └── UserEntity.php
│ └── Repositories/
│ ├── AccessTokenRepository.php
│ ├── AuthCodeRepository.php
│ ├── ClientRepository.php
│ ├── DeviceCodeRepository.php
│ ├── RefreshTokenRepository.php
│ ├── ScopeRepository.php
│ └── UserRepository.php
├── phpcs.xml.dist
├── phpstan.neon.dist
├── phpunit.xml.dist
├── src/
│ ├── AuthorizationServer.php
│ ├── AuthorizationValidators/
│ │ ├── AuthorizationValidatorInterface.php
│ │ └── BearerTokenValidator.php
│ ├── CodeChallengeVerifiers/
│ │ ├── CodeChallengeVerifierInterface.php
│ │ ├── PlainVerifier.php
│ │ └── S256Verifier.php
│ ├── CryptKey.php
│ ├── CryptKeyInterface.php
│ ├── CryptTrait.php
│ ├── Entities/
│ │ ├── AccessTokenEntityInterface.php
│ │ ├── AuthCodeEntityInterface.php
│ │ ├── ClientEntityInterface.php
│ │ ├── DeviceCodeEntityInterface.php
│ │ ├── RefreshTokenEntityInterface.php
│ │ ├── ScopeEntityInterface.php
│ │ ├── TokenInterface.php
│ │ ├── Traits/
│ │ │ ├── AccessTokenTrait.php
│ │ │ ├── AuthCodeTrait.php
│ │ │ ├── ClientTrait.php
│ │ │ ├── DeviceCodeTrait.php
│ │ │ ├── EntityTrait.php
│ │ │ ├── RefreshTokenTrait.php
│ │ │ ├── ScopeTrait.php
│ │ │ └── TokenEntityTrait.php
│ │ └── UserEntityInterface.php
│ ├── EventEmitting/
│ │ ├── AbstractEvent.php
│ │ ├── EmitterAwareInterface.php
│ │ ├── EmitterAwarePolyfill.php
│ │ └── EventEmitter.php
│ ├── Exception/
│ │ ├── OAuthServerException.php
│ │ └── UniqueTokenIdentifierConstraintViolationException.php
│ ├── Grant/
│ │ ├── AbstractAuthorizeGrant.php
│ │ ├── AbstractGrant.php
│ │ ├── AuthCodeGrant.php
│ │ ├── ClientCredentialsGrant.php
│ │ ├── DeviceCodeGrant.php
│ │ ├── GrantTypeInterface.php
│ │ ├── ImplicitGrant.php
│ │ ├── PasswordGrant.php
│ │ └── RefreshTokenGrant.php
│ ├── Middleware/
│ │ ├── AuthorizationServerMiddleware.php
│ │ └── ResourceServerMiddleware.php
│ ├── RedirectUriValidators/
│ │ ├── RedirectUriValidator.php
│ │ └── RedirectUriValidatorInterface.php
│ ├── Repositories/
│ │ ├── AccessTokenRepositoryInterface.php
│ │ ├── AuthCodeRepositoryInterface.php
│ │ ├── ClientRepositoryInterface.php
│ │ ├── DeviceCodeRepositoryInterface.php
│ │ ├── RefreshTokenRepositoryInterface.php
│ │ ├── RepositoryInterface.php
│ │ ├── ScopeRepositoryInterface.php
│ │ └── UserRepositoryInterface.php
│ ├── RequestAccessTokenEvent.php
│ ├── RequestEvent.php
│ ├── RequestRefreshTokenEvent.php
│ ├── RequestTypes/
│ │ ├── AuthorizationRequest.php
│ │ └── AuthorizationRequestInterface.php
│ ├── ResourceServer.php
│ └── ResponseTypes/
│ ├── AbstractResponseType.php
│ ├── BearerTokenResponse.php
│ ├── DeviceCodeResponse.php
│ ├── RedirectResponse.php
│ └── ResponseTypeInterface.php
└── tests/
├── AuthorizationServerTest.php
├── AuthorizationValidators/
│ └── BearerTokenValidatorTest.php
├── CodeChallengeVerifiers/
│ ├── PlainVerifierTest.php
│ └── S256VerifierTest.php
├── EventEmitting/
│ └── EmitterAwarePolyfillTest.php
├── Exception/
│ └── OAuthServerExceptionTest.php
├── Grant/
│ ├── AbstractGrantTest.php
│ ├── AuthCodeGrantTest.php
│ ├── ClientCredentialsGrantTest.php
│ ├── DeviceCodeGrantTest.php
│ ├── ImplicitGrantTest.php
│ ├── PasswordGrantTest.php
│ └── RefreshTokenGrantTest.php
├── Middleware/
│ ├── AuthorizationServerMiddlewareTest.php
│ └── ResourceServerMiddlewareTest.php
├── PHPStan/
│ └── AbstractGrantExtension.php
├── RedirectUriValidators/
│ └── RedirectUriValidatorTest.php
├── ResourceServerTest.php
├── ResponseTypes/
│ ├── BearerResponseTypeTest.php
│ ├── BearerTokenResponseWithParams.php
│ └── DeviceCodeResponseTypeTest.php
├── Stubs/
│ ├── .gitattributes
│ ├── AccessTokenEntity.php
│ ├── AuthCodeEntity.php
│ ├── ClientEntity.php
│ ├── CryptTraitStub.php
│ ├── DeviceCodeEntity.php
│ ├── GrantType.php
│ ├── RefreshTokenEntity.php
│ ├── ScopeEntity.php
│ ├── StubResponseType.php
│ ├── UserEntity.php
│ ├── private.key
│ ├── private.key.crlf
│ └── public.key
└── Utils/
├── CryptKeyTest.php
└── CryptTraitTest.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
* text=auto
/examples export-ignore
/tests export-ignore
/.gitattributes export-ignore
/.github export-ignore
/.gitignore export-ignore
/.scrutinizer.yml export-ignore
/.styleci.yml export-ignore
/phpstan.neon export-ignore
/phpunit.xml.dist export-ignore
/CHANGELOG.md export-ignore
/CONTRIBUTING.md export-ignore
/README.md export-ignore
/CODE_OF_CONDUCT.md export-ignore
================================================
FILE: .github/FUNDING.yml
================================================
github: [sephster]
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: composer
directory: "/"
schedule:
interval: daily
time: "11:00"
open-pull-requests-limit: 10
ignore:
- dependency-name: league/event
versions:
- 3.0.0
================================================
FILE: .github/workflows/backwards-compatibility.yml
================================================
name: "Backwards compatibility check"
on:
pull_request:
jobs:
bc-check:
name: "Backwards compatibility check"
runs-on: "ubuntu-latest"
steps:
- name: "Checkout"
uses: "actions/checkout@v4"
with:
fetch-depth: 0
- name: Fix git safe.directory in container
run: mkdir -p /home/runner/work/_temp/_github_home && printf "[safe]\n\tdirectory = /github/workspace" > /home/runner/work/_temp/_github_home/.gitconfig
- name: "Backwards Compatibility Check"
uses: docker://nyholm/roave-bc-check-ga
with:
args: --from=${{ github.event.pull_request.base.sha }}
================================================
FILE: .github/workflows/coding-standards.yml
================================================
name: Coding Standards
on:
pull_request:
push:
jobs:
coding-standards:
name: Coding Standards
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
php-version:
- 8.5
operating-system:
- ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
coverage: none
php-version: ${{ matrix.php-version }}
ini-values: memory_limit=-1
tools: composer:v2, cs2pr
- name: Install Dependencies
run: composer update --prefer-stable --prefer-dist --no-interaction --no-progress
- name: Run Codesniffer
run: vendor/bin/phpcs
================================================
FILE: .github/workflows/static-analysis.yml
================================================
name: Static Analysis
on:
push:
pull_request:
jobs:
static-analysis:
name: Static Analysis
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
php-version: [8.2, 8.3, 8.4, 8.5]
composer-stability: [prefer-lowest, prefer-stable]
operating-system:
- ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
coverage: none
php-version: ${{ matrix.php-version }}
ini-values: memory_limit=-1
tools: composer:v2, cs2pr
- name: Install Dependencies
run: composer update --${{ matrix.composer-stability }} --prefer-dist --no-interaction --no-progress
- name: Run Static Analysis
run: vendor/bin/phpstan analyse
================================================
FILE: .github/workflows/tests.yml
================================================
name: tests
on:
push:
pull_request:
schedule:
- cron: "0 0 * * *"
jobs:
tests:
strategy:
fail-fast: false
matrix:
php: [8.2, 8.3, 8.4, 8.5]
os: [ubuntu-latest, windows-latest]
stability: [prefer-lowest, prefer-stable]
runs-on: ${{ matrix.os }}
name: PHP ${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, sodium, zip
coverage: pcov
- name: Install dependencies
run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress
- name: Install Scrutinizer/Ocular
run: composer global require scrutinizer/ocular
- name: Execute tests
run: vendor/bin/phpunit --coverage-clover=coverage.clover
- name: Code coverage
if: ${{ github.ref == 'refs/heads/master' && github.repository == 'thephpleague/oauth2-server' && startsWith(matrix.os, 'ubuntu') }}
run: ~/.composer/vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover
================================================
FILE: .gitignore
================================================
/vendor
/composer.lock
phpunit.xml
.phpunit.result.cache
.idea
/examples/vendor
examples/public.key
examples/private.key
build
*.orig
================================================
FILE: .scrutinizer.yml
================================================
build:
environment:
php:
version: 8.3.3
nodes:
analysis:
tests:
override:
- php-scrutinizer-run
filter:
excluded_paths:
- tests/*
- vendor/*
checks:
php:
code_rating: true
remove_extra_empty_lines: true
remove_php_closing_tag: true
remove_trailing_whitespace: true
fix_use_statements:
remove_unused: true
preserve_multiple: false
preserve_blanklines: true
order_alphabetically: true
fix_php_opening_tag: true
fix_linefeed: true
fix_line_ending: true
fix_identation_4spaces: true
fix_doc_comments: true
tools:
external_code_coverage:
timeout: 1800
php_code_coverage: false
php_code_sniffer:
config:
standard: PSR2
filter:
paths: ['src']
php_loc:
enabled: true
excluded_dirs: [vendor, tests, examples]
php_cpd:
enabled: true
excluded_dirs: [vendor, tests, examples]
================================================
FILE: .styleci.yml
================================================
preset: psr12
risky: true
enabled:
- blank_line_before_return
- fully_qualified_strict_types
- hash_to_slash_comment
- include
- method_separation
- native_function_casing
- no_duplicate_semicolons
- no_multiline_whitespace_before_semicolons
- no_php4_constructor
- no_short_bool_cast
- no_singleline_whitespace_before_semicolons
- no_trailing_comma_in_singleline_array
- 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
- single_quote
- spaces_cast
- standardize_not_equal
- trailing_comma_in_multiline_array
- trim_array_spaces
- whitespace_after_comma_in_array
================================================
FILE: CHANGELOG.md
================================================
# 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).
## [Unreleased]
### Changed
- User ID is now passed to the finalizeScopes method for the Refresh Grant (PR #1414)
### Removed
- Removed support for PHP 8.1 (PR #1500)
## [9.3.0] - released 2025-11-25
### Added
- Added sensitive parameter to avoid sensitive data being included in stack traces (PR #1483)
- Support for PHP 8.5 (PR #1492)
### Fixed
- Made the Bearer header case insensitive to match the specs correctly (PR #1491)
## [9.2.0] - released 2025-02-15
### Added
- Added a new function to the provided ClientTrait, `supportsGrantType` to allow the auth server to issue the response `unauthorized_client` when applicable (PR #1420)
### Fixed
- Fix a bug on setting interval visibility of device authorization grant (PR #1410)
- Fix a bug where the new poll date were not persisted when `slow_down` error happens, because the exception is thrown before calling `persistDeviceCode`. (PR #1410)
- Fix a bug where `slow_down` error response may have been returned even after the user has completed the auth flow (already approved / denied the request). (PR #1410)
- Clients only validated for Refresh, Device Code, and Password grants if the client is confidential (PR #1420)
- Emit `RequestAccessTokenEvent` and `RequestRefreshTokenEvent` events instead of the general `RequestEvent` event when an access / refresh token is issued using device authorization grant. (PR #1467)
### Changed
- Key permission checks ignored on Windows regardless of userland choice as cannot be run successfully on this OS (PR #1447)
## [9.1.0] - released 2024-11-21
### Added
- Support for PHP 8.4 (PR #1454)
### Fixed
- In the Auth Code grant, when requesting an access token with an invalid auth code, we now respond with an invalid_grant error instead of invalid_request (PR #1433)
- Fixed spec compliance issue where device access token request was mistakenly expecting to receive scopes in the request (PR #1412)
- Refresh tokens pre version 9 might have had user IDs set as ints which meant they were incorrectly rejected. We now cast these values to strings to allow old refresh tokens (PR #1436)
## [9.0.1] - released 2024-10-14
### Fixed
- Auto-generated event emitter is now persisted. Previously, a new emitter was generated every time (PR #1428)
- Fixed bug where you could not omit a redirect uri even if one had not been specified during the auth request (PR #1428)
- Fixed bug where "state" parameter wasn't present on `invalid_scope` error response and wasn't on fragment part of `access_denied` redirect URI on Implicit grant (PR #1298)
- Fixed bug where disabling refresh token revocation via `revokeRefreshTokens(false)` unintentionally disables issuing new refresh token (PR #1449)
## [9.0.0] - released 2024-05-13
### Added
- Device Authorization Grant added (PR #1074)
- GrantTypeInterface has a new function, `revokeRefreshTokens()` for enabling or disabling refresh tokens after use (PR #1375)
- A CryptKeyInterface to allow developers to change the CryptKey implementation with greater ease (PR #1044)
- The authorization server can now finalize scopes when a client uses a refresh token (PR #1094)
- An AuthorizationRequestInterface to make it easier to extend the AuthorizationRequest (PR #1110)
- Added function `getKeyContents()` to the `CryptKeyInterface` (PR #1375)
### Fixed
- Basic authorization is now case insensitive (PR #1403)
- If a refresh token has expired, been revoked, cannot be decrypted, or does not belong to the correct client, the server will now issue an `invalid_grant` error and a HTTP 400 response. In previous versions the server incorrectly issued an `invalid_request` and HTTP 401 response (PR #1042) (PR #1082)
### Changed
- All interfaces now specify types for all params and return values. Strict typing enforced (PR #1074)
- Request parameters are now parsed into strings to use internally in the library (PR #1402)
- Authorization Request objects are now created through the factory method, `createAuthorizationRequest()` (PR #1111)
- Changed parameters for `finalizeScopes()` to allow a reference to an auth code ID (PR #1112)
- AccessTokenEntityInterface now requires the implementation of `toString()` instead of the magic method `__toString()` (PR #1395)
### Removed
- Removed message property from OAuthException HTTP response. Now just use error_description as per the OAuth 2 spec (PR #1375)
## [9.0.0-RC1] - released 2024-03-27
### Added
- Device Authorization Grant added (PR #1074)
- GrantTypeInterface has a new function, `revokeRefreshTokens()` for enabling or disabling refresh tokens after use (PR #1375)
- A CryptKeyInterface to allow developers to change the CryptKey implementation with greater ease (PR #1044)
- The authorization server can now finalize scopes when a client uses a refresh token (PR #1094)
- An AuthorizationRequestInterface to make it easier to extend the AuthorizationRequest (PR #1110)
- Added function `getKeyContents()` to the `CryptKeyInterface` (PR #1375)
### Fixed
- If a refresh token has expired, been revoked, cannot be decrypted, or does not belong to the correct client, the server will now issue an `invalid_grant` error and a HTTP 400 response. In previous versions the server incorrectly issued an `invalid_request` and HTTP 401 response (PR #1042) (PR #1082)
### Changed
- Authorization Request objects are now created through the factory method, `createAuthorizationRequest()` (PR #1111)
- Changed parameters for `finalizeScopes()` to allow a reference to an auth code ID (PR #1112)
- AccessTokenEntityInterface now requires the implementation of `toString()` instead of the magic method `__toString()` (PR #1395)
### Removed
- Removed message property from OAuthException HTTP response. Now just use error_description as per the OAuth 2 spec (PR #1375)
## [8.5.4] - released 2023-08-25
### Added
- Support for league/uri ^7.0 (PR #1367)
## [8.5.3] - released 2023-07-06
### Security
- If a key string is provided to the CryptKey constructor with an invalid
passphrase, the LogicException message generated will expose the given key.
The key is no longer leaked via this exception (PR #1353)
## [8.5.2] - released 2023-06-16
### Changed
- Bumped the versions for laminas/diactoros and psr/http-message to support
PSR-7 v2.0 (PR #1339)
## [8.5.1] - released 2023-04-04
### Fixed
- Fixed PHP version constraints and lcobucci/clock version constraint to support PHP 8.1 (PR #1336)
## [8.5.0] - released 2023-04-03
### Added
- Support for PHP 8.1 and 8.2 (PR #1333)
### Removed
- Support PHP 7.2, 7.3, and 7.4 (PR #1333)
## [8.4.1] - released 2023-03-22
### Fixed
- Fix deprecation notices for PHP 8.x (PR #1329)
## [8.4.0] - released 2023-02-15
### Added
- You can now set a leeway for time drift between servers when validating a JWT (PR #1304)
### Security
- Access token requests that contain a code_verifier but are not bound to a code_challenge will be rejected to prevent
a PKCE downgrade attack (PR #1326)
## [8.3.6] - released 2022-11-14
### Fixed
- Use LooseValidAt instead of StrictValidAt so that users aren't forced to use claims such as NBF in their JWT tokens (PR #1312)
## [8.3.5] - released 2022-05-12
### Fixed
- Use InMemory::plainText('empty', 'empty') instead of InMemory::plainText('') to avoid [new empty string exception](https://github.com/lcobucci/jwt/pull/833) thrown by lcobucci/jwt (PR #1282)
## [8.3.4] - released 2022-04-07
### Fixed
- Server previously rejected valid uris with custom schemes. Now use league/uri for parsing to accept all valid uris (PR #1274)
## [8.3.3] - released 2021-10-11
### Security
- Removed the use of `LocalFileReference()` in lcobucci/jwt. Function deprecated as per [GHSA-7322-jrq4-x5hf](https://github.com/lcobucci/jwt/security/advisories/GHSA-7322-jrq4-x5hf) (PR #1249)
## [8.3.2] - released 2021-07-27
### Changed
- Conditionally support the `StrictValidAt()` method in lcobucci/jwt so we can use version 4.1.x or greater of the library (PR #1236)
- When providing invalid credentials, the library now responds with the error message _The user credentials were incorrect_ (PR #1230)
- Keys are always stored in memory now and are not written to a file in the /tmp directory (PR #1180)
- The regex for matching the bearer token has been simplified (PR #1238)
## [8.3.1] - released 2021-06-04
### Fixed
- Revert check on clientID. We will no longer require this to be a string (PR #1233)
## [8.3.0] - released 2021-06-03
### Added
- The server will now validate redirect uris according to rfc8252 (PR #1203)
- Events emitted now include the refresh token and access token payloads (PR #1211)
- Use the `revokeRefreshTokens()` function to decide whether refresh tokens are revoked or not upon use (PR #1189)
### Changed
- Keys are now validated using `openssl_pkey_get_private()` and `openssl_pkey_get_public()` instead of regex matching (PR #1215)
### Fixed
- The server will now only recognise and handle an authorization header if the value of the header is non-empty. This is to circumvent issues where some common frameworks set this header even if no value is present (PR #1170)
- Added type validation for redirect uri, client ID, client secret, scopes, auth code, state, username, and password inputs (PR #1210)
- Allow scope "0" to be used. Previously this was removed from a request because it failed an `empty()` check (PR #1181)
## [8.2.4] - released 2020-12-10
### Fixed
- Reverted the enforcement of at least one redirect_uri for a client. This change has instead been moved to version 9 (PR #1169)
## [8.2.3] - released 2020-12-02
### Added
- Re-added support for PHP 7.2 (PR #1165, #1167)
## [8.2.2] - released 2020-11-30
### Fixed
- Fix issue where the private key passphrase isn't correctly passed to JWT library (PR #1164)
## [8.2.1] - released 2020-11-26
### Fixed
- If you have a password on your private key, it is now passed correctly to the JWT configuration object. (PR #1159)
## [8.2.0] - released 2020-11-25
### Added
- Add a `getRedirectUri` function to the `OAuthServerException` class (PR #1123)
- Support for PHP 8.0 (PR #1146)
### Removed
- Removed support for PHP 7.2 (PR #1146)
### Fixed
- Fix typo in parameter hint. `code_challenged` changed to `code_challenge`. Thrown by Auth Code Grant when the code challenge does not match the regex. (PR #1130)
- Undefined offset was returned when no client redirect URI was set. Now throw an invalidClient exception if no redirect URI is set against a client (PR #1140)
## [8.1.1] - released 2020-07-01
### Fixed
- If you provide a valid redirect_uri with the auth code grant and an invalid scope, the server will use the given
redirect_uri instead of the default client redirect uri (PR #1126)
## [8.1.0] - released 2020-04-29
### Added
- Added support for PHP 7.4 (PR #1075)
### Changed
- If an error is encountered when running `preg_match()` to validate an RSA key, the server will now throw a RuntimeException (PR #1047)
- Replaced deprecated methods with recommended ones when using `Lcobucci\JWT\Builder` to build a JWT token. (PR #1060)
- When storing a key, we no longer touch the file before writing it as this is an unnecessary step (PR #1064)
- Prefix native PHP functions in namespaces with backslashes for micro-optimisations (PR #1071)
### Removed
- Support for PHP 7.1 (PR #1075)
### Fixed
- Clients are now explicitly prevented from using the Client Credentials grant unless they are confidential to conform
with the OAuth2 spec (PR #1035)
- Abstract method `getIdentifier()` added to AccessTokenTrait. The trait cannot be used without the `getIdentifier()`
method being defined (PR #1051)
- An exception is now thrown if a refresh token is accidentally sent in place of an authorization code when using the
Auth Code Grant (PR #1057)
- Can now send access token request without being forced to specify a redirect URI (PR #1096)
- In the BearerTokenValidator, if an implementation is using PDO, there is a possibility that a RuntimeException will be thrown when checking if an access token is revoked. This scenario no longer incorrectly issues an exception with a hint mentioning an issue with JSON decoding. (PR #1107)
## [8.0.0] - released 2019-07-13
### Added
- Flag, `requireCodeChallengeForPublicClients`, used to reject public clients that do not provide a code challenge for the Auth Code Grant; use AuthCodeGrant::disableRequireCodeCallengeForPublicClients() to turn off this requirement (PR #938)
- Public clients can now use the Auth Code Grant (PR #938)
- `isConfidential` getter added to `ClientEntity` to identify type of client (PR #938)
- Function `validateClient()` added to validate clients which was previously performed by the `getClientEntity()` function (PR #938)
- Add a new function to the AbstractGrant class called `getClientEntityOrFail()`. This is a wrapper around the `getClientEntity()` function that ensures we emit and throw an exception if the repo doesn't return a client entity. (PR #1010)
### Changed
- Replace `convertToJWT()` interface with a more generic `__toString()` to improve extensibility; AccessTokenEntityInterface now requires `setPrivateKey(CryptKey $privateKey)` so `__toString()` has everything it needs to work (PR #874)
- The `invalidClient()` function accepts a PSR-7 compliant `$serverRequest` argument to avoid accessing the `$_SERVER` global variable and improve testing (PR #899)
- `issueAccessToken()` in the Abstract Grant no longer sets access token client, user ID or scopes. These values should already have been set when calling `getNewToken()` (PR #919)
- No longer need to enable PKCE with `enableCodeExchangeProof` flag. Any client sending a code challenge will initiate PKCE checks. (PR #938)
- Function `getClientEntity()` no longer performs client validation (PR #938)
- Password Grant now returns an invalid_grant error instead of invalid_credentials if a user cannot be validated (PR #967)
- Use `DateTimeImmutable()` instead of `DateTime()`, `time()` instead of `(new DateTime())->getTimeStamp()`, and `DateTime::getTimeStamp()` instead of `DateTime::format('U')` (PR #963)
### Removed
- `enableCodeExchangeProof` flag (PR #938)
- Support for PHP 7.0 (PR #1014)
- Remove JTI claim from JWT header (PR #1031)
## [7.4.0] - released 2019-05-05
### Changed
- RefreshTokenRepository can now return null, allowing refresh tokens to be optional. (PR #649)
## [7.3.3] - released 2019-03-29
### Added
- Added `error_description` to the error payload to improve standards compliance. The contents of this are copied from the existing `message` value. (PR #1006)
### Deprecated
- Error payload will not issue `message` value in the next major release (PR #1006)
## [7.3.2] - released 2018-11-21
### Fixed
- Revert setting keys on response type to be inside `getResponseType()` function instead of AuthorizationServer constructor (PR #969)
## [7.3.1] - released 2018-11-15
### Fixed
- Fix issue with previous release where interface had changed for the AuthorizationServer. Reverted to the previous interface while maintaining functionality changes (PR #970)
## [7.3.0] - released 2018-11-13
### Changed
- Moved the `finalizeScopes()` call from `validateAuthorizationRequest` method to the `completeAuthorizationRequest` method so it is called just before the access token is issued (PR #923)
### Added
- Added a ScopeTrait to provide an implementation for jsonSerialize (PR #952)
- Ability to nest exceptions (PR #965)
### Fixed
- Fix issue where AuthorizationServer is not stateless as ResponseType could store state of a previous request (PR #960)
## [7.2.0] - released 2018-06-23
### Changed
- Added new`validateRedirectUri` method AbstractGrant to remove three instances of code duplication (PR #912)
- Allow 640 as a crypt key file permission (PR #917)
### Added
- Function `hasRedirect()` added to `OAuthServerException` (PR #703)
### Fixed
- Catch and handle `BadMethodCallException` from the `verify()` method of the JWT token in the `validateAuthorization` method (PR #904)
## [4.1.7] - released 2018-06-23
### Fixed
- Ensure `empty()` function call only contains variable to be compatible with PHP 5.4 (PR #918)
## [7.1.1] - released 2018-05-21
### Fixed
- No longer set a WWW-Authenticate header for invalid clients if the client did not send an Authorization header in the original request (PR #902)
## [7.1.0] - released 2018-04-22
### Changed
- Changed hint for unsupportedGrantType exception so it no longer references the grant type parameter which isn't always expected (PR #893)
- Upgrade PHPStan checks to level 7 (PR #856)
### Added
- Added event emitters for issued access and refresh tokens (PR #860)
- Can now use Defuse\Crypto\Key for encryption/decryption of keys which is faster than the Cryto class (PR #812)
### Removed
- Remove paragone/random_compat from dependencies
## [7.0.0] - released 2018-02-18
### Added
- Use PHPStan for static analysis of code (PR #848)
- Enforce stricter static analysis checks and upgrade library dependencies (PR #852)
- Provide PHPStan coverage for tests and update PHPUnit (PR #849)
- Get and set methods for OAuth Server Exception payloads. Allow implementer to specify the JSON encode options (PR #719)
### Changed
- ClientRepository interface will now accept null for the Grant type to improve extensibility options (PR #607)
- Do not issue an error if key file permissions are 400 or 440 (PR #839)
- Skip key file creation if the file already exists (PR #845)
- Change changelog format and update readme
### Removed
- Support for PHP 5.6
- Support for version 5.x and 6.x of the library
### Fixed
- PKCE implementation (PR #744)
- Set correct redirect URI when validating scopes (PR #840)
- S256 code challenege method (PR #842)
- Accept RSA key with CRLF line endings (PR #805)
## [6.1.1] - 2017-12-23
- Removed check on empty scopes
## [6.1.0] - 2017-12-23
- Changed the token type issued by the Implicit Grant to be Bearer instead of bearer. (PR #724)
- Replaced call to array_key_exists() with the faster isset() on the Implicit Grant. (PR #749)
- Allow specification of query delimiter character in the Password Grant (PR #801)
- Add Zend Diactoros library dependency to examples (PR #678)
- Can set default scope for the authorization endpoint. If no scope is passed during an authorization request, the default scope will be used if set. If not, the server will issue an invalid scope exception (PR #811)
- Added validation for redirect URIs on the authorization end point to ensure exactly one redirection URI has been passed (PR #573)
## [6.0.2] - 2017-08-03
- An invalid refresh token that can't be decrypted now returns a HTTP 401 error instead of HTTP 400 (Issue #759)
- Removed chmod from CryptKey and add toggle to disable checking (Issue #776)
- Fixes invalid code challenge method payload key name (Issue #777)
## [6.0.1] - 2017-07-19
To address feedback from the security release the following change has been made:
- If an RSA key cannot be chmod'ed to 600 then it will now throw a E_USER_NOTICE instead of an exception.
## [6.0.0] - 2017-07-01
- Breaking change: The `AuthorizationServer` constructor now expects an encryption key string instead of a public key
- Remove support for HHVM
- Remove support for PHP 5.5
## [5.1.4] - 2017-07-01
- Fixed multiple security vulnerabilities as a result of a security audit paid for by the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source). All users of this library are encouraged to update as soon as possible to this version or version 6.0 or greater.
- It is recommended on each `AuthorizationServer` instance you set the `setEncryptionKey()`. This will result in stronger encryption being used. If this method is not set messages will be sent to the defined error handling routines (using `error_log`). Please see the examples and documentation for examples.
- TravisCI now tests PHP 7.1 (Issue #671)
- Fix middleware example fatal error (Issue #682)
- Fix typo in the first README sentence (Issue #690)
- Corrected DateInterval from 1 min to 1 month (Issue #709)
## [5.1.3] - 2016-10-12
- Fixed WWW-Authenticate header (Issue #669)
- Increase the recommended RSA key length from 1024 to 2048 bits (Issue #668)
## [5.1.2] - 2016-09-19
- Fixed `finalizeScopes` call (Issue #650)
## [4.1.6] - 2016-09-13
- Less restrictive on Authorization header check (Issue #652)
## [5.1.1] - 2016-07-26
- Improved test suite (Issue #614)
- Updated docblocks (Issue #616)
- Replace `array_shift` with `foreach` loop (Issue #621)
- Allow easy addition of custom fields to Bearer token response (Issue #624)
- Key file auto-generation from string (Issue #625)
## [5.1.0] - 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
- `state` parameter is now correctly returned after implicit grant authorization
- Small code and docblock improvements
## [5.0.1] - 2016-04-18
- Fixes an issue (#550) whereby it was unclear whether or not to validate a client's secret during a request.
## [5.0.0] - 2016-04-17
Version 5 is a complete code rewrite.
- Renamed Server class to AuthorizationServer
- Added ResourceServer class
- Run unit tests again PHP 5.5.9 as it's the minimum supported version
- Enable PHPUnit 5.0 support
- Improved examples and documentation
- Make it clearer that the implicit grant doesn't support refresh tokens
- Improved refresh token validation errors
- Fixed refresh token expiry date
## [5.0.0-RC2] - 2016-04-10
- Allow multiple client redirect URIs (Issue #511)
- Remove unused mac token interface (Issue #503)
- Handle RSA key passphrase (Issue #502)
- Remove access token repository from response types (Issue #501)
- Remove unnecessary methods from entity interfaces (Issue #490)
- Ensure incoming JWT hasn't expired (Issue #509)
- Fix client identifier passed where user identifier is expected (Issue #498)
- Removed built-in entities; added traits to for quick re-use (Issue #504)
- Redirect uri is required only if the "redirect_uri" parameter was included in the authorization request (Issue #514)
- Removed templating for auth code and implicit grants (Issue #499)
## [5.0.0-RC1] - 2016-03-24
Version 5 is a complete code rewrite.
- JWT support
- PSR-7 support
- Improved exception errors
- Replace all occurrences of the term "Storage" with "Repository"
- Simplify repositories
- Entities conform to interfaces and use traits
- Auth code grant updated
- Allow support for public clients
- Add support for #439
- Client credentials grant updated
- Password grant updated
- Allow support for public clients
- Refresh token grant updated
- Implement Implicit grant
- Bearer token output type
- Remove MAC token output type
- Authorization server rewrite
- Resource server class moved to PSR-7 middleware
- Tests
- Much much better documentation
## [4.1.5] - 2016-01-04
- Enable Symfony 3.0 support (#412)
## [4.1.4] - 2015-11-13
- Fix for determining access token in header (Issue #328)
- Refresh tokens are now returned for MAC responses (Issue #356)
- Added integration list to readme (Issue #341)
- Expose parameter passed to exceptions (Issue #345)
- Removed duplicate routing setup code (Issue #346)
- Docs fix (Issues #347, #360, #380)
- Examples fix (Issues #348, #358)
- Fix typo in docblock (Issue #352)
- Improved timeouts for MAC tokens (Issue #364)
- `hash_hmac()` should output raw binary data, not hexits (Issue #370)
- Improved regex for matching all Base64 characters (Issue #371)
- Fix incorrect signature parameter (Issue #372)
- AuthCodeGrant and RefreshTokenGrant don't require client_secret (Issue #377)
- Added priority argument to event listener (Issue #388)
## [4.1.3] - 2015-03-22
- Docblock, namespace and inconsistency fixes (Issue #303)
- Docblock type fix (Issue #310)
- Example bug fix (Issue #300)
- Updated league/event to ~2.1 (Issue #311)
- Fixed missing session scope (Issue #319)
- Updated interface docs (Issue #323)
- `.travis.yml` updates
## [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
**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
## [1.0.8] - 2013-03-18
- Fixed check for required state parameter
- Fixed check that user's credentials are correct in Password grant
## [1.0.7] - 2013-03-04
- Added method `requireStateParam()`
- Added method `requireScopeParam()`
## [1.0.6] - 2013-02-22
- Added links to tutorials in the README
- Added missing `state` parameter request to the `checkAuthoriseParams()` method.
## [1.0.5] - 2013-02-21
- Fixed the SQL example for SessionInterface::getScopes()
## [1.0.3] - 2013-02-20
- Changed all instances of the "authentication server" to "authorization server"
## [1.0.2] - 2013-02-20
- Fixed MySQL create table order
- Fixed version number in composer.json
## [1.0.1] - 2013-02-19
- Updated AuthServer.php to use `self::getParam()`
## 1.0.0 - 2013-02-15
- First major release
[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/9.3.0...HEAD
[9.3.0]: https://github.com/thephpleague/oauth2-server/compare/9.2.0...9.3.0
[9.2.0]: https://github.com/thephpleague/oauth2-server/compare/9.1.0...9.2.0
[9.1.0]: https://github.com/thephpleague/oauth2-server/compare/9.0.1...9.1.0
[9.0.1]: https://github.com/thephpleague/oauth2-server/compare/9.0.0...9.0.1
[9.0.0]: https://github.com/thephpleague/oauth2-server/compare/9.0.0-RC1...9.0.0
[9.0.0-RC1]: https://github.com/thephpleague/oauth2-server/compare/8.5.4...9.0.0-RC1
[8.5.4]: https://github.com/thephpleague/oauth2-server/compare/8.5.3...8.5.4
[8.5.3]: https://github.com/thephpleague/oauth2-server/compare/8.5.2...8.5.3
[8.5.2]: https://github.com/thephpleague/oauth2-server/compare/8.5.1...8.5.2
[8.5.1]: https://github.com/thephpleague/oauth2-server/compare/8.5.0...8.5.1
[8.5.0]: https://github.com/thephpleague/oauth2-server/compare/8.4.1...8.5.0
[8.4.1]: https://github.com/thephpleague/oauth2-server/compare/8.4.0...8.4.1
[8.4.0]: https://github.com/thephpleague/oauth2-server/compare/8.3.6...8.4.0
[8.3.6]: https://github.com/thephpleague/oauth2-server/compare/8.3.5...8.3.6
[8.3.5]: https://github.com/thephpleague/oauth2-server/compare/8.3.4...8.3.5
[8.3.4]: https://github.com/thephpleague/oauth2-server/compare/8.3.3...8.3.4
[8.3.3]: https://github.com/thephpleague/oauth2-server/compare/8.3.2...8.3.3
[8.3.2]: https://github.com/thephpleague/oauth2-server/compare/8.3.1...8.3.2
[8.3.1]: https://github.com/thephpleague/oauth2-server/compare/8.3.0...8.3.1
[8.3.0]: https://github.com/thephpleague/oauth2-server/compare/8.2.4...8.3.0
[8.2.4]: https://github.com/thephpleague/oauth2-server/compare/8.2.3...8.2.4
[8.2.3]: https://github.com/thephpleague/oauth2-server/compare/8.2.2...8.2.3
[8.2.2]: https://github.com/thephpleague/oauth2-server/compare/8.2.1...8.2.2
[8.2.1]: https://github.com/thephpleague/oauth2-server/compare/8.2.0...8.2.1
[8.2.0]: https://github.com/thephpleague/oauth2-server/compare/8.1.1...8.2.0
[8.1.1]: https://github.com/thephpleague/oauth2-server/compare/8.1.0...8.1.1
[8.1.0]: https://github.com/thephpleague/oauth2-server/compare/8.0.0...8.1.0
[8.0.0]: https://github.com/thephpleague/oauth2-server/compare/7.4.0...8.0.0
[7.4.0]: https://github.com/thephpleague/oauth2-server/compare/7.3.3...7.4.0
[7.3.3]: https://github.com/thephpleague/oauth2-server/compare/7.3.2...7.3.3
[7.3.2]: https://github.com/thephpleague/oauth2-server/compare/7.3.1...7.3.2
[7.3.1]: https://github.com/thephpleague/oauth2-server/compare/7.3.0...7.3.1
[7.3.0]: https://github.com/thephpleague/oauth2-server/compare/7.2.0...7.3.0
[7.2.0]: https://github.com/thephpleague/oauth2-server/compare/7.1.1...7.2.0
[7.1.1]: https://github.com/thephpleague/oauth2-server/compare/7.1.0...7.1.1
[7.1.0]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...7.1.0
[7.0.0]: https://github.com/thephpleague/oauth2-server/compare/6.1.1...7.0.0
[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.7]: https://github.com/thephpleague/oauth2-server/compare/4.1.6...4.1.7
[4.1.6]: https://github.com/thephpleague/oauth2-server/compare/4.1.5...4.1.6
[4.1.5]: https://github.com/thephpleague/oauth2-server/compare/4.1.4...4.1.5
[4.1.4]: https://github.com/thephpleague/oauth2-server/compare/4.1.3...4.1.4
[4.1.3]: https://github.com/thephpleague/oauth2-server/compare/4.1.2...4.1.3
[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
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# 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
================================================
FILE: CONTRIBUTING.md
================================================
Thanks for contributing to this project.
**Please submit your pull request against the `master` branch only.**
Please ensure that you run `phpunit` from the project root after you've made any changes.
If you've added something new please create a new unit test, if you've changed something please update any unit tests as appropritate.
We're trying to ensure there is **100%** test code coverage (including testing PHP errors and exceptions) so please ensure any new/updated tests cover all of your changes.
Thank you,
@alexbilbie
================================================
FILE: LICENSE
================================================
MIT License
Copyright (C) Alex Bilbie
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# PHP OAuth 2.0 Server
[](https://github.com/thephpleague/oauth2-server/releases)
[](LICENSE.md)
[](https://github.com/thephpleague/oauth2-server/actions)
[](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)
`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:
- Authorization code grant
- Client credentials grant
- Device authorization grant
- Implicit grant
- Refresh grant
- Resource owner password credentials grant
The following RFCs are implemented:
- [RFC6749 "OAuth 2.0"](https://tools.ietf.org/html/rfc6749)
- [RFC6750 "The OAuth 2.0 Authorization Framework: Bearer Token Usage"](https://tools.ietf.org/html/rfc6750)
- [RFC7519 "JSON Web Token (JWT)"](https://tools.ietf.org/html/rfc7519)
- [RFC7636 "Proof Key for Code Exchange by OAuth Public Clients"](https://tools.ietf.org/html/rfc7636)
- [RFC8628 "OAuth 2.0 Device Authorization Grant](https://tools.ietf.org/html/rfc8628)
This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](https://twitter.com/alexbilbie).
## Requirements
The latest version of this package supports the following versions of PHP:
- PHP 8.2
- PHP 8.3
- PHP 8.4
- PHP 8.5
The `openssl` and `json` extensions are 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 with other packages and frameworks.
## Installation
```
composer require league/oauth2-server
```
## Documentation
The library documentation can be found at [https://oauth2.thephpleague.com](https://oauth2.thephpleague.com).
You can contribute to the documentation in the [gh-pages branch](https://github.com/thephpleague/oauth2-server/tree/gh-pages/).
## Testing
The library uses [PHPUnit](https://phpunit.de/) for unit tests.
```
vendor/bin/phpunit
```
## Continuous Integration
We use [Github Actions](https://github.com/features/actions), [Scrutinizer](https://scrutinizer-ci.com/), and [StyleCI](https://styleci.io/) for continuous integration. Check out [our](https://github.com/thephpleague/oauth2-server/blob/master/.github/workflows/tests.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)
- [OAuth 2 Server for Mezzio](https://github.com/mezzio/mezzio-authentication-oauth2)
- [OAuth 2 Server Bundle (Symfony)](https://github.com/thephpleague/oauth2-server-bundle)
- [Heimdall for CodeIgniter 4](https://github.com/ezralazuardy/heimdall)
## Changelog
See the [project changelog](https://github.com/thephpleague/oauth2-server/blob/master/CHANGELOG.md)
## 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.
## 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.
## Security
If you discover any security related issues, please email `andrew@noexceptions.io` instead of using the issue tracker.
## License
This package is released under the MIT License. See the bundled [LICENSE](https://github.com/thephpleague/oauth2-server/blob/master/LICENSE) file for details.
## Credits
This code is principally developed and maintained by [Andy Millington](https://twitter.com/Sephster).
Between 2012 and 2017 this library was developed and maintained by [Alex Bilbie](https://alexbilbie.com/).
PHP OAuth 2.0 Server is one of many packages provided by The PHP League. To find out more, please visit [our website](https://thephpleague.com).
Special thanks to [all of these awesome contributors](https://github.com/thephpleague/oauth2-server/contributors).
Additional thanks go to the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source) for funding a security audit of this library.
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.
================================================
FILE: composer.json
================================================
{
"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": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
"ext-openssl": "*",
"league/event": "^3.0",
"league/uri": "^7.8",
"lcobucci/jwt": "^5.6",
"psr/http-message": "^2.0",
"defuse/php-encryption": "^2.4",
"ext-json": "*",
"lcobucci/clock": "^3.3",
"psr/http-server-middleware": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^11.5.50",
"laminas/laminas-diactoros": "^3.8",
"phpstan/phpstan": "^2.1.38",
"phpstan/phpstan-phpunit": "^2.0.12",
"roave/security-advisories": "dev-master",
"phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan-strict-rules": "^2.0",
"slevomat/coding-standard": "^8.27.1",
"php-parallel-lint/php-parallel-lint": "^1.4",
"squizlabs/php_codesniffer": "^4.0",
"paragonie/random_compat": "^9.99.100"
},
"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",
"protect",
"secure"
],
"authors": [
{
"name": "Alex Bilbie",
"email": "hello@alexbilbie.com",
"homepage": "http://www.alexbilbie.com",
"role": "Developer"
},
{
"name": "Andy Millington",
"email": "andrew@noexceptions.io",
"homepage": "https://www.noexceptions.io",
"role": "Developer"
}
],
"replace": {
"lncd/oauth2": "*",
"league/oauth2server": "*"
},
"autoload": {
"psr-4": {
"League\\OAuth2\\Server\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"LeagueTests\\": "tests/"
}
},
"config": {
"allow-plugins": {
"ocramius/package-versions": true,
"phpstan/extension-installer": true,
"dealerdirect/phpcodesniffer-composer-installer": false
}
}
}
================================================
FILE: examples/README.md
================================================
# Example implementations (via [`Slim 3`](https://github.com/slimphp/Slim/tree/3.x))
## Installation
0. Run `composer install` in this directory to install dependencies
0. Create a private key `openssl genrsa -out private.key 2048`
0. Export the public key `openssl rsa -in private.key -pubout > public.key`
0. Start local PHP server `php -S 127.0.0.1:4444 -t public/`
## 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}}"
```
## Testing the device authorization grant example
Send the following cURL request. This will return a device code which can be exchanged for an access token.
```
curl -X "POST" "http://localhost:4444/device_code.php/device_authorization" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Accept: 1.0" \
--data-urlencode "client_id=myawesomeapp" \
--data-urlencode "client_secret=abc123" \
--data-urlencode "scope=basic email"
```
We have set up the example so that a user ID is already associated with the device code. In a production application you
would implement an authorization view to allow a user to authorize the device.
Issue the following cURL request to exchange your device code for an access token. Replace `{{DEVICE_CODE}}` with the
device code returned from your first cURL post:
```
curl -X "POST" "http://localhost:4444/device_code.php/access_token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Accept: 1.0" \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
--data-urlencode "device_code={{DEVICE_CODE}}" \
--data-urlencode "client_id=myawesomeapp" \
--data-urlencode "client_secret=abc123"
```
================================================
FILE: examples/composer.json
================================================
{
"require": {
"slim/slim": "^3.12.3"
},
"require-dev": {
"league/event": "^3.0",
"lcobucci/jwt": "^3.4.6 || ^4.0.4",
"psr/http-message": "^1.0.1",
"defuse/php-encryption": "^2.2.1",
"laminas/laminas-diactoros": "^2.5.0"
},
"autoload": {
"psr-4": {
"OAuth2ServerExamples\\": "src/",
"League\\OAuth2\\Server\\": "../src/"
}
}
}
================================================
FILE: examples/public/api.php
================================================
<?php
declare(strict_types=1);
include __DIR__ . '/../vendor/autoload.php';
use League\OAuth2\Server\Middleware\ResourceServerMiddleware;
use League\OAuth2\Server\ResourceServer;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
$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 ResourceServerMiddleware(
$app->getContainer()->get(ResourceServer::class)
)
);
// An example endpoint secured with OAuth 2.0
$app->get(
'/users',
function (ServerRequestInterface $request, ResponseInterface $response) {
$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();
================================================
FILE: examples/public/auth_code.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
include __DIR__ . '/../vendor/autoload.php';
use Laminas\Diactoros\Stream;
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;
$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();
================================================
FILE: examples/public/client_credentials.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
include __DIR__ . '/../vendor/autoload.php';
use Laminas\Diactoros\Stream;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\ClientRepository;
use OAuth2ServerExamples\Repositories\ScopeRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
$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 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();
================================================
FILE: examples/public/device_code.php
================================================
<?php
/**
* @author Andrew Millington <andrew@noexceptions.io>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
include __DIR__ . '/../vendor/autoload.php';
use Laminas\Diactoros\Stream;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\DeviceCodeGrant;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\ClientRepository;
use OAuth2ServerExamples\Repositories\DeviceCodeRepository;
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
use OAuth2ServerExamples\Repositories\ScopeRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
$app = new App([
'settings' => [
'displayErrorDetails' => true,
],
AuthorizationServer::class => function () {
// Init our repositories
$clientRepository = new ClientRepository();
$scopeRepository = new ScopeRepository();
$accessTokenRepository = new AccessTokenRepository();
$refreshTokenRepository = new RefreshTokenRepository();
$deviceCodeRepository = new DeviceCodeRepository();
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
// Set up the authorization server
$server = new AuthorizationServer(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKeyPath,
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
);
// Enable the device code grant on the server with a token TTL of 1 hour
$server->enableGrantType(
new DeviceCodeGrant(
$deviceCodeRepository,
$refreshTokenRepository,
new DateInterval('PT10M'),
'http://foo/bar'
),
new DateInterval('PT1H')
);
return $server;
},
]);
$app->post('/device_authorization', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
/* @var \League\OAuth2\Server\AuthorizationServer $server */
$server = $app->getContainer()->get(AuthorizationServer::class);
try {
$deviceCodeResponse = $server->respondToDeviceAuthorizationRequest($request, $response);
return $deviceCodeResponse;
// Extract the device code. Usually we would then assign the user ID to
// the device code but for the purposes of this example, we've hard
// coded it in the response above.
// $deviceCode = json_decode((string) $deviceCodeResponse->getBody());
// Once the user has logged in and approved the request, set the user on the device code
// $server->completeDeviceAuthorizationRequest($deviceCode->user_code, 1);
} catch (OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
} catch (Exception $exception) {
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
return $response->withStatus(500)->withBody($body);
}
});
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
/* @var \League\OAuth2\Server\AuthorizationServer $server */
$server = $app->getContainer()->get(AuthorizationServer::class);
try {
return $server->respondToAccessTokenRequest($request, $response);
} catch (OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
} catch (Exception $exception) {
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
return $response->withStatus(500)->withBody($body);
}
});
$app->run();
================================================
FILE: examples/public/implicit.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
include __DIR__ . '/../vendor/autoload.php';
use Laminas\Diactoros\Stream;
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;
$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();
================================================
FILE: examples/public/middleware_use.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
include __DIR__ . '/../vendor/autoload.php';
use Laminas\Diactoros\Stream;
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;
$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 (): void {
})->add(new AuthorizationServerMiddleware($app->getContainer()->get(AuthorizationServer::class)));
// Secured API
$app->group('/api', function (): void {
$app->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();
================================================
FILE: examples/public/password.php
================================================
<?php
declare(strict_types=1);
include __DIR__ . '/../vendor/autoload.php';
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\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;
$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();
================================================
FILE: examples/public/refresh_token.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
include __DIR__ . '/../vendor/autoload.php';
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\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;
$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();
================================================
FILE: examples/src/Entities/AccessTokenEntity.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace OAuth2ServerExamples\Entities;
use League\OAuth2\Server\Entities\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;
use TokenEntityTrait;
use EntityTrait;
}
================================================
FILE: examples/src/Entities/AuthCodeEntity.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace OAuth2ServerExamples\Entities;
use League\OAuth2\Server\Entities\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;
use TokenEntityTrait;
use AuthCodeTrait;
}
================================================
FILE: examples/src/Entities/ClientEntity.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace OAuth2ServerExamples\Entities;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\Traits\ClientTrait;
use League\OAuth2\Server\Entities\Traits\EntityTrait;
class ClientEntity implements ClientEntityInterface
{
use EntityTrait;
use ClientTrait;
public function setName(string $name): void
{
$this->name = $name;
}
public function setRedirectUri(string $uri): void
{
$this->redirectUri = $uri;
}
public function setConfidential(): void
{
$this->isConfidential = true;
}
}
================================================
FILE: examples/src/Entities/DeviceCodeEntity.php
================================================
<?php
/**
* @author Andrew Millington <andrew@noexceptions.io>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace OAuth2ServerExamples\Entities;
use League\OAuth2\Server\Entities\DeviceCodeEntityInterface;
use League\OAuth2\Server\Entities\Traits\DeviceCodeTrait;
use League\OAuth2\Server\Entities\Traits\EntityTrait;
use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
class DeviceCodeEntity implements DeviceCodeEntityInterface
{
use EntityTrait;
use DeviceCodeTrait;
use TokenEntityTrait;
}
================================================
FILE: examples/src/Entities/RefreshTokenEntity.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace OAuth2ServerExamples\Entities;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use League\OAuth2\Server\Entities\Traits\EntityTrait;
use League\OAuth2\Server\Entities\Traits\RefreshTokenTrait;
class RefreshTokenEntity implements RefreshTokenEntityInterface
{
use RefreshTokenTrait;
use EntityTrait;
}
================================================
FILE: examples/src/Entities/ScopeEntity.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace OAuth2ServerExamples\Entities;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Entities\Traits\EntityTrait;
use League\OAuth2\Server\Entities\Traits\ScopeTrait;
class ScopeEntity implements ScopeEntityInterface
{
use EntityTrait;
use ScopeTrait;
}
================================================
FILE: examples/src/Entities/UserEntity.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace OAuth2ServerExamples\Entities;
use League\OAuth2\Server\Entities\UserEntityInterface;
class UserEntity implements UserEntityInterface
{
/**
* Return the user's identifier.
*/
public function getIdentifier(): string
{
return '1';
}
}
================================================
FILE: examples/src/Repositories/AccessTokenRepository.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace OAuth2ServerExamples\Repositories;
use 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): void
{
// Some logic here to save the access token to a database
}
/**
* {@inheritdoc}
*/
public function revokeAccessToken($tokenId): void
{
// Some logic here to revoke the access token
}
/**
* {@inheritdoc}
*/
public function isAccessTokenRevoked($tokenId): bool
{
return false; // Access token hasn't been revoked
}
/**
* {@inheritdoc}
*/
public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null): AccessTokenEntityInterface
{
$accessToken = new AccessTokenEntity();
$accessToken->setClient($clientEntity);
foreach ($scopes as $scope) {
$accessToken->addScope($scope);
}
if ($userIdentifier !== null) {
$accessToken->setUserIdentifier((string) $userIdentifier);
}
return $accessToken;
}
}
================================================
FILE: examples/src/Repositories/AuthCodeRepository.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace OAuth2ServerExamples\Repositories;
use 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): void
{
// Some logic to persist the auth code to a database
}
/**
* {@inheritdoc}
*/
public function revokeAuthCode($codeId): void
{
// Some logic to revoke the auth code in a database
}
/**
* {@inheritdoc}
*/
public function isAuthCodeRevoked($codeId): bool
{
return false; // The auth code has not been revoked
}
/**
* {@inheritdoc}
*/
public function getNewAuthCode(): AuthCodeEntityInterface
{
return new AuthCodeEntity();
}
}
================================================
FILE: examples/src/Repositories/ClientRepository.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace OAuth2ServerExamples\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use OAuth2ServerExamples\Entities\ClientEntity;
use function array_key_exists;
use function password_hash;
use function password_verify;
class ClientRepository implements ClientRepositoryInterface
{
private const CLIENT_NAME = 'My Awesome App';
private const REDIRECT_URI = 'http://foo/bar';
/**
* {@inheritdoc}
*/
public function getClientEntity(string $clientIdentifier): ?ClientEntityInterface
{
$client = new ClientEntity();
$client->setIdentifier($clientIdentifier);
$client->setName(self::CLIENT_NAME);
$client->setRedirectUri(self::REDIRECT_URI);
$client->setConfidential();
return $client;
}
/**
* {@inheritdoc}
*/
public function validateClient($clientIdentifier, $clientSecret, $grantType): bool
{
$clients = [
'myawesomeapp' => [
'secret' => password_hash('abc123', PASSWORD_BCRYPT),
'name' => self::CLIENT_NAME,
'redirect_uri' => self::REDIRECT_URI,
'is_confidential' => true,
],
];
// Check if client is registered
if (array_key_exists($clientIdentifier, $clients) === false) {
return false;
}
if (password_verify($clientSecret, $clients[$clientIdentifier]['secret']) === false) {
return false;
}
return true;
}
}
================================================
FILE: examples/src/Repositories/DeviceCodeRepository.php
================================================
<?php
/**
* @author Andrew Millington <andrew@noexceptions.io>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace OAuth2ServerExamples\Repositories;
use DateTimeImmutable;
use League\OAuth2\Server\Entities\DeviceCodeEntityInterface;
use League\OAuth2\Server\Repositories\DeviceCodeRepositoryInterface;
use OAuth2ServerExamples\Entities\ClientEntity;
use OAuth2ServerExamples\Entities\DeviceCodeEntity;
use OAuth2ServerExamples\Entities\ScopeEntity;
class DeviceCodeRepository implements DeviceCodeRepositoryInterface
{
/**
* {@inheritdoc}
*/
public function getNewDeviceCode(): DeviceCodeEntityInterface
{
return new DeviceCodeEntity();
}
/**
* {@inheritdoc}
*/
public function persistDeviceCode(DeviceCodeEntityInterface $deviceCodeEntity): void
{
// Some logic to persist a new device code to a database
}
/**
* {@inheritdoc}
*/
public function getDeviceCodeEntityByDeviceCode($deviceCode): ?DeviceCodeEntityInterface
{
$clientEntity = new ClientEntity();
$clientEntity->setIdentifier('myawesomeapp');
$deviceCodeEntity = new DeviceCodeEntity();
$deviceCodeEntity->setIdentifier($deviceCode);
$deviceCodeEntity->setExpiryDateTime(new DateTimeImmutable('now +1 hour'));
$deviceCodeEntity->setClient($clientEntity);
$deviceCodeEntity->setLastPolledAt(new DateTimeImmutable());
$scopes = [];
foreach ($scopes as $scope) {
$scopeEntity = new ScopeEntity();
$scopeEntity->setIdentifier($scope);
$deviceCodeEntity->addScope($scopeEntity);
}
// The user identifier should be set when the user authenticates on the
// OAuth server, along with whether they approved the request
$deviceCodeEntity->setUserApproved(true);
$deviceCodeEntity->setUserIdentifier('1');
return $deviceCodeEntity;
}
/**
* {@inheritdoc}
*/
public function revokeDeviceCode($codeId): void
{
// Some logic to revoke device code
}
/**
* {@inheritdoc}
*/
public function isDeviceCodeRevoked($codeId): bool
{
return false;
}
}
================================================
FILE: examples/src/Repositories/RefreshTokenRepository.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace OAuth2ServerExamples\Repositories;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use OAuth2ServerExamples\Entities\RefreshTokenEntity;
class RefreshTokenRepository implements RefreshTokenRepositoryInterface
{
/**
* {@inheritdoc}
*/
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity): void
{
// Some logic to persist the refresh token in a database
}
/**
* {@inheritdoc}
*/
public function revokeRefreshToken($tokenId): void
{
// Some logic to revoke the refresh token in a database
}
/**
* {@inheritdoc}
*/
public function isRefreshTokenRevoked($tokenId): bool
{
return false; // The refresh token has not been revoked
}
/**
* {@inheritdoc}
*/
public function getNewRefreshToken(): ?RefreshTokenEntityInterface
{
return new RefreshTokenEntity();
}
}
================================================
FILE: examples/src/Repositories/ScopeRepository.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace OAuth2ServerExamples\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use OAuth2ServerExamples\Entities\ScopeEntity;
use function array_key_exists;
class ScopeRepository implements ScopeRepositoryInterface
{
/**
* {@inheritdoc}
*/
public function getScopeEntityByIdentifier(string $identifier): ?ScopeEntityInterface
{
$scopes = [
'basic' => [
'description' => 'Basic details about you',
],
'email' => [
'description' => 'Your email address',
],
];
if (array_key_exists($identifier, $scopes) === false) {
return null;
}
$scope = new ScopeEntity();
$scope->setIdentifier($identifier);
return $scope;
}
/**
* {@inheritdoc}
*/
public function finalizeScopes(
array $scopes,
$grantType,
ClientEntityInterface $clientEntity,
$userIdentifier = null,
$authCodeId = null
): array {
// Example of programmatically modifying the final scope of the access token
if ((int) $userIdentifier === 1) {
$scope = new ScopeEntity();
$scope->setIdentifier('email');
$scopes[] = $scope;
}
return $scopes;
}
}
================================================
FILE: examples/src/Repositories/UserRepository.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace OAuth2ServerExamples\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\UserEntityInterface;
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
use OAuth2ServerExamples\Entities\UserEntity;
class UserRepository implements UserRepositoryInterface
{
/**
* {@inheritdoc}
*/
public function getUserEntityByUserCredentials(
$username,
$password,
$grantType,
ClientEntityInterface $clientEntity
): ?UserEntityInterface {
if ($username === 'alex' && $password === 'whisky') {
return new UserEntity();
}
return null;
}
}
================================================
FILE: phpcs.xml.dist
================================================
<?xml version="1.0"?>
<ruleset
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd"
>
<arg name="basepath" value="." />
<arg name="extensions" value="php" />
<arg name="parallel" value="80" />
<arg name="colors" />
<arg name="cache" value=".phpcs.cache" />
<arg value="p" />
<file>src</file>
<file>tests</file>
<file>examples</file>
<exclude-pattern>examples/vendor/*</exclude-pattern>
<rule ref="PSR12">
<exclude name="Generic.Files.LineLength.TooLong" />
</rule>
<config name="installed_paths" value="../../slevomat/coding-standard" />
<rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes">
<properties>
<property name="spacesCountAroundEqualsSign" type="int" value="0" />
</properties>
</rule>
<rule ref="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly" />
<rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses" />
<rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint" />
<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint" />
<rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint" />
<rule ref="SlevomatCodingStandard.Commenting.EmptyComment" />
<rule ref="SlevomatCodingStandard.Classes.RequireConstructorPropertyPromotion" />
<rule ref="SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue" />
</ruleset>
================================================
FILE: phpstan.neon.dist
================================================
parameters:
level: 8
paths:
- src
- tests
ignoreErrors:
-
message: '#Deprecated since v5.5, please use {@see self::withValidationConstraints\(\)} instead#'
reportUnmatched: false
================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
failOnRisky="true"
failOnWarning="true"
stopOnError="true"
stopOnFailure="true"
>
<source>
<include>
<directory>src</directory>
</include>
</source>
<testsuites>
<testsuite name="Tests">
<directory>./tests/</directory>
</testsuite>
</testsuites>
</phpunit>
================================================
FILE: src/AuthorizationServer.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server;
use DateInterval;
use Defuse\Crypto\Key;
use League\OAuth2\Server\EventEmitting\EmitterAwareInterface;
use League\OAuth2\Server\EventEmitting\EmitterAwarePolyfill;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\GrantTypeInterface;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use League\OAuth2\Server\RequestTypes\AuthorizationRequestInterface;
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 SensitiveParameter;
class AuthorizationServer implements EmitterAwareInterface
{
use EmitterAwarePolyfill;
/**
* @var GrantTypeInterface[]
*/
protected array $enabledGrantTypes = [];
/**
* @var DateInterval[]
*/
protected array $grantTypeAccessTokenTTL = [];
protected CryptKeyInterface $privateKey;
protected CryptKeyInterface $publicKey;
protected ResponseTypeInterface $responseType;
private string|Key $encryptionKey;
private string $defaultScope = '';
private bool $revokeRefreshTokens = true;
/**
* New server instance
*/
public function __construct(
private ClientRepositoryInterface $clientRepository,
private AccessTokenRepositoryInterface $accessTokenRepository,
private ScopeRepositoryInterface $scopeRepository,
#[SensitiveParameter]
CryptKeyInterface|string $privateKey,
#[SensitiveParameter]
Key|string $encryptionKey,
ResponseTypeInterface|null $responseType = null
) {
if ($privateKey instanceof CryptKeyInterface === false) {
$privateKey = new CryptKey($privateKey);
}
$this->privateKey = $privateKey;
$this->encryptionKey = $encryptionKey;
if ($responseType === null) {
$responseType = new BearerTokenResponse();
} else {
$responseType = clone $responseType;
}
$this->responseType = $responseType;
}
/**
* Enable a grant type on the server
*/
public function enableGrantType(GrantTypeInterface $grantType, DateInterval|null $accessTokenTTL = null): void
{
if ($accessTokenTTL === null) {
$accessTokenTTL = new DateInterval('PT1H');
}
$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);
$grantType->revokeRefreshTokens($this->revokeRefreshTokens);
$this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType;
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] = $accessTokenTTL;
}
/**
* Validate an authorization request
*
* @throws OAuthServerException
*/
public function validateAuthorizationRequest(ServerRequestInterface $request): AuthorizationRequestInterface
{
foreach ($this->enabledGrantTypes as $grantType) {
if ($grantType->canRespondToAuthorizationRequest($request)) {
return $grantType->validateAuthorizationRequest($request);
}
}
throw OAuthServerException::unsupportedGrantType();
}
/**
* Complete an authorization request
*/
public function completeAuthorizationRequest(
AuthorizationRequestInterface $authRequest,
ResponseInterface $response
): ResponseInterface {
return $this->enabledGrantTypes[$authRequest->getGrantTypeId()]
->completeAuthorizationRequest($authRequest)
->generateHttpResponse($response);
}
/**
* Respond to device authorization request
*
* @throws OAuthServerException
*/
public function respondToDeviceAuthorizationRequest(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
foreach ($this->enabledGrantTypes as $grantType) {
if ($grantType->canRespondToDeviceAuthorizationRequest($request)) {
return $grantType
->respondToDeviceAuthorizationRequest($request)
->generateHttpResponse($response);
}
}
throw OAuthServerException::unsupportedGrantType();
}
/**
* Complete a device authorization request
*/
public function completeDeviceAuthorizationRequest(string $deviceCode, string $userId, bool $userApproved): void
{
$this->enabledGrantTypes['urn:ietf:params:oauth:grant-type:device_code']
->completeDeviceAuthorizationRequest($deviceCode, $userId, $userApproved);
}
/**
* Return an access token response.
*
* @throws OAuthServerException
*/
public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
foreach ($this->enabledGrantTypes as $grantType) {
if (!$grantType->canRespondToAccessTokenRequest($request)) {
continue;
}
$tokenResponse = $grantType->respondToAccessTokenRequest(
$request,
$this->getResponseType(),
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()]
);
return $tokenResponse->generateHttpResponse($response);
}
throw OAuthServerException::unsupportedGrantType();
}
/**
* Get the token type that grants will return in the HTTP response.
*/
protected function getResponseType(): ResponseTypeInterface
{
$responseType = clone $this->responseType;
if ($responseType instanceof AbstractResponseType) {
$responseType->setPrivateKey($this->privateKey);
}
$responseType->setEncryptionKey($this->encryptionKey);
return $responseType;
}
/**
* Set the default scope for the authorization server.
*/
public function setDefaultScope(string $defaultScope): void
{
$this->defaultScope = $defaultScope;
}
/**
* Sets whether to revoke refresh tokens or not (for all grant types).
*/
public function revokeRefreshTokens(bool $revokeRefreshTokens): void
{
$this->revokeRefreshTokens = $revokeRefreshTokens;
}
}
================================================
FILE: src/AuthorizationValidators/AuthorizationValidatorInterface.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\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.
*/
public function validateAuthorization(ServerRequestInterface $request): ServerRequestInterface;
}
================================================
FILE: src/AuthorizationValidators/BearerTokenValidator.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\AuthorizationValidators;
use DateInterval;
use DateTimeZone;
use Lcobucci\Clock\SystemClock;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Exception;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
use League\OAuth2\Server\CryptKeyInterface;
use League\OAuth2\Server\CryptTrait;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use Psr\Http\Message\ServerRequestInterface;
use RuntimeException;
use function date_default_timezone_get;
use function preg_replace;
use function trim;
class BearerTokenValidator implements AuthorizationValidatorInterface
{
use CryptTrait;
protected CryptKeyInterface $publicKey;
private Configuration $jwtConfiguration;
public function __construct(private AccessTokenRepositoryInterface $accessTokenRepository, private ?DateInterval $jwtValidAtDateLeeway = null)
{
}
/**
* Set the public key
*/
public function setPublicKey(CryptKeyInterface $key): void
{
$this->publicKey = $key;
$this->initJwtConfiguration();
}
/**
* Initialise the JWT configuration.
*/
private function initJwtConfiguration(): void
{
$this->jwtConfiguration = Configuration::forSymmetricSigner(
new Sha256(),
InMemory::plainText('empty', 'empty')
);
$clock = new SystemClock(new DateTimeZone(date_default_timezone_get()));
$publicKeyContents = $this->publicKey->getKeyContents();
if ($publicKeyContents === '') {
throw new RuntimeException('Public key is empty');
}
// TODO: next major release: replace deprecated method and remove phpstan ignored error
$this->jwtConfiguration->setValidationConstraints(
new LooseValidAt($clock, $this->jwtValidAtDateLeeway),
new SignedWith(
new Sha256(),
InMemory::plainText($publicKeyContents, $this->publicKey->getPassPhrase() ?? '')
)
);
}
/**
* {@inheritdoc}
*/
public function validateAuthorization(ServerRequestInterface $request): ServerRequestInterface
{
if ($request->hasHeader('authorization') === false) {
throw OAuthServerException::accessDenied('Missing "Authorization" header');
}
$header = $request->getHeader('authorization');
$jwt = trim((string) preg_replace('/^\s*Bearer\s/i', '', $header[0]));
if ($jwt === '') {
throw OAuthServerException::accessDenied('Missing "Bearer" token');
}
try {
// Attempt to parse the JWT
$token = $this->jwtConfiguration->parser()->parse($jwt);
} catch (Exception $exception) {
throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception);
}
try {
// Attempt to validate the JWT
$constraints = $this->jwtConfiguration->validationConstraints();
$this->jwtConfiguration->validator()->assert($token, ...$constraints);
} catch (RequiredConstraintsViolated $exception) {
throw OAuthServerException::accessDenied('Access token could not be verified', null, $exception);
}
if (!$token instanceof UnencryptedToken) {
throw OAuthServerException::accessDenied('Access token is not an instance of UnencryptedToken');
}
$claims = $token->claims();
// Check if token has been revoked
if ($this->accessTokenRepository->isAccessTokenRevoked($claims->get('jti'))) {
throw OAuthServerException::accessDenied('Access token has been revoked');
}
// Return the request with additional attributes
return $request
->withAttribute('oauth_access_token_id', $claims->get('jti'))
->withAttribute('oauth_client_id', $claims->get('aud')[0])
->withAttribute('oauth_user_id', $claims->get('sub'))
->withAttribute('oauth_scopes', $claims->get('scopes'));
}
}
================================================
FILE: src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php
================================================
<?php
/**
* @author Lukáš Unger <lookymsc@gmail.com>
* @copyright Copyright (c) Lukáš Unger
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\CodeChallengeVerifiers;
interface CodeChallengeVerifierInterface
{
/**
* Return code challenge method.
*/
public function getMethod(): string;
/**
* Verify the code challenge.
*/
public function verifyCodeChallenge(string $codeVerifier, string $codeChallenge): bool;
}
================================================
FILE: src/CodeChallengeVerifiers/PlainVerifier.php
================================================
<?php
/**
* @author Lukáš Unger <lookymsc@gmail.com>
* @copyright Copyright (c) Lukáš Unger
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\CodeChallengeVerifiers;
use function hash_equals;
class PlainVerifier implements CodeChallengeVerifierInterface
{
/**
* Return code challenge method.
*/
public function getMethod(): string
{
return 'plain';
}
/**
* Verify the code challenge.
*/
public function verifyCodeChallenge(string $codeVerifier, string $codeChallenge): bool
{
return hash_equals($codeVerifier, $codeChallenge);
}
}
================================================
FILE: src/CodeChallengeVerifiers/S256Verifier.php
================================================
<?php
/**
* @author Lukáš Unger <lookymsc@gmail.com>
* @copyright Copyright (c) Lukáš Unger
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\CodeChallengeVerifiers;
use function base64_encode;
use function hash;
use function hash_equals;
use function rtrim;
use function strtr;
class S256Verifier implements CodeChallengeVerifierInterface
{
/**
* Return code challenge method.
*/
public function getMethod(): string
{
return 'S256';
}
/**
* Verify the code challenge.
*/
public function verifyCodeChallenge(string $codeVerifier, string $codeChallenge): bool
{
return hash_equals(
strtr(rtrim(base64_encode(hash('sha256', $codeVerifier, true)), '='), '+/', '-_'),
$codeChallenge
);
}
}
================================================
FILE: src/CryptKey.php
================================================
<?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
*/
declare(strict_types=1);
namespace League\OAuth2\Server;
use LogicException;
use OpenSSLAsymmetricKey;
use SensitiveParameter;
use function decoct;
use function file_get_contents;
use function fileperms;
use function in_array;
use function is_file;
use function is_readable;
use function openssl_pkey_get_details;
use function openssl_pkey_get_private;
use function openssl_pkey_get_public;
use function sprintf;
use function trigger_error;
class CryptKey implements CryptKeyInterface
{
private const FILE_PREFIX = 'file://';
/**
* @var string Key contents
*/
protected string $keyContents;
protected string $keyPath;
public function __construct(
string $keyPath,
#[SensitiveParameter]
protected ?string $passPhrase = null,
bool $keyPermissionsCheck = true
) {
if (str_starts_with($keyPath, self::FILE_PREFIX) === false && $this->isValidKey($keyPath, $this->passPhrase ?? '')) {
$this->keyContents = $keyPath;
$this->keyPath = '';
// There's no file, so no need for permission check.
$keyPermissionsCheck = false;
} elseif (is_file($keyPath)) {
if (str_starts_with($keyPath, self::FILE_PREFIX) === false) {
$keyPath = self::FILE_PREFIX . $keyPath;
}
if (!is_readable($keyPath)) {
throw new LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
}
$keyContents = file_get_contents($keyPath);
if ($keyContents === false) {
throw new LogicException('Unable to read key from file ' . $keyPath);
}
$this->keyContents = $keyContents;
$this->keyPath = $keyPath;
if (!$this->isValidKey($this->keyContents, $this->passPhrase ?? '')) {
throw new LogicException('Unable to read key from file ' . $keyPath);
}
} else {
throw new LogicException('Invalid key supplied');
}
if ($keyPermissionsCheck === true && PHP_OS_FAMILY !== 'Windows') {
// Verify the permissions of the key
$keyPathPerms = decoct(fileperms($this->keyPath) & 0777);
if (in_array($keyPathPerms, ['400', '440', '600', '640', '660'], true) === false) {
trigger_error(
sprintf(
'Key file "%s" permissions are not correct, recommend changing to 600 or 660 instead of %s',
$this->keyPath,
$keyPathPerms
),
E_USER_NOTICE
);
}
}
}
/**
* {@inheritdoc}
*/
public function getKeyContents(): string
{
return $this->keyContents;
}
/**
* Validate key contents.
*/
private function isValidKey(
#[SensitiveParameter]
string $contents,
#[SensitiveParameter]
string $passPhrase
): bool {
$privateKey = openssl_pkey_get_private($contents, $passPhrase);
$key = $privateKey instanceof OpenSSLAsymmetricKey ? $privateKey : openssl_pkey_get_public($contents);
if ($key === false) {
return false;
}
$details = openssl_pkey_get_details($key);
return $details !== false && in_array(
$details['type'] ?? -1,
[OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_EC],
true
);
}
/**
* {@inheritdoc}
*/
public function getKeyPath(): string
{
return $this->keyPath;
}
/**
* {@inheritdoc}
*/
public function getPassPhrase(): ?string
{
return $this->passPhrase;
}
}
================================================
FILE: src/CryptKeyInterface.php
================================================
<?php
declare(strict_types=1);
namespace League\OAuth2\Server;
interface CryptKeyInterface
{
/**
* Retrieve key path.
*/
public function getKeyPath(): string;
/**
* Retrieve key pass phrase.
*/
public function getPassPhrase(): ?string;
/**
* Get key contents
*
* @return string Key contents
*/
public function getKeyContents(): string;
}
================================================
FILE: src/CryptTrait.php
================================================
<?php
/**
* Encrypt/decrypt with encryptionKey.
*
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server;
use Defuse\Crypto\Crypto;
use Defuse\Crypto\Exception\EnvironmentIsBrokenException;
use Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException;
use Defuse\Crypto\Key;
use Exception;
use InvalidArgumentException;
use LogicException;
use SensitiveParameter;
use function is_string;
trait CryptTrait
{
protected string|Key|null $encryptionKey = null;
/**
* Encrypt data with encryptionKey.
*
* @throws LogicException
*/
protected function encrypt(string $unencryptedData): string
{
try {
if ($this->encryptionKey instanceof Key) {
return Crypto::encrypt($unencryptedData, $this->encryptionKey);
}
if (is_string($this->encryptionKey)) {
return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey);
}
throw new LogicException('Encryption key not set when attempting to encrypt');
} catch (Exception $e) {
throw new LogicException($e->getMessage(), 0, $e);
}
}
/**
* Decrypt data with encryptionKey.
*
* @throws LogicException
*/
protected function decrypt(string $encryptedData): string
{
try {
if ($this->encryptionKey instanceof Key) {
return Crypto::decrypt($encryptedData, $this->encryptionKey);
}
if (is_string($this->encryptionKey)) {
return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey);
}
throw new LogicException('Encryption key not set when attempting to decrypt');
} catch (WrongKeyOrModifiedCiphertextException $e) {
$exceptionMessage = 'The authcode or decryption key/password used '
. 'is not correct';
throw new InvalidArgumentException($exceptionMessage, 0, $e);
} catch (EnvironmentIsBrokenException $e) {
$exceptionMessage = 'Auth code decryption failed. This is likely '
. 'due to an environment issue or runtime bug in the '
. 'decryption library';
throw new LogicException($exceptionMessage, 0, $e);
} catch (Exception $e) {
throw new LogicException($e->getMessage(), 0, $e);
}
}
public function setEncryptionKey(
#[SensitiveParameter]
Key|string|null $key = null
): void {
$this->encryptionKey = $key;
}
}
================================================
FILE: src/Entities/AccessTokenEntityInterface.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities;
use League\OAuth2\Server\CryptKeyInterface;
interface AccessTokenEntityInterface extends TokenInterface
{
/**
* Set a private key used to encrypt the access token.
*/
public function setPrivateKey(CryptKeyInterface $privateKey): void;
/**
* Generate a string representation of the access token.
*/
public function toString(): string;
}
================================================
FILE: src/Entities/AuthCodeEntityInterface.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities;
interface AuthCodeEntityInterface extends TokenInterface
{
public function getRedirectUri(): string|null;
public function setRedirectUri(string $uri): void;
}
================================================
FILE: src/Entities/ClientEntityInterface.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities;
interface ClientEntityInterface
{
/**
* Get the client's identifier.
*
* @return non-empty-string
*/
public function getIdentifier(): string;
/**
* Get the client's name.
*/
public function getName(): string;
/**
* Returns the registered redirect URI (as a string). Alternatively return
* an indexed array of redirect URIs.
*
* @return string|string[]
*/
public function getRedirectUri(): string|array;
/**
* Returns true if the client is confidential.
*/
public function isConfidential(): bool;
/*
* Returns true if the client supports the given grant type.
*
* TODO: To be added in a future major release.
*/
// public function supportsGrantType(string $grantType): bool;
}
================================================
FILE: src/Entities/DeviceCodeEntityInterface.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities;
use DateTimeImmutable;
interface DeviceCodeEntityInterface extends TokenInterface
{
public function getUserCode(): string;
public function setUserCode(string $userCode): void;
public function getVerificationUri(): string;
public function setVerificationUri(string $verificationUri): void;
public function getVerificationUriComplete(): string;
public function getLastPolledAt(): ?DateTimeImmutable;
public function setLastPolledAt(DateTimeImmutable $lastPolledAt): void;
public function getInterval(): int;
public function setInterval(int $interval): void;
public function getUserApproved(): bool;
public function setUserApproved(bool $userApproved): void;
}
================================================
FILE: src/Entities/RefreshTokenEntityInterface.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities;
use DateTimeImmutable;
interface RefreshTokenEntityInterface
{
/**
* Get the token's identifier.
*
* @return non-empty-string
*/
public function getIdentifier(): string;
/**
* Set the token's identifier.
*
* @param non-empty-string $identifier
*/
public function setIdentifier(string $identifier): void;
/**
* Get the token's expiry date time.
*/
public function getExpiryDateTime(): DateTimeImmutable;
/**
* Set the date time when the token expires.
*/
public function setExpiryDateTime(DateTimeImmutable $dateTime): void;
/**
* Set the access token that the refresh token was associated with.
*/
public function setAccessToken(AccessTokenEntityInterface $accessToken): void;
/**
* Get the access token that the refresh token was originally associated with.
*/
public function getAccessToken(): AccessTokenEntityInterface;
}
================================================
FILE: src/Entities/ScopeEntityInterface.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities;
use JsonSerializable;
interface ScopeEntityInterface extends JsonSerializable
{
/**
* Get the scope's identifier.
*
* @return non-empty-string
*/
public function getIdentifier(): string;
}
================================================
FILE: src/Entities/TokenInterface.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities;
use DateTimeImmutable;
interface TokenInterface
{
/**
* Get the token's identifier.
*
* @return non-empty-string
*/
public function getIdentifier(): string;
/**
* Set the token's identifier.
*
* @param non-empty-string $identifier
*/
public function setIdentifier(string $identifier): void;
/**
* Get the token's expiry date time.
*/
public function getExpiryDateTime(): DateTimeImmutable;
/**
* Set the date time when the token expires.
*/
public function setExpiryDateTime(DateTimeImmutable $dateTime): void;
/**
* Set the identifier of the user associated with the token.
*
* @param non-empty-string $identifier
*/
public function setUserIdentifier(string $identifier): void;
/**
* Get the token user's identifier.
*
* @return non-empty-string|null
*/
public function getUserIdentifier(): string|null;
/**
* Get the client that the token was issued to.
*/
public function getClient(): ClientEntityInterface;
/**
* Set the client that the token was issued to.
*/
public function setClient(ClientEntityInterface $client): void;
/**
* Associate a scope with the token.
*/
public function addScope(ScopeEntityInterface $scope): void;
/**
* Return an array of scopes associated with the token.
*
* @return ScopeEntityInterface[]
*/
public function getScopes(): array;
}
================================================
FILE: src/Entities/Traits/AccessTokenTrait.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities\Traits;
use DateTimeImmutable;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use Lcobucci\JWT\Token;
use League\OAuth2\Server\CryptKeyInterface;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use RuntimeException;
use SensitiveParameter;
trait AccessTokenTrait
{
private CryptKeyInterface $privateKey;
private Configuration $jwtConfiguration;
/**
* Set the private key used to encrypt this access token.
*/
public function setPrivateKey(
#[SensitiveParameter]
CryptKeyInterface $privateKey
): void {
$this->privateKey = $privateKey;
}
/**
* Initialise the JWT Configuration.
*/
public function initJwtConfiguration(): void
{
$privateKeyContents = $this->privateKey->getKeyContents();
if ($privateKeyContents === '') {
throw new RuntimeException('Private key is empty');
}
$this->jwtConfiguration = Configuration::forAsymmetricSigner(
new Sha256(),
InMemory::plainText($privateKeyContents, $this->privateKey->getPassPhrase() ?? ''),
InMemory::plainText('empty', 'empty')
);
}
/**
* Generate a JWT from the access token
*/
private function convertToJWT(): Token
{
$this->initJwtConfiguration();
return $this->jwtConfiguration->builder()
->permittedFor($this->getClient()->getIdentifier())
->identifiedBy($this->getIdentifier())
->issuedAt(new DateTimeImmutable())
->canOnlyBeUsedAfter(new DateTimeImmutable())
->expiresAt($this->getExpiryDateTime())
->relatedTo($this->getSubjectIdentifier())
->withClaim('scopes', $this->getScopes())
->getToken($this->jwtConfiguration->signer(), $this->jwtConfiguration->signingKey());
}
/**
* Generate a string representation from the access token
*/
public function toString(): string
{
return $this->convertToJWT()->toString();
}
abstract public function getClient(): ClientEntityInterface;
abstract public function getExpiryDateTime(): DateTimeImmutable;
/**
* @return non-empty-string|null
*/
abstract public function getUserIdentifier(): string|null;
/**
* @return ScopeEntityInterface[]
*/
abstract public function getScopes(): array;
/**
* @return non-empty-string
*/
abstract public function getIdentifier(): string;
/**
* @return non-empty-string
*/
private function getSubjectIdentifier(): string
{
return $this->getUserIdentifier() ?? $this->getClient()->getIdentifier();
}
}
================================================
FILE: src/Entities/Traits/AuthCodeTrait.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities\Traits;
trait AuthCodeTrait
{
protected ?string $redirectUri = null;
public function getRedirectUri(): string|null
{
return $this->redirectUri;
}
public function setRedirectUri(string $uri): void
{
$this->redirectUri = $uri;
}
}
================================================
FILE: src/Entities/Traits/ClientTrait.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities\Traits;
trait ClientTrait
{
protected string $name;
/**
* @var string|string[]
*/
protected string|array $redirectUri;
protected bool $isConfidential = false;
/**
* Get the client's name.
*
*
* @codeCoverageIgnore
*/
public function getName(): string
{
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(): string|array
{
return $this->redirectUri;
}
/**
* Returns true if the client is confidential.
*/
public function isConfidential(): bool
{
return $this->isConfidential;
}
/**
* Returns true if the client supports the given grant type.
*/
public function supportsGrantType(string $grantType): bool
{
return true;
}
}
================================================
FILE: src/Entities/Traits/DeviceCodeTrait.php
================================================
<?php
/**
* @author Andrew Millington <andrew@noexceptions.io>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities\Traits;
use DateTimeImmutable;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
trait DeviceCodeTrait
{
private bool $userApproved = false;
private bool $includeVerificationUriComplete = false;
private int $interval = 5;
private string $userCode;
private string $verificationUri;
private ?DateTimeImmutable $lastPolledAt = null;
public function getUserCode(): string
{
return $this->userCode;
}
public function setUserCode(string $userCode): void
{
$this->userCode = $userCode;
}
public function getVerificationUri(): string
{
return $this->verificationUri;
}
public function setVerificationUri(string $verificationUri): void
{
$this->verificationUri = $verificationUri;
}
public function getVerificationUriComplete(): string
{
return $this->verificationUri . '?user_code=' . $this->userCode;
}
abstract public function getClient(): ClientEntityInterface;
abstract public function getExpiryDateTime(): DateTimeImmutable;
/**
* @return ScopeEntityInterface[]
*/
abstract public function getScopes(): array;
/**
* @return non-empty-string
*/
abstract public function getIdentifier(): string;
public function getLastPolledAt(): ?DateTimeImmutable
{
return $this->lastPolledAt;
}
public function setLastPolledAt(DateTimeImmutable $lastPolledAt): void
{
$this->lastPolledAt = $lastPolledAt;
}
public function getInterval(): int
{
return $this->interval;
}
public function setInterval(int $interval): void
{
$this->interval = $interval;
}
public function getUserApproved(): bool
{
return $this->userApproved;
}
public function setUserApproved(bool $userApproved): void
{
$this->userApproved = $userApproved;
}
public function getVerificationUriCompleteInAuthResponse(): bool
{
return $this->includeVerificationUriComplete;
}
public function setVerificationUriCompleteInAuthResponse(bool $includeVerificationUriComplete): void
{
$this->includeVerificationUriComplete = $includeVerificationUriComplete;
}
}
================================================
FILE: src/Entities/Traits/EntityTrait.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities\Traits;
trait EntityTrait
{
/**
* @var non-empty-string
*/
protected string $identifier;
/**
* @return non-empty-string
*/
public function getIdentifier(): string
{
return $this->identifier;
}
/**
* @param non-empty-string $identifier
*/
public function setIdentifier(string $identifier): void
{
$this->identifier = $identifier;
}
}
================================================
FILE: src/Entities/Traits/RefreshTokenTrait.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities\Traits;
use DateTimeImmutable;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
trait RefreshTokenTrait
{
protected AccessTokenEntityInterface $accessToken;
protected DateTimeImmutable $expiryDateTime;
/**
* {@inheritdoc}
*/
public function setAccessToken(AccessTokenEntityInterface $accessToken): void
{
$this->accessToken = $accessToken;
}
/**
* {@inheritdoc}
*/
public function getAccessToken(): AccessTokenEntityInterface
{
return $this->accessToken;
}
/**
* Get the token's expiry date time.
*/
public function getExpiryDateTime(): DateTimeImmutable
{
return $this->expiryDateTime;
}
/**
* Set the date time when the token expires.
*/
public function setExpiryDateTime(DateTimeImmutable $dateTime): void
{
$this->expiryDateTime = $dateTime;
}
}
================================================
FILE: src/Entities/Traits/ScopeTrait.php
================================================
<?php
/**
* @author Andrew Millington <andrew@noexceptions.io>
* @copyright Copyright (c) Andrew Millington
* @license http://mit-license.org
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities\Traits;
trait ScopeTrait
{
/**
* Serialize the object to the scopes string identifier when using json_encode().
*/
public function jsonSerialize(): string
{
return $this->getIdentifier();
}
abstract public function getIdentifier(): string;
}
================================================
FILE: src/Entities/Traits/TokenEntityTrait.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities\Traits;
use DateTimeImmutable;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use function array_values;
trait TokenEntityTrait
{
/**
* @var ScopeEntityInterface[]
*/
protected array $scopes = [];
protected DateTimeImmutable $expiryDateTime;
/**
* @var non-empty-string|null
*/
protected string|null $userIdentifier = null;
protected ClientEntityInterface $client;
/**
* Associate a scope with the token.
*/
public function addScope(ScopeEntityInterface $scope): void
{
$this->scopes[$scope->getIdentifier()] = $scope;
}
/**
* Return an array of scopes associated with the token.
*
* @return ScopeEntityInterface[]
*/
public function getScopes(): array
{
return array_values($this->scopes);
}
/**
* Get the token's expiry date time.
*/
public function getExpiryDateTime(): DateTimeImmutable
{
return $this->expiryDateTime;
}
/**
* Set the date time when the token expires.
*/
public function setExpiryDateTime(DateTimeImmutable $dateTime): void
{
$this->expiryDateTime = $dateTime;
}
/**
* Set the identifier of the user associated with the token.
*
* @param non-empty-string $identifier The identifier of the user
*/
public function setUserIdentifier(string $identifier): void
{
$this->userIdentifier = $identifier;
}
/**
* Get the token user's identifier.
*
* @return non-empty-string|null
*/
public function getUserIdentifier(): string|null
{
return $this->userIdentifier;
}
/**
* Get the client that the token was issued to.
*/
public function getClient(): ClientEntityInterface
{
return $this->client;
}
/**
* Set the client that the token was issued to.
*/
public function setClient(ClientEntityInterface $client): void
{
$this->client = $client;
}
}
================================================
FILE: src/Entities/UserEntityInterface.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Entities;
interface UserEntityInterface
{
/**
* Return the user's identifier.
*
* @return non-empty-string
*/
public function getIdentifier(): string;
}
================================================
FILE: src/EventEmitting/AbstractEvent.php
================================================
<?php
declare(strict_types=1);
namespace League\OAuth2\Server\EventEmitting;
use League\Event\HasEventName;
use Psr\EventDispatcher\StoppableEventInterface;
class AbstractEvent implements StoppableEventInterface, HasEventName
{
private bool $propagationStopped = false;
public function __construct(private string $name)
{
}
public function eventName(): string
{
return $this->name;
}
/**
* Backwards compatibility method
*
* @deprecated use eventName instead
*/
public function getName(): string
{
return $this->name;
}
public function isPropagationStopped(): bool
{
return $this->propagationStopped;
}
public function stopPropagation(): self
{
$this->propagationStopped = true;
return $this;
}
}
================================================
FILE: src/EventEmitting/EmitterAwareInterface.php
================================================
<?php
declare(strict_types=1);
namespace League\OAuth2\Server\EventEmitting;
interface EmitterAwareInterface
{
public function getEmitter(): EventEmitter;
public function setEmitter(EventEmitter $emitter): self;
}
================================================
FILE: src/EventEmitting/EmitterAwarePolyfill.php
================================================
<?php
declare(strict_types=1);
namespace League\OAuth2\Server\EventEmitting;
use League\Event\ListenerRegistry;
use Psr\EventDispatcher\EventDispatcherInterface;
trait EmitterAwarePolyfill
{
private EventEmitter $emitter;
public function getEmitter(): EventEmitter
{
return $this->emitter ??= new EventEmitter();
}
public function setEmitter(EventEmitter $emitter): self
{
$this->emitter = $emitter;
return $this;
}
public function getEventDispatcher(): EventDispatcherInterface
{
return $this->getEmitter();
}
public function getListenerRegistry(): ListenerRegistry
{
return $this->getEmitter();
}
}
================================================
FILE: src/EventEmitting/EventEmitter.php
================================================
<?php
declare(strict_types=1);
namespace League\OAuth2\Server\EventEmitting;
use League\Event\EventDispatcher;
use League\Event\ListenerPriority;
final class EventEmitter extends EventDispatcher
{
public function addListener(string $event, callable $listener, int $priority = ListenerPriority::NORMAL): self
{
$this->subscribeTo($event, $listener, $priority);
return $this;
}
public function emit(object $event): object
{
return $this->dispatch($event);
}
}
================================================
FILE: src/Exception/OAuthServerException.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Exception;
use Exception;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Throwable;
use function htmlspecialchars;
use function http_build_query;
use function sprintf;
class OAuthServerException extends Exception
{
/**
* @var array<string, string>
*/
private array $payload;
private ServerRequestInterface $serverRequest;
/**
* Throw a new exception.
*/
final public function __construct(string $message, int $code, private string $errorType, private int $httpStatusCode = 400, private ?string $hint = null, private ?string $redirectUri = null, ?Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$this->payload = [
'error' => $errorType,
'error_description' => $message,
];
if ($hint !== null) {
$this->payload['hint'] = $hint;
}
}
/**
* Returns the current payload.
*
* @return array<string, string>
*/
public function getPayload(): array
{
return $this->payload;
}
/**
* Updates the current payload.
*
* @param array<string, string> $payload
*/
public function setPayload(array $payload): void
{
$this->payload = $payload;
}
/**
* Set the server request that is responsible for generating the exception
*/
public function setServerRequest(ServerRequestInterface $serverRequest): void
{
$this->serverRequest = $serverRequest;
}
/**
* Unsupported grant type error.
*/
public static function unsupportedGrantType(): static
{
$errorMessage = 'The authorization grant type is not supported by the authorization server.';
$hint = 'Check that all required parameters have been provided';
return new static($errorMessage, 2, 'unsupported_grant_type', 400, $hint);
}
/**
* Invalid request error.
*/
public static function invalidRequest(string $parameter, ?string $hint = null, ?Throwable $previous = null): static
{
$errorMessage = 'The request is missing a required parameter, includes an invalid parameter value, ' .
'includes a parameter more than once, or is otherwise malformed.';
$hint = ($hint === null) ? sprintf('Check the `%s` parameter', $parameter) : $hint;
return new static($errorMessage, 3, 'invalid_request', 400, $hint, null, $previous);
}
/**
* Invalid client error.
*/
public static function invalidClient(ServerRequestInterface $serverRequest): static
{
$exception = new static('Client authentication failed', 4, 'invalid_client', 401);
$exception->setServerRequest($serverRequest);
return $exception;
}
/**
* Invalid scope error
*/
public static function invalidScope(string $scope, string|null $redirectUri = null): static
{
$errorMessage = 'The requested scope is invalid, unknown, or malformed';
if ($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.
*/
public static function invalidCredentials(): static
{
return new static('The user credentials were incorrect.', 6, 'invalid_grant', 400);
}
/**
* Server error.
*
* @codeCoverageIgnore
*/
public static function serverError(string $hint, ?Throwable $previous = null): static
{
return new static(
'The authorization server encountered an unexpected condition which prevented it from fulfilling'
. ' the request: ' . $hint,
7,
'server_error',
500,
null,
null,
$previous
);
}
/**
* Invalid refresh token.
*/
public static function invalidRefreshToken(?string $hint = null, ?Throwable $previous = null): static
{
return new static('The refresh token is invalid.', 8, 'invalid_grant', 400, $hint, null, $previous);
}
/**
* Access denied.
*/
public static function accessDenied(?string $hint = null, ?string $redirectUri = null, ?Throwable $previous = null): static
{
return new static(
'The resource owner or authorization server denied the request.',
9,
'access_denied',
401,
$hint,
$redirectUri,
$previous
);
}
/**
* Invalid grant.
*/
public static function invalidGrant(string $hint = ''): static
{
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
);
}
public function getErrorType(): string
{
return $this->errorType;
}
/**
* Expired token error.
*
* @param Throwable $previous Previous exception
*
* @return static
*/
public static function expiredToken(?string $hint = null, ?Throwable $previous = null): static
{
$errorMessage = 'The `device_code` has expired and the device ' .
'authorization session has concluded.';
return new static($errorMessage, 11, 'expired_token', 400, $hint, null, $previous);
}
public static function authorizationPending(string $hint = '', ?Throwable $previous = null): static
{
return new static(
'The authorization request is still pending as the end user ' .
'hasn\'t yet completed the user interaction steps. The client ' .
'SHOULD repeat the Access Token Request to the token endpoint',
12,
'authorization_pending',
400,
$hint,
null,
$previous
);
}
/**
* Slow down error used with the Device Authorization Grant.
*
*
* @return static
*/
public static function slowDown(string $hint = '', ?Throwable $previous = null): static
{
return new static(
'The authorization request is still pending and polling should ' .
'continue, but the interval MUST be increased ' .
'by 5 seconds for this and all subsequent requests.',
13,
'slow_down',
400,
$hint,
null,
$previous
);
}
/**
* Unauthorized client error.
*/
public static function unauthorizedClient(?string $hint = null): static
{
return new static(
'The authenticated client is not authorized to use this authorization grant type.',
14,
'unauthorized_client',
400,
$hint
);
}
/**
* Generate a HTTP response.
*/
public function generateHttpResponse(ResponseInterface $response, bool $useFragment = false, int $jsonOptions = 0): ResponseInterface
{
$headers = $this->getHttpHeaders();
$payload = $this->getPayload();
if ($this->redirectUri !== null) {
if ($useFragment === true) {
$this->redirectUri .= (str_contains($this->redirectUri, '#') === false) ? '#' : '&';
} else {
$this->redirectUri .= (str_contains($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);
}
$jsonEncodedPayload = json_encode($payload, $jsonOptions);
$responseBody = $jsonEncodedPayload === false ? 'JSON encoding of payload failed' : $jsonEncodedPayload;
$response->getBody()->write($responseBody);
return $response->withStatus($this->getHttpStatusCode());
}
/**
* Get all headers that have to be send with the error response.
*
* @return array<string, string> Array with header values
*/
public function getHttpHeaders(): array
{
$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.
if ($this->errorType === 'invalid_client' && $this->requestHasAuthorizationHeader()) {
$authScheme = str_starts_with($this->serverRequest->getHeader('Authorization')[0], 'Bearer') ? 'Bearer' : 'Basic';
$headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"';
}
return $headers;
}
/**
* Check if the exception has an associated redirect URI.
*
* Returns whether the exception includes a redirect, since
* getHttpStatusCode() doesn't return a 302 when there's a
* redirect enabled. This helps when you want to override local
* error pages but want to let redirects through.
*/
public function hasRedirect(): bool
{
return $this->redirectUri !== null;
}
/**
* Returns the Redirect URI used for redirecting.
*/
public function getRedirectUri(): ?string
{
return $this->redirectUri;
}
/**
* Returns the HTTP status code to send when the exceptions is output.
*/
public function getHttpStatusCode(): int
{
return $this->httpStatusCode;
}
public function getHint(): ?string
{
return $this->hint;
}
/**
* Check if the request has a non-empty 'Authorization' header value.
*
* Returns true if the header is present and not an empty string, false
* otherwise.
*/
private function requestHasAuthorizationHeader(): bool
{
if (!$this->serverRequest->hasHeader('Authorization')) {
return false;
}
$authorizationHeader = $this->serverRequest->getHeader('Authorization');
// Common .htaccess configurations yield an empty string for the
// 'Authorization' header when one is not provided by the client.
// For practical purposes that case should be treated as though the
// header isn't present.
// See https://github.com/thephpleague/oauth2-server/issues/1162
if ($authorizationHeader === [] || $authorizationHeader[0] === '') {
return false;
}
return true;
}
}
================================================
FILE: src/Exception/UniqueTokenIdentifierConstraintViolationException.php
================================================
<?php
/**
* @author Ivan Kurnosov <zerkms@zerkms.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Exception;
class UniqueTokenIdentifierConstraintViolationException extends OAuthServerException
{
public static function create(): UniqueTokenIdentifierConstraintViolationException
{
$errorMessage = 'Could not create unique access token identifier';
return new static($errorMessage, 100, 'access_token_duplicate', 500);
}
}
================================================
FILE: src/Grant/AbstractAuthorizeGrant.php
================================================
<?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
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Grant;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
use League\OAuth2\Server\RequestTypes\AuthorizationRequestInterface;
use function http_build_query;
abstract class AbstractAuthorizeGrant extends AbstractGrant
{
/**
* @param array<array-key,mixed> $params
*/
public function makeRedirectUri(string $uri, array $params = [], string $queryDelimiter = '?'): string
{
$uri .= str_contains($uri, $queryDelimiter) ? '&' : $queryDelimiter;
return $uri . http_build_query($params);
}
protected function createAuthorizationRequest(): AuthorizationRequestInterface
{
return new AuthorizationRequest();
}
/**
* Get the client redirect URI.
*/
protected function getClientRedirectUri(ClientEntityInterface $client): string
{
return is_array($client->getRedirectUri())
? $client->getRedirectUri()[0]
: $client->getRedirectUri();
}
}
================================================
FILE: src/Grant/AbstractGrant.php
================================================
<?php
/**
* OAuth 2.0 Abstract grant.
*
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Grant;
use DateInterval;
use DateTimeImmutable;
use DomainException;
use Error;
use Exception;
use League\OAuth2\Server\CryptKeyInterface;
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\EventEmitting\EmitterAwarePolyfill;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
use League\OAuth2\Server\RedirectUriValidators\RedirectUriValidator;
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\AuthorizationRequestInterface;
use League\OAuth2\Server\ResponseTypes\DeviceCodeResponse;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use LogicException;
use Psr\Http\Message\ServerRequestInterface;
use TypeError;
use function array_filter;
use function array_key_exists;
use function base64_decode;
use function bin2hex;
use function explode;
use function is_string;
use function random_bytes;
use function substr;
use function trim;
/**
* Abstract grant class.
*/
abstract class AbstractGrant implements GrantTypeInterface
{
use EmitterAwarePolyfill;
use CryptTrait;
protected const SCOPE_DELIMITER_STRING = ' ';
protected const MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS = 10;
protected ClientRepositoryInterface $clientRepository;
protected AccessTokenRepositoryInterface $accessTokenRepository;
protected ScopeRepositoryInterface $scopeRepository;
protected AuthCodeRepositoryInterface $authCodeRepository;
protected RefreshTokenRepositoryInterface $refreshTokenRepository;
protected UserRepositoryInterface $userRepository;
protected DateInterval $refreshTokenTTL;
protected CryptKeyInterface $privateKey;
protected string $defaultScope;
protected bool $revokeRefreshTokens = true;
public function setClientRepository(ClientRepositoryInterface $clientRepository): void
{
$this->clientRepository = $clientRepository;
}
public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository): void
{
$this->accessTokenRepository = $accessTokenRepository;
}
public function setScopeRepository(ScopeRepositoryInterface $scopeRepository): void
{
$this->scopeRepository = $scopeRepository;
}
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository): void
{
$this->refreshTokenRepository = $refreshTokenRepository;
}
public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository): void
{
$this->authCodeRepository = $authCodeRepository;
}
public function setUserRepository(UserRepositoryInterface $userRepository): void
{
$this->userRepository = $userRepository;
}
/**
* {@inheritdoc}
*/
public function setRefreshTokenTTL(DateInterval $refreshTokenTTL): void
{
$this->refreshTokenTTL = $refreshTokenTTL;
}
/**
* Set the private key
*/
public function setPrivateKey(CryptKeyInterface $privateKey): void
{
$this->privateKey = $privateKey;
}
public function setDefaultScope(string $scope): void
{
$this->defaultScope = $scope;
}
public function revokeRefreshTokens(bool $willRevoke): void
{
$this->revokeRefreshTokens = $willRevoke;
}
/**
* Validate the client.
*
* @throws OAuthServerException
*/
protected function validateClient(ServerRequestInterface $request): ClientEntityInterface
{
[$clientId, $clientSecret] = $this->getClientCredentials($request);
$client = $this->getClientEntityOrFail($clientId, $request);
if ($client->isConfidential()) {
if ($clientSecret === '') {
throw OAuthServerException::invalidRequest('client_secret');
}
if ($this->clientRepository->validateClient($clientId, $clientSecret, $this->getIdentifier()) === false) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient($request);
}
}
return $client;
}
/**
* Wrapper around ClientRepository::getClientEntity() that ensures we emit
* an event and throw an exception if the repo doesn't return a client
* entity.
*
* This is a bit of defensive coding because the interface contract
* doesn't actually enforce non-null returns/exception-on-no-client so
* getClientEntity might return null. By contrast, this method will
* always either return a ClientEntityInterface or throw.
*
* @throws OAuthServerException
*/
protected function getClientEntityOrFail(string $clientId, ServerRequestInterface $request): ClientEntityInterface
{
$client = $this->clientRepository->getClientEntity($clientId);
if ($client instanceof ClientEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient($request);
}
if ($this->supportsGrantType($client, $this->getIdentifier()) === false) {
throw OAuthServerException::unauthorizedClient();
}
return $client;
}
/**
* Returns true if the given client is authorized to use the given grant type.
*/
protected function supportsGrantType(ClientEntityInterface $client, string $grantType): bool
{
return method_exists($client, 'supportsGrantType') === false
|| $client->supportsGrantType($grantType) === true;
}
/**
* Gets the client credentials from the request from the request body or
* the Http Basic Authorization header
*
* @return array{0:non-empty-string,1:string}
*
* @throws OAuthServerException
*/
protected function getClientCredentials(ServerRequestInterface $request): array
{
[$basicAuthUser, $basicAuthPassword] = $this->getBasicAuthCredentials($request);
$clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser);
if ($clientId === null) {
throw OAuthServerException::invalidRequest('client_id');
}
$clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword);
return [$clientId, $clientSecret ?? ''];
}
/**
* Validate redirectUri from the request. If a redirect URI is provided
* ensure it matches what is pre-registered
*
* @throws OAuthServerException
*/
protected function validateRedirectUri(
string $redirectUri,
ClientEntityInterface $client,
ServerRequestInterface $request
): void {
$validator = new RedirectUriValidator($client->getRedirectUri());
if (!$validator->validateRedirectUri($redirectUri)) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient($request);
}
}
/**
* Validate scopes in the request.
*
* @param null|string|string[] $scopes
*
* @throws OAuthServerException
*
* @return ScopeEntityInterface[]
*/
public function validateScopes(string|array|null $scopes, ?string $redirectUri = null): array
{
if ($scopes === null) {
$scopes = [];
} elseif (is_string($scopes)) {
$scopes = $this->convertScopesQueryStringToArray($scopes);
}
$validScopes = [];
foreach ($scopes as $scopeItem) {
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem);
if ($scope instanceof ScopeEntityInterface === false) {
throw OAuthServerException::invalidScope($scopeItem, $redirectUri);
}
$validScopes[] = $scope;
}
return $validScopes;
}
/**
* Converts a scopes query string to an array to easily iterate for validation.
*
* @return string[]
*/
private function convertScopesQueryStringToArray(string $scopes): array
{
return array_filter(explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), static fn ($scope) => $scope !== '');
}
/**
* Parse request parameter.
*
* @param array<array-key, mixed> $request
*
* @return non-empty-string|null
*
* @throws OAuthServerException
*/
private static function parseParam(string $parameter, array $request, ?string $default = null): ?string
{
$value = $request[$parameter] ?? '';
if (is_scalar($value)) {
$value = trim((string) $value);
} else {
throw OAuthServerException::invalidRequest($parameter);
}
if ($value === '') {
$value = $default === null ? null : trim($default);
if ($value === '') {
$value = null;
}
}
return $value;
}
/**
* Retrieve request parameter.
*
* @return non-empty-string|null
*
* @throws OAuthServerException
*/
protected function getRequestParameter(string $parameter, ServerRequestInterface $request, ?string $default = null): ?string
{
return self::parseParam($parameter, (array) $request->getParsedBody(), $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].
*
* @return array{0:non-empty-string,1:string}|array{0:null,1:null}
*/
protected function getBasicAuthCredentials(ServerRequestInterface $request): array
{
if (!$request->hasHeader('Authorization')) {
return [null, null];
}
$header = $request->getHeader('Authorization')[0];
if (stripos($header, 'Basic ') !== 0) {
return [null, null];
}
$decoded = base64_decode(substr($header, 6), true);
if ($decoded === false) {
return [null, null];
}
if (str_contains($decoded, ':') === false) {
return [null, null]; // HTTP Basic header without colon isn't valid
}
[$username, $password] = explode(':', $decoded, 2);
if ($username === '') {
return [null, null];
}
return [$username, $password];
}
/**
* Retrieve query string parameter.
*
* @return non-empty-string|null
*
* @throws OAuthServerException
*/
protected function getQueryStringParameter(string $parameter, ServerRequestInterface $request, ?string $default = null): ?string
{
return self::parseParam($parameter, $request->getQueryParams(), $default);
}
/**
* Retrieve cookie parameter.
*
* @return non-empty-string|null
*
* @throws OAuthServerException
*/
protected function getCookieParameter(string $parameter, ServerRequestInterface $request, ?string $default = null): ?string
{
return self::parseParam($parameter, $request->getCookieParams(), $default);
}
/**
* Retrieve server parameter.
*
* @return non-empty-string|null
*
* @throws OAuthServerException
*/
protected function getServerParameter(string $parameter, ServerRequestInterface $request, ?string $default = null): ?string
{
return self::parseParam($parameter, $request->getServerParams(), $default);
}
/**
* Issue an access token.
*
* @param ScopeEntityInterface[] $scopes
*
* @throws OAuthServerException
* @throws UniqueTokenIdentifierConstraintViolationException
*/
protected function issueAccessToken(
DateInterval $accessTokenTTL,
ClientEntityInterface $client,
string|null $userIdentifier,
array $scopes = []
): AccessTokenEntityInterface {
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
$accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier);
$accessToken->setExpiryDateTime((new DateTimeImmutable())->add($accessTokenTTL));
$accessToken->setPrivateKey($this->privateKey);
while ($maxGenerationAttempts-- > 0) {
$accessToken->setIdentifier($this->generateUniqueIdentifier());
try {
$this->accessTokenRepository->persistNewAccessToken($accessToken);
return $accessToken;
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
if ($maxGenerationAttempts === 0) {
throw $e;
}
}
}
// This should never be hit. It is here to work around a PHPStan false error
return $accessToken;
}
/**
* Issue an auth code.
*
* @param non-empty-string $userIdentifier
* @param ScopeEntityInterface[] $scopes
*
* @throws OAuthServerException
* @throws UniqueTokenIdentifierConstraintViolationException
*/
protected function issueAuthCode(
DateInterval $authCodeTTL,
ClientEntityInterface $client,
string $userIdentifier,
?string $redirectUri,
array $scopes = []
): AuthCodeEntityInterface {
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
$authCode = $this->authCodeRepository->getNewAuthCode();
$authCode->setExpiryDateTime((new DateTimeImmutable())->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;
}
}
}
// This should never be hit. It is here to work around a PHPStan false error
return $authCode;
}
/**
* @throws OAuthServerException
* @throws UniqueTokenIdentifierConstraintViolationException
*/
protected function issueRefreshToken(AccessTokenEntityInterface $accessToken): ?RefreshTokenEntityInterface
{
if ($this->supportsGrantType($accessToken->getClient(), 'refresh_token') === false) {
return null;
}
$refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
if ($refreshToken === null) {
return null;
}
$refreshToken->setExpiryDateTime((new DateTimeImmutable())->add($this->refreshTokenTTL));
$refreshToken->setAccessToken($accessToken);
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
while ($maxGenerationAttempts-- > 0) {
$refreshToken->setIdentifier($this->generateUniqueIdentifier());
try {
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
return $refreshToken;
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
if ($maxGenerationAttempts === 0) {
throw $e;
}
}
}
// This should never be hit. It is here to work around a PHPStan false error
return $refreshToken;
}
/**
* Generate a new unique identifier.
*
* @return non-empty-string
*
* @throws OAuthServerException
*/
protected function generateUniqueIdentifier(int $length = 40): string
{
try {
if ($length < 1) {
throw new DomainException('Length must be a positive integer');
}
return bin2hex(random_bytes($length));
// @codeCoverageIgnoreStart
} catch (TypeError | Error $e) {
throw OAuthServerException::serverError('An unexpected error has occurred', $e);
} catch (Exception $e) {
// If you get this message, the CSPRNG failed hard.
throw OAuthServerException::serverError('Could not generate a random string', $e);
}
// @codeCoverageIgnoreEnd
}
/**
* {@inheritdoc}
*/
public function canRespondToAccessTokenRequest(ServerRequestInterface $request): bool
{
$requestParameters = (array) $request->getParsedBody();
return (
array_key_exists('grant_type', $requestParameters)
&& $requestParameters['grant_type'] === $this->getIdentifier()
);
}
/**
* {@inheritdoc}
*/
public function canRespondToAuthorizationRequest(ServerRequestInterface $request): bool
{
return false;
}
/**
* {@inheritdoc}
*/
public function validateAuthorizationRequest(ServerRequestInterface $request): AuthorizationRequestInterface
{
throw new LogicException('This grant cannot validate an authorization request');
}
/**
* {@inheritdoc}
*/
public function completeAuthorizationRequest(AuthorizationRequestInterface $authorizationRequest): ResponseTypeInterface
{
throw new LogicException('This grant cannot complete an authorization request');
}
/**
* {@inheritdoc}
*/
public function canRespondToDeviceAuthorizationRequest(ServerRequestInterface $request): bool
{
return false;
}
/**
* {@inheritdoc}
*/
public function respondToDeviceAuthorizationRequest(ServerRequestInterface $request): DeviceCodeResponse
{
throw new LogicException('This grant cannot validate a device authorization request');
}
/**
* {@inheritdoc}
*/
public function completeDeviceAuthorizationRequest(string $deviceCode, string $userId, bool $userApproved): void
{
throw new LogicException('This grant cannot complete a device authorization request');
}
/**
* {@inheritdoc}
*/
public function setIntervalVisibility(bool $intervalVisibility): void
{
throw new LogicException('This grant does not support the interval parameter');
}
/**
* {@inheritdoc}
*/
public function getIntervalVisibility(): bool
{
return false;
}
/**
* {@inheritdoc}
*/
public function setIncludeVerificationUriComplete(bool $includeVerificationUriComplete): void
{
throw new LogicException('This grant does not support the verification_uri_complete parameter');
}
}
================================================
FILE: src/Grant/AuthCodeGrant.php
================================================
<?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\Grant;
use DateInterval;
use DateTimeImmutable;
use Exception;
use InvalidArgumentException;
use League\OAuth2\Server\CodeChallengeVerifiers\CodeChallengeVerifierInterface;
use League\OAuth2\Server\CodeChallengeVerifiers\PlainVerifier;
use League\OAuth2\Server\CodeChallengeVerifiers\S256Verifier;
use League\OAuth2\Server\Entities\ClientEntityInterface;
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\RequestAccessTokenEvent;
use League\OAuth2\Server\RequestEvent;
use League\OAuth2\Server\RequestRefreshTokenEvent;
use League\OAuth2\Server\RequestTypes\AuthorizationRequestInterface;
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use LogicException;
use Psr\Http\Message\ServerRequestInterface;
use stdClass;
use function array_key_exists;
use function array_keys;
use function array_map;
use function count;
use function hash_algos;
use function implode;
use function in_array;
use function is_array;
use function json_decode;
use function json_encode;
use function preg_match;
use function property_exists;
use function sprintf;
use function time;
class AuthCodeGrant extends AbstractAuthorizeGrant
{
private bool $requireCodeChallengeForPublicClients = true;
/**
* @var CodeChallengeVerifierInterface[]
*/
private array $codeChallengeVerifiers = [];
/**
* @throws Exception
*/
public function __construct(
AuthCodeRepositoryInterface $authCodeRepository,
RefreshTokenRepositoryInterface $refreshTokenRepository,
private DateInterval $authCodeTTL
) {
$this->setAuthCodeRepository($authCodeRepository);
$this->setRefreshTokenRepository($refreshTokenRepository);
$this->refreshTokenTTL = new DateInterval('P1M');
if (in_array('sha256', hash_algos(), true)) {
$s256Verifier = new S256Verifier();
$this->codeChallengeVerifiers[$s256Verifier->getMethod()] = $s256Verifier;
}
$plainVerifier = new PlainVerifier();
$this->codeChallengeVerifiers[$plainVerifier->getMethod()] = $plainVerifier;
}
/**
* Disable the requirement for a code challenge for public clients.
*/
public function disableRequireCodeChallengeForPublicClients(): void
{
$this->requireCodeChallengeForPublicClients = false;
}
/**
* Respond to an access token request.
*
* @throws OAuthServerException
*/
public function respondToAccessTokenRequest(
ServerRequestInterface $request,
ResponseTypeInterface $responseType,
DateInterval $accessTokenTTL
): ResponseTypeInterface {
$client = $this->validateClient($request);
$encryptedAuthCode = $this->getRequestParameter('code', $request);
if ($encryptedAuthCode === null) {
throw OAuthServerException::invalidRequest('code');
}
try {
$authCodePayload = json_decode($this->decrypt($encryptedAuthCode));
$this->validateAuthorizationCode($authCodePayload, $client, $request);
$scopes = $this->scopeRepository->finalizeScopes(
$this->validateScopes($authCodePayload->scopes),
$this->getIdentifier(),
$client,
$authCodePayload->user_id,
$authCodePayload->auth_code_id
);
} catch (InvalidArgumentException $e) {
throw OAuthServerException::invalidGrant('Cannot validate the provided authorization code');
} catch (LogicException $e) {
throw OAuthServerException::invalidRequest('code', 'Issue decrypting the authorization code', $e);
}
$codeVerifier = $this->getRequestParameter('code_verifier', $request);
// If a code challenge isn't present but a code verifier is, reject the request to block PKCE downgrade attack
if (!isset($authCodePayload->code_challenge) && $codeVerifier !== null) {
throw OAuthServerException::invalidRequest(
'code_challenge',
'code_verifier received when no code_challenge is present'
);
}
if (isset($authCodePayload->code_challenge)) {
$this->validateCodeChallenge($authCodePayload, $codeVerifier);
}
// Issue and persist new access token
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $authCodePayload->user_id, $scopes);
$this->getEmitter()->emit(new RequestAccessTokenEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request, $accessToken));
$responseType->setAccessToken($accessToken);
// Issue and persist new refresh token if given
$refreshToken = $this->issueRefreshToken($accessToken);
if ($refreshToken !== null) {
$this->getEmitter()->emit(new RequestRefreshTokenEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request, $refreshToken));
$responseType->setRefreshToken($refreshToken);
}
// Revoke used auth code
$this->authCodeRepository->revokeAuthCode($authCodePayload->auth_code_id);
return $responseType;
}
private function validateCodeChallenge(object $authCodePayload, ?string $codeVerifier): void
{
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.'
);
}
if (property_exists($authCodePayload, 'code_challenge_method')) {
if (isset($this->codeChallengeVerifiers[$authCodePayload->code_challenge_method])) {
$codeChallengeVerifier = $this->codeChallengeVerifiers[$authCodePayload->code_challenge_method];
if (
!property_exists($authCodePayload, 'code_challenge') ||
!isset($authCodePayload->code_challenge) ||
$codeChallengeVerifier->verifyCodeChallenge($codeVerifier, $authCodePayload->code_challenge) === false
) {
throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.');
}
} else {
throw OAuthServerException::serverError(
sprintf(
'Unsupported code challenge method `%s`',
$authCodePayload->code_challenge_method
)
);
}
}
}
/**
* Validate the authorization code.
*/
private function validateAuthorizationCode(
stdClass $authCodePayload,
ClientEntityInterface $client,
ServerRequestInterface $request
): void {
if (!property_exists($authCodePayload, 'auth_code_id')) {
throw OAuthServerException::invalidRequest('code', 'Authorization code malformed');
}
if (time() > $authCodePayload->expire_time) {
throw OAuthServerException::invalidGrant('Authorization code has expired');
}
if ($this->authCodeRepository->isAuthCodeRevoked($authCodePayload->auth_code_id) === true) {
throw OAuthServerException::invalidGrant('Authorization code has been revoked');
}
if ($authCodePayload->client_id !== $client->getIdentifier()) {
throw OAuthServerException::invalidRequest('code', 'Authorization code was not issued to this client');
}
// The redirect URI is required in this request if it was specified
// in the authorization request
$redirectUri = $this->getRequestParameter('redirect_uri', $request);
if ($authCodePayload->redirect_uri !== null && $redirectUri === null) {
gitextract_nrzlm6b4/
├── .gitattributes
├── .github/
│ ├── FUNDING.yml
│ ├── dependabot.yml
│ └── workflows/
│ ├── backwards-compatibility.yml
│ ├── coding-standards.yml
│ ├── static-analysis.yml
│ └── tests.yml
├── .gitignore
├── .scrutinizer.yml
├── .styleci.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── composer.json
├── examples/
│ ├── README.md
│ ├── composer.json
│ ├── public/
│ │ ├── api.php
│ │ ├── auth_code.php
│ │ ├── client_credentials.php
│ │ ├── device_code.php
│ │ ├── implicit.php
│ │ ├── middleware_use.php
│ │ ├── password.php
│ │ └── refresh_token.php
│ └── src/
│ ├── Entities/
│ │ ├── AccessTokenEntity.php
│ │ ├── AuthCodeEntity.php
│ │ ├── ClientEntity.php
│ │ ├── DeviceCodeEntity.php
│ │ ├── RefreshTokenEntity.php
│ │ ├── ScopeEntity.php
│ │ └── UserEntity.php
│ └── Repositories/
│ ├── AccessTokenRepository.php
│ ├── AuthCodeRepository.php
│ ├── ClientRepository.php
│ ├── DeviceCodeRepository.php
│ ├── RefreshTokenRepository.php
│ ├── ScopeRepository.php
│ └── UserRepository.php
├── phpcs.xml.dist
├── phpstan.neon.dist
├── phpunit.xml.dist
├── src/
│ ├── AuthorizationServer.php
│ ├── AuthorizationValidators/
│ │ ├── AuthorizationValidatorInterface.php
│ │ └── BearerTokenValidator.php
│ ├── CodeChallengeVerifiers/
│ │ ├── CodeChallengeVerifierInterface.php
│ │ ├── PlainVerifier.php
│ │ └── S256Verifier.php
│ ├── CryptKey.php
│ ├── CryptKeyInterface.php
│ ├── CryptTrait.php
│ ├── Entities/
│ │ ├── AccessTokenEntityInterface.php
│ │ ├── AuthCodeEntityInterface.php
│ │ ├── ClientEntityInterface.php
│ │ ├── DeviceCodeEntityInterface.php
│ │ ├── RefreshTokenEntityInterface.php
│ │ ├── ScopeEntityInterface.php
│ │ ├── TokenInterface.php
│ │ ├── Traits/
│ │ │ ├── AccessTokenTrait.php
│ │ │ ├── AuthCodeTrait.php
│ │ │ ├── ClientTrait.php
│ │ │ ├── DeviceCodeTrait.php
│ │ │ ├── EntityTrait.php
│ │ │ ├── RefreshTokenTrait.php
│ │ │ ├── ScopeTrait.php
│ │ │ └── TokenEntityTrait.php
│ │ └── UserEntityInterface.php
│ ├── EventEmitting/
│ │ ├── AbstractEvent.php
│ │ ├── EmitterAwareInterface.php
│ │ ├── EmitterAwarePolyfill.php
│ │ └── EventEmitter.php
│ ├── Exception/
│ │ ├── OAuthServerException.php
│ │ └── UniqueTokenIdentifierConstraintViolationException.php
│ ├── Grant/
│ │ ├── AbstractAuthorizeGrant.php
│ │ ├── AbstractGrant.php
│ │ ├── AuthCodeGrant.php
│ │ ├── ClientCredentialsGrant.php
│ │ ├── DeviceCodeGrant.php
│ │ ├── GrantTypeInterface.php
│ │ ├── ImplicitGrant.php
│ │ ├── PasswordGrant.php
│ │ └── RefreshTokenGrant.php
│ ├── Middleware/
│ │ ├── AuthorizationServerMiddleware.php
│ │ └── ResourceServerMiddleware.php
│ ├── RedirectUriValidators/
│ │ ├── RedirectUriValidator.php
│ │ └── RedirectUriValidatorInterface.php
│ ├── Repositories/
│ │ ├── AccessTokenRepositoryInterface.php
│ │ ├── AuthCodeRepositoryInterface.php
│ │ ├── ClientRepositoryInterface.php
│ │ ├── DeviceCodeRepositoryInterface.php
│ │ ├── RefreshTokenRepositoryInterface.php
│ │ ├── RepositoryInterface.php
│ │ ├── ScopeRepositoryInterface.php
│ │ └── UserRepositoryInterface.php
│ ├── RequestAccessTokenEvent.php
│ ├── RequestEvent.php
│ ├── RequestRefreshTokenEvent.php
│ ├── RequestTypes/
│ │ ├── AuthorizationRequest.php
│ │ └── AuthorizationRequestInterface.php
│ ├── ResourceServer.php
│ └── ResponseTypes/
│ ├── AbstractResponseType.php
│ ├── BearerTokenResponse.php
│ ├── DeviceCodeResponse.php
│ ├── RedirectResponse.php
│ └── ResponseTypeInterface.php
└── tests/
├── AuthorizationServerTest.php
├── AuthorizationValidators/
│ └── BearerTokenValidatorTest.php
├── CodeChallengeVerifiers/
│ ├── PlainVerifierTest.php
│ └── S256VerifierTest.php
├── EventEmitting/
│ └── EmitterAwarePolyfillTest.php
├── Exception/
│ └── OAuthServerExceptionTest.php
├── Grant/
│ ├── AbstractGrantTest.php
│ ├── AuthCodeGrantTest.php
│ ├── ClientCredentialsGrantTest.php
│ ├── DeviceCodeGrantTest.php
│ ├── ImplicitGrantTest.php
│ ├── PasswordGrantTest.php
│ └── RefreshTokenGrantTest.php
├── Middleware/
│ ├── AuthorizationServerMiddlewareTest.php
│ └── ResourceServerMiddlewareTest.php
├── PHPStan/
│ └── AbstractGrantExtension.php
├── RedirectUriValidators/
│ └── RedirectUriValidatorTest.php
├── ResourceServerTest.php
├── ResponseTypes/
│ ├── BearerResponseTypeTest.php
│ ├── BearerTokenResponseWithParams.php
│ └── DeviceCodeResponseTypeTest.php
├── Stubs/
│ ├── .gitattributes
│ ├── AccessTokenEntity.php
│ ├── AuthCodeEntity.php
│ ├── ClientEntity.php
│ ├── CryptTraitStub.php
│ ├── DeviceCodeEntity.php
│ ├── GrantType.php
│ ├── RefreshTokenEntity.php
│ ├── ScopeEntity.php
│ ├── StubResponseType.php
│ ├── UserEntity.php
│ ├── private.key
│ ├── private.key.crlf
│ └── public.key
└── Utils/
├── CryptKeyTest.php
└── CryptTraitTest.php
SYMBOL INDEX (739 symbols across 110 files)
FILE: examples/src/Entities/AccessTokenEntity.php
class AccessTokenEntity (line 20) | class AccessTokenEntity implements AccessTokenEntityInterface
FILE: examples/src/Entities/AuthCodeEntity.php
class AuthCodeEntity (line 20) | class AuthCodeEntity implements AuthCodeEntityInterface
FILE: examples/src/Entities/ClientEntity.php
class ClientEntity (line 19) | class ClientEntity implements ClientEntityInterface
method setName (line 24) | public function setName(string $name): void
method setRedirectUri (line 29) | public function setRedirectUri(string $uri): void
method setConfidential (line 34) | public function setConfidential(): void
FILE: examples/src/Entities/DeviceCodeEntity.php
class DeviceCodeEntity (line 20) | class DeviceCodeEntity implements DeviceCodeEntityInterface
FILE: examples/src/Entities/RefreshTokenEntity.php
class RefreshTokenEntity (line 19) | class RefreshTokenEntity implements RefreshTokenEntityInterface
FILE: examples/src/Entities/ScopeEntity.php
class ScopeEntity (line 19) | class ScopeEntity implements ScopeEntityInterface
FILE: examples/src/Entities/UserEntity.php
class UserEntity (line 17) | class UserEntity implements UserEntityInterface
method getIdentifier (line 22) | public function getIdentifier(): string
FILE: examples/src/Repositories/AccessTokenRepository.php
class AccessTokenRepository (line 20) | class AccessTokenRepository implements AccessTokenRepositoryInterface
method persistNewAccessToken (line 25) | public function persistNewAccessToken(AccessTokenEntityInterface $acce...
method revokeAccessToken (line 33) | public function revokeAccessToken($tokenId): void
method isAccessTokenRevoked (line 41) | public function isAccessTokenRevoked($tokenId): bool
method getNewToken (line 49) | public function getNewToken(ClientEntityInterface $clientEntity, array...
FILE: examples/src/Repositories/AuthCodeRepository.php
class AuthCodeRepository (line 19) | class AuthCodeRepository implements AuthCodeRepositoryInterface
method persistNewAuthCode (line 24) | public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEn...
method revokeAuthCode (line 32) | public function revokeAuthCode($codeId): void
method isAuthCodeRevoked (line 40) | public function isAuthCodeRevoked($codeId): bool
method getNewAuthCode (line 48) | public function getNewAuthCode(): AuthCodeEntityInterface
FILE: examples/src/Repositories/ClientRepository.php
class ClientRepository (line 23) | class ClientRepository implements ClientRepositoryInterface
method getClientEntity (line 31) | public function getClientEntity(string $clientIdentifier): ?ClientEnti...
method validateClient (line 46) | public function validateClient($clientIdentifier, $clientSecret, $gran...
FILE: examples/src/Repositories/DeviceCodeRepository.php
class DeviceCodeRepository (line 22) | class DeviceCodeRepository implements DeviceCodeRepositoryInterface
method getNewDeviceCode (line 27) | public function getNewDeviceCode(): DeviceCodeEntityInterface
method persistDeviceCode (line 35) | public function persistDeviceCode(DeviceCodeEntityInterface $deviceCod...
method getDeviceCodeEntityByDeviceCode (line 43) | public function getDeviceCodeEntityByDeviceCode($deviceCode): ?DeviceC...
method revokeDeviceCode (line 73) | public function revokeDeviceCode($codeId): void
method isDeviceCodeRevoked (line 81) | public function isDeviceCodeRevoked($codeId): bool
FILE: examples/src/Repositories/RefreshTokenRepository.php
class RefreshTokenRepository (line 19) | class RefreshTokenRepository implements RefreshTokenRepositoryInterface
method persistNewRefreshToken (line 24) | public function persistNewRefreshToken(RefreshTokenEntityInterface $re...
method revokeRefreshToken (line 32) | public function revokeRefreshToken($tokenId): void
method isRefreshTokenRevoked (line 40) | public function isRefreshTokenRevoked($tokenId): bool
method getNewRefreshToken (line 48) | public function getNewRefreshToken(): ?RefreshTokenEntityInterface
FILE: examples/src/Repositories/ScopeRepository.php
class ScopeRepository (line 22) | class ScopeRepository implements ScopeRepositoryInterface
method getScopeEntityByIdentifier (line 27) | public function getScopeEntityByIdentifier(string $identifier): ?Scope...
method finalizeScopes (line 51) | public function finalizeScopes(
FILE: examples/src/Repositories/UserRepository.php
class UserRepository (line 20) | class UserRepository implements UserRepositoryInterface
method getUserEntityByUserCredentials (line 25) | public function getUserEntityByUserCredentials(
FILE: src/AuthorizationServer.php
class AuthorizationServer (line 32) | class AuthorizationServer implements EmitterAwareInterface
method __construct (line 61) | public function __construct(
method enableGrantType (line 90) | public function enableGrantType(GrantTypeInterface $grantType, DateInt...
method validateAuthorizationRequest (line 114) | public function validateAuthorizationRequest(ServerRequestInterface $r...
method completeAuthorizationRequest (line 128) | public function completeAuthorizationRequest(
method respondToDeviceAuthorizationRequest (line 142) | public function respondToDeviceAuthorizationRequest(ServerRequestInter...
method completeDeviceAuthorizationRequest (line 158) | public function completeDeviceAuthorizationRequest(string $deviceCode,...
method respondToAccessTokenRequest (line 169) | public function respondToAccessTokenRequest(ServerRequestInterface $re...
method getResponseType (line 191) | protected function getResponseType(): ResponseTypeInterface
method setDefaultScope (line 207) | public function setDefaultScope(string $defaultScope): void
method revokeRefreshTokens (line 215) | public function revokeRefreshTokens(bool $revokeRefreshTokens): void
FILE: src/AuthorizationValidators/AuthorizationValidatorInterface.php
type AuthorizationValidatorInterface (line 17) | interface AuthorizationValidatorInterface
method validateAuthorization (line 23) | public function validateAuthorization(ServerRequestInterface $request)...
FILE: src/AuthorizationValidators/BearerTokenValidator.php
class BearerTokenValidator (line 37) | class BearerTokenValidator implements AuthorizationValidatorInterface
method __construct (line 45) | public function __construct(private AccessTokenRepositoryInterface $ac...
method setPublicKey (line 52) | public function setPublicKey(CryptKeyInterface $key): void
method initJwtConfiguration (line 62) | private function initJwtConfiguration(): void
method validateAuthorization (line 90) | public function validateAuthorization(ServerRequestInterface $request)...
FILE: src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php
type CodeChallengeVerifierInterface (line 15) | interface CodeChallengeVerifierInterface
method getMethod (line 20) | public function getMethod(): string;
method verifyCodeChallenge (line 25) | public function verifyCodeChallenge(string $codeVerifier, string $code...
FILE: src/CodeChallengeVerifiers/PlainVerifier.php
class PlainVerifier (line 17) | class PlainVerifier implements CodeChallengeVerifierInterface
method getMethod (line 22) | public function getMethod(): string
method verifyCodeChallenge (line 30) | public function verifyCodeChallenge(string $codeVerifier, string $code...
FILE: src/CodeChallengeVerifiers/S256Verifier.php
class S256Verifier (line 21) | class S256Verifier implements CodeChallengeVerifierInterface
method getMethod (line 26) | public function getMethod(): string
method verifyCodeChallenge (line 34) | public function verifyCodeChallenge(string $codeVerifier, string $code...
FILE: src/CryptKey.php
class CryptKey (line 33) | class CryptKey implements CryptKeyInterface
method __construct (line 44) | public function __construct(
method getKeyContents (line 99) | public function getKeyContents(): string
method isValidKey (line 107) | private function isValidKey(
method getKeyPath (line 133) | public function getKeyPath(): string
method getPassPhrase (line 141) | public function getPassPhrase(): ?string
FILE: src/CryptKeyInterface.php
type CryptKeyInterface (line 7) | interface CryptKeyInterface
method getKeyPath (line 12) | public function getKeyPath(): string;
method getPassPhrase (line 17) | public function getPassPhrase(): ?string;
method getKeyContents (line 24) | public function getKeyContents(): string;
FILE: src/CryptTrait.php
type CryptTrait (line 28) | trait CryptTrait
method encrypt (line 37) | protected function encrypt(string $unencryptedData): string
method decrypt (line 59) | protected function decrypt(string $encryptedData): string
method setEncryptionKey (line 87) | public function setEncryptionKey(
FILE: src/Entities/AccessTokenEntityInterface.php
type AccessTokenEntityInterface (line 17) | interface AccessTokenEntityInterface extends TokenInterface
method setPrivateKey (line 22) | public function setPrivateKey(CryptKeyInterface $privateKey): void;
method toString (line 27) | public function toString(): string;
FILE: src/Entities/AuthCodeEntityInterface.php
type AuthCodeEntityInterface (line 15) | interface AuthCodeEntityInterface extends TokenInterface
method getRedirectUri (line 17) | public function getRedirectUri(): string|null;
method setRedirectUri (line 19) | public function setRedirectUri(string $uri): void;
FILE: src/Entities/ClientEntityInterface.php
type ClientEntityInterface (line 15) | interface ClientEntityInterface
method getIdentifier (line 22) | public function getIdentifier(): string;
method getName (line 27) | public function getName(): string;
method getRedirectUri (line 35) | public function getRedirectUri(): string|array;
method isConfidential (line 40) | public function isConfidential(): bool;
FILE: src/Entities/DeviceCodeEntityInterface.php
type DeviceCodeEntityInterface (line 17) | interface DeviceCodeEntityInterface extends TokenInterface
method getUserCode (line 19) | public function getUserCode(): string;
method setUserCode (line 21) | public function setUserCode(string $userCode): void;
method getVerificationUri (line 23) | public function getVerificationUri(): string;
method setVerificationUri (line 25) | public function setVerificationUri(string $verificationUri): void;
method getVerificationUriComplete (line 27) | public function getVerificationUriComplete(): string;
method getLastPolledAt (line 29) | public function getLastPolledAt(): ?DateTimeImmutable;
method setLastPolledAt (line 31) | public function setLastPolledAt(DateTimeImmutable $lastPolledAt): void;
method getInterval (line 33) | public function getInterval(): int;
method setInterval (line 35) | public function setInterval(int $interval): void;
method getUserApproved (line 37) | public function getUserApproved(): bool;
method setUserApproved (line 39) | public function setUserApproved(bool $userApproved): void;
FILE: src/Entities/RefreshTokenEntityInterface.php
type RefreshTokenEntityInterface (line 17) | interface RefreshTokenEntityInterface
method getIdentifier (line 24) | public function getIdentifier(): string;
method setIdentifier (line 31) | public function setIdentifier(string $identifier): void;
method getExpiryDateTime (line 36) | public function getExpiryDateTime(): DateTimeImmutable;
method setExpiryDateTime (line 41) | public function setExpiryDateTime(DateTimeImmutable $dateTime): void;
method setAccessToken (line 46) | public function setAccessToken(AccessTokenEntityInterface $accessToken...
method getAccessToken (line 51) | public function getAccessToken(): AccessTokenEntityInterface;
FILE: src/Entities/ScopeEntityInterface.php
type ScopeEntityInterface (line 17) | interface ScopeEntityInterface extends JsonSerializable
method getIdentifier (line 24) | public function getIdentifier(): string;
FILE: src/Entities/TokenInterface.php
type TokenInterface (line 17) | interface TokenInterface
method getIdentifier (line 24) | public function getIdentifier(): string;
method setIdentifier (line 31) | public function setIdentifier(string $identifier): void;
method getExpiryDateTime (line 36) | public function getExpiryDateTime(): DateTimeImmutable;
method setExpiryDateTime (line 41) | public function setExpiryDateTime(DateTimeImmutable $dateTime): void;
method setUserIdentifier (line 48) | public function setUserIdentifier(string $identifier): void;
method getUserIdentifier (line 55) | public function getUserIdentifier(): string|null;
method getClient (line 60) | public function getClient(): ClientEntityInterface;
method setClient (line 65) | public function setClient(ClientEntityInterface $client): void;
method addScope (line 70) | public function addScope(ScopeEntityInterface $scope): void;
method getScopes (line 77) | public function getScopes(): array;
FILE: src/Entities/Traits/AccessTokenTrait.php
type AccessTokenTrait (line 26) | trait AccessTokenTrait
method setPrivateKey (line 35) | public function setPrivateKey(
method initJwtConfiguration (line 45) | public function initJwtConfiguration(): void
method convertToJWT (line 63) | private function convertToJWT(): Token
method toString (line 81) | public function toString(): string
method getClient (line 86) | abstract public function getClient(): ClientEntityInterface;
method getExpiryDateTime (line 88) | abstract public function getExpiryDateTime(): DateTimeImmutable;
method getUserIdentifier (line 93) | abstract public function getUserIdentifier(): string|null;
method getScopes (line 98) | abstract public function getScopes(): array;
method getIdentifier (line 103) | abstract public function getIdentifier(): string;
method getSubjectIdentifier (line 108) | private function getSubjectIdentifier(): string
FILE: src/Entities/Traits/AuthCodeTrait.php
type AuthCodeTrait (line 15) | trait AuthCodeTrait
method getRedirectUri (line 19) | public function getRedirectUri(): string|null
method setRedirectUri (line 24) | public function setRedirectUri(string $uri): void
FILE: src/Entities/Traits/ClientTrait.php
type ClientTrait (line 15) | trait ClientTrait
method getName (line 32) | public function getName(): string
method getRedirectUri (line 43) | public function getRedirectUri(): string|array
method isConfidential (line 51) | public function isConfidential(): bool
method supportsGrantType (line 59) | public function supportsGrantType(string $grantType): bool
FILE: src/Entities/Traits/DeviceCodeTrait.php
type DeviceCodeTrait (line 19) | trait DeviceCodeTrait
method getUserCode (line 28) | public function getUserCode(): string
method setUserCode (line 33) | public function setUserCode(string $userCode): void
method getVerificationUri (line 38) | public function getVerificationUri(): string
method setVerificationUri (line 43) | public function setVerificationUri(string $verificationUri): void
method getVerificationUriComplete (line 48) | public function getVerificationUriComplete(): string
method getClient (line 53) | abstract public function getClient(): ClientEntityInterface;
method getExpiryDateTime (line 55) | abstract public function getExpiryDateTime(): DateTimeImmutable;
method getScopes (line 60) | abstract public function getScopes(): array;
method getIdentifier (line 65) | abstract public function getIdentifier(): string;
method getLastPolledAt (line 67) | public function getLastPolledAt(): ?DateTimeImmutable
method setLastPolledAt (line 72) | public function setLastPolledAt(DateTimeImmutable $lastPolledAt): void
method getInterval (line 77) | public function getInterval(): int
method setInterval (line 82) | public function setInterval(int $interval): void
method getUserApproved (line 87) | public function getUserApproved(): bool
method setUserApproved (line 92) | public function setUserApproved(bool $userApproved): void
method getVerificationUriCompleteInAuthResponse (line 97) | public function getVerificationUriCompleteInAuthResponse(): bool
method setVerificationUriCompleteInAuthResponse (line 102) | public function setVerificationUriCompleteInAuthResponse(bool $include...
FILE: src/Entities/Traits/EntityTrait.php
type EntityTrait (line 15) | trait EntityTrait
method getIdentifier (line 25) | public function getIdentifier(): string
method setIdentifier (line 33) | public function setIdentifier(string $identifier): void
FILE: src/Entities/Traits/RefreshTokenTrait.php
type RefreshTokenTrait (line 18) | trait RefreshTokenTrait
method setAccessToken (line 27) | public function setAccessToken(AccessTokenEntityInterface $accessToken...
method getAccessToken (line 35) | public function getAccessToken(): AccessTokenEntityInterface
method getExpiryDateTime (line 43) | public function getExpiryDateTime(): DateTimeImmutable
method setExpiryDateTime (line 51) | public function setExpiryDateTime(DateTimeImmutable $dateTime): void
FILE: src/Entities/Traits/ScopeTrait.php
type ScopeTrait (line 15) | trait ScopeTrait
method jsonSerialize (line 20) | public function jsonSerialize(): string
method getIdentifier (line 25) | abstract public function getIdentifier(): string;
FILE: src/Entities/Traits/TokenEntityTrait.php
type TokenEntityTrait (line 21) | trait TokenEntityTrait
method addScope (line 40) | public function addScope(ScopeEntityInterface $scope): void
method getScopes (line 50) | public function getScopes(): array
method getExpiryDateTime (line 58) | public function getExpiryDateTime(): DateTimeImmutable
method setExpiryDateTime (line 66) | public function setExpiryDateTime(DateTimeImmutable $dateTime): void
method setUserIdentifier (line 76) | public function setUserIdentifier(string $identifier): void
method getUserIdentifier (line 86) | public function getUserIdentifier(): string|null
method getClient (line 94) | public function getClient(): ClientEntityInterface
method setClient (line 102) | public function setClient(ClientEntityInterface $client): void
FILE: src/Entities/UserEntityInterface.php
type UserEntityInterface (line 15) | interface UserEntityInterface
method getIdentifier (line 22) | public function getIdentifier(): string;
FILE: src/EventEmitting/AbstractEvent.php
class AbstractEvent (line 10) | class AbstractEvent implements StoppableEventInterface, HasEventName
method __construct (line 14) | public function __construct(private string $name)
method eventName (line 18) | public function eventName(): string
method getName (line 28) | public function getName(): string
method isPropagationStopped (line 33) | public function isPropagationStopped(): bool
method stopPropagation (line 38) | public function stopPropagation(): self
FILE: src/EventEmitting/EmitterAwareInterface.php
type EmitterAwareInterface (line 7) | interface EmitterAwareInterface
method getEmitter (line 9) | public function getEmitter(): EventEmitter;
method setEmitter (line 11) | public function setEmitter(EventEmitter $emitter): self;
FILE: src/EventEmitting/EmitterAwarePolyfill.php
type EmitterAwarePolyfill (line 10) | trait EmitterAwarePolyfill
method getEmitter (line 14) | public function getEmitter(): EventEmitter
method setEmitter (line 19) | public function setEmitter(EventEmitter $emitter): self
method getEventDispatcher (line 26) | public function getEventDispatcher(): EventDispatcherInterface
method getListenerRegistry (line 31) | public function getListenerRegistry(): ListenerRegistry
FILE: src/EventEmitting/EventEmitter.php
class EventEmitter (line 10) | final class EventEmitter extends EventDispatcher
method addListener (line 12) | public function addListener(string $event, callable $listener, int $pr...
method emit (line 19) | public function emit(object $event): object
FILE: src/Exception/OAuthServerException.php
class OAuthServerException (line 24) | class OAuthServerException extends Exception
method __construct (line 36) | final public function __construct(string $message, int $code, private ...
method getPayload (line 54) | public function getPayload(): array
method setPayload (line 64) | public function setPayload(array $payload): void
method setServerRequest (line 72) | public function setServerRequest(ServerRequestInterface $serverRequest...
method unsupportedGrantType (line 80) | public static function unsupportedGrantType(): static
method invalidRequest (line 91) | public static function invalidRequest(string $parameter, ?string $hint...
method invalidClient (line 103) | public static function invalidClient(ServerRequestInterface $serverReq...
method invalidScope (line 115) | public static function invalidScope(string $scope, string|null $redire...
method invalidCredentials (line 134) | public static function invalidCredentials(): static
method serverError (line 144) | public static function serverError(string $hint, ?Throwable $previous ...
method invalidRefreshToken (line 161) | public static function invalidRefreshToken(?string $hint = null, ?Thro...
method accessDenied (line 169) | public static function accessDenied(?string $hint = null, ?string $red...
method invalidGrant (line 185) | public static function invalidGrant(string $hint = ''): static
method getErrorType (line 198) | public function getErrorType(): string
method expiredToken (line 210) | public static function expiredToken(?string $hint = null, ?Throwable $...
method authorizationPending (line 218) | public static function authorizationPending(string $hint = '', ?Throwa...
method slowDown (line 239) | public static function slowDown(string $hint = '', ?Throwable $previou...
method unauthorizedClient (line 257) | public static function unauthorizedClient(?string $hint = null): static
method generateHttpResponse (line 271) | public function generateHttpResponse(ResponseInterface $response, bool...
method getHttpHeaders (line 305) | public function getHttpHeaders(): array
method hasRedirect (line 336) | public function hasRedirect(): bool
method getRedirectUri (line 344) | public function getRedirectUri(): ?string
method getHttpStatusCode (line 352) | public function getHttpStatusCode(): int
method getHint (line 357) | public function getHint(): ?string
method requestHasAuthorizationHeader (line 368) | private function requestHasAuthorizationHeader(): bool
FILE: src/Exception/UniqueTokenIdentifierConstraintViolationException.php
class UniqueTokenIdentifierConstraintViolationException (line 15) | class UniqueTokenIdentifierConstraintViolationException extends OAuthSer...
method create (line 17) | public static function create(): UniqueTokenIdentifierConstraintViolat...
FILE: src/Grant/AbstractAuthorizeGrant.php
class AbstractAuthorizeGrant (line 23) | abstract class AbstractAuthorizeGrant extends AbstractGrant
method makeRedirectUri (line 28) | public function makeRedirectUri(string $uri, array $params = [], strin...
method createAuthorizationRequest (line 35) | protected function createAuthorizationRequest(): AuthorizationRequestI...
method getClientRedirectUri (line 43) | protected function getClientRedirectUri(ClientEntityInterface $client)...
FILE: src/Grant/AbstractGrant.php
class AbstractGrant (line 60) | abstract class AbstractGrant implements GrantTypeInterface
method setClientRepository (line 89) | public function setClientRepository(ClientRepositoryInterface $clientR...
method setAccessTokenRepository (line 94) | public function setAccessTokenRepository(AccessTokenRepositoryInterfac...
method setScopeRepository (line 99) | public function setScopeRepository(ScopeRepositoryInterface $scopeRepo...
method setRefreshTokenRepository (line 104) | public function setRefreshTokenRepository(RefreshTokenRepositoryInterf...
method setAuthCodeRepository (line 109) | public function setAuthCodeRepository(AuthCodeRepositoryInterface $aut...
method setUserRepository (line 114) | public function setUserRepository(UserRepositoryInterface $userReposit...
method setRefreshTokenTTL (line 122) | public function setRefreshTokenTTL(DateInterval $refreshTokenTTL): void
method setPrivateKey (line 130) | public function setPrivateKey(CryptKeyInterface $privateKey): void
method setDefaultScope (line 135) | public function setDefaultScope(string $scope): void
method revokeRefreshTokens (line 140) | public function revokeRefreshTokens(bool $willRevoke): void
method validateClient (line 150) | protected function validateClient(ServerRequestInterface $request): Cl...
method getClientEntityOrFail (line 183) | protected function getClientEntityOrFail(string $clientId, ServerReque...
method supportsGrantType (line 202) | protected function supportsGrantType(ClientEntityInterface $client, st...
method getClientCredentials (line 216) | protected function getClientCredentials(ServerRequestInterface $reques...
method validateRedirectUri (line 237) | protected function validateRedirectUri(
method validateScopes (line 259) | public function validateScopes(string|array|null $scopes, ?string $red...
method convertScopesQueryStringToArray (line 287) | private function convertScopesQueryStringToArray(string $scopes): array
method parseParam (line 301) | private static function parseParam(string $parameter, array $request, ...
method getRequestParameter (line 329) | protected function getRequestParameter(string $parameter, ServerReques...
method getBasicAuthCredentials (line 343) | protected function getBasicAuthCredentials(ServerRequestInterface $req...
method getQueryStringParameter (line 380) | protected function getQueryStringParameter(string $parameter, ServerRe...
method getCookieParameter (line 392) | protected function getCookieParameter(string $parameter, ServerRequest...
method getServerParameter (line 404) | protected function getServerParameter(string $parameter, ServerRequest...
method issueAccessToken (line 417) | protected function issueAccessToken(
method issueAuthCode (line 455) | protected function issueAuthCode(
method issueRefreshToken (line 498) | protected function issueRefreshToken(AccessTokenEntityInterface $acces...
method generateUniqueIdentifier (line 539) | protected function generateUniqueIdentifier(int $length = 40): string
method canRespondToAccessTokenRequest (line 560) | public function canRespondToAccessTokenRequest(ServerRequestInterface ...
method canRespondToAuthorizationRequest (line 573) | public function canRespondToAuthorizationRequest(ServerRequestInterfac...
method validateAuthorizationRequest (line 581) | public function validateAuthorizationRequest(ServerRequestInterface $r...
method completeAuthorizationRequest (line 589) | public function completeAuthorizationRequest(AuthorizationRequestInter...
method canRespondToDeviceAuthorizationRequest (line 597) | public function canRespondToDeviceAuthorizationRequest(ServerRequestIn...
method respondToDeviceAuthorizationRequest (line 605) | public function respondToDeviceAuthorizationRequest(ServerRequestInter...
method completeDeviceAuthorizationRequest (line 613) | public function completeDeviceAuthorizationRequest(string $deviceCode,...
method setIntervalVisibility (line 621) | public function setIntervalVisibility(bool $intervalVisibility): void
method getIntervalVisibility (line 629) | public function getIntervalVisibility(): bool
method setIncludeVerificationUriComplete (line 637) | public function setIncludeVerificationUriComplete(bool $includeVerific...
FILE: src/Grant/AuthCodeGrant.php
class AuthCodeGrant (line 52) | class AuthCodeGrant extends AbstractAuthorizeGrant
method __construct (line 64) | public function __construct(
method disableRequireCodeChallengeForPublicClients (line 85) | public function disableRequireCodeChallengeForPublicClients(): void
method respondToAccessTokenRequest (line 95) | public function respondToAccessTokenRequest(
method validateCodeChallenge (line 159) | private function validateCodeChallenge(object $authCodePayload, ?strin...
method validateAuthorizationCode (line 199) | private function validateAuthorizationCode(
method getIdentifier (line 236) | public function getIdentifier(): string
method canRespondToAuthorizationRequest (line 244) | public function canRespondToAuthorizationRequest(ServerRequestInterfac...
method validateAuthorizationRequest (line 256) | public function validateAuthorizationRequest(ServerRequestInterface $r...
method completeAuthorizationRequest (line 349) | public function completeAuthorizationRequest(AuthorizationRequestInter...
FILE: src/Grant/ClientCredentialsGrant.php
class ClientCredentialsGrant (line 27) | class ClientCredentialsGrant extends AbstractGrant
method respondToAccessTokenRequest (line 32) | public function respondToAccessTokenRequest(
method getIdentifier (line 65) | public function getIdentifier(): string
FILE: src/Grant/DeviceCodeGrant.php
class DeviceCodeGrant (line 44) | class DeviceCodeGrant extends AbstractGrant
method __construct (line 51) | public function __construct(
method canRespondToDeviceAuthorizationRequest (line 69) | public function canRespondToDeviceAuthorizationRequest(ServerRequestIn...
method respondToDeviceAuthorizationRequest (line 77) | public function respondToDeviceAuthorizationRequest(ServerRequestInter...
method completeDeviceAuthorizationRequest (line 118) | public function completeDeviceAuthorizationRequest(string $deviceCode,...
method respondToAccessTokenRequest (line 139) | public function respondToAccessTokenRequest(
method validateDeviceCode (line 190) | protected function validateDeviceCode(ServerRequestInterface $request,...
method deviceCodePolledTooSoon (line 223) | private function deviceCodePolledTooSoon(?DateTimeImmutable $lastPoll)...
method setVerificationUri (line 231) | public function setVerificationUri(string $verificationUri): void
method getIdentifier (line 239) | public function getIdentifier(): string
method setDeviceCodeRepository (line 244) | private function setDeviceCodeRepository(DeviceCodeRepositoryInterface...
method issueDeviceCode (line 257) | protected function issueDeviceCode(
method generateUserCode (line 299) | protected function generateUserCode(int $length = 8): string
method setIntervalVisibility (line 320) | public function setIntervalVisibility(bool $intervalVisibility): void
method getIntervalVisibility (line 325) | public function getIntervalVisibility(): bool
method setIncludeVerificationUriComplete (line 330) | public function setIncludeVerificationUriComplete(bool $includeVerific...
FILE: src/Grant/GrantTypeInterface.php
type GrantTypeInterface (line 32) | interface GrantTypeInterface extends EmitterAwareInterface
method setRefreshTokenTTL (line 37) | public function setRefreshTokenTTL(DateInterval $refreshTokenTTL): void;
method getIdentifier (line 42) | public function getIdentifier(): string;
method respondToAccessTokenRequest (line 47) | public function respondToAccessTokenRequest(
method canRespondToAuthorizationRequest (line 56) | public function canRespondToAuthorizationRequest(ServerRequestInterfac...
method validateAuthorizationRequest (line 65) | public function validateAuthorizationRequest(ServerRequestInterface $r...
method completeAuthorizationRequest (line 72) | public function completeAuthorizationRequest(AuthorizationRequestInter...
method canRespondToAccessTokenRequest (line 79) | public function canRespondToAccessTokenRequest(ServerRequestInterface ...
method canRespondToDeviceAuthorizationRequest (line 84) | public function canRespondToDeviceAuthorizationRequest(ServerRequestIn...
method respondToDeviceAuthorizationRequest (line 93) | public function respondToDeviceAuthorizationRequest(ServerRequestInter...
method completeDeviceAuthorizationRequest (line 101) | public function completeDeviceAuthorizationRequest(string $deviceCode,...
method setClientRepository (line 106) | public function setClientRepository(ClientRepositoryInterface $clientR...
method setAccessTokenRepository (line 111) | public function setAccessTokenRepository(AccessTokenRepositoryInterfac...
method setScopeRepository (line 116) | public function setScopeRepository(ScopeRepositoryInterface $scopeRepo...
method setDefaultScope (line 121) | public function setDefaultScope(string $scope): void;
method setPrivateKey (line 126) | public function setPrivateKey(CryptKeyInterface $privateKey): void;
method setEncryptionKey (line 128) | public function setEncryptionKey(Key|string|null $key = null): void;
method revokeRefreshTokens (line 133) | public function revokeRefreshTokens(bool $willRevoke): void;
method setIntervalVisibility (line 139) | public function setIntervalVisibility(bool $intervalVisibility): void;
method getIntervalVisibility (line 145) | public function getIntervalVisibility(): bool;
method setIncludeVerificationUriComplete (line 152) | public function setIncludeVerificationUriComplete(bool $includeVerific...
FILE: src/Grant/ImplicitGrant.php
class ImplicitGrant (line 31) | class ImplicitGrant extends AbstractAuthorizeGrant
method __construct (line 33) | public function __construct(private DateInterval $accessTokenTTL, priv...
method setRefreshTokenTTL (line 40) | public function setRefreshTokenTTL(DateInterval $refreshTokenTTL): void
method setRefreshTokenRepository (line 48) | public function setRefreshTokenRepository(RefreshTokenRepositoryInterf...
method canRespondToAccessTokenRequest (line 56) | public function canRespondToAccessTokenRequest(ServerRequestInterface ...
method getIdentifier (line 64) | public function getIdentifier(): string
method respondToAccessTokenRequest (line 72) | public function respondToAccessTokenRequest(
method canRespondToAuthorizationRequest (line 83) | public function canRespondToAuthorizationRequest(ServerRequestInterfac...
method validateAuthorizationRequest (line 95) | public function validateAuthorizationRequest(ServerRequestInterface $r...
method completeAuthorizationRequest (line 149) | public function completeAuthorizationRequest(AuthorizationRequestInter...
FILE: src/Grant/PasswordGrant.php
class PasswordGrant (line 32) | class PasswordGrant extends AbstractGrant
method __construct (line 34) | public function __construct(
method respondToAccessTokenRequest (line 47) | public function respondToAccessTokenRequest(
method validateUser (line 83) | protected function validateUser(ServerRequestInterface $request, Clien...
method getIdentifier (line 110) | public function getIdentifier(): string
FILE: src/Grant/RefreshTokenGrant.php
class RefreshTokenGrant (line 35) | class RefreshTokenGrant extends AbstractGrant
method __construct (line 37) | public function __construct(RefreshTokenRepositoryInterface $refreshTo...
method respondToAccessTokenRequest (line 47) | public function respondToAccessTokenRequest(
method validateOldRefreshToken (line 106) | protected function validateOldRefreshToken(ServerRequestInterface $req...
method getIdentifier (line 138) | public function getIdentifier(): string
FILE: src/Middleware/AuthorizationServerMiddleware.php
class AuthorizationServerMiddleware (line 20) | class AuthorizationServerMiddleware
method __construct (line 22) | public function __construct(private AuthorizationServer $server)
method __invoke (line 26) | public function __invoke(ServerRequestInterface $request, ResponseInte...
FILE: src/Middleware/ResourceServerMiddleware.php
class ResourceServerMiddleware (line 20) | class ResourceServerMiddleware
method __construct (line 22) | public function __construct(private ResourceServer $server)
method __invoke (line 26) | public function __invoke(ServerRequestInterface $request, ResponseInte...
FILE: src/RedirectUriValidators/RedirectUriValidator.php
class RedirectUriValidator (line 21) | class RedirectUriValidator implements RedirectUriValidatorInterface
method __construct (line 33) | public function __construct(array|string $allowedRedirectUris)
method validateRedirectUri (line 47) | public function validateRedirectUri(string $redirectUri): bool
method isLoopbackUri (line 61) | private function isLoopbackUri(string $redirectUri): bool
method matchExactUri (line 76) | private function matchExactUri(string $redirectUri): bool
method matchUriExcludingPort (line 84) | private function matchUriExcludingPort(string $redirectUri): bool
method parseUrlAndRemovePort (line 100) | private function parseUrlAndRemovePort(string $url): string
FILE: src/RedirectUriValidators/RedirectUriValidatorInterface.php
type RedirectUriValidatorInterface (line 15) | interface RedirectUriValidatorInterface
method validateRedirectUri (line 20) | public function validateRedirectUri(string $redirectUri): bool;
FILE: src/Repositories/AccessTokenRepositoryInterface.php
type AccessTokenRepositoryInterface (line 23) | interface AccessTokenRepositoryInterface extends RepositoryInterface
method getNewToken (line 30) | public function getNewToken(
method persistNewAccessToken (line 39) | public function persistNewAccessToken(AccessTokenEntityInterface $acce...
method revokeAccessToken (line 41) | public function revokeAccessToken(string $tokenId): void;
method isAccessTokenRevoked (line 43) | public function isAccessTokenRevoked(string $tokenId): bool;
FILE: src/Repositories/AuthCodeRepositoryInterface.php
type AuthCodeRepositoryInterface (line 21) | interface AuthCodeRepositoryInterface extends RepositoryInterface
method getNewAuthCode (line 23) | public function getNewAuthCode(): AuthCodeEntityInterface;
method persistNewAuthCode (line 28) | public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEn...
method revokeAuthCode (line 30) | public function revokeAuthCode(string $codeId): void;
method isAuthCodeRevoked (line 32) | public function isAuthCodeRevoked(string $codeId): bool;
FILE: src/Repositories/ClientRepositoryInterface.php
type ClientRepositoryInterface (line 20) | interface ClientRepositoryInterface extends RepositoryInterface
method getClientEntity (line 25) | public function getClientEntity(string $clientIdentifier): ?ClientEnti...
method validateClient (line 30) | public function validateClient(string $clientIdentifier, ?string $clie...
FILE: src/Repositories/DeviceCodeRepositoryInterface.php
type DeviceCodeRepositoryInterface (line 18) | interface DeviceCodeRepositoryInterface extends RepositoryInterface
method getNewDeviceCode (line 23) | public function getNewDeviceCode(): DeviceCodeEntityInterface;
method persistDeviceCode (line 30) | public function persistDeviceCode(DeviceCodeEntityInterface $deviceCod...
method getDeviceCodeEntityByDeviceCode (line 35) | public function getDeviceCodeEntityByDeviceCode(
method revokeDeviceCode (line 42) | public function revokeDeviceCode(string $codeId): void;
method isDeviceCodeRevoked (line 49) | public function isDeviceCodeRevoked(string $codeId): bool;
FILE: src/Repositories/RefreshTokenRepositoryInterface.php
type RefreshTokenRepositoryInterface (line 21) | interface RefreshTokenRepositoryInterface extends RepositoryInterface
method getNewRefreshToken (line 23) | public function getNewRefreshToken(): ?RefreshTokenEntityInterface;
method persistNewRefreshToken (line 28) | public function persistNewRefreshToken(RefreshTokenEntityInterface $re...
method revokeRefreshToken (line 30) | public function revokeRefreshToken(string $tokenId): void;
method isRefreshTokenRevoked (line 32) | public function isRefreshTokenRevoked(string $tokenId): bool;
FILE: src/Repositories/RepositoryInterface.php
type RepositoryInterface (line 18) | interface RepositoryInterface
FILE: src/Repositories/ScopeRepositoryInterface.php
type ScopeRepositoryInterface (line 21) | interface ScopeRepositoryInterface extends RepositoryInterface
method getScopeEntityByIdentifier (line 28) | public function getScopeEntityByIdentifier(string $identifier): ?Scope...
method finalizeScopes (line 38) | public function finalizeScopes(
FILE: src/Repositories/UserRepositoryInterface.php
type UserRepositoryInterface (line 18) | interface UserRepositoryInterface extends RepositoryInterface
method getUserEntityByUserCredentials (line 23) | public function getUserEntityByUserCredentials(
FILE: src/RequestAccessTokenEvent.php
class RequestAccessTokenEvent (line 19) | class RequestAccessTokenEvent extends RequestEvent
method __construct (line 21) | public function __construct(
method getAccessToken (line 33) | public function getAccessToken(): AccessTokenEntityInterface
FILE: src/RequestEvent.php
class RequestEvent (line 18) | class RequestEvent extends AbstractEvent
method __construct (line 27) | public function __construct(string $name, private ServerRequestInterfa...
method getRequest (line 35) | public function getRequest(): ServerRequestInterface
FILE: src/RequestRefreshTokenEvent.php
class RequestRefreshTokenEvent (line 19) | class RequestRefreshTokenEvent extends RequestEvent
method __construct (line 21) | public function __construct(
method getRefreshToken (line 33) | public function getRefreshToken(): RefreshTokenEntityInterface
FILE: src/RequestTypes/AuthorizationRequest.php
class AuthorizationRequest (line 19) | class AuthorizationRequest implements AuthorizationRequestInterface
method getGrantTypeId (line 68) | public function getGrantTypeId(): string
method setGrantTypeId (line 73) | public function setGrantTypeId(string $grantTypeId): void
method getClient (line 78) | public function getClient(): ClientEntityInterface
method setClient (line 83) | public function setClient(ClientEntityInterface $client): void
method getUser (line 88) | public function getUser(): ?UserEntityInterface
method setUser (line 93) | public function setUser(UserEntityInterface $user): void
method getScopes (line 101) | public function getScopes(): array
method setScopes (line 109) | public function setScopes(array $scopes): void
method isAuthorizationApproved (line 114) | public function isAuthorizationApproved(): bool
method setAuthorizationApproved (line 119) | public function setAuthorizationApproved(bool $authorizationApproved):...
method getRedirectUri (line 124) | public function getRedirectUri(): ?string
method setRedirectUri (line 129) | public function setRedirectUri(?string $redirectUri): void
method getState (line 134) | public function getState(): ?string
method setState (line 139) | public function setState(string $state): void
method getCodeChallenge (line 144) | public function getCodeChallenge(): ?string
method setCodeChallenge (line 149) | public function setCodeChallenge(string $codeChallenge): void
method getCodeChallengeMethod (line 154) | public function getCodeChallengeMethod(): ?string
method setCodeChallengeMethod (line 159) | public function setCodeChallengeMethod(string $codeChallengeMethod): void
FILE: src/RequestTypes/AuthorizationRequestInterface.php
type AuthorizationRequestInterface (line 19) | interface AuthorizationRequestInterface
method getUser (line 21) | public function getUser(): UserEntityInterface|null;
method setState (line 23) | public function setState(string $state): void;
method getClient (line 25) | public function getClient(): ClientEntityInterface;
method setAuthorizationApproved (line 27) | public function setAuthorizationApproved(bool $authorizationApproved):...
method setScopes (line 32) | public function setScopes(array $scopes): void;
method setRedirectUri (line 34) | public function setRedirectUri(?string $redirectUri): void;
method getRedirectUri (line 36) | public function getRedirectUri(): ?string;
method getCodeChallengeMethod (line 38) | public function getCodeChallengeMethod(): ?string;
method setGrantTypeId (line 40) | public function setGrantTypeId(string $grantTypeId): void;
method setUser (line 42) | public function setUser(UserEntityInterface $user): void;
method setClient (line 44) | public function setClient(ClientEntityInterface $client): void;
method setCodeChallenge (line 46) | public function setCodeChallenge(string $codeChallenge): void;
method isAuthorizationApproved (line 48) | public function isAuthorizationApproved(): bool;
method getState (line 50) | public function getState(): ?string;
method getCodeChallenge (line 52) | public function getCodeChallenge(): ?string;
method setCodeChallengeMethod (line 54) | public function setCodeChallengeMethod(string $codeChallengeMethod): v...
method getScopes (line 59) | public function getScopes(): array;
method getGrantTypeId (line 61) | public function getGrantTypeId(): string;
FILE: src/ResourceServer.php
class ResourceServer (line 21) | class ResourceServer
method __construct (line 25) | public function __construct(
method getAuthorizationValidator (line 36) | protected function getAuthorizationValidator(): AuthorizationValidator...
method validateAuthenticatedRequest (line 54) | public function validateAuthenticatedRequest(ServerRequestInterface $r...
FILE: src/ResponseTypes/AbstractResponseType.php
class AbstractResponseType (line 23) | abstract class AbstractResponseType implements ResponseTypeInterface
method setAccessToken (line 33) | public function setAccessToken(
method setRefreshToken (line 40) | public function setRefreshToken(
method setPrivateKey (line 47) | public function setPrivateKey(
FILE: src/ResponseTypes/BearerTokenResponse.php
class BearerTokenResponse (line 26) | class BearerTokenResponse extends AbstractResponseType
method generateHttpResponse (line 28) | public function generateHttpResponse(ResponseInterface $response): Res...
method getExtraParams (line 79) | protected function getExtraParams(
FILE: src/ResponseTypes/DeviceCodeResponse.php
class DeviceCodeResponse (line 24) | class DeviceCodeResponse extends AbstractResponseType
method generateHttpResponse (line 33) | public function generateHttpResponse(ResponseInterface $response): Res...
method setDeviceCodeEntity (line 72) | public function setDeviceCodeEntity(DeviceCodeEntityInterface $deviceC...
method includeVerificationUriComplete (line 77) | public function includeVerificationUriComplete(): void
method includeInterval (line 82) | public function includeInterval(): void
method getExtraParams (line 94) | protected function getExtraParams(DeviceCodeEntityInterface $deviceCod...
FILE: src/ResponseTypes/RedirectResponse.php
class RedirectResponse (line 19) | class RedirectResponse extends AbstractResponseType
method setRedirectUri (line 23) | public function setRedirectUri(string $redirectUri): void
method generateHttpResponse (line 28) | public function generateHttpResponse(ResponseInterface $response): Res...
FILE: src/ResponseTypes/ResponseTypeInterface.php
type ResponseTypeInterface (line 22) | interface ResponseTypeInterface
method setAccessToken (line 24) | public function setAccessToken(AccessTokenEntityInterface $accessToken...
method setRefreshToken (line 26) | public function setRefreshToken(RefreshTokenEntityInterface $refreshTo...
method generateHttpResponse (line 28) | public function generateHttpResponse(ResponseInterface $response): Res...
method setEncryptionKey (line 30) | public function setEncryptionKey(Key|string|null $key = null): void;
FILE: tests/AuthorizationServerTest.php
class AuthorizationServerTest (line 40) | class AuthorizationServerTest extends TestCase
method setUp (line 45) | public function setUp(): void
method testGrantTypeGetsEnabled (line 53) | public function testGrantTypeGetsEnabled(): void
method testRespondToRequestInvalidGrantType (line 70) | public function testRespondToRequestInvalidGrantType(): void
method testRespondToRequest (line 91) | public function testRespondToRequest(): void
method testGetResponseType (line 129) | public function testGetResponseType(): void
method testGetResponseTypeExtended (line 147) | public function testGetResponseTypeExtended(): void
method testMultipleRequestsGetDifferentResponseTypeInstances (line 177) | public function testMultipleRequestsGetDifferentResponseTypeInstances(...
method testCompleteAuthorizationRequest (line 226) | public function testCompleteAuthorizationRequest(): void
method testValidateAuthorizationRequest (line 268) | public function testValidateAuthorizationRequest(): void
method testValidateAuthorizationRequestUnregistered (line 315) | public function testValidateAuthorizationRequestUnregistered(): void
FILE: tests/AuthorizationValidators/BearerTokenValidatorTest.php
class BearerTokenValidatorTest (line 21) | class BearerTokenValidatorTest extends TestCase
method testBearerTokenValidatorAcceptsValidToken (line 23) | public function testBearerTokenValidatorAcceptsValidToken(): void
method testBearerTokenValidatorRejectsExpiredToken (line 50) | public function testBearerTokenValidatorRejectsExpiredToken(): void
method testBearerTokenValidatorAcceptsExpiredTokenWithinLeeway (line 78) | public function testBearerTokenValidatorAcceptsExpiredTokenWithinLeewa...
method testBearerTokenValidatorRejectsExpiredTokenBeyondLeeway (line 108) | public function testBearerTokenValidatorRejectsExpiredTokenBeyondLeewa...
method testBearerTokenValidatorCaseInsensitiveWithBearerHeader (line 139) | public function testBearerTokenValidatorCaseInsensitiveWithBearerHeade...
FILE: tests/CodeChallengeVerifiers/PlainVerifierTest.php
class PlainVerifierTest (line 10) | class PlainVerifierTest extends TestCase
method testGetMethod (line 12) | public function testGetMethod(): void
method testVerifyCodeChallenge (line 19) | public function testVerifyCodeChallenge(): void
FILE: tests/CodeChallengeVerifiers/S256VerifierTest.php
class S256VerifierTest (line 15) | class S256VerifierTest extends TestCase
method testGetMethod (line 17) | public function testGetMethod(): void
method testVerifyCodeChallengeSucceeds (line 24) | public function testVerifyCodeChallengeSucceeds(): void
method testVerifyCodeChallengeFails (line 32) | public function testVerifyCodeChallengeFails(): void
method createCodeChallenge (line 40) | private function createCodeChallenge(string $codeVerifier): string
FILE: tests/EventEmitting/EmitterAwarePolyfillTest.php
class EmitterAwarePolyfillTest (line 11) | class EmitterAwarePolyfillTest extends TestCase
method testGetEmitter (line 13) | public function testGetEmitter(): void
FILE: tests/Exception/OAuthServerExceptionTest.php
class OAuthServerExceptionTest (line 17) | class OAuthServerExceptionTest extends TestCase
method testInvalidClientExceptionSetsAuthenticateHeader (line 19) | public function testInvalidClientExceptionSetsAuthenticateHeader(): void
method testInvalidClientExceptionSetsBearerAuthenticateHeader (line 36) | public function testInvalidClientExceptionSetsBearerAuthenticateHeader...
method testInvalidClientExceptionOmitsAuthenticateHeader (line 53) | public function testInvalidClientExceptionOmitsAuthenticateHeader(): void
method testInvalidClientExceptionOmitsAuthenticateHeaderGivenEmptyAuthorizationHeader (line 69) | public function testInvalidClientExceptionOmitsAuthenticateHeaderGiven...
method issueInvalidClientException (line 91) | private function issueInvalidClientException(ServerRequestInterface $s...
method testHasRedirect (line 108) | public function testHasRedirect(): void
method testDoesNotHaveRedirect (line 115) | public function testDoesNotHaveRedirect(): void
method testHasPrevious (line 122) | public function testHasPrevious(): void
method testDoesNotHavePrevious (line 132) | public function testDoesNotHavePrevious(): void
method testCanGetRedirectionUri (line 139) | public function testCanGetRedirectionUri(): void
method testInvalidCredentialsIsInvalidGrant (line 146) | public function testInvalidCredentialsIsInvalidGrant(): void
FILE: tests/Grant/AbstractGrantTest.php
class AbstractGrantTest (line 32) | class AbstractGrantTest extends TestCase
method testHttpBasicWithPassword (line 34) | public function testHttpBasicWithPassword(): void
method testHttpBasicNoPassword (line 47) | public function testHttpBasicNoPassword(): void
method testHttpBasicNotBasic (line 60) | public function testHttpBasicNotBasic(): void
method testHttpBasicCaseInsensitive (line 73) | public function testHttpBasicCaseInsensitive(): void
method testHttpBasicNotBase64 (line 86) | public function testHttpBasicNotBase64(): void
method testHttpBasicNoColon (line 99) | public function testHttpBasicNoColon(): void
method testGetClientCredentialsClientSecretNotAString (line 112) | public function testGetClientCredentialsClientSecretNotAString(): void
method testValidateClientPublic (line 144) | public function testValidateClientPublic(): void
method testValidateClientConfidential (line 171) | public function testValidateClientConfidential(): void
method testValidateClientMissingClientId (line 200) | public function testValidateClientMissingClientId(): void
method testValidateClientMissingClientSecret (line 221) | public function testValidateClientMissingClientSecret(): void
method testValidateClientInvalidClientSecret (line 244) | public function testValidateClientInvalidClientSecret(): void
method testValidateClientBadClient (line 268) | public function testValidateClientBadClient(): void
method testCanRespondToRequest (line 292) | public function testCanRespondToRequest(): void
method testIssueRefreshToken (line 307) | public function testIssueRefreshToken(): void
method testIssueNullRefreshToken (line 333) | public function testIssueNullRefreshToken(): void
method testIssueNullRefreshTokenUnauthorizedClient (line 355) | public function testIssueNullRefreshTokenUnauthorizedClient(): void
method testIssueAccessToken (line 382) | public function testIssueAccessToken(): void
method testIssueAuthCode (line 408) | public function testIssueAuthCode(): void
method testGetCookieParameter (line 437) | public function testGetCookieParameter(): void
method testGetQueryStringParameter (line 454) | public function testGetQueryStringParameter(): void
method testValidateScopes (line 471) | public function testValidateScopes(): void
method testValidateScopesBadScope (line 485) | public function testValidateScopesBadScope(): void
method testGenerateUniqueIdentifier (line 500) | public function testGenerateUniqueIdentifier(): void
method testCanRespondToAuthorizationRequest (line 512) | public function testCanRespondToAuthorizationRequest(): void
method testValidateAuthorizationRequest (line 520) | public function testValidateAuthorizationRequest(): void
method testCompleteAuthorizationRequest (line 531) | public function testCompleteAuthorizationRequest(): void
method testUnauthorizedClient (line 542) | public function testUnauthorizedClient(): void
FILE: tests/Grant/AuthCodeGrantTest.php
class AuthCodeGrantTest (line 41) | class AuthCodeGrantTest extends TestCase
method setUp (line 52) | public function setUp(): void
method testGetIdentifier (line 57) | public function testGetIdentifier(): void
method testCanRespondToAuthorizationRequest (line 68) | public function testCanRespondToAuthorizationRequest(): void
method testValidateAuthorizationRequest (line 93) | public function testValidateAuthorizationRequest(): void
method testValidateAuthorizationRequestRedirectUriArray (line 133) | public function testValidateAuthorizationRequestRedirectUriArray(): void
method testValidateAuthorizationRequestWithoutRedirectUri (line 172) | public function testValidateAuthorizationRequestWithoutRedirectUri(): ...
method testValidateAuthorizationRequestCodeChallenge (line 214) | public function testValidateAuthorizationRequestCodeChallenge(): void
method testValidateAuthorizationRequestCodeChallengeInvalidLengthTooShort (line 254) | public function testValidateAuthorizationRequestCodeChallengeInvalidLe...
method testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLong (line 284) | public function testValidateAuthorizationRequestCodeChallengeInvalidLe...
method testValidateAuthorizationRequestCodeChallengeInvalidCharacters (line 314) | public function testValidateAuthorizationRequestCodeChallengeInvalidCh...
method testValidateAuthorizationRequestMissingClientId (line 344) | public function testValidateAuthorizationRequestMissingClientId(): void
method testValidateAuthorizationRequestInvalidClientId (line 365) | public function testValidateAuthorizationRequestInvalidClientId(): void
method testValidateAuthorizationRequestBadRedirectUriString (line 388) | public function testValidateAuthorizationRequestBadRedirectUriString()...
method testValidateAuthorizationRequestBadRedirectUriArray (line 414) | public function testValidateAuthorizationRequestBadRedirectUriArray():...
method testValidateAuthorizationRequestInvalidCodeChallengeMethod (line 440) | public function testValidateAuthorizationRequestInvalidCodeChallengeMe...
method testValidateAuthorizationRequestInvalidScopes (line 475) | public function testValidateAuthorizationRequestInvalidScopes(): void
method testCompleteAuthorizationRequest (line 518) | public function testCompleteAuthorizationRequest(): void
method testCompleteAuthorizationRequestWithMultipleRedirectUrisOnClient (line 543) | public function testCompleteAuthorizationRequestWithMultipleRedirectUr...
method testCompleteAuthorizationRequestDenied (line 568) | public function testCompleteAuthorizationRequestDenied(): void
method testRespondToAccessTokenRequest (line 603) | public function testRespondToAccessTokenRequest(): void
method testRespondToAccessTokenRequestWithDefaultRedirectUri (line 705) | public function testRespondToAccessTokenRequestWithDefaultRedirectUri(...
method testRespondToAccessTokenRequestUsingHttpBasicAuth (line 778) | public function testRespondToAccessTokenRequestUsingHttpBasicAuth(): void
method testRespondToAccessTokenRequestForPublicClient (line 844) | public function testRespondToAccessTokenRequestForPublicClient(): void
method testRespondToAccessTokenRequestNullRefreshToken (line 912) | public function testRespondToAccessTokenRequestNullRefreshToken(): void
method testRespondToAccessTokenRequestCodeChallengePlain (line 981) | public function testRespondToAccessTokenRequestCodeChallengePlain(): void
method testRespondToAccessTokenRequestCodeChallengeS256 (line 1058) | public function testRespondToAccessTokenRequestCodeChallengeS256(): void
method testPKCEDowngradeBlocked (line 1135) | public function testPKCEDowngradeBlocked(): void
method testRespondToAccessTokenRequestMissingRedirectUri (line 1208) | public function testRespondToAccessTokenRequestMissingRedirectUri(): void
method testRespondToAccessTokenRequestRedirectUriMismatch (line 1258) | public function testRespondToAccessTokenRequestRedirectUriMismatch(): ...
method testRejectAccessTokenRequestIfRedirectUriSpecifiedButNotInOriginalAuthCodeRequest (line 1309) | public function testRejectAccessTokenRequestIfRedirectUriSpecifiedButN...
method testRespondToAccessTokenRequestMissingCode (line 1360) | public function testRespondToAccessTokenRequestMissingCode(): void
method testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode (line 1409) | public function testRespondToAccessTokenRequestWithRefreshTokenInstead...
method testRespondToAccessTokenRequestWithAuthCodeNotAString (line 1461) | public function testRespondToAccessTokenRequestWithAuthCodeNotAString(...
method testRespondToAccessTokenRequestExpiredCode (line 1499) | public function testRespondToAccessTokenRequestExpiredCode(): void
method testRespondToAccessTokenRequestRevokedCode (line 1551) | public function testRespondToAccessTokenRequestRevokedCode(): void
method testRespondToAccessTokenRequestClientMismatch (line 1619) | public function testRespondToAccessTokenRequestClientMismatch(): void
method testRespondToAccessTokenRequestBadCode (line 1683) | public function testRespondToAccessTokenRequestBadCode(): void
method testRespondToAccessTokenRequestNoEncryptionKey (line 1739) | public function testRespondToAccessTokenRequestNoEncryptionKey(): void
method testRespondToAccessTokenRequestBadCodeVerifierPlain (line 1795) | public function testRespondToAccessTokenRequestBadCodeVerifierPlain():...
method testRespondToAccessTokenRequestBadCodeVerifierS256 (line 1869) | public function testRespondToAccessTokenRequestBadCodeVerifierS256(): ...
method testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInvalidChars (line 1943) | public function testRespondToAccessTokenRequestMalformedCodeVerifierS2...
method testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInvalidLength (line 2017) | public function testRespondToAccessTokenRequestMalformedCodeVerifierS2...
method testRespondToAccessTokenRequestMissingCodeVerifier (line 2091) | public function testRespondToAccessTokenRequestMissingCodeVerifier(): ...
method testAuthCodeRepositoryUniqueConstraintCheck (line 2164) | public function testAuthCodeRepositoryUniqueConstraintCheck(): void
method testAuthCodeRepositoryFailToPersist (line 2202) | public function testAuthCodeRepositoryFailToPersist(): void
method testAuthCodeRepositoryFailToPersistUniqueNoInfiniteLoop (line 2231) | public function testAuthCodeRepositoryFailToPersistUniqueNoInfiniteLoo...
method testRefreshTokenRepositoryUniqueConstraintCheck (line 2259) | public function testRefreshTokenRepositoryUniqueConstraintCheck(): void
method testRefreshTokenRepositoryFailToPersist (line 2337) | public function testRefreshTokenRepositoryFailToPersist(): void
method testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop (line 2408) | public function testRefreshTokenRepositoryFailToPersistUniqueNoInfinit...
method testCompleteAuthorizationRequestNoUser (line 2479) | public function testCompleteAuthorizationRequestNoUser(): void
method testPublicClientAuthCodeRequestRejectedWhenCodeChallengeRequiredButNotGiven (line 2492) | public function testPublicClientAuthCodeRequestRejectedWhenCodeChallen...
method testUseValidRedirectUriIfScopeCheckFails (line 2527) | public function testUseValidRedirectUriIfScopeCheckFails(): void
FILE: tests/Grant/ClientCredentialsGrantTest.php
class ClientCredentialsGrantTest (line 22) | class ClientCredentialsGrantTest extends TestCase
method testGetIdentifier (line 26) | public function testGetIdentifier(): void
method testRespondToRequest (line 32) | public function testRespondToRequest(): void
FILE: tests/Grant/DeviceCodeGrantTest.php
class DeviceCodeGrantTest (line 36) | class DeviceCodeGrantTest extends TestCase
method testGetIdentifier (line 41) | public function testGetIdentifier(): void
method testCanRespondToDeviceAuthorizationRequest (line 56) | public function testCanRespondToDeviceAuthorizationRequest(): void
method testRespondToDeviceAuthorizationRequest (line 73) | public function testRespondToDeviceAuthorizationRequest(): void
method testRespondToDeviceAuthorizationRequestWithVerificationUriComplete (line 115) | public function testRespondToDeviceAuthorizationRequestWithVerificatio...
method testValidateDeviceAuthorizationRequestMissingClient (line 161) | public function testValidateDeviceAuthorizationRequestMissingClient():...
method testValidateDeviceAuthorizationRequestEmptyScope (line 192) | public function testValidateDeviceAuthorizationRequestEmptyScope(): void
method testValidateDeviceAuthorizationRequestClientMismatch (line 223) | public function testValidateDeviceAuthorizationRequestClientMismatch()...
method testCompleteDeviceAuthorizationRequest (line 252) | public function testCompleteDeviceAuthorizationRequest(): void
method testDeviceAuthorizationResponse (line 273) | public function testDeviceAuthorizationResponse(): void
method testRespondToAccessTokenRequest (line 329) | public function testRespondToAccessTokenRequest(): void
method testRespondToRequestMissingClient (line 429) | public function testRespondToRequestMissingClient(): void
method testRespondToRequestMissingDeviceCode (line 459) | public function testRespondToRequestMissingDeviceCode(): void
method testIssueSlowDownError (line 504) | public function testIssueSlowDownError(): void
method testIssueAuthorizationPendingError (line 552) | public function testIssueAuthorizationPendingError(): void
method testIssueExpiredTokenError (line 599) | public function testIssueExpiredTokenError(): void
method testSettingDeviceCodeIntervalRate (line 646) | public function testSettingDeviceCodeIntervalRate(): void
method testSettingInternalVisibility (line 692) | public function testSettingInternalVisibility(): void
method testIssueAccessDeniedError (line 737) | public function testIssueAccessDeniedError(): void
FILE: tests/Grant/ImplicitGrantTest.php
class ImplicitGrantTest (line 31) | class ImplicitGrantTest extends TestCase
method setUp (line 38) | public function setUp(): void
method testGetIdentifier (line 43) | public function testGetIdentifier(): void
method testCanRespondToAccessTokenRequest (line 49) | public function testCanRespondToAccessTokenRequest(): void
method testRespondToAccessTokenRequest (line 58) | public function testRespondToAccessTokenRequest(): void
method testCanRespondToAuthorizationRequest (line 71) | public function testCanRespondToAuthorizationRequest(): void
method testValidateAuthorizationRequest (line 83) | public function testValidateAuthorizationRequest(): void
method testValidateAuthorizationRequestRedirectUriArray (line 108) | public function testValidateAuthorizationRequestRedirectUriArray(): void
method testValidateAuthorizationRequestMissingClientId (line 133) | public function testValidateAuthorizationRequestMissingClientId(): void
method testValidateAuthorizationRequestInvalidClientId (line 148) | public function testValidateAuthorizationRequestInvalidClientId(): void
method testValidateAuthorizationRequestBadRedirectUriString (line 167) | public function testValidateAuthorizationRequestBadRedirectUriString()...
method testValidateAuthorizationRequestBadRedirectUriArray (line 189) | public function testValidateAuthorizationRequestBadRedirectUriArray():...
method testValidateAuthorizationRequestInvalidScopes (line 211) | public function testValidateAuthorizationRequestInvalidScopes(): void
method testCompleteAuthorizationRequest (line 249) | public function testCompleteAuthorizationRequest(): void
method testCompleteAuthorizationRequestDenied (line 295) | public function testCompleteAuthorizationRequestDenied(): void
method testAccessTokenRepositoryUniqueConstraintCheck (line 333) | public function testAccessTokenRepositoryUniqueConstraintCheck(): void
method testAccessTokenRepositoryFailToPersist (line 375) | public function testAccessTokenRepositoryFailToPersist(): void
method testAccessTokenRepositoryFailToPersistUniqueNoInfiniteLoop (line 407) | public function testAccessTokenRepositoryFailToPersistUniqueNoInfinite...
method testSetRefreshTokenTTL (line 439) | public function testSetRefreshTokenTTL(): void
method testSetRefreshTokenRepository (line 448) | public function testSetRefreshTokenRepository(): void
method testCompleteAuthorizationRequestNoUser (line 459) | public function testCompleteAuthorizationRequestNoUser(): void
FILE: tests/Grant/PasswordGrantTest.php
class PasswordGrantTest (line 29) | class PasswordGrantTest extends TestCase
method testGetIdentifier (line 33) | public function testGetIdentifier(): void
method testRespondToRequest (line 42) | public function testRespondToRequest(): void
method testRespondToRequestNullRefreshToken (line 119) | public function testRespondToRequestNullRefreshToken(): void
method testRespondToRequestMissingUsername (line 167) | public function testRespondToRequestMissingUsername(): void
method testRespondToRequestMissingPassword (line 195) | public function testRespondToRequestMissingPassword(): void
method testRespondToRequestBadCredentials (line 229) | public function testRespondToRequestBadCredentials(): void
FILE: tests/Grant/RefreshTokenGrantTest.php
class RefreshTokenGrantTest (line 33) | class RefreshTokenGrantTest extends TestCase
method setUp (line 37) | public function setUp(): void
method testGetIdentifier (line 42) | public function testGetIdentifier(): void
method testRespondToRequest (line 50) | public function testRespondToRequest(): void
method testRespondToRequestNullRefreshToken (line 145) | public function testRespondToRequestNullRefreshToken(): void
method testRespondToReducedScopes (line 212) | public function testRespondToReducedScopes(): void
method testRespondToUnexpectedScope (line 278) | public function testRespondToUnexpectedScope(): void
method testRespondToRequestMissingOldToken (line 340) | public function testRespondToRequestMissingOldToken(): void
method testRespondToRequestInvalidOldToken (line 372) | public function testRespondToRequestInvalidOldToken(): void
method testRespondToRequestClientMismatch (line 407) | public function testRespondToRequestClientMismatch(): void
method testRespondToRequestExpiredToken (line 462) | public function testRespondToRequestExpiredToken(): void
method testRespondToRequestRevokedToken (line 514) | public function testRespondToRequestRevokedToken(): void
method testRespondToRequestFinalizeScopes (line 567) | public function testRespondToRequestFinalizeScopes(): void
method testRevokedRefreshToken (line 650) | public function testRevokedRefreshToken(): void
method testUnrevokedRefreshToken (line 719) | public function testUnrevokedRefreshToken(): void
method testRespondToRequestWithIntUserId (line 803) | public function testRespondToRequestWithIntUserId(): void
FILE: tests/Middleware/AuthorizationServerMiddlewareTest.php
class AuthorizationServerMiddlewareTest (line 27) | class AuthorizationServerMiddlewareTest extends TestCase
method testValidResponse (line 31) | public function testValidResponse(): void
method testOAuthErrorResponse (line 78) | public function testOAuthErrorResponse(): void
method testOAuthErrorResponseRedirectUri (line 113) | public function testOAuthErrorResponseRedirectUri(): void
method testOAuthErrorResponseRedirectUriFragment (line 125) | public function testOAuthErrorResponseRedirectUriFragment(): void
FILE: tests/Middleware/ResourceServerMiddlewareTest.php
class ResourceServerMiddlewareTest (line 22) | class ResourceServerMiddlewareTest extends TestCase
method testValidResponse (line 24) | public function testValidResponse(): void
method testValidResponseExpiredToken (line 59) | public function testValidResponseExpiredToken(): void
method testErrorResponse (line 94) | public function testErrorResponse(): void
FILE: tests/PHPStan/AbstractGrantExtension.php
class AbstractGrantExtension (line 19) | final class AbstractGrantExtension implements DynamicMethodReturnTypeExt...
method getClass (line 21) | public function getClass(): string
method isMethodSupported (line 26) | public function isMethodSupported(MethodReflection $methodReflection):...
method getTypeFromMethodCall (line 35) | public function getTypeFromMethodCall(MethodReflection $methodReflecti...
FILE: tests/RedirectUriValidators/RedirectUriValidatorTest.php
class RedirectUriValidatorTest (line 10) | class RedirectUriValidatorTest extends TestCase
method testInvalidNonLoopbackUri (line 12) | public function testInvalidNonLoopbackUri(): void
method testValidNonLoopbackUri (line 27) | public function testValidNonLoopbackUri(): void
method testInvalidLoopbackUri (line 42) | public function testInvalidLoopbackUri(): void
method testValidLoopbackUri (line 54) | public function testValidLoopbackUri(): void
method testValidIpv6LoopbackUri (line 66) | public function testValidIpv6LoopbackUri(): void
method testCanValidateUrn (line 78) | public function testCanValidateUrn(): void
method canValidateCustomSchemeHost (line 88) | public function canValidateCustomSchemeHost(): void
method canValidateCustomSchemePath (line 98) | public function canValidateCustomSchemePath(): void
FILE: tests/ResourceServerTest.php
class ResourceServerTest (line 13) | class ResourceServerTest extends TestCase
method testValidateAuthenticatedRequest (line 15) | public function testValidateAuthenticatedRequest(): void
FILE: tests/ResponseTypes/BearerResponseTypeTest.php
class BearerResponseTypeTest (line 27) | class BearerResponseTypeTest extends TestCase
method testGenerateHttpResponse (line 29) | public function testGenerateHttpResponse(): void
method testGenerateHttpResponseWithExtraParams (line 72) | public function testGenerateHttpResponseWithExtraParams(): void
method testDetermineAccessTokenInHeaderValidToken (line 118) | public function testDetermineAccessTokenInHeaderValidToken(): void
method testDetermineAccessTokenInHeaderInvalidJWT (line 161) | public function testDetermineAccessTokenInHeaderInvalidJWT(): void
method testDetermineAccessTokenInHeaderRevokedToken (line 205) | public function testDetermineAccessTokenInHeaderRevokedToken(): void
method testDetermineAccessTokenInHeaderInvalidToken (line 250) | public function testDetermineAccessTokenInHeaderInvalidToken(): void
method testDetermineMissingBearerInHeader (line 273) | public function testDetermineMissingBearerInHeader(): void
FILE: tests/ResponseTypes/BearerTokenResponseWithParams.php
class BearerTokenResponseWithParams (line 10) | class BearerTokenResponseWithParams extends BearerTokenResponse
method getExtraParams (line 15) | protected function getExtraParams(AccessTokenEntityInterface $accessTo...
FILE: tests/ResponseTypes/DeviceCodeResponseTypeTest.php
class DeviceCodeResponseTypeTest (line 21) | class DeviceCodeResponseTypeTest extends TestCase
method testGenerateHttpResponse (line 23) | public function testGenerateHttpResponse(): void
FILE: tests/Stubs/AccessTokenEntity.php
class AccessTokenEntity (line 12) | class AccessTokenEntity implements AccessTokenEntityInterface
FILE: tests/Stubs/AuthCodeEntity.php
class AuthCodeEntity (line 12) | class AuthCodeEntity implements AuthCodeEntityInterface
FILE: tests/Stubs/ClientEntity.php
class ClientEntity (line 11) | class ClientEntity implements ClientEntityInterface
method setRedirectUri (line 19) | public function setRedirectUri(string|array $uri): void
method setConfidential (line 24) | public function setConfidential(): void
FILE: tests/Stubs/CryptTraitStub.php
class CryptTraitStub (line 13) | class CryptTraitStub
method __construct (line 17) | public function __construct()
method getKey (line 22) | public function getKey(): string|Key|null
method doEncrypt (line 27) | public function doEncrypt(string $unencryptedData): string
method doDecrypt (line 32) | public function doDecrypt(string $encryptedData): string
FILE: tests/Stubs/DeviceCodeEntity.php
class DeviceCodeEntity (line 12) | class DeviceCodeEntity implements DeviceCodeEntityInterface
FILE: tests/Stubs/GrantType.php
class GrantType (line 22) | final class GrantType implements GrantTypeInterface
method setEmitter (line 26) | public function setEmitter(EventEmitter $emitter): self
method getEmitter (line 33) | public function getEmitter(): EventEmitter
method setRefreshTokenTTL (line 38) | public function setRefreshTokenTTL(DateInterval $refreshTokenTTL): void
method getIdentifier (line 42) | public function getIdentifier(): string
method respondToAccessTokenRequest (line 47) | public function respondToAccessTokenRequest(
method canRespondToAuthorizationRequest (line 55) | public function canRespondToAuthorizationRequest(ServerRequestInterfac...
method validateAuthorizationRequest (line 60) | public function validateAuthorizationRequest(ServerRequestInterface $r...
method completeAuthorizationRequest (line 68) | public function completeAuthorizationRequest(AuthorizationRequestInter...
method canRespondToAccessTokenRequest (line 73) | public function canRespondToAccessTokenRequest(ServerRequestInterface ...
method setClientRepository (line 78) | public function setClientRepository(ClientRepositoryInterface $clientR...
method setAccessTokenRepository (line 82) | public function setAccessTokenRepository(AccessTokenRepositoryInterfac...
method setScopeRepository (line 86) | public function setScopeRepository(ScopeRepositoryInterface $scopeRepo...
method setDefaultScope (line 90) | public function setDefaultScope(string $scope): void
method setPrivateKey (line 94) | public function setPrivateKey(CryptKeyInterface $privateKey): void
method setEncryptionKey (line 98) | public function setEncryptionKey(Key|string|null $key = null): void
method revokeRefreshTokens (line 102) | public function revokeRefreshTokens(bool $willRevoke): void
method canRespondToDeviceAuthorizationRequest (line 106) | public function canRespondToDeviceAuthorizationRequest(ServerRequestIn...
method completeDeviceAuthorizationRequest (line 111) | public function completeDeviceAuthorizationRequest(string $deviceCode,...
method respondToDeviceAuthorizationRequest (line 115) | public function respondToDeviceAuthorizationRequest(ServerRequestInter...
method setIntervalVisibility (line 120) | public function setIntervalVisibility(bool $intervalVisibility): void
method getIntervalVisibility (line 124) | public function getIntervalVisibility(): bool
method setIncludeVerificationUriComplete (line 129) | public function setIncludeVerificationUriComplete(bool $includeVerific...
FILE: tests/Stubs/RefreshTokenEntity.php
class RefreshTokenEntity (line 11) | class RefreshTokenEntity implements RefreshTokenEntityInterface
FILE: tests/Stubs/ScopeEntity.php
class ScopeEntity (line 12) | class ScopeEntity implements ScopeEntityInterface
method jsonSerialize (line 17) | #[ReturnTypeWillChange]
FILE: tests/Stubs/StubResponseType.php
class StubResponseType (line 15) | class StubResponseType extends AbstractResponseType
method getAccessToken (line 17) | public function getAccessToken(): AccessTokenEntityInterface
method getRefreshToken (line 22) | public function getRefreshToken(): RefreshTokenEntityInterface|null
method setAccessToken (line 27) | public function setAccessToken(AccessTokenEntityInterface $accessToken...
method setRefreshToken (line 32) | public function setRefreshToken(RefreshTokenEntityInterface $refreshTo...
method validateAccessToken (line 40) | public function validateAccessToken(ServerRequestInterface $request): ...
method generateHttpResponse (line 49) | public function generateHttpResponse(ResponseInterface $response): Res...
FILE: tests/Stubs/UserEntity.php
class UserEntity (line 10) | class UserEntity implements UserEntityInterface
method __construct (line 14) | public function __construct()
FILE: tests/Utils/CryptKeyTest.php
class CryptKeyTest (line 20) | class CryptKeyTest extends TestCase
method testNoFile (line 22) | public function testNoFile(): void
method testKeyCreation (line 29) | public function testKeyCreation(): void
method testKeyString (line 38) | public function testKeyString(): void
method testUnsupportedKeyType (line 67) | public function testUnsupportedKeyType(): void
method testECKeyType (line 96) | public function testECKeyType(): void
method testRSAKeyType (line 122) | public function testRSAKeyType(): void
method generateKeyPath (line 148) | private static function generateKeyPath(string $keyContent): string
FILE: tests/Utils/CryptTraitTest.php
class CryptTraitTest (line 14) | class CryptTraitTest extends TestCase
method setUp (line 18) | protected function setUp(): void
method testEncryptDecryptWithPassword (line 23) | public function testEncryptDecryptWithPassword(): void
method testEncryptDecryptWithKey (line 30) | public function testEncryptDecryptWithKey(): void
method encryptDecrypt (line 37) | private function encryptDecrypt(): void
Condensed preview — 143 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (606K chars).
[
{
"path": ".gitattributes",
"chars": 375,
"preview": "* text=auto\n\n/examples export-ignore\n/tests export-ignore\n/.gitattributes export-ignore\n/.github export-ignore\n/.gitigno"
},
{
"path": ".github/FUNDING.yml",
"chars": 19,
"preview": "github: [sephster]\n"
},
{
"path": ".github/dependabot.yml",
"chars": 218,
"preview": "version: 2\nupdates:\n- package-ecosystem: composer\n directory: \"/\"\n schedule:\n interval: daily\n time: \"11:00\"\n o"
},
{
"path": ".github/workflows/backwards-compatibility.yml",
"chars": 646,
"preview": "name: \"Backwards compatibility check\"\n\non:\n pull_request:\n\njobs:\n bc-check:\n name: \"Backwards compatibility check\"\n"
},
{
"path": ".github/workflows/coding-standards.yml",
"chars": 754,
"preview": "name: Coding Standards\n\non:\n pull_request:\n push:\n\njobs:\n coding-standards:\n name: Coding Standards\n\n runs-on: "
},
{
"path": ".github/workflows/static-analysis.yml",
"chars": 848,
"preview": "name: Static Analysis\n\non:\n push:\n pull_request:\n\njobs:\n static-analysis:\n name: Static Analysis\n\n runs-on: ${{"
},
{
"path": ".github/workflows/tests.yml",
"chars": 1301,
"preview": "name: tests\n\non:\n push:\n pull_request:\n schedule:\n - cron: \"0 0 * * *\"\n\njobs:\n tests:\n strategy:\n fail-fa"
},
{
"path": ".gitignore",
"chars": 134,
"preview": "/vendor\n/composer.lock\nphpunit.xml\n.phpunit.result.cache\n.idea\n/examples/vendor\nexamples/public.key\nexamples/private.key"
},
{
"path": ".scrutinizer.yml",
"chars": 1100,
"preview": "build:\n environment:\n php:\n version: 8.3.3\n nodes:\n analysis:\n tests:\n "
},
{
"path": ".styleci.yml",
"chars": 955,
"preview": "preset: psr12\n\nrisky: true\n\nenabled:\n - blank_line_before_return\n - fully_qualified_strict_types\n - hash_to_slash_com"
},
{
"path": "CHANGELOG.md",
"chars": 38327,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3230,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CONTRIBUTING.md",
"chars": 539,
"preview": "Thanks for contributing to this project.\n\n\n**Please submit your pull request against the `master` branch only.**\n\n\nPleas"
},
{
"path": "LICENSE",
"chars": 1062,
"preview": "MIT License\n\nCopyright (C) Alex Bilbie\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\n"
},
{
"path": "README.md",
"chars": 5593,
"preview": "# PHP OAuth 2.0 Server\n\n["
},
{
"path": "composer.json",
"chars": 2402,
"preview": "{\n \"name\": \"league/oauth2-server\",\n \"description\": \"A lightweight and powerful OAuth 2.0 authorization and resource se"
},
{
"path": "examples/README.md",
"chars": 2944,
"preview": "# Example implementations (via [`Slim 3`](https://github.com/slimphp/Slim/tree/3.x))\n\n## Installation\n\n0. Run `composer "
},
{
"path": "examples/composer.json",
"chars": 439,
"preview": "{\n \"require\": {\n \"slim/slim\": \"^3.12.3\"\n },\n \"require-dev\": {\n \"league/event\": \"^3.0\",\n \"l"
},
{
"path": "examples/public/api.php",
"chars": 2288,
"preview": "<?php\n\ndeclare(strict_types=1);\n\ninclude __DIR__ . '/../vendor/autoload.php';\n\nuse League\\OAuth2\\Server\\Middleware\\Resou"
},
{
"path": "examples/public/auth_code.php",
"chars": 3964,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/public/client_credentials.php",
"chars": 2788,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/public/device_code.php",
"chars": 3904,
"preview": "<?php\n\n/**\n * @author Andrew Millington <andrew@noexceptions.io>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/public/implicit.php",
"chars": 2899,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/public/middleware_use.php",
"chars": 3639,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/public/password.php",
"chars": 2765,
"preview": "<?php\n\ndeclare(strict_types=1);\n\ninclude __DIR__ . '/../vendor/autoload.php';\n\nuse League\\OAuth2\\Server\\AuthorizationSer"
},
{
"path": "examples/public/refresh_token.php",
"chars": 2543,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/src/Entities/AccessTokenEntity.php",
"chars": 656,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/src/Entities/AuthCodeEntity.php",
"chars": 641,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/src/Entities/ClientEntity.php",
"chars": 833,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/src/Entities/DeviceCodeEntity.php",
"chars": 641,
"preview": "<?php\n\n/**\n * @author Andrew Millington <andrew@noexceptions.io>\n * @copyright Copyright (c) Alex Bilbie\n * @license htt"
},
{
"path": "examples/src/Entities/RefreshTokenEntity.php",
"chars": 576,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/src/Entities/ScopeEntity.php",
"chars": 541,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/src/Entities/UserEntity.php",
"chars": 519,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/src/Repositories/AccessTokenRepository.php",
"chars": 1664,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/src/Repositories/AuthCodeRepository.php",
"chars": 1199,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/src/Repositories/ClientRepository.php",
"chars": 1851,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/src/Repositories/DeviceCodeRepository.php",
"chars": 2354,
"preview": "<?php\n\n/**\n * @author Andrew Millington <andrew@noexceptions.io>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/src/Repositories/RefreshTokenRepository.php",
"chars": 1266,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/src/Repositories/ScopeRepository.php",
"chars": 1696,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "examples/src/Repositories/UserRepository.php",
"chars": 926,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "phpcs.xml.dist",
"chars": 1498,
"preview": "<?xml version=\"1.0\"?>\n<ruleset\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:noNamespaceSchemaLocati"
},
{
"path": "phpstan.neon.dist",
"chars": 240,
"preview": "parameters:\n level: 8\n paths:\n - src\n - tests\n ignoreErrors:\n - \n message: '#De"
},
{
"path": "phpunit.xml.dist",
"chars": 572,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:noNam"
},
{
"path": "src/AuthorizationServer.php",
"chars": 7108,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/AuthorizationValidators/AuthorizationValidatorInterface.php",
"chars": 632,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/AuthorizationValidators/BearerTokenValidator.php",
"chars": 4567,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php",
"chars": 576,
"preview": "<?php\n\n/**\n * @author Lukáš Unger <lookymsc@gmail.com>\n * @copyright Copyright (c) Lukáš Unger\n * @license ht"
},
{
"path": "src/CodeChallengeVerifiers/PlainVerifier.php",
"chars": 729,
"preview": "<?php\n\n/**\n * @author Lukáš Unger <lookymsc@gmail.com>\n * @copyright Copyright (c) Lukáš Unger\n * @license ht"
},
{
"path": "src/CodeChallengeVerifiers/S256Verifier.php",
"chars": 916,
"preview": "<?php\n\n/**\n * @author Lukáš Unger <lookymsc@gmail.com>\n * @copyright Copyright (c) Lukáš Unger\n * @license ht"
},
{
"path": "src/CryptKey.php",
"chars": 4024,
"preview": "<?php\n\n/**\n * Cryptography key holder.\n *\n * @author Julián Gutiérrez <juliangut@gmail.com>\n * @copyright Copyrig"
},
{
"path": "src/CryptKeyInterface.php",
"chars": 407,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace League\\OAuth2\\Server;\n\ninterface CryptKeyInterface\n{\n /**\n * Retrieve "
},
{
"path": "src/CryptTrait.php",
"chars": 2771,
"preview": "<?php\n\n/**\n * Encrypt/decrypt with encryptionKey.\n *\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright "
},
{
"path": "src/Entities/AccessTokenEntityInterface.php",
"chars": 656,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Entities/AuthCodeEntityInterface.php",
"chars": 450,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Entities/ClientEntityInterface.php",
"chars": 1087,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Entities/DeviceCodeEntityInterface.php",
"chars": 995,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Entities/RefreshTokenEntityInterface.php",
"chars": 1242,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Entities/ScopeEntityInterface.php",
"chars": 500,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Entities/TokenInterface.php",
"chars": 1790,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Entities/Traits/AccessTokenTrait.php",
"chars": 3083,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Entities/Traits/AuthCodeTrait.php",
"chars": 556,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Entities/Traits/ClientTrait.php",
"chars": 1249,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Entities/Traits/DeviceCodeTrait.php",
"chars": 2598,
"preview": "<?php\n\n/**\n * @author Andrew Millington <andrew@noexceptions.io>\n * @copyright Copyright (c) Alex Bilbie\n * @lice"
},
{
"path": "src/Entities/Traits/EntityTrait.php",
"chars": 702,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Entities/Traits/RefreshTokenTrait.php",
"chars": 1194,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Entities/Traits/ScopeTrait.php",
"chars": 567,
"preview": "<?php\n\n/**\n * @author Andrew Millington <andrew@noexceptions.io>\n * @copyright Copyright (c) Andrew Millington\n * @li"
},
{
"path": "src/Entities/Traits/TokenEntityTrait.php",
"chars": 2363,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Entities/UserEntityInterface.php",
"chars": 453,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/EventEmitting/AbstractEvent.php",
"chars": 835,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace League\\OAuth2\\Server\\EventEmitting;\n\nuse League\\Event\\HasEventName;\nuse Psr\\E"
},
{
"path": "src/EventEmitting/EmitterAwareInterface.php",
"chars": 226,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace League\\OAuth2\\Server\\EventEmitting;\n\ninterface EmitterAwareInterface\n{\n pu"
},
{
"path": "src/EventEmitting/EmitterAwarePolyfill.php",
"chars": 702,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace League\\OAuth2\\Server\\EventEmitting;\n\nuse League\\Event\\ListenerRegistry;\nuse P"
},
{
"path": "src/EventEmitting/EventEmitter.php",
"chars": 512,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace League\\OAuth2\\Server\\EventEmitting;\n\nuse League\\Event\\EventDispatcher;\nuse Le"
},
{
"path": "src/Exception/OAuthServerException.php",
"chars": 11649,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Exception/UniqueTokenIdentifierConstraintViolationException.php",
"chars": 624,
"preview": "<?php\n\n/**\n * @author Ivan Kurnosov <zerkms@zerkms.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license h"
},
{
"path": "src/Grant/AbstractAuthorizeGrant.php",
"chars": 1331,
"preview": "<?php\n\n/**\n * Abstract authorization grant.\n *\n * @author Julián Gutiérrez <juliangut@gmail.com>\n * @copyright Co"
},
{
"path": "src/Grant/AbstractGrant.php",
"chars": 20218,
"preview": "<?php\n\n/**\n * OAuth 2.0 Abstract grant.\n *\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright "
},
{
"path": "src/Grant/AuthCodeGrant.php",
"chars": 16014,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Grant/ClientCredentialsGrant.php",
"chars": 2043,
"preview": "<?php\n\n/**\n * OAuth 2.0 Client credentials grant.\n *\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright "
},
{
"path": "src/Grant/DeviceCodeGrant.php",
"chars": 11266,
"preview": "<?php\n\n/**\n * OAuth 2.0 Device Code grant.\n *\n * @author Andrew Millington <andrew@noexceptions.io>\n * @copyright "
},
{
"path": "src/Grant/GrantTypeInterface.php",
"chars": 5649,
"preview": "<?php\n\n/**\n * OAuth 2.0 Grant type interface.\n *\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copy"
},
{
"path": "src/Grant/ImplicitGrant.php",
"chars": 7104,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Grant/PasswordGrant.php",
"chars": 3749,
"preview": "<?php\n\n/**\n * OAuth 2.0 Password grant.\n *\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright "
},
{
"path": "src/Grant/RefreshTokenGrant.php",
"chars": 4912,
"preview": "<?php\n\n/**\n * OAuth 2.0 Refresh token grant.\n *\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyr"
},
{
"path": "src/Middleware/AuthorizationServerMiddleware.php",
"chars": 1085,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Middleware/ResourceServerMiddleware.php",
"chars": 1059,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/RedirectUriValidators/RedirectUriValidator.php",
"chars": 2741,
"preview": "<?php\n\n/**\n * @author Sebastiano Degan <sebdeg87@gmail.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/RedirectUriValidators/RedirectUriValidatorInterface.php",
"chars": 461,
"preview": "<?php\n\n/**\n * @author Sebastiano Degan <sebdeg87@gmail.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Repositories/AccessTokenRepositoryInterface.php",
"chars": 1243,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Repositories/AuthCodeRepositoryInterface.php",
"chars": 895,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Repositories/ClientRepositoryInterface.php",
"chars": 736,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Repositories/DeviceCodeRepositoryInterface.php",
"chars": 1381,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Repositories/RefreshTokenRepositoryInterface.php",
"chars": 930,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Repositories/RepositoryInterface.php",
"chars": 353,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Repositories/ScopeRepositoryInterface.php",
"chars": 1227,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/Repositories/UserRepositoryInterface.php",
"chars": 711,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/RequestAccessTokenEvent.php",
"chars": 858,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/RequestEvent.php",
"chars": 1059,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/RequestRefreshTokenEvent.php",
"chars": 865,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/RequestTypes/AuthorizationRequest.php",
"chars": 3532,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/RequestTypes/AuthorizationRequestInterface.php",
"chars": 1662,
"preview": "<?php\n\n/**\n * @author Patrick Rodacker <dev@rodacker.de>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/ResourceServer.php",
"chars": 1905,
"preview": "<?php\n\n/**\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyright (c) Alex Bilbie\n * @license "
},
{
"path": "src/ResponseTypes/AbstractResponseType.php",
"chars": 1323,
"preview": "<?php\n\n/**\n * OAuth 2.0 Abstract Response Type.\n *\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Co"
},
{
"path": "src/ResponseTypes/BearerTokenResponse.php",
"chars": 2856,
"preview": "<?php\n\n/**\n * OAuth 2.0 Bearer Token Response.\n *\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Cop"
},
{
"path": "src/ResponseTypes/DeviceCodeResponse.php",
"chars": 2904,
"preview": "<?php\n\n/**\n * OAuth 2.0 Bearer Token Response.\n *\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Cop"
},
{
"path": "src/ResponseTypes/RedirectResponse.php",
"chars": 759,
"preview": "<?php\n\n/**\n * OAuth 2.0 Redirect Response.\n *\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright Copyrig"
},
{
"path": "src/ResponseTypes/ResponseTypeInterface.php",
"chars": 888,
"preview": "<?php\n\n/**\n * OAuth 2.0 Response Type Interface.\n *\n * @author Alex Bilbie <hello@alexbilbie.com>\n * @copyright C"
},
{
"path": "tests/AuthorizationServerTest.php",
"chars": 13520,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests;\n\nuse DateInterval;\nuse Defuse\\Crypto\\Key;\nuse Laminas\\Diactoros\\"
},
{
"path": "tests/AuthorizationValidators/BearerTokenValidatorTest.php",
"chars": 8085,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\AuthorizationValidators;\n\nuse DateInterval;\nuse DateTimeImmutable"
},
{
"path": "tests/CodeChallengeVerifiers/PlainVerifierTest.php",
"chars": 630,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\CodeChallengeVerifiers;\n\nuse League\\OAuth2\\Server\\CodeChallengeVe"
},
{
"path": "tests/CodeChallengeVerifiers/S256VerifierTest.php",
"chars": 1152,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\CodeChallengeVerifiers;\n\nuse League\\OAuth2\\Server\\CodeChallengeVe"
},
{
"path": "tests/EventEmitting/EmitterAwarePolyfillTest.php",
"chars": 1674,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\EventEmitting;\n\nuse League\\OAuth2\\Server\\EventEmitting\\EmitterAwa"
},
{
"path": "tests/Exception/OAuthServerExceptionTest.php",
"chars": 5058,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Exception;\n\nuse Exception;\nuse Laminas\\Diactoros\\Response;\nuse La"
},
{
"path": "tests/Grant/AbstractGrantTest.php",
"chars": 22713,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Grant;\n\nuse DateInterval;\nuse Laminas\\Diactoros\\ServerRequest;\nus"
},
{
"path": "tests/Grant/AuthCodeGrantTest.php",
"chars": 108611,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Grant;\n\nuse DateInterval;\nuse Laminas\\Diactoros\\Response;\nuse Lam"
},
{
"path": "tests/Grant/ClientCredentialsGrantTest.php",
"chars": 3310,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Grant;\n\nuse DateInterval;\nuse Laminas\\Diactoros\\ServerRequest;\nus"
},
{
"path": "tests/Grant/DeviceCodeGrantTest.php",
"chars": 34286,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Grant;\n\nuse DateInterval;\nuse DateTimeImmutable;\nuse Laminas\\Diac"
},
{
"path": "tests/Grant/ImplicitGrantTest.php",
"chars": 18680,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Grant;\n\nuse DateInterval;\nuse Laminas\\Diactoros\\ServerRequest;\nus"
},
{
"path": "tests/Grant/PasswordGrantTest.php",
"chars": 12042,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Grant;\n\nuse DateInterval;\nuse Laminas\\Diactoros\\ServerRequest;\nus"
},
{
"path": "tests/Grant/RefreshTokenGrantTest.php",
"chars": 37054,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Grant;\n\nuse DateInterval;\nuse Laminas\\Diactoros\\Response;\nuse Lam"
},
{
"path": "tests/Middleware/AuthorizationServerMiddlewareTest.php",
"chars": 5117,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Middleware;\n\nuse DateInterval;\nuse Laminas\\Diactoros\\Response;\nus"
},
{
"path": "tests/Middleware/ResourceServerMiddlewareTest.php",
"chars": 3805,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Middleware;\n\nuse DateInterval;\nuse DateTimeImmutable;\nuse Laminas"
},
{
"path": "tests/PHPStan/AbstractGrantExtension.php",
"chars": 1223,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\PHPStan;\n\nuse League\\OAuth2\\Server\\Grant\\AbstractGrant;\nuse PhpPa"
},
{
"path": "tests/RedirectUriValidators/RedirectUriValidatorTest.php",
"chars": 3279,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\RedirectUriValidators;\n\nuse League\\OAuth2\\Server\\RedirectUriValid"
},
{
"path": "tests/ResourceServerTest.php",
"chars": 847,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests;\n\nuse Laminas\\Diactoros\\ServerRequestFactory;\nuse League\\OAuth2\\S"
},
{
"path": "tests/ResponseTypes/BearerResponseTypeTest.php",
"chars": 13041,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\ResponseTypes;\n\nuse DateInterval;\nuse DateTimeImmutable;\nuse Lami"
},
{
"path": "tests/ResponseTypes/BearerTokenResponseWithParams.php",
"chars": 486,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\ResponseTypes;\n\nuse League\\OAuth2\\Server\\Entities\\AccessTokenEnti"
},
{
"path": "tests/ResponseTypes/DeviceCodeResponseTypeTest.php",
"chars": 2231,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\ResponseTypes;\n\nuse DateInterval;\nuse DateTimeImmutable;\nuse Lami"
},
{
"path": "tests/Stubs/.gitattributes",
"chars": 31,
"preview": "private.key.crlf text eol=crlf\n"
},
{
"path": "tests/Stubs/AccessTokenEntity.php",
"chars": 437,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Stubs;\n\nuse League\\OAuth2\\Server\\Entities\\AccessTokenEntityInterf"
},
{
"path": "tests/Stubs/AuthCodeEntity.php",
"chars": 422,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Stubs;\n\nuse League\\OAuth2\\Server\\Entities\\AuthCodeEntityInterface"
},
{
"path": "tests/Stubs/ClientEntity.php",
"chars": 581,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Stubs;\n\nuse League\\OAuth2\\Server\\Entities\\ClientEntityInterface;\n"
},
{
"path": "tests/Stubs/CryptTraitStub.php",
"chars": 680,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Stubs;\n\nuse Defuse\\Crypto\\Key;\nuse League\\OAuth2\\Server\\CryptTrai"
},
{
"path": "tests/Stubs/DeviceCodeEntity.php",
"chars": 432,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Stubs;\n\nuse League\\OAuth2\\Server\\Entities\\DeviceCodeEntityInterfa"
},
{
"path": "tests/Stubs/GrantType.php",
"chars": 3571,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Stubs;\n\nuse DateInterval;\nuse Defuse\\Crypto\\Key;\nuse League\\OAuth"
},
{
"path": "tests/Stubs/RefreshTokenEntity.php",
"chars": 357,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Stubs;\n\nuse League\\OAuth2\\Server\\Entities\\RefreshTokenEntityInter"
},
{
"path": "tests/Stubs/ScopeEntity.php",
"chars": 472,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Stubs;\n\nuse League\\OAuth2\\Server\\Entities\\ScopeEntityInterface;\nu"
},
{
"path": "tests/Stubs/StubResponseType.php",
"chars": 1509,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Stubs;\n\nuse Laminas\\Diactoros\\Response;\nuse League\\OAuth2\\Server\\"
},
{
"path": "tests/Stubs/UserEntity.php",
"chars": 330,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Stubs;\n\nuse League\\OAuth2\\Server\\Entities\\Traits\\EntityTrait;\nuse"
},
{
"path": "tests/Stubs/private.key",
"chars": 1679,
"preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAwVBWI/czetM28h+oUxi3iHpchFUIvVVsTaPRfq/WypSTpL5P\nkwXxAgY14B3rBC6NOpVSmyI"
},
{
"path": "tests/Stubs/private.key.crlf",
"chars": 1706,
"preview": "-----BEGIN RSA PRIVATE KEY-----\r\nMIIEpAIBAAKCAQEAtHYxRBYATiiyDFs3pEhFg6Ei/UiQEmolTaQyQK810xHY23+X\r\n4elLl6HP1J09mefmJ3ZdI"
},
{
"path": "tests/Stubs/public.key",
"chars": 451,
"preview": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwVBWI/czetM28h+oUxi3\niHpchFUIvVVsTaPRfq/WypSTpL5P"
},
{
"path": "tests/Utils/CryptKeyTest.php",
"chars": 4228,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Utils;\n\nuse League\\OAuth2\\Server\\CryptKey;\nuse LogicException;\nus"
},
{
"path": "tests/Utils/CryptTraitTest.php",
"chars": 1073,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace LeagueTests\\Utils;\n\nuse Defuse\\Crypto\\Key;\nuse LeagueTests\\Stubs\\CryptTraitSt"
}
]
About this extraction
This page contains the full source code of the thephpleague/oauth2-server GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 143 files (564.1 KB), approximately 135.2k tokens, and a symbol index with 739 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.