From 946aad10bce27dda6a77077c16fb3b8dd4b91974 Mon Sep 17 00:00:00 2001 From: rhertogh Date: Sun, 15 Aug 2021 12:00:59 +0200 Subject: [PATCH 01/46] Added exception chaining in BearerTokenValidator Added exception chaining for RequiredConstraintsViolated in `\League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator::validateAuthorization()` similar to line 103. --- src/AuthorizationValidators/BearerTokenValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AuthorizationValidators/BearerTokenValidator.php b/src/AuthorizationValidators/BearerTokenValidator.php index e41e1a2b7..6f767ceef 100644 --- a/src/AuthorizationValidators/BearerTokenValidator.php +++ b/src/AuthorizationValidators/BearerTokenValidator.php @@ -108,7 +108,7 @@ public function validateAuthorization(ServerRequestInterface $request) $constraints = $this->jwtConfiguration->validationConstraints(); $this->jwtConfiguration->validator()->assert($token, ...$constraints); } catch (RequiredConstraintsViolated $exception) { - throw OAuthServerException::accessDenied('Access token could not be verified'); + throw OAuthServerException::accessDenied('Access token could not be verified', null, $exception); } $claims = $token->claims(); From f8245a0942736923ee7db368ed6f1b45866c34b7 Mon Sep 17 00:00:00 2001 From: Christopher Davis Date: Wed, 16 Nov 2022 09:15:33 -0600 Subject: [PATCH 02/46] Update the Param Type on the Validate Scopes Method `validateScopes` does accept and handle `null` values. --- src/Grant/AbstractGrant.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 0a5b514fc..c8eb6b813 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -294,8 +294,8 @@ protected function validateRedirectUri( /** * Validate scopes in the request. * - * @param string|array $scopes - * @param string $redirectUri + * @param string|array|null $scopes + * @param string $redirectUri * * @throws OAuthServerException * From aeae9e44b2c82721b1d706f6357d56337869746d Mon Sep 17 00:00:00 2001 From: Anner Visser Date: Mon, 3 Oct 2022 15:59:28 +0200 Subject: [PATCH 03/46] Allow configuring leeway for the validation of jwt token dates Fixes #1021 --- .../BearerTokenValidator.php | 16 +++-- .../BearerTokenValidatorTest.php | 63 +++++++++++++++++++ 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/AuthorizationValidators/BearerTokenValidator.php b/src/AuthorizationValidators/BearerTokenValidator.php index faef53585..7723eda81 100644 --- a/src/AuthorizationValidators/BearerTokenValidator.php +++ b/src/AuthorizationValidators/BearerTokenValidator.php @@ -14,8 +14,8 @@ use Lcobucci\JWT\Configuration; use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Signer\Rsa\Sha256; -use Lcobucci\JWT\Validation\Constraint\SignedWith; 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; @@ -43,12 +43,19 @@ class BearerTokenValidator implements AuthorizationValidatorInterface */ private $jwtConfiguration; + /** + * @var \DateInterval|null + */ + private $jwtValidAtDateLeeway; + /** * @param AccessTokenRepositoryInterface $accessTokenRepository + * @param \DateInterval|null $jwtValidAtDateLeeway */ - public function __construct(AccessTokenRepositoryInterface $accessTokenRepository) + public function __construct(AccessTokenRepositoryInterface $accessTokenRepository, \DateInterval $jwtValidAtDateLeeway = null) { $this->accessTokenRepository = $accessTokenRepository; + $this->jwtValidAtDateLeeway = $jwtValidAtDateLeeway; } /** @@ -73,10 +80,11 @@ private function initJwtConfiguration() InMemory::plainText('empty', 'empty') ); + $clock = new SystemClock(new DateTimeZone(\date_default_timezone_get())); $this->jwtConfiguration->setValidationConstraints( \class_exists(LooseValidAt::class) - ? new LooseValidAt(new SystemClock(new DateTimeZone(\date_default_timezone_get()))) - : new ValidAt(new SystemClock(new DateTimeZone(\date_default_timezone_get()))), + ? new LooseValidAt($clock, $this->jwtValidAtDateLeeway) + : new ValidAt($clock, $this->jwtValidAtDateLeeway), new SignedWith( new Sha256(), InMemory::plainText($this->publicKey->getKeyContents(), $this->publicKey->getPassPhrase() ?? '') diff --git a/tests/AuthorizationValidators/BearerTokenValidatorTest.php b/tests/AuthorizationValidators/BearerTokenValidatorTest.php index 838d2bbae..536618b9a 100644 --- a/tests/AuthorizationValidators/BearerTokenValidatorTest.php +++ b/tests/AuthorizationValidators/BearerTokenValidatorTest.php @@ -71,4 +71,67 @@ public function testBearerTokenValidatorRejectsExpiredToken() $bearerTokenValidator->validateAuthorization($request); } + + public function testBearerTokenValidatorAcceptsExpiredTokenWithinLeeway() + { + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + + // We fake generating this token 10 seconds into the future, an extreme example of possible time drift between servers + $future = (new DateTimeImmutable())->add(new DateInterval('PT10S')); + + $bearerTokenValidator = new BearerTokenValidator($accessTokenRepositoryMock, new \DateInterval('PT10S')); + $bearerTokenValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $bearerTokenValidatorReflection = new ReflectionClass(BearerTokenValidator::class); + $jwtConfiguration = $bearerTokenValidatorReflection->getProperty('jwtConfiguration'); + $jwtConfiguration->setAccessible(true); + + $jwtTokenFromFutureWithinLeeway = $jwtConfiguration->getValue($bearerTokenValidator)->builder() + ->permittedFor('client-id') + ->identifiedBy('token-id') + ->issuedAt($future) + ->canOnlyBeUsedAfter($future) + ->expiresAt((new DateTimeImmutable())->add(new DateInterval('PT1H'))) + ->relatedTo('user-id') + ->withClaim('scopes', 'scope1 scope2 scope3 scope4') + ->getToken(new Sha256(), InMemory::file(__DIR__ . '/../Stubs/private.key')); + + $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $jwtTokenFromFutureWithinLeeway->toString())); + + $validRequest = $bearerTokenValidator->validateAuthorization($request); + + $this->assertArrayHasKey('authorization', $validRequest->getHeaders()); + } + + public function testBearerTokenValidatorRejectsExpiredTokenBeyondLeeway() + { + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + + // We fake generating this token 10 seconds into the future, an extreme example of possible time drift between servers + $future = (new DateTimeImmutable())->add(new DateInterval('PT20S')); + + $bearerTokenValidator = new BearerTokenValidator($accessTokenRepositoryMock, new \DateInterval('PT10S')); + $bearerTokenValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $bearerTokenValidatorReflection = new ReflectionClass(BearerTokenValidator::class); + $jwtConfiguration = $bearerTokenValidatorReflection->getProperty('jwtConfiguration'); + $jwtConfiguration->setAccessible(true); + + $jwtTokenFromFutureBeyondLeeway = $jwtConfiguration->getValue($bearerTokenValidator)->builder() + ->permittedFor('client-id') + ->identifiedBy('token-id') + ->issuedAt($future) + ->canOnlyBeUsedAfter($future) + ->expiresAt((new DateTimeImmutable())->add(new DateInterval('PT1H'))) + ->relatedTo('user-id') + ->withClaim('scopes', 'scope1 scope2 scope3 scope4') + ->getToken(new Sha256(), InMemory::file(__DIR__ . '/../Stubs/private.key')); + + $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $jwtTokenFromFutureBeyondLeeway->toString())); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(9); + + $bearerTokenValidator->validateAuthorization($request); + } } From a0d1de1772514f016f2faddb29463db9ee04c86d Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Thu, 17 Nov 2022 21:59:10 +0000 Subject: [PATCH 04/46] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e59504d2..3366be840 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ 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] +### 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 ### Fixed From b8436ace9ecacc8b8c86b674626dea03a2baedb9 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Thu, 17 Nov 2022 21:59:28 +0000 Subject: [PATCH 05/46] Fix comment in test --- tests/AuthorizationValidators/BearerTokenValidatorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/AuthorizationValidators/BearerTokenValidatorTest.php b/tests/AuthorizationValidators/BearerTokenValidatorTest.php index 536618b9a..94899ddd4 100644 --- a/tests/AuthorizationValidators/BearerTokenValidatorTest.php +++ b/tests/AuthorizationValidators/BearerTokenValidatorTest.php @@ -107,7 +107,7 @@ public function testBearerTokenValidatorRejectsExpiredTokenBeyondLeeway() { $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - // We fake generating this token 10 seconds into the future, an extreme example of possible time drift between servers + // We fake generating this token 20 seconds into the future, an extreme example of possible time drift between servers $future = (new DateTimeImmutable())->add(new DateInterval('PT20S')); $bearerTokenValidator = new BearerTokenValidator($accessTokenRepositoryMock, new \DateInterval('PT10S')); From 2a8c22c49caffe71ebf62683dcc4a87d1e81ae41 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 15 Feb 2023 15:49:41 +0000 Subject: [PATCH 06/46] Move code challenge verification to private function --- src/Grant/AuthCodeGrant.php | 74 ++++++++++++++++++------------- tests/Grant/AuthCodeGrantTest.php | 71 +++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 31 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 3fac0344e..1bde2044e 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -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 (!empty($authCodePayload->code_challenge)) { + $this->validateCodeChallenge($authCodePayload, $codeVerifier); + } - 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 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' + ); } // Issue and persist new access token @@ -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. * diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 860c6e863..a8e4feef3 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -917,6 +917,77 @@ public function testRespondToAccessTokenRequestCodeChallengeS256() $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } + public function testPKCEDowngradeBlocked() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $client->setRedirectUri('http://foo/bar'); + $client->setConfidential(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeEntity = new ScopeEntity(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); + $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); + + $grant = new AuthCodeGrant( + $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), + $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), + new DateInterval('PT10M') + ); + + $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $request = new ServerRequest( + [], + [], + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code_verifier' => self::CODE_VERIFIER, + 'code' => $this->cryptStub->doEncrypt( + \json_encode( + [ + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => 'http://foo/bar', + ] + ) + ), + ] + ); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(3); + + /** @var StubResponseType $response */ + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); + } + public function testRespondToAccessTokenRequestMissingRedirectUri() { $client = new ClientEntity(); From c9255bd524f2e40219698e46c2ba7eacc79e7ae1 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 15 Feb 2023 15:54:09 +0000 Subject: [PATCH 07/46] StyleCI fix --- tests/Grant/AuthCodeGrantTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index a8e4feef3..536e071d8 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -984,7 +984,7 @@ public function testPKCEDowngradeBlocked() $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(3); - /** @var StubResponseType $response */ + /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } From 1480564d751fadf600f9bd7448c5888a8588c1f9 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 15 Feb 2023 15:57:35 +0000 Subject: [PATCH 08/46] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3366be840..2e886b227 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### 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) From 0d523ddedd4d519a954610ac10cbbdbd64b983ba Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 15 Feb 2023 15:59:31 +0000 Subject: [PATCH 09/46] Move pkce check so it happens prior to validation of code challenge --- src/Grant/AuthCodeGrant.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 1bde2044e..8336cf649 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -129,10 +129,6 @@ public function respondToAccessTokenRequest( $codeVerifier = $this->getRequestParameter('code_verifier', $request, null); - if (!empty($authCodePayload->code_challenge)) { - $this->validateCodeChallenge($authCodePayload, $codeVerifier); - } - // 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( @@ -141,6 +137,10 @@ public function respondToAccessTokenRequest( ); } + if (!empty($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)); From 859ccb008b9e44bbbb192c36e1af28c79655af0b Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 15 Feb 2023 16:07:16 +0000 Subject: [PATCH 10/46] Update changelog for version 8.4.0 --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e886b227..6e4f34e59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ 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.4.0] - released 2023-02-15 ### Added - You can now set a leeway for time drift between servers when validating a JWT (PR #1304) @@ -574,7 +576,8 @@ 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.4.0...HEAD +[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 From 539f4340c14eca8d44578fd118f6bdc0ad16d1ce Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 15 Feb 2023 16:08:35 +0000 Subject: [PATCH 11/46] Fix changelog headings --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e4f34e59..7f5f79c4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] -### [8.4.0] - released 2023-02-15 +## [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) @@ -14,11 +14,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - 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 +## [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) From f598e28945d0e90a1b60fea9ccb6331085b113f1 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Tue, 21 Feb 2023 21:48:01 +0000 Subject: [PATCH 12/46] Initial fixes --- phpunit.xml.dist | 13 +++++++++++-- tests/AuthorizationServerTest.php | 5 ++++- tests/Bootstrap.php | 2 ++ tests/Grant/AuthCodeGrantTest.php | 15 ++++++++++++--- tests/Grant/ImplicitGrantTest.php | 7 ++++++- tests/Stubs/ScopeEntity.php | 1 + 6 files changed, 36 insertions(+), 7 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5f8851b28..641a4946b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,6 +1,15 @@ - + ./tests/ diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 1ba4ad9cc..d167088e0 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -245,9 +245,12 @@ public function testCompleteAuthorizationRequest() $server->enableGrantType($grant); + $client = new ClientEntity(); + $client->setRedirectUri('https://foo/bar'); + $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); - $authRequest->setClient(new ClientEntity()); + $authRequest->setClient($client); $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); diff --git a/tests/Bootstrap.php b/tests/Bootstrap.php index b02cb7be4..fd3cbd8a7 100644 --- a/tests/Bootstrap.php +++ b/tests/Bootstrap.php @@ -1,5 +1,7 @@ setRedirectUri('https://foo/bar'); + $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); - $authRequest->setClient(new ClientEntity()); + $authRequest->setClient($client); $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); @@ -478,9 +481,12 @@ public function testCompleteAuthorizationRequest() public function testCompleteAuthorizationRequestDenied() { + $client = new ClientEntity(); + $client->setRedirectUri('https://foo/bar'); + $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(false); - $authRequest->setClient(new ClientEntity()); + $authRequest->setClient($client); $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); @@ -1815,9 +1821,12 @@ public function testRespondToAccessTokenRequestMissingCodeVerifier() public function testAuthCodeRepositoryUniqueConstraintCheck() { + $client = new ClientEntity(); + $client->setRedirectUri('https://foo/bar'); + $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); - $authRequest->setClient(new ClientEntity()); + $authRequest->setClient($client); $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index 546450384..7cb4d33d4 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -208,6 +208,7 @@ public function testCompleteAuthorizationRequest() { $client = new ClientEntity(); $client->setIdentifier('identifier'); + $client->setRedirectUri('https://foo/bar'); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); @@ -235,9 +236,12 @@ public function testCompleteAuthorizationRequest() public function testCompleteAuthorizationRequestDenied() { + $client = new ClientEntity(); + $client->setRedirectUri('https://foo/bar'); + $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(false); - $authRequest->setClient(new ClientEntity()); + $authRequest->setClient($client); $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); @@ -263,6 +267,7 @@ public function testAccessTokenRepositoryUniqueConstraintCheck() { $client = new ClientEntity(); $client->setIdentifier('identifier'); + $client->setRedirectUri('https://foo/bar'); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); diff --git a/tests/Stubs/ScopeEntity.php b/tests/Stubs/ScopeEntity.php index 4e4a6bec5..4c93d91dc 100644 --- a/tests/Stubs/ScopeEntity.php +++ b/tests/Stubs/ScopeEntity.php @@ -9,6 +9,7 @@ class ScopeEntity implements ScopeEntityInterface { use EntityTrait; + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->getIdentifier(); From 9aad537edadeff55c1b38c8d075e56efc56e29e7 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 22 Feb 2023 22:15:37 +0000 Subject: [PATCH 13/46] Fix styleci issue --- tests/Bootstrap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Bootstrap.php b/tests/Bootstrap.php index fd3cbd8a7..a31ef6f34 100644 --- a/tests/Bootstrap.php +++ b/tests/Bootstrap.php @@ -1,6 +1,6 @@ Date: Wed, 22 Feb 2023 22:27:26 +0000 Subject: [PATCH 14/46] Temp fix to gh runner --- .github/workflows/backwards-compatibility.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/backwards-compatibility.yml b/.github/workflows/backwards-compatibility.yml index e15e132b5..cc1971dc9 100644 --- a/.github/workflows/backwards-compatibility.yml +++ b/.github/workflows/backwards-compatibility.yml @@ -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: From f6b884ec4b14510a3ffe6f17aeeee7e29c130ec9 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 22 Feb 2023 22:40:14 +0000 Subject: [PATCH 15/46] Move redirect uri in test to a const --- tests/AuthorizationServerTest.php | 7 +- tests/Grant/AuthCodeGrantTest.php | 177 +++++++++++++++--------------- tests/Grant/ImplicitGrantTest.php | 19 ++-- 3 files changed, 103 insertions(+), 100 deletions(-) diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index d167088e0..af8c89d8a 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -32,6 +32,7 @@ class AuthorizationServerTest extends TestCase { const DEFAULT_SCOPE = 'basic'; + const REDIRECT_URI = 'https://foo/bar'; public function setUp(): void { @@ -86,7 +87,7 @@ public function testRespondToRequest() $client = new ClientEntity(); $client->setConfidential(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepository->method('getClientEntity')->willReturn($client); @@ -246,7 +247,7 @@ public function testCompleteAuthorizationRequest() $server->enableGrantType($grant); $client = new ClientEntity(); - $client->setRedirectUri('https://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); @@ -263,7 +264,7 @@ public function testCompleteAuthorizationRequest() public function testValidateAuthorizationRequest() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 54b8b260d..7c2c20d11 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -31,6 +31,7 @@ class AuthCodeGrantTest extends TestCase { const DEFAULT_SCOPE = 'basic'; + const REDIRECT_URI = 'https://foo/bar'; /** * @var CryptTraitStub @@ -85,7 +86,7 @@ public function testCanRespondToAuthorizationRequest() public function testValidateAuthorizationRequest() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -115,7 +116,7 @@ public function testValidateAuthorizationRequest() [ 'response_type' => 'code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ); @@ -125,7 +126,7 @@ public function testValidateAuthorizationRequest() public function testValidateAuthorizationRequestRedirectUriArray() { $client = new ClientEntity(); - $client->setRedirectUri(['http://foo/bar']); + $client->setRedirectUri([self::REDIRECT_URI]); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -154,7 +155,7 @@ public function testValidateAuthorizationRequestRedirectUriArray() [ 'response_type' => 'code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ); @@ -164,7 +165,7 @@ public function testValidateAuthorizationRequestRedirectUriArray() public function testValidateAuthorizationRequestWithoutRedirectUri() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -206,7 +207,7 @@ public function testValidateAuthorizationRequestWithoutRedirectUri() public function testValidateAuthorizationRequestCodeChallenge() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -235,7 +236,7 @@ public function testValidateAuthorizationRequestCodeChallenge() [ 'response_type' => 'code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => self::CODE_CHALLENGE, ] ); @@ -246,7 +247,7 @@ public function testValidateAuthorizationRequestCodeChallenge() public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooShort() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -261,7 +262,7 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooSho $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => \str_repeat('A', 42), ]); @@ -273,7 +274,7 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooSho public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLong() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -288,7 +289,7 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLon $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => \str_repeat('A', 129), ]); @@ -300,7 +301,7 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLon public function testValidateAuthorizationRequestCodeChallengeInvalidCharacters() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -315,7 +316,7 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidCharacters() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => \str_repeat('A', 42) . '!', ]); @@ -371,7 +372,7 @@ public function testValidateAuthorizationRequestInvalidClientId() public function testValidateAuthorizationRequestBadRedirectUriString() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -397,7 +398,7 @@ public function testValidateAuthorizationRequestBadRedirectUriString() public function testValidateAuthorizationRequestBadRedirectUriArray() { $client = new ClientEntity(); - $client->setRedirectUri(['http://foo/bar']); + $client->setRedirectUri([self::REDIRECT_URI]); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -423,7 +424,7 @@ public function testValidateAuthorizationRequestBadRedirectUriArray() public function testValidateAuthorizationRequestInvalidCodeChallengeMethod() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -444,7 +445,7 @@ public function testValidateAuthorizationRequestInvalidCodeChallengeMethod() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => 'foobar', 'code_challenge_method' => 'foo', ]); @@ -458,7 +459,7 @@ public function testValidateAuthorizationRequestInvalidCodeChallengeMethod() public function testCompleteAuthorizationRequest() { $client = new ClientEntity(); - $client->setRedirectUri('https://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); @@ -482,7 +483,7 @@ public function testCompleteAuthorizationRequest() public function testCompleteAuthorizationRequestDenied() { $client = new ClientEntity(); - $client->setRedirectUri('https://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(false); @@ -510,7 +511,7 @@ public function testRespondToAccessTokenRequest() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -552,7 +553,7 @@ public function testRespondToAccessTokenRequest() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( \json_encode( [ @@ -561,7 +562,7 @@ public function testRespondToAccessTokenRequest() 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), @@ -578,7 +579,7 @@ public function testRespondToAccessTokenRequest() public function testRespondToAccessTokenRequestUsingHttpBasicAuth() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setIdentifier('foo'); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -618,7 +619,7 @@ public function testRespondToAccessTokenRequestUsingHttpBasicAuth() [], [ 'grant_type' => 'authorization_code', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( \json_encode( [ @@ -627,7 +628,7 @@ public function testRespondToAccessTokenRequestUsingHttpBasicAuth() 'expire_time' => \time() + 3600, 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), @@ -645,7 +646,7 @@ public function testRespondToAccessTokenRequestForPublicClient() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -686,7 +687,7 @@ public function testRespondToAccessTokenRequestForPublicClient() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( \json_encode( [ @@ -695,7 +696,7 @@ public function testRespondToAccessTokenRequestForPublicClient() 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), @@ -713,7 +714,7 @@ public function testRespondToAccessTokenRequestNullRefreshToken() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -754,7 +755,7 @@ public function testRespondToAccessTokenRequestNullRefreshToken() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( \json_encode( [ @@ -763,7 +764,7 @@ public function testRespondToAccessTokenRequestNullRefreshToken() 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), @@ -781,7 +782,7 @@ public function testRespondToAccessTokenRequestCodeChallengePlain() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -824,7 +825,7 @@ public function testRespondToAccessTokenRequestCodeChallengePlain() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, 'code' => $this->cryptStub->doEncrypt( \json_encode( @@ -834,7 +835,7 @@ public function testRespondToAccessTokenRequestCodeChallengePlain() 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => self::CODE_VERIFIER, 'code_challenge_method' => 'plain', ] @@ -854,7 +855,7 @@ public function testRespondToAccessTokenRequestCodeChallengeS256() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -897,7 +898,7 @@ public function testRespondToAccessTokenRequestCodeChallengeS256() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, 'code' => $this->cryptStub->doEncrypt( \json_encode( @@ -907,7 +908,7 @@ public function testRespondToAccessTokenRequestCodeChallengeS256() 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => self::CODE_CHALLENGE, 'code_challenge_method' => 'S256', ] @@ -927,7 +928,7 @@ public function testPKCEDowngradeBlocked() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -970,7 +971,7 @@ public function testPKCEDowngradeBlocked() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, 'code' => $this->cryptStub->doEncrypt( \json_encode( @@ -980,7 +981,7 @@ public function testPKCEDowngradeBlocked() 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), @@ -999,7 +1000,7 @@ public function testRespondToAccessTokenRequestMissingRedirectUri() $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setConfidential(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1029,7 +1030,7 @@ public function testRespondToAccessTokenRequestMissingRedirectUri() 'auth_code_id' => \uniqid(), 'expire_time' => \time() + 3600, 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), @@ -1078,7 +1079,7 @@ public function testRespondToAccessTokenRequestRedirectUriMismatch() 'auth_code_id' => \uniqid(), 'expire_time' => \time() + 3600, 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), @@ -1094,7 +1095,7 @@ public function testRespondToAccessTokenRequestRedirectUriMismatch() public function testRespondToAccessTokenRequestMissingCode() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1125,7 +1126,7 @@ public function testRespondToAccessTokenRequestMissingCode() 'grant_type' => 'authorization_code', 'client_id' => 'foo', 'client_secret' => 'bar', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ); @@ -1139,7 +1140,7 @@ public function testRespondToAccessTokenRequestMissingCode() public function testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1165,7 +1166,7 @@ public function testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( \json_encode( [ @@ -1192,7 +1193,7 @@ public function testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode public function testRespondToAccessTokenRequestWithAuthCodeNotAString() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1218,7 +1219,7 @@ public function testRespondToAccessTokenRequestWithAuthCodeNotAString() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code' => ['not', 'a', 'string'], ] ); @@ -1230,7 +1231,7 @@ public function testRespondToAccessTokenRequestWithAuthCodeNotAString() public function testRespondToAccessTokenRequestExpiredCode() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1256,7 +1257,7 @@ public function testRespondToAccessTokenRequestExpiredCode() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( \json_encode( [ @@ -1265,7 +1266,7 @@ public function testRespondToAccessTokenRequestExpiredCode() 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), @@ -1284,7 +1285,7 @@ public function testRespondToAccessTokenRequestRevokedCode() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1320,7 +1321,7 @@ public function testRespondToAccessTokenRequestRevokedCode() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( \json_encode( [ @@ -1329,7 +1330,7 @@ public function testRespondToAccessTokenRequestRevokedCode() 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), @@ -1348,7 +1349,7 @@ public function testRespondToAccessTokenRequestClientMismatch() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1381,7 +1382,7 @@ public function testRespondToAccessTokenRequestClientMismatch() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( \json_encode( [ @@ -1390,7 +1391,7 @@ public function testRespondToAccessTokenRequestClientMismatch() 'client_id' => 'bar', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), @@ -1409,7 +1410,7 @@ public function testRespondToAccessTokenRequestBadCodeEncryption() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1442,7 +1443,7 @@ public function testRespondToAccessTokenRequestBadCodeEncryption() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code' => 'sdfsfsd', ] ); @@ -1459,7 +1460,7 @@ public function testRespondToAccessTokenRequestBadCodeVerifierPlain() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1501,7 +1502,7 @@ public function testRespondToAccessTokenRequestBadCodeVerifierPlain() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, 'code' => $this->cryptStub->doEncrypt( \json_encode( @@ -1511,7 +1512,7 @@ public function testRespondToAccessTokenRequestBadCodeVerifierPlain() 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => 'foobar', 'code_challenge_method' => 'plain', ] @@ -1532,7 +1533,7 @@ public function testRespondToAccessTokenRequestBadCodeVerifierS256() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1574,7 +1575,7 @@ public function testRespondToAccessTokenRequestBadCodeVerifierS256() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => 'nope', 'code' => $this->cryptStub->doEncrypt( \json_encode( @@ -1584,7 +1585,7 @@ public function testRespondToAccessTokenRequestBadCodeVerifierS256() 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => 'foobar', 'code_challenge_method' => 'S256', ] @@ -1605,7 +1606,7 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1647,7 +1648,7 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => 'dqX7C-RbqjHYtytmhGTigKdZCXfxq-+xbsk9_GxUcaE', // Malformed code. Contains `+`. 'code' => $this->cryptStub->doEncrypt( \json_encode( @@ -1657,7 +1658,7 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => self::CODE_CHALLENGE, 'code_challenge_method' => 'S256', ] @@ -1678,7 +1679,7 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1720,7 +1721,7 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => 'dqX7C-RbqjHY', // Malformed code. Invalid length. 'code' => $this->cryptStub->doEncrypt( \json_encode( @@ -1730,7 +1731,7 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => 'R7T1y1HPNFvs1WDCrx4lfoBS6KD2c71pr8OHvULjvv8', 'code_challenge_method' => 'S256', ] @@ -1751,7 +1752,7 @@ public function testRespondToAccessTokenRequestMissingCodeVerifier() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1793,7 +1794,7 @@ public function testRespondToAccessTokenRequestMissingCodeVerifier() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( \json_encode( [ @@ -1802,7 +1803,7 @@ public function testRespondToAccessTokenRequestMissingCodeVerifier() 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => 'foobar', 'code_challenge_method' => 'plain', ] @@ -1822,7 +1823,7 @@ public function testRespondToAccessTokenRequestMissingCodeVerifier() public function testAuthCodeRepositoryUniqueConstraintCheck() { $client = new ClientEntity(); - $client->setRedirectUri('https://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); @@ -1908,7 +1909,7 @@ public function testRefreshTokenRepositoryUniqueConstraintCheck() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1959,7 +1960,7 @@ public function testRefreshTokenRepositoryUniqueConstraintCheck() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( \json_encode( [ @@ -1968,7 +1969,7 @@ public function testRefreshTokenRepositoryUniqueConstraintCheck() 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), @@ -1986,7 +1987,7 @@ public function testRefreshTokenRepositoryFailToPersist() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -2027,7 +2028,7 @@ public function testRefreshTokenRepositoryFailToPersist() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( \json_encode( [ @@ -2036,7 +2037,7 @@ public function testRefreshTokenRepositoryFailToPersist() 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), @@ -2057,7 +2058,7 @@ public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop() { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -2098,7 +2099,7 @@ public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop() [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( \json_encode( [ @@ -2107,7 +2108,7 @@ public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop() 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), @@ -2140,7 +2141,7 @@ public function testCompleteAuthorizationRequestNoUser() public function testPublicClientAuthCodeRequestRejectedWhenCodeChallengeRequiredButNotGiven() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -2162,7 +2163,7 @@ public function testPublicClientAuthCodeRequestRejectedWhenCodeChallengeRequired $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ]); $this->expectException(OAuthServerException::class); @@ -2174,7 +2175,7 @@ public function testPublicClientAuthCodeRequestRejectedWhenCodeChallengeRequired public function testUseValidRedirectUriIfScopeCheckFails() { $client = new ClientEntity(); - $client->setRedirectUri(['http://foo/bar', 'http://bar/foo']); + $client->setRedirectUri([self::REDIRECT_URI, 'http://bar/foo']); $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index 7cb4d33d4..5f69242c7 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -25,6 +25,7 @@ class ImplicitGrantTest extends TestCase { const DEFAULT_SCOPE = 'basic'; + const REDIRECT_URI = 'https://foo/bar'; /** * CryptTrait stub @@ -79,7 +80,7 @@ public function testCanRespondToAuthorizationRequest() public function testValidateAuthorizationRequest() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -95,7 +96,7 @@ public function testValidateAuthorizationRequest() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ]); $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); @@ -104,7 +105,7 @@ public function testValidateAuthorizationRequest() public function testValidateAuthorizationRequestRedirectUriArray() { $client = new ClientEntity(); - $client->setRedirectUri(['http://foo/bar']); + $client->setRedirectUri([self::REDIRECT_URI]); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -120,7 +121,7 @@ public function testValidateAuthorizationRequestRedirectUriArray() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => self::REDIRECT_URI, ]); $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); @@ -163,7 +164,7 @@ public function testValidateAuthorizationRequestInvalidClientId() public function testValidateAuthorizationRequestBadRedirectUriString() { $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -185,7 +186,7 @@ public function testValidateAuthorizationRequestBadRedirectUriString() public function testValidateAuthorizationRequestBadRedirectUriArray() { $client = new ClientEntity(); - $client->setRedirectUri(['http://foo/bar']); + $client->setRedirectUri([self::REDIRECT_URI]); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -208,7 +209,7 @@ public function testCompleteAuthorizationRequest() { $client = new ClientEntity(); $client->setIdentifier('identifier'); - $client->setRedirectUri('https://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); @@ -237,7 +238,7 @@ public function testCompleteAuthorizationRequest() public function testCompleteAuthorizationRequestDenied() { $client = new ClientEntity(); - $client->setRedirectUri('https://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(false); @@ -267,7 +268,7 @@ public function testAccessTokenRepositoryUniqueConstraintCheck() { $client = new ClientEntity(); $client->setIdentifier('identifier'); - $client->setRedirectUri('https://foo/bar'); + $client->setRedirectUri(self::REDIRECT_URI); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); From ac027164a01b6dc528fae33676e21b096fd2a394 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 22 Mar 2023 11:25:44 +0000 Subject: [PATCH 16/46] Add ReturnTypeWillChange to jsonSerialize() --- src/Entities/Traits/ScopeTrait.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Entities/Traits/ScopeTrait.php b/src/Entities/Traits/ScopeTrait.php index a132234fc..7eacc3359 100644 --- a/src/Entities/Traits/ScopeTrait.php +++ b/src/Entities/Traits/ScopeTrait.php @@ -16,6 +16,7 @@ trait ScopeTrait * * @return string */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->getIdentifier(); From ec7d98aa1c2204f3f0c71cac726d8c6b1bc80d50 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 22 Mar 2023 11:37:12 +0000 Subject: [PATCH 17/46] Fix styleci issues --- examples/public/client_credentials.php | 4 ---- examples/public/password.php | 5 ----- src/Entities/Traits/ClientTrait.php | 1 + src/RequestAccessTokenEvent.php | 1 + src/RequestEvent.php | 1 + src/RequestRefreshTokenEvent.php | 1 + 6 files changed, 4 insertions(+), 9 deletions(-) diff --git a/examples/public/client_credentials.php b/examples/public/client_credentials.php index 51a1ca0b7..1e5f090d7 100644 --- a/examples/public/client_credentials.php +++ b/examples/public/client_credentials.php @@ -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()); diff --git a/examples/public/password.php b/examples/public/password.php index 6857e988a..db65d7840 100644 --- a/examples/public/password.php +++ b/examples/public/password.php @@ -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 @@ -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()); diff --git a/src/Entities/Traits/ClientTrait.php b/src/Entities/Traits/ClientTrait.php index a0078d8d7..370163c35 100644 --- a/src/Entities/Traits/ClientTrait.php +++ b/src/Entities/Traits/ClientTrait.php @@ -30,6 +30,7 @@ trait ClientTrait * Get the client's name. * * @return string + * * @codeCoverageIgnore */ public function getName() diff --git a/src/RequestAccessTokenEvent.php b/src/RequestAccessTokenEvent.php index 99d17bf36..c2f478284 100644 --- a/src/RequestAccessTokenEvent.php +++ b/src/RequestAccessTokenEvent.php @@ -31,6 +31,7 @@ public function __construct($name, ServerRequestInterface $request, AccessTokenE /** * @return AccessTokenEntityInterface + * * @codeCoverageIgnore */ public function getAccessToken() diff --git a/src/RequestEvent.php b/src/RequestEvent.php index b1ca3f6b8..4f7dad097 100644 --- a/src/RequestEvent.php +++ b/src/RequestEvent.php @@ -40,6 +40,7 @@ public function __construct($name, ServerRequestInterface $request) /** * @return ServerRequestInterface + * * @codeCoverageIgnore */ public function getRequest() diff --git a/src/RequestRefreshTokenEvent.php b/src/RequestRefreshTokenEvent.php index 0682e57f5..326a115ed 100644 --- a/src/RequestRefreshTokenEvent.php +++ b/src/RequestRefreshTokenEvent.php @@ -31,6 +31,7 @@ public function __construct($name, ServerRequestInterface $request, RefreshToken /** * @return RefreshTokenEntityInterface + * * @codeCoverageIgnore */ public function getRefreshToken() From f4b65ecae58a5713bdc1e8dc4a4a810fd759de8e Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 22 Mar 2023 11:45:38 +0000 Subject: [PATCH 18/46] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f5f79c4b..902b6119a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ 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] +### Fixed +- Fix deprecation notices for PHP 8.x (PR #1329) ## [8.4.0] - released 2023-02-15 ### Added From eed31d86d8cc8e6e9c9f58fbb2113494f8b41e24 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 22 Mar 2023 11:47:53 +0000 Subject: [PATCH 19/46] Update changelog for version 8.4.1 --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 902b6119a..86aead527 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ 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.4.1] - released 2023-03-22 ### Fixed - Fix deprecation notices for PHP 8.x (PR #1329) @@ -578,7 +580,8 @@ Version 5 is a complete code rewrite. - First major release -[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.4.0...HEAD +[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.4.1...HEAD +[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 From 9717a63b10c1643b0dc32f714a20f2a1901e5f12 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 3 Apr 2023 11:31:51 +0000 Subject: [PATCH 20/46] Drop old PHP version support --- .github/workflows/tests.yml | 2 +- CHANGELOG.md | 2 + README.md | 5 +-- composer.json | 15 ++++---- phpunit.xml.dist | 38 +++++++++---------- .../BearerTokenValidator.php | 5 +-- tests/Utils/CryptKeyTest.php | 3 ++ 7 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1f1db9e1b..12be492c6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ 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 }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 86aead527..38527743f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ 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 +- Change to support PHP 8.0, 8.1, and 8.2 (PR #1333) ## [8.4.1] - released 2023-03-22 ### Fixed diff --git a/README.md b/README.md index 5307b840a..84a8584a5 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/composer.json b/composer.json index 593f75a75..417fe3ad1 100644 --- a/composer.json +++ b/composer.json @@ -4,18 +4,19 @@ "homepage": "https://oauth2.thephpleague.com/", "license": "MIT", "require": { - "php": "^7.2 || ^8.0", + "php": "~8.0.0 || ~8.1.0 || ~8.2.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.0 || ^5.0.0", "psr/http-message": "^1.0.1", - "defuse/php-encryption": "^2.2.1", - "ext-json": "*" + "defuse/php-encryption": "^2.3.0", + "ext-json": "*", + "lcobucci/clock": "^2.2.0 || ^3.1.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" diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 641a4946b..9ab509138 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,23 +1,21 @@ - - - - ./tests/ - - - - - src - - + + + src + + + + + ./tests/ + + diff --git a/src/AuthorizationValidators/BearerTokenValidator.php b/src/AuthorizationValidators/BearerTokenValidator.php index 7723eda81..c817d53b1 100644 --- a/src/AuthorizationValidators/BearerTokenValidator.php +++ b/src/AuthorizationValidators/BearerTokenValidator.php @@ -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; @@ -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() ?? '') diff --git a/tests/Utils/CryptKeyTest.php b/tests/Utils/CryptKeyTest.php index b9c53b660..7190864c6 100644 --- a/tests/Utils/CryptKeyTest.php +++ b/tests/Utils/CryptKeyTest.php @@ -54,6 +54,9 @@ public function testKeyString() public function testUnsupportedKeyType() { + if (\str_starts_with(\phpversion(), '8.0')) { + $this->markTestSkipped('Cannot generate key on PHP 8.0 runner. Investigating'); + } $this->expectException(\LogicException::class); $this->expectExceptionMessage('Unable to read key'); From ad4c7448343e92fd4a5449fbff6d7c9edfee39e5 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 3 Apr 2023 13:15:19 +0100 Subject: [PATCH 21/46] fix coverage --- .github/workflows/tests.yml | 5 +++-- composer.json | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 12be492c6..7c7a368e3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -31,12 +31,13 @@ jobs: - name: Install dependencies run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress + run: composer require --dev 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' }} + if: ${{ github.ref == 'refs/heads/master' && github.repository == 'thephpleague/oauth2-server' }} run: | wget https://scrutinizer-ci.com/ocular.phar - php ocular.phar code-coverage:upload --format=php-clover coverage.clover + vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover diff --git a/composer.json b/composer.json index 417fe3ad1..894fafbc0 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ "laminas/laminas-diactoros": "^2.24.0", "phpstan/phpstan": "^0.12.57", "phpstan/phpstan-phpunit": "^0.12.16", - "roave/security-advisories": "dev-master" + "roave/security-advisories": "dev-master", + "scrutinizer/ocular": "^1.9" }, "repositories": [ { From b0408e613a06d22bd9144169f23e59d0fb4b7f1e Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 3 Apr 2023 13:17:50 +0100 Subject: [PATCH 22/46] fix github action --- .github/workflows/tests.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7c7a368e3..32cb5f958 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,8 +30,9 @@ jobs: coverage: pcov - name: Install dependencies - run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress - run: composer require --dev scrutinizer/ocular + run: | + composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress + composer require --dev scrutinizer/ocular - name: Execute tests run: vendor/bin/phpunit --verbose --coverage-clover=coverage.clover From 2c9ab1322bd422f5dea35357a07b1e7910d26e0d Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 3 Apr 2023 13:22:41 +0100 Subject: [PATCH 23/46] fix coverage --- .github/workflows/tests.yml | 4 +++- composer.json | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 32cb5f958..dcd11222b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,7 +20,9 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 + with: + fetch-depth: 1 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/composer.json b/composer.json index 894fafbc0..417fe3ad1 100644 --- a/composer.json +++ b/composer.json @@ -19,8 +19,7 @@ "laminas/laminas-diactoros": "^2.24.0", "phpstan/phpstan": "^0.12.57", "phpstan/phpstan-phpunit": "^0.12.16", - "roave/security-advisories": "dev-master", - "scrutinizer/ocular": "^1.9" + "roave/security-advisories": "dev-master" }, "repositories": [ { From 886c8ec1cb2ee97759b6ff0f1cdd5c58dd670033 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 3 Apr 2023 13:24:42 +0100 Subject: [PATCH 24/46] fix coverage --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index dcd11222b..fd9e2bf22 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: - name: Checkout code uses: actions/checkout@v3 with: - fetch-depth: 1 + fetch-depth: 0 - name: Setup PHP uses: shivammathur/setup-php@v2 From 79e9cd509380ae469c9fc23b38b73bed066761d3 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 3 Apr 2023 13:28:18 +0100 Subject: [PATCH 25/46] fix github action --- .github/workflows/tests.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fd9e2bf22..ec0675ef0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,6 +41,5 @@ jobs: - name: Code coverage if: ${{ github.ref == 'refs/heads/master' && github.repository == 'thephpleague/oauth2-server' }} - run: | - wget https://scrutinizer-ci.com/ocular.phar + run: vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover From 6d6ef341b30777209bc608f6abbfc4c67ba87b1d Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 3 Apr 2023 15:48:18 +0100 Subject: [PATCH 26/46] Fix code coverage --- .github/workflows/tests.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ec0675ef0..6b82845c8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -32,9 +32,12 @@ jobs: coverage: pcov - name: Install dependencies - run: | + run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress - composer require --dev scrutinizer/ocular + + - name: Install Scrutinizer/Ocular + run: + composer global require scrutinizer/ocular - name: Execute tests run: vendor/bin/phpunit --verbose --coverage-clover=coverage.clover @@ -42,4 +45,4 @@ jobs: - name: Code coverage if: ${{ github.ref == 'refs/heads/master' && github.repository == 'thephpleague/oauth2-server' }} run: - vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover + ~/.composer/vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover From 8170913a2aeadebbba17b46df4dc18d17c891516 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 3 Apr 2023 15:54:34 +0100 Subject: [PATCH 27/46] Update readme --- CHANGELOG.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38527743f..7ef516eec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,13 @@ 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 -- Change to support PHP 8.0, 8.1, and 8.2 (PR #1333) + +## [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 @@ -582,7 +587,8 @@ Version 5 is a complete code rewrite. - First major release -[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.4.1...HEAD +[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.5.0...HEAD +[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 From bf55178077b53a730276f2039f628cc76ee80164 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Mon, 3 Apr 2023 17:05:37 +0100 Subject: [PATCH 28/46] Tweaked version constraints --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 417fe3ad1..d3f0a03d7 100644 --- a/composer.json +++ b/composer.json @@ -4,15 +4,15 @@ "homepage": "https://oauth2.thephpleague.com/", "license": "MIT", "require": { - "php": "~8.0.0 || ~8.1.0 || ~8.2.0", + "php": "^8.0", "ext-openssl": "*", "league/event": "^2.2", "league/uri": "^6.7", - "lcobucci/jwt": "^4.3.0 || ^5.0.0", + "lcobucci/jwt": "^4.3 || ^5.0", "psr/http-message": "^1.0.1", - "defuse/php-encryption": "^2.3.0", + "defuse/php-encryption": "^2.3", "ext-json": "*", - "lcobucci/clock": "^2.2.0 || ^3.1.0" + "lcobucci/clock": "^2.2 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^9.6.6", From bd480887d93d73a6285d557d838d3b1624eb65d6 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Mon, 3 Apr 2023 21:56:02 +0100 Subject: [PATCH 29/46] Lowered `lcobucci/clock` min version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d3f0a03d7..9699116ee 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "psr/http-message": "^1.0.1", "defuse/php-encryption": "^2.3", "ext-json": "*", - "lcobucci/clock": "^2.2 || ^3.1" + "lcobucci/clock": "^2.2 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^9.6.6", From 89d0ca5d0c05a22269114dfee14f5f95d2b7c1b2 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Tue, 4 Apr 2023 11:19:10 +0100 Subject: [PATCH 30/46] Update changelog for version 8.5.1 --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ef516eec..24c7fed97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +## [8.5.1] - released 2023-04-04 +- 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) @@ -587,7 +590,8 @@ Version 5 is a complete code rewrite. - First major release -[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.5.0...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 From 43cd4d406906c6be5c8de2cee9bd3ad3753544ef Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Tue, 4 Apr 2023 11:20:16 +0100 Subject: [PATCH 31/46] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24c7fed97..4d502a650 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [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 From dc8a8af319c54683eb2f2193747d19c5dc144151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Anne?= Date: Fri, 21 Apr 2023 11:18:22 +0200 Subject: [PATCH 32/46] Remove requirement of json extension --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 9699116ee..24684a273 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,6 @@ "lcobucci/jwt": "^4.3 || ^5.0", "psr/http-message": "^1.0.1", "defuse/php-encryption": "^2.3", - "ext-json": "*", "lcobucci/clock": "^2.2 || ^3.0" }, "require-dev": { From 54942a7640ab3a1e216c103b071bf74aa2f6bf1c Mon Sep 17 00:00:00 2001 From: erikn69 Date: Tue, 18 Apr 2023 12:11:18 -0500 Subject: [PATCH 33/46] Bump laminas/laminas-diactoros, psr/http-message versions --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 9699116ee..94e89de99 100644 --- a/composer.json +++ b/composer.json @@ -9,14 +9,14 @@ "league/event": "^2.2", "league/uri": "^6.7", "lcobucci/jwt": "^4.3 || ^5.0", - "psr/http-message": "^1.0.1", + "psr/http-message": "^1.0.1 || ^2.0", "defuse/php-encryption": "^2.3", "ext-json": "*", "lcobucci/clock": "^2.2 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^9.6.6", - "laminas/laminas-diactoros": "^2.24.0", + "laminas/laminas-diactoros": "^2.24.0 || ^3.0.0", "phpstan/phpstan": "^0.12.57", "phpstan/phpstan-phpunit": "^0.12.16", "roave/security-advisories": "dev-master" From 8f89bf254d3975a4dc263660dd724dedc21511b5 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Tue, 6 Jun 2023 21:44:02 +0100 Subject: [PATCH 34/46] Remove skipping of test --- tests/Utils/CryptKeyTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/Utils/CryptKeyTest.php b/tests/Utils/CryptKeyTest.php index 7190864c6..b9c53b660 100644 --- a/tests/Utils/CryptKeyTest.php +++ b/tests/Utils/CryptKeyTest.php @@ -54,9 +54,6 @@ public function testKeyString() public function testUnsupportedKeyType() { - if (\str_starts_with(\phpversion(), '8.0')) { - $this->markTestSkipped('Cannot generate key on PHP 8.0 runner. Investigating'); - } $this->expectException(\LogicException::class); $this->expectExceptionMessage('Unable to read key'); From 75f210dc7be2d81761a9a13a79b5e9b3c5f37b3c Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Tue, 6 Jun 2023 21:56:22 +0100 Subject: [PATCH 35/46] Run 8.0 tests on Ubuntu 20.04 --- .github/workflows/tests.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6b82845c8..1d17d65c5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,13 +8,17 @@ on: jobs: tests: - runs-on: ubuntu-latest - strategy: fail-fast: false matrix: - php: [8.0, 8.1, 8.2] + php: [8.1, 8.2] + os: [ubuntu-22.04] stability: [prefer-lowest, prefer-stable] + include: + - os: ubuntu-20.04 + php: 8.0 + + runs-on: ${{ matrix.os }} name: PHP ${{ matrix.php }} - ${{ matrix.stability }} From a10cde3daf65c0c19c5c8a05e9b40cd435016489 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Tue, 6 Jun 2023 21:58:33 +0100 Subject: [PATCH 36/46] Include stability preferences for composer install --- .github/workflows/tests.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1d17d65c5..ade0c245b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,6 +17,10 @@ jobs: include: - os: ubuntu-20.04 php: 8.0 + stability: prefer-lowest + - os: ubuntu-20.04 + php: 8.0 + stability: prefer-stable runs-on: ${{ matrix.os }} From 9a97128a1fea3fdfd257d9f5c652f4f83898c92e Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Tue, 6 Jun 2023 22:26:18 +0100 Subject: [PATCH 37/46] Upgrade laminas/diactoros --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 613784833..43dee449c 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ }, "require-dev": { "phpunit/phpunit": "^9.6.6", - "laminas/laminas-diactoros": "^2.24.0 || ^3.0.0", + "laminas/laminas-diactoros": "^3.0.0", "phpstan/phpstan": "^0.12.57", "phpstan/phpstan-phpunit": "^0.12.16", "roave/security-advisories": "dev-master" From 3b88400b45866fce443e89dc34dc3ea2a6df5f0c Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Tue, 6 Jun 2023 22:35:38 +0100 Subject: [PATCH 38/46] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d502a650..32aaabedd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ 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 +- 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 From 8ab731e84eef904b5913ba31b38116acf8ea50b6 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Fri, 16 Jun 2023 16:32:47 +0100 Subject: [PATCH 39/46] Update changelog for version 8.5.2 --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32aaabedd..2e57b2118 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ 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.2] - released 2023-06-16 ### Changed - Bumped the versions for laminas/diactoros and psr/http-message to support PSR-7 v2.0 (PR #1339) @@ -594,7 +596,8 @@ Version 5 is a complete code rewrite. - First major release -[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.5.1...HEAD +[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.5.2...HEAD +[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 From 0143d52cea8c30cc1ccbb3e9ab9ec24566566931 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 5 Jul 2023 23:50:06 +0100 Subject: [PATCH 40/46] Remove potential key from exception message --- src/CryptKey.php | 2 +- tests/Utils/CryptKeyTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CryptKey.php b/src/CryptKey.php index 14cd8e7b4..bef2f3cdb 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -64,7 +64,7 @@ public function __construct($keyPath, $passPhrase = null, $keyPermissionsCheck = throw new LogicException('Unable to read key from file ' . $keyPath); } } else { - throw new LogicException('Unable to read key from file ' . $keyPath); + throw new LogicException('Invalid key supplied'); } if ($keyPermissionsCheck === true) { diff --git a/tests/Utils/CryptKeyTest.php b/tests/Utils/CryptKeyTest.php index b9c53b660..8edadb8b0 100644 --- a/tests/Utils/CryptKeyTest.php +++ b/tests/Utils/CryptKeyTest.php @@ -55,7 +55,7 @@ public function testKeyString() public function testUnsupportedKeyType() { $this->expectException(\LogicException::class); - $this->expectExceptionMessage('Unable to read key'); + $this->expectExceptionMessage('Invalid key supplied'); try { // Create the keypair From 20f07b06ac56e7f667d33fe3399cf9918ef119ce Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 5 Jul 2023 23:55:12 +0100 Subject: [PATCH 41/46] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e57b2118..6505c27e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +## [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 contain 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 From 605f6f0b7d134d7c0153f6bb9369d5b8e63d9d0b Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 5 Jul 2023 23:56:21 +0100 Subject: [PATCH 42/46] Change wording of changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6505c27e5..4ab849c11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [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 contain the given key. +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 From eb91b4190e7f6169053ebf8ffa352d47e756b2ce Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Thu, 6 Jul 2023 00:01:32 +0100 Subject: [PATCH 43/46] Update changelog links --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ab849c11..83ea5d328 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -602,7 +602,8 @@ Version 5 is a complete code rewrite. - First major release -[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.5.2...HEAD +[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.5.3...HEAD +[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 From e16ae3f6d3134025ff97af4d23c103c34ce0cfa1 Mon Sep 17 00:00:00 2001 From: erikn69 Date: Mon, 14 Aug 2023 08:56:19 -0500 Subject: [PATCH 44/46] Bump league/uri to ^7.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 43dee449c..5c9dfb398 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "php": "^8.0", "ext-openssl": "*", "league/event": "^2.2", - "league/uri": "^6.7", + "league/uri": "^6.7 || ^7.0", "lcobucci/jwt": "^4.3 || ^5.0", "psr/http-message": "^1.0.1 || ^2.0", "defuse/php-encryption": "^2.3", From 404f00cf5402c76bd724c306f74de42af5d0eb4d Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Fri, 25 Aug 2023 23:33:15 +0100 Subject: [PATCH 45/46] Update changelog for v8.5.4 --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83ea5d328..cddd18a3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +## [8.5.4] - released 2023-08-25 +### Added +- Support for league/uri ^7.0 + ## [8.5.3] - released 2023-07-06 ### Security - If a key string is provided to the CryptKey constructor with an invalid @@ -602,7 +606,8 @@ Version 5 is a complete code rewrite. - First major release -[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.5.3...HEAD +[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.5.4...HEAD +[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 From ab7714d073844497fd222d5d0a217629089936bc Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Fri, 25 Aug 2023 23:35:12 +0100 Subject: [PATCH 46/46] Add pr number to changelog record --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cddd18a3c..46b8c78e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [8.5.4] - released 2023-08-25 ### Added -- Support for league/uri ^7.0 +- Support for league/uri ^7.0 (PR #1367) ## [8.5.3] - released 2023-07-06 ### Security