Skip to content

Commit

Permalink
Merge branch 'thephpleague:master' into patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
hafezdivandari authored Apr 10, 2023
2 parents 20239fc + 43cd4d4 commit 4650de7
Show file tree
Hide file tree
Showing 21 changed files with 313 additions and 172 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/backwards-compatibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ jobs:
uses: "actions/checkout@v2"
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:
Expand Down
20 changes: 13 additions & 7 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ jobs:
strategy:
fail-fast: false
matrix:
php: [7.2, 7.3, 7.4, 8.0]
php: [8.0, 8.1, 8.2]
stability: [prefer-lowest, prefer-stable]

name: PHP ${{ matrix.php }} - ${{ matrix.stability }}

steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Setup PHP
uses: shivammathur/setup-php@v2
Expand All @@ -30,13 +32,17 @@ jobs:
coverage: pcov

- name: Install dependencies
run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress
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 --verbose --coverage-clover=coverage.clover

- name: Code coverage
if: ${{ github.ref == 'refs/heads/master' && matrix.php != 8.0 && github.repository == 'thephpleague/oauth2-server' }}
run: |
wget https://scrutinizer-ci.com/ocular.phar
php ocular.phar code-coverage:upload --format=php-clover coverage.clover
if: ${{ github.ref == 'refs/heads/master' && github.repository == 'thephpleague/oauth2-server' }}
run:
~/.composer/vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover
31 changes: 28 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,35 @@ 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]

## [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)

### [8.3.6] - released 2022-11-14
### 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
## [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)

Expand Down Expand Up @@ -570,7 +591,11 @@ Version 5 is a complete code rewrite.

- First major release

[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.3.6...HEAD
[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.5.1...HEAD
[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
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,9 @@ This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](ht

The latest version of this package supports the following versions of PHP:

* PHP 7.2
* PHP 7.3
* PHP 7.4
* PHP 8.0
* PHP 8.1
* PHP 8.2

The `openssl` and `json` extensions are also required.

Expand Down
15 changes: 8 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@
"homepage": "https://oauth2.thephpleague.com/",
"license": "MIT",
"require": {
"php": "^7.2 || ^8.0",
"php": "^8.0",
"ext-openssl": "*",
"league/event": "^2.2",
"league/uri": "^6.4",
"lcobucci/jwt": "^3.4.6 || ^4.0.4",
"league/uri": "^6.7",
"lcobucci/jwt": "^4.3 || ^5.0",
"psr/http-message": "^1.0.1",
"defuse/php-encryption": "^2.2.1",
"ext-json": "*"
"defuse/php-encryption": "^2.3",
"ext-json": "*",
"lcobucci/clock": "^2.2 || ^3.0"
},
"require-dev": {
"phpunit/phpunit": "^8.5.13",
"laminas/laminas-diactoros": "^2.4.1",
"phpunit/phpunit": "^9.6.6",
"laminas/laminas-diactoros": "^2.24.0",
"phpstan/phpstan": "^0.12.57",
"phpstan/phpstan-phpunit": "^0.12.16",
"roave/security-advisories": "dev-master"
Expand Down
4 changes: 0 additions & 4 deletions examples/public/client_credentials.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,16 @@
]);

$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());
Expand Down
5 changes: 0 additions & 5 deletions examples/public/password.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
$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
Expand Down Expand Up @@ -46,20 +45,16 @@
$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());
Expand Down
31 changes: 19 additions & 12 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" stopOnError="true"
stopOnFailure="true" stopOnIncomplete="false" stopOnSkipped="false" bootstrap="tests/Bootstrap.php">
<testsuites>
<testsuite name="Tests">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
</whitelist>
</filter>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
colors="true"
stopOnError="true"
stopOnFailure="true"
stopOnIncomplete="false"
stopOnSkipped="false"
bootstrap="tests/Bootstrap.php"
>
<coverage includeUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Tests">
<directory>./tests/</directory>
</testsuite>
</testsuites>
</phpunit>
5 changes: 1 addition & 4 deletions src/AuthorizationValidators/BearerTokenValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use Lcobucci\JWT\Signer\Rsa\Sha256;
use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Lcobucci\JWT\Validation\Constraint\ValidAt;
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\CryptTrait;
Expand Down Expand Up @@ -82,9 +81,7 @@ private function initJwtConfiguration()

$clock = new SystemClock(new DateTimeZone(\date_default_timezone_get()));
$this->jwtConfiguration->setValidationConstraints(
\class_exists(LooseValidAt::class)
? new LooseValidAt($clock, $this->jwtValidAtDateLeeway)
: new ValidAt($clock, $this->jwtValidAtDateLeeway),
new LooseValidAt($clock, $this->jwtValidAtDateLeeway),
new SignedWith(
new Sha256(),
InMemory::plainText($this->publicKey->getKeyContents(), $this->publicKey->getPassPhrase() ?? '')
Expand Down
1 change: 1 addition & 0 deletions src/Entities/Traits/ClientTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ trait ClientTrait
* Get the client's name.
*
* @return string
*
* @codeCoverageIgnore
*/
public function getName()
Expand Down
1 change: 1 addition & 0 deletions src/Entities/Traits/ScopeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ trait ScopeTrait
*
* @return string
*/
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->getIdentifier();
Expand Down
74 changes: 43 additions & 31 deletions src/Grant/AuthCodeGrant.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,39 +127,18 @@ public function respondToAccessTokenRequest(
throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code', $e);
}

// Validate code challenge
if (!empty($authCodePayload->code_challenge)) {
$codeVerifier = $this->getRequestParameter('code_verifier', $request, null);

if ($codeVerifier === null) {
throw OAuthServerException::invalidRequest('code_verifier');
}
$codeVerifier = $this->getRequestParameter('code_verifier', $request, null);

// 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 a code challenge isn't present but a code verifier is, reject the request to block PKCE downgrade attack
if (empty($authCodePayload->code_challenge) && $codeVerifier !== null) {
throw OAuthServerException::invalidRequest(
'code_challenge',
'code_verifier received when no code_challenge is present'
);
}

if (\property_exists($authCodePayload, 'code_challenge_method')) {
if (isset($this->codeChallengeVerifiers[$authCodePayload->code_challenge_method])) {
$codeChallengeVerifier = $this->codeChallengeVerifiers[$authCodePayload->code_challenge_method];

if ($codeChallengeVerifier->verifyCodeChallenge($codeVerifier, $authCodePayload->code_challenge) === false) {
throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.');
}
} else {
throw OAuthServerException::serverError(
\sprintf(
'Unsupported code challenge method `%s`',
$authCodePayload->code_challenge_method
)
);
}
}
if (!empty($authCodePayload->code_challenge)) {
$this->validateCodeChallenge($authCodePayload, $codeVerifier);
}

// Issue and persist new access token
Expand All @@ -181,6 +160,39 @@ public function respondToAccessTokenRequest(
return $responseType;
}

private function validateCodeChallenge($authCodePayload, $codeVerifier)
{
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 ($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.
*
Expand Down
1 change: 1 addition & 0 deletions src/RequestAccessTokenEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public function __construct($name, ServerRequestInterface $request, AccessTokenE

/**
* @return AccessTokenEntityInterface
*
* @codeCoverageIgnore
*/
public function getAccessToken()
Expand Down
1 change: 1 addition & 0 deletions src/RequestEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public function __construct($name, ServerRequestInterface $request)

/**
* @return ServerRequestInterface
*
* @codeCoverageIgnore
*/
public function getRequest()
Expand Down
1 change: 1 addition & 0 deletions src/RequestRefreshTokenEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public function __construct($name, ServerRequestInterface $request, RefreshToken

/**
* @return RefreshTokenEntityInterface
*
* @codeCoverageIgnore
*/
public function getRefreshToken()
Expand Down
Loading

0 comments on commit 4650de7

Please sign in to comment.