From fd72d79ad3fb2b491e319d0c8aac7e7056b3ffd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= Date: Thu, 15 Mar 2018 14:27:37 +0100 Subject: [PATCH 001/104] Generalized access token format --- src/Entities/AccessTokenEntityInterface.php | 9 +++++++++ src/Entities/Traits/AccessTokenTrait.php | 12 ++++++++++++ src/Grant/ImplicitGrant.php | 2 +- src/ResponseTypes/BearerTokenResponse.php | 4 +--- tests/Middleware/ResourceServerMiddlewareTest.php | 4 ++-- 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/Entities/AccessTokenEntityInterface.php b/src/Entities/AccessTokenEntityInterface.php index 4da7600e..c30a51f9 100644 --- a/src/Entities/AccessTokenEntityInterface.php +++ b/src/Entities/AccessTokenEntityInterface.php @@ -22,4 +22,13 @@ interface AccessTokenEntityInterface extends TokenInterface * @return Token */ public function convertToJWT(CryptKey $privateKey); + + /** + * Generate a string representation from the access token + * + * @param CryptKey $privateKey + * + * @return string + */ + public function getResponseString(CryptKey $privateKey); } diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php index 81fc1bfd..71b87732 100644 --- a/src/Entities/Traits/AccessTokenTrait.php +++ b/src/Entities/Traits/AccessTokenTrait.php @@ -40,6 +40,18 @@ trait AccessTokenTrait ->getToken(); } + /** + * Generate a string representation from the access token + * + * @param CryptKey $privateKey + * + * @return string + */ + public function getResponseString(CryptKey $privateKey) + { + return (string) $this->convertToJWT($privateKey); + } + /** * @return ClientEntityInterface */ diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index b4157883..d915f61d 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -216,7 +216,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant $this->makeRedirectUri( $finalRedirectUri, [ - 'access_token' => (string) $accessToken->convertToJWT($this->privateKey), + 'access_token' => $accessToken->getResponseString($this->privateKey), 'token_type' => 'Bearer', 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(), 'state' => $authorizationRequest->getState(), diff --git a/src/ResponseTypes/BearerTokenResponse.php b/src/ResponseTypes/BearerTokenResponse.php index a57573a0..980965ed 100644 --- a/src/ResponseTypes/BearerTokenResponse.php +++ b/src/ResponseTypes/BearerTokenResponse.php @@ -24,12 +24,10 @@ class BearerTokenResponse extends AbstractResponseType { $expireDateTime = $this->accessToken->getExpiryDateTime()->getTimestamp(); - $jwtAccessToken = $this->accessToken->convertToJWT($this->privateKey); - $responseParams = [ 'token_type' => 'Bearer', 'expires_in' => $expireDateTime - (new \DateTime())->getTimestamp(), - 'access_token' => (string) $jwtAccessToken, + 'access_token' => $this->accessToken->getResponseString($this->privateKey), ]; if ($this->refreshToken instanceof RefreshTokenEntityInterface) { diff --git a/tests/Middleware/ResourceServerMiddlewareTest.php b/tests/Middleware/ResourceServerMiddlewareTest.php index 2269c45a..dbb507e2 100644 --- a/tests/Middleware/ResourceServerMiddlewareTest.php +++ b/tests/Middleware/ResourceServerMiddlewareTest.php @@ -30,7 +30,7 @@ class ResourceServerMiddlewareTest extends TestCase $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); $accessToken->setClient($client); - $token = $accessToken->convertToJWT(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $token = $accessToken->getResponseString(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest(); $request = $request->withHeader('authorization', sprintf('Bearer %s', $token)); @@ -65,7 +65,7 @@ class ResourceServerMiddlewareTest extends TestCase $accessToken->setExpiryDateTime((new \DateTime())->sub(new \DateInterval('PT1H'))); $accessToken->setClient($client); - $token = $accessToken->convertToJWT(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $token = $accessToken->getResponseString(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest(); $request = $request->withHeader('authorization', sprintf('Bearer %s', $token)); From 48ce5f36cf8c7a2d8a243872928181826315f963 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 31 Mar 2018 10:45:15 +0100 Subject: [PATCH 002/104] Change function name to be less technically specific --- src/Entities/AccessTokenEntityInterface.php | 2 +- src/Entities/Traits/AccessTokenTrait.php | 2 +- src/Grant/ImplicitGrant.php | 2 +- src/ResponseTypes/BearerTokenResponse.php | 2 +- tests/Middleware/ResourceServerMiddlewareTest.php | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Entities/AccessTokenEntityInterface.php b/src/Entities/AccessTokenEntityInterface.php index c30a51f9..3849512c 100644 --- a/src/Entities/AccessTokenEntityInterface.php +++ b/src/Entities/AccessTokenEntityInterface.php @@ -30,5 +30,5 @@ interface AccessTokenEntityInterface extends TokenInterface * * @return string */ - public function getResponseString(CryptKey $privateKey); + public function convertToAccessToken(CryptKey $privateKey); } diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php index 71b87732..08231784 100644 --- a/src/Entities/Traits/AccessTokenTrait.php +++ b/src/Entities/Traits/AccessTokenTrait.php @@ -47,7 +47,7 @@ trait AccessTokenTrait * * @return string */ - public function getResponseString(CryptKey $privateKey) + public function convertToAccessToken(CryptKey $privateKey) { return (string) $this->convertToJWT($privateKey); } diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index d915f61d..e5504151 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -216,7 +216,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant $this->makeRedirectUri( $finalRedirectUri, [ - 'access_token' => $accessToken->getResponseString($this->privateKey), + 'access_token' => $accessToken->convertToAccessToken($this->privateKey), 'token_type' => 'Bearer', 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(), 'state' => $authorizationRequest->getState(), diff --git a/src/ResponseTypes/BearerTokenResponse.php b/src/ResponseTypes/BearerTokenResponse.php index 980965ed..b630685c 100644 --- a/src/ResponseTypes/BearerTokenResponse.php +++ b/src/ResponseTypes/BearerTokenResponse.php @@ -27,7 +27,7 @@ class BearerTokenResponse extends AbstractResponseType $responseParams = [ 'token_type' => 'Bearer', 'expires_in' => $expireDateTime - (new \DateTime())->getTimestamp(), - 'access_token' => $this->accessToken->getResponseString($this->privateKey), + 'access_token' => $this->accessToken->convertToAccessToken($this->privateKey), ]; if ($this->refreshToken instanceof RefreshTokenEntityInterface) { diff --git a/tests/Middleware/ResourceServerMiddlewareTest.php b/tests/Middleware/ResourceServerMiddlewareTest.php index dbb507e2..0db71cdd 100644 --- a/tests/Middleware/ResourceServerMiddlewareTest.php +++ b/tests/Middleware/ResourceServerMiddlewareTest.php @@ -30,7 +30,7 @@ class ResourceServerMiddlewareTest extends TestCase $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); $accessToken->setClient($client); - $token = $accessToken->getResponseString(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $token = $accessToken->convertToAccessToken(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest(); $request = $request->withHeader('authorization', sprintf('Bearer %s', $token)); @@ -65,7 +65,7 @@ class ResourceServerMiddlewareTest extends TestCase $accessToken->setExpiryDateTime((new \DateTime())->sub(new \DateInterval('PT1H'))); $accessToken->setClient($client); - $token = $accessToken->getResponseString(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $token = $accessToken->convertToAccessToken(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest(); $request = $request->withHeader('authorization', sprintf('Bearer %s', $token)); From a1da9beb92e30150382beab19806b66b5eb23a96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= Date: Thu, 19 Apr 2018 17:08:10 +0200 Subject: [PATCH 003/104] Removed convertToJWT() method from AccessTokenEntityInterface --- src/Entities/AccessTokenEntityInterface.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Entities/AccessTokenEntityInterface.php b/src/Entities/AccessTokenEntityInterface.php index 3849512c..8cc20dd2 100644 --- a/src/Entities/AccessTokenEntityInterface.php +++ b/src/Entities/AccessTokenEntityInterface.php @@ -14,15 +14,6 @@ use League\OAuth2\Server\CryptKey; interface AccessTokenEntityInterface extends TokenInterface { - /** - * Generate a JWT from the access token - * - * @param CryptKey $privateKey - * - * @return Token - */ - public function convertToJWT(CryptKey $privateKey); - /** * Generate a string representation from the access token * From 577065c270ad67ea4cf06c4c9713e0691bf7d493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= Date: Tue, 8 May 2018 11:34:42 +0200 Subject: [PATCH 004/104] Use native typehints --- src/Entities/AccessTokenEntityInterface.php | 7 +------ src/Entities/Traits/AccessTokenTrait.php | 6 +----- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/Entities/AccessTokenEntityInterface.php b/src/Entities/AccessTokenEntityInterface.php index 8cc20dd2..5d69eefd 100644 --- a/src/Entities/AccessTokenEntityInterface.php +++ b/src/Entities/AccessTokenEntityInterface.php @@ -9,17 +9,12 @@ namespace League\OAuth2\Server\Entities; -use Lcobucci\JWT\Token; use League\OAuth2\Server\CryptKey; interface AccessTokenEntityInterface extends TokenInterface { /** * Generate a string representation from the access token - * - * @param CryptKey $privateKey - * - * @return string */ - public function convertToAccessToken(CryptKey $privateKey); + public function convertToAccessToken(CryptKey $privateKey): string; } diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php index 08231784..fab41c46 100644 --- a/src/Entities/Traits/AccessTokenTrait.php +++ b/src/Entities/Traits/AccessTokenTrait.php @@ -42,12 +42,8 @@ trait AccessTokenTrait /** * Generate a string representation from the access token - * - * @param CryptKey $privateKey - * - * @return string */ - public function convertToAccessToken(CryptKey $privateKey) + public function convertToAccessToken(CryptKey $privateKey): string { return (string) $this->convertToJWT($privateKey); } From ff5e9f57a59d3865a93df227759b366cd1e4239f Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Thu, 10 May 2018 22:07:03 +0100 Subject: [PATCH 005/104] Only add authenticate header if present in original request thephpleague/oauth2-server#745 --- src/Exception/OAuthServerException.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 65fe861e..a62d961d 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -294,13 +294,9 @@ class OAuthServerException extends \Exception // include the "WWW-Authenticate" response header field // matching the authentication scheme used by the client. // @codeCoverageIgnoreStart - if ($this->errorType === 'invalid_client') { - $authScheme = 'Basic'; - if (array_key_exists('HTTP_AUTHORIZATION', $_SERVER) !== false - && strpos($_SERVER['HTTP_AUTHORIZATION'], 'Bearer') === 0 - ) { - $authScheme = 'Bearer'; - } + if ($this->errorType === 'invalid_client' && array_key_exists('HTTP_AUTHORIZATION', $_SERVER) !== false) { + $authScheme = strpos($_SERVER['HTTP_AUTHORIZATION'], 'Bearer') === 0 ? 'Bearer' : 'Basic'; + $headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"'; } // @codeCoverageIgnoreEnd From 33ce8496175955ac5503b95f3704fce59fdf350d Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 13 May 2018 17:29:07 +0100 Subject: [PATCH 006/104] Add tests for invalid client exception --- src/Exception/OAuthServerException.php | 30 +++++++-- src/Grant/AbstractGrant.php | 6 +- src/Grant/AuthCodeGrant.php | 8 +-- src/Grant/ImplicitGrant.php | 8 +-- tests/Exception/OAuthServerExceptionTest.php | 66 ++++++++++++++++++++ tests/Grant/AbstractGrantTest.php | 2 +- 6 files changed, 104 insertions(+), 16 deletions(-) create mode 100644 tests/Exception/OAuthServerExceptionTest.php diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index a62d961d..8b296164 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -9,6 +9,7 @@ namespace League\OAuth2\Server\Exception; +use Psr\Http\Message\ServerRequest; use Psr\Http\Message\ResponseInterface; class OAuthServerException extends \Exception @@ -38,6 +39,11 @@ class OAuthServerException extends \Exception */ private $payload; + /** + * @var ServerRequest + */ + private $serverRequest; + /** * Throw a new exception. * @@ -84,6 +90,16 @@ class OAuthServerException extends \Exception $this->payload = $payload; } + /** + * Set the server request that is responsible for generating the exception + * + * @return void + */ + public function setServerRequest($serverRequest) + { + $this->ServerRequest = $serverRequest; + } + /** * Unsupported grant type error. * @@ -117,13 +133,19 @@ class OAuthServerException extends \Exception /** * Invalid client error. * + * @param ServerRequest $serverRequest + * * @return static */ - public static function invalidClient() + public static function invalidClient($serverRequest) { $errorMessage = 'Client authentication failed'; - return new static($errorMessage, 4, 'invalid_client', 401); + $exception = new static('Client authentication failed', 4, 'invalid_client', 401); + + $exception->setServerRequest($serverRequest); + + return $exception; } /** @@ -294,8 +316,8 @@ class OAuthServerException extends \Exception // include the "WWW-Authenticate" response header field // matching the authentication scheme used by the client. // @codeCoverageIgnoreStart - if ($this->errorType === 'invalid_client' && array_key_exists('HTTP_AUTHORIZATION', $_SERVER) !== false) { - $authScheme = strpos($_SERVER['HTTP_AUTHORIZATION'], 'Bearer') === 0 ? 'Bearer' : 'Basic'; + if ($this->errorType === 'invalid_client' && $this->ServerRequest->hasHeader('Authorization') === true) { + $authScheme = strpos($this->ServerRequest->getHeader('Authorization')[0], 'Bearer') === 0 ? 'Bearer' : 'Basic'; $headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"'; } diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 79a1ac47..05b73faa 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -190,7 +190,7 @@ abstract class AbstractGrant implements GrantTypeInterface if ($client instanceof ClientEntityInterface === false) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient(); + throw OAuthServerException::invalidClient($request); } // If a redirect URI is provided ensure it matches what is pre-registered @@ -201,13 +201,13 @@ abstract class AbstractGrant implements GrantTypeInterface && (strcmp($client->getRedirectUri(), $redirectUri) !== 0) ) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient(); + throw OAuthServerException::invalidClient($request); } elseif ( is_array($client->getRedirectUri()) && in_array($redirectUri, $client->getRedirectUri(), true) === false ) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient(); + throw OAuthServerException::invalidClient($request); } } diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 80e1cd0f..084e21d4 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -236,7 +236,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant if ($client instanceof ClientEntityInterface === false) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient(); + throw OAuthServerException::invalidClient($request); } $redirectUri = $this->getQueryStringParameter('redirect_uri', $request); @@ -247,18 +247,18 @@ class AuthCodeGrant extends AbstractAuthorizeGrant && (strcmp($client->getRedirectUri(), $redirectUri) !== 0) ) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient(); + throw OAuthServerException::invalidClient($request); } elseif ( is_array($client->getRedirectUri()) && in_array($redirectUri, $client->getRedirectUri(), true) === false ) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient(); + throw OAuthServerException::invalidClient($request); } } elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1 || empty($client->getRedirectUri())) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient(); + throw OAuthServerException::invalidClient($request); } else { $redirectUri = is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index b4157883..c740f75c 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -131,7 +131,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant if ($client instanceof ClientEntityInterface === false) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient(); + throw OAuthServerException::invalidClient($request); } $redirectUri = $this->getQueryStringParameter('redirect_uri', $request); @@ -141,18 +141,18 @@ class ImplicitGrant extends AbstractAuthorizeGrant && (strcmp($client->getRedirectUri(), $redirectUri) !== 0) ) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient(); + throw OAuthServerException::invalidClient($request); } elseif ( is_array($client->getRedirectUri()) && in_array($redirectUri, $client->getRedirectUri(), true) === false ) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient(); + throw OAuthServerException::invalidClient($request); } } elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1 || empty($client->getRedirectUri())) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient(); + throw OAuthServerException::invalidClient($request); } else { $redirectUri = is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] diff --git a/tests/Exception/OAuthServerExceptionTest.php b/tests/Exception/OAuthServerExceptionTest.php new file mode 100644 index 00000000..11704ade --- /dev/null +++ b/tests/Exception/OAuthServerExceptionTest.php @@ -0,0 +1,66 @@ +withParsedBody([ + 'client_id' => 'foo', + ]) + ->withAddedHeader('Authorization', 'Basic fakeauthdetails'); + + try { + $this->issueInvalidClientException($serverRequest); + } catch (OAuthServerException $e) { + $response = $e->generateHttpResponse(new Response()); + + $this->assertTrue($response->hasHeader('WWW-Authenticate')); + } + } + + public function testInvalidClientExceptionOmitsAuthenticateHeader() + { + $serverRequest = (new ServerRequest()) + ->withParsedBody([ + 'client_id' => 'foo', + ]); + + try { + $this->issueInvalidClientException($serverRequest); + } catch (OAuthServerException $e) { + $response = $e->generateHttpResponse(new Response()); + + $this->assertFalse($response->hasHeader('WWW-Authenticate')); + } + } + + /** + * Issue an invalid client exception + * + * @return void + * @throws OAuthServerException + */ + private function issueInvalidClientException($serverRequest) + { + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn(false); + + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setClientRepository($clientRepositoryMock); + + $abstractGrantReflection = new ReflectionClass($grantMock); + + $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); + $validateClientMethod->setAccessible(true); + + $validateClientMethod->invoke($grantMock, $serverRequest); + } +} diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 6266df0a..5da2776e 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -122,7 +122,7 @@ class AbstractGrantTest extends TestCase $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); - $result = $validateClientMethod->invoke($grantMock, $serverRequest, true, true); + $result = $validateClientMethod->invoke($grantMock, $serverRequest); $this->assertEquals($client, $result); } From c2dcdee26667704843a6b297e7e469926372d4bc Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 13 May 2018 17:34:06 +0100 Subject: [PATCH 007/104] Change order of use statements --- src/Exception/OAuthServerException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 8b296164..72f23e6e 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -9,8 +9,8 @@ namespace League\OAuth2\Server\Exception; -use Psr\Http\Message\ServerRequest; use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequest; class OAuthServerException extends \Exception { From cbce5f45ba4f227e471f287c7a92de51df7c1528 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 13 May 2018 17:38:07 +0100 Subject: [PATCH 008/104] Fix case for serverRequest variable and remove unused variable --- src/Exception/OAuthServerException.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 72f23e6e..4713eba5 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -97,7 +97,7 @@ class OAuthServerException extends \Exception */ public function setServerRequest($serverRequest) { - $this->ServerRequest = $serverRequest; + $this->serverRequest = $serverRequest; } /** @@ -139,8 +139,6 @@ class OAuthServerException extends \Exception */ public static function invalidClient($serverRequest) { - $errorMessage = 'Client authentication failed'; - $exception = new static('Client authentication failed', 4, 'invalid_client', 401); $exception->setServerRequest($serverRequest); @@ -316,8 +314,8 @@ class OAuthServerException extends \Exception // include the "WWW-Authenticate" response header field // matching the authentication scheme used by the client. // @codeCoverageIgnoreStart - if ($this->errorType === 'invalid_client' && $this->ServerRequest->hasHeader('Authorization') === true) { - $authScheme = strpos($this->ServerRequest->getHeader('Authorization')[0], 'Bearer') === 0 ? 'Bearer' : 'Basic'; + if ($this->errorType === 'invalid_client' && $this->serverRequest->hasHeader('Authorization') === true) { + $authScheme = strpos($this->serverRequest->getHeader('Authorization')[0], 'Bearer') === 0 ? 'Bearer' : 'Basic'; $headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"'; } From f8c2e721a05c18917bce16e696df4dc659ab3ddb Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 13 May 2018 17:41:21 +0100 Subject: [PATCH 009/104] Remove return voids and fix docblock and use orders --- src/Exception/OAuthServerException.php | 2 +- tests/Exception/OAuthServerExceptionTest.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 4713eba5..ddd4c534 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -93,7 +93,7 @@ class OAuthServerException extends \Exception /** * Set the server request that is responsible for generating the exception * - * @return void + * @param ServerRequest $serverRequest */ public function setServerRequest($serverRequest) { diff --git a/tests/Exception/OAuthServerExceptionTest.php b/tests/Exception/OAuthServerExceptionTest.php index 11704ade..b86f33a1 100644 --- a/tests/Exception/OAuthServerExceptionTest.php +++ b/tests/Exception/OAuthServerExceptionTest.php @@ -1,9 +1,9 @@ Date: Sun, 13 May 2018 17:52:45 +0100 Subject: [PATCH 010/104] Fix ServerRequestInterface docblock type --- src/Exception/OAuthServerException.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index ddd4c534..345b99a0 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -10,7 +10,7 @@ namespace League\OAuth2\Server\Exception; use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\ServerRequest; +use Psr\Http\Message\ServerRequestInterface; class OAuthServerException extends \Exception { @@ -40,7 +40,7 @@ class OAuthServerException extends \Exception private $payload; /** - * @var ServerRequest + * @var ServerRequestInterface */ private $serverRequest; @@ -93,7 +93,7 @@ class OAuthServerException extends \Exception /** * Set the server request that is responsible for generating the exception * - * @param ServerRequest $serverRequest + * @param ServerRequestInterface $serverRequest */ public function setServerRequest($serverRequest) { @@ -133,7 +133,7 @@ class OAuthServerException extends \Exception /** * Invalid client error. * - * @param ServerRequest $serverRequest + * @param ServerRequestInterface $serverRequest * * @return static */ From b1b33207ab4f3164f497accdd9dbefadb0189343 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 13 May 2018 18:02:23 +0100 Subject: [PATCH 011/104] Fix namespacing for Exception test --- tests/Exception/OAuthServerExceptionTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Exception/OAuthServerExceptionTest.php b/tests/Exception/OAuthServerExceptionTest.php index b86f33a1..bac914e4 100644 --- a/tests/Exception/OAuthServerExceptionTest.php +++ b/tests/Exception/OAuthServerExceptionTest.php @@ -1,5 +1,7 @@ getMockForAbstractClass(AbstractGrant::class); $grantMock->setClientRepository($clientRepositoryMock); - $abstractGrantReflection = new ReflectionClass($grantMock); + $abstractGrantReflection = new \ReflectionClass($grantMock); $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); From 98812e6fabe5f82917d0a5d8ae143260e83c1bce Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 21 May 2018 11:21:44 +0100 Subject: [PATCH 012/104] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcac3bfd..1e27f9ac 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] +### Changed +- The `invalidClient()` function accepts a PSR-7 compliant `$serverRequest` argument to avoid accessing the `$_SERVER` global variable and improve testing (PR #899) + ### Fixed - No longer set a WWW-Authenticate header for invalid clients if the client did not send an Authorization header in the original request From b182389395599df2890f27dbc47d24ed80222746 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 21 May 2018 15:45:09 +0100 Subject: [PATCH 013/104] Remove native type hints --- src/Entities/AccessTokenEntityInterface.php | 2 +- src/Entities/Traits/AccessTokenTrait.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Entities/AccessTokenEntityInterface.php b/src/Entities/AccessTokenEntityInterface.php index 5d69eefd..5729c3fc 100644 --- a/src/Entities/AccessTokenEntityInterface.php +++ b/src/Entities/AccessTokenEntityInterface.php @@ -16,5 +16,5 @@ interface AccessTokenEntityInterface extends TokenInterface /** * Generate a string representation from the access token */ - public function convertToAccessToken(CryptKey $privateKey): string; + public function convertToAccessToken(CryptKey $privateKey); } diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php index fab41c46..c2ee70a6 100644 --- a/src/Entities/Traits/AccessTokenTrait.php +++ b/src/Entities/Traits/AccessTokenTrait.php @@ -43,7 +43,7 @@ trait AccessTokenTrait /** * Generate a string representation from the access token */ - public function convertToAccessToken(CryptKey $privateKey): string + public function convertToAccessToken(CryptKey $privateKey) { return (string) $this->convertToJWT($privateKey); } From 4b0383b16c948b9339d6ff6c65e411a7e0c5a447 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 21 May 2018 16:20:48 +0100 Subject: [PATCH 014/104] Updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20a54fb0..20b11e8d 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] ### Changed +- Replace `convertToJWT()` interface with a more generic `convertToAccessToken` to improve extensibility (PR #874) - The `invalidClient()` function accepts a PSR-7 compliant `$serverRequest` argument to avoid accessing the `$_SERVER` global variable and improve testing (PR #899) ## [7.1.1] - released 2018-05-21 From 61156ef8c7a8f79e45c5c19ff2f35b18e7b82dd6 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 23 May 2018 16:34:39 +0100 Subject: [PATCH 015/104] Use __toString() for access token --- src/Entities/AccessTokenEntityInterface.php | 9 +++++++-- src/Entities/Traits/AccessTokenTrait.php | 19 ++++++++++++++++--- src/Grant/AbstractGrant.php | 1 + src/Grant/ImplicitGrant.php | 2 +- src/ResponseTypes/BearerTokenResponse.php | 2 +- tests/Grant/AbstractGrantTest.php | 2 ++ tests/Grant/AuthCodeGrantTest.php | 10 +++++++++- tests/Grant/ClientCredentialsGrantTest.php | 2 ++ tests/Grant/PasswordGrantTest.php | 2 ++ .../ResourceServerMiddlewareTest.php | 6 ++++-- .../ResponseTypes/BearerResponseTypeTest.php | 5 +++++ 11 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/Entities/AccessTokenEntityInterface.php b/src/Entities/AccessTokenEntityInterface.php index 5729c3fc..fd459914 100644 --- a/src/Entities/AccessTokenEntityInterface.php +++ b/src/Entities/AccessTokenEntityInterface.php @@ -14,7 +14,12 @@ use League\OAuth2\Server\CryptKey; interface AccessTokenEntityInterface extends TokenInterface { /** - * Generate a string representation from the access token + * Set a private key used to encrypt the access token. */ - public function convertToAccessToken(CryptKey $privateKey); + public function setPrivateKey(CryptKey $privateKey); + + /** + * Generate a string representation of the access token. + */ + public function __toString(); } diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php index c2ee70a6..cbed3122 100644 --- a/src/Entities/Traits/AccessTokenTrait.php +++ b/src/Entities/Traits/AccessTokenTrait.php @@ -19,6 +19,19 @@ use League\OAuth2\Server\Entities\ScopeEntityInterface; trait AccessTokenTrait { + /** + * @var CryptKey $privateKey + */ + private $privateKey; + + /** + * Set the private key used to encrypt this access token. + */ + public function setPrivateKey(CryptKey $privateKey) + { + $this->privateKey = $privateKey; + } + /** * Generate a JWT from the access token * @@ -26,7 +39,7 @@ trait AccessTokenTrait * * @return Token */ - public function convertToJWT(CryptKey $privateKey) + private function convertToJWT(CryptKey $privateKey) { return (new Builder()) ->setAudience($this->getClient()->getIdentifier()) @@ -43,9 +56,9 @@ trait AccessTokenTrait /** * Generate a string representation from the access token */ - public function convertToAccessToken(CryptKey $privateKey) + public function __toString() { - return (string) $this->convertToJWT($privateKey); + return (string) $this->convertToJWT($this->privateKey); } /** diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 05b73faa..d020c6ad 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -361,6 +361,7 @@ abstract class AbstractGrant implements GrantTypeInterface $accessToken->setClient($client); $accessToken->setUserIdentifier($userIdentifier); $accessToken->setExpiryDateTime((new \DateTime())->add($accessTokenTTL)); + $accessToken->setPrivateKey($this->privateKey); foreach ($scopes as $scope) { $accessToken->addScope($scope); diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 0aedaa55..5d6035e4 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -216,7 +216,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant $this->makeRedirectUri( $finalRedirectUri, [ - 'access_token' => $accessToken->convertToAccessToken($this->privateKey), + 'access_token' => (string) $accessToken, 'token_type' => 'Bearer', 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(), 'state' => $authorizationRequest->getState(), diff --git a/src/ResponseTypes/BearerTokenResponse.php b/src/ResponseTypes/BearerTokenResponse.php index b630685c..2e658215 100644 --- a/src/ResponseTypes/BearerTokenResponse.php +++ b/src/ResponseTypes/BearerTokenResponse.php @@ -27,7 +27,7 @@ class BearerTokenResponse extends AbstractResponseType $responseParams = [ 'token_type' => 'Bearer', 'expires_in' => $expireDateTime - (new \DateTime())->getTimestamp(), - 'access_token' => $this->accessToken->convertToAccessToken($this->privateKey), + 'access_token' => (string) $this->accessToken, ]; if ($this->refreshToken instanceof RefreshTokenEntityInterface) { diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 5da2776e..a5916de7 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -3,6 +3,7 @@ namespace LeagueTests\Grant; use League\Event\Emitter; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\AuthCodeEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; @@ -353,6 +354,7 @@ class AbstractGrantTest extends TestCase /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grantMock->setAccessTokenRepository($accessTokenRepoMock); $abstractGrantReflection = new \ReflectionClass($grantMock); diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 6a319234..589e488c 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -2,6 +2,7 @@ namespace LeagueTests\Grant; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; use League\OAuth2\Server\Exception\OAuthServerException; @@ -40,7 +41,7 @@ class AuthCodeGrantTest extends TestCase public function setUp() { - $this->cryptStub = new CryptTraitStub; + $this->cryptStub = new CryptTraitStub(); } public function testGetIdentifier() @@ -608,6 +609,7 @@ class AuthCodeGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest( [], @@ -676,6 +678,7 @@ class AuthCodeGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest( [], @@ -747,6 +750,7 @@ class AuthCodeGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest( [], @@ -1537,6 +1541,7 @@ class AuthCodeGrantTest extends TestCase new \DateInterval('PT10M') ); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } @@ -1624,6 +1629,7 @@ class AuthCodeGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest( [], @@ -1695,6 +1701,7 @@ class AuthCodeGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest( [], @@ -1766,6 +1773,7 @@ class AuthCodeGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest( [], diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index 6c7b5a36..dfd78b41 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -2,6 +2,7 @@ namespace LeagueTests\Grant; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Grant\ClientCredentialsGrant; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; @@ -44,6 +45,7 @@ class ClientCredentialsGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setDefaultScope(self::DEFAULT_SCOPE); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = new ServerRequest(); $serverRequest = $serverRequest->withParsedBody( diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index 2ee700f8..c90a83db 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -2,6 +2,7 @@ namespace LeagueTests\Grant; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; use League\OAuth2\Server\Grant\PasswordGrant; @@ -60,6 +61,7 @@ class PasswordGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setDefaultScope(self::DEFAULT_SCOPE); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = new ServerRequest(); $serverRequest = $serverRequest->withParsedBody( diff --git a/tests/Middleware/ResourceServerMiddlewareTest.php b/tests/Middleware/ResourceServerMiddlewareTest.php index 0db71cdd..d1a96042 100644 --- a/tests/Middleware/ResourceServerMiddlewareTest.php +++ b/tests/Middleware/ResourceServerMiddlewareTest.php @@ -29,8 +29,9 @@ class ResourceServerMiddlewareTest extends TestCase $accessToken->setUserIdentifier(123); $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); $accessToken->setClient($client); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $token = $accessToken->convertToAccessToken(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $token = (string) $accessToken; $request = new ServerRequest(); $request = $request->withHeader('authorization', sprintf('Bearer %s', $token)); @@ -64,8 +65,9 @@ class ResourceServerMiddlewareTest extends TestCase $accessToken->setUserIdentifier(123); $accessToken->setExpiryDateTime((new \DateTime())->sub(new \DateInterval('PT1H'))); $accessToken->setClient($client); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $token = $accessToken->convertToAccessToken(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $token = (string) $accessToken; $request = new ServerRequest(); $request = $request->withHeader('authorization', sprintf('Bearer %s', $token)); diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 31245b07..2eb87238 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -35,6 +35,7 @@ class BearerResponseTypeTest extends TestCase $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); $accessToken->setClient($client); $accessToken->addScope($scope); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); @@ -77,6 +78,7 @@ class BearerResponseTypeTest extends TestCase $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); $accessToken->setClient($client); $accessToken->addScope($scope); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); @@ -119,6 +121,7 @@ class BearerResponseTypeTest extends TestCase $accessToken->setUserIdentifier(123); $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); $accessToken->setClient($client); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); @@ -164,6 +167,7 @@ class BearerResponseTypeTest extends TestCase $accessToken->setUserIdentifier(123); $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); $accessToken->setClient($client); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); @@ -206,6 +210,7 @@ class BearerResponseTypeTest extends TestCase $accessToken->setUserIdentifier(123); $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); $accessToken->setClient($client); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); From aac64e49cf67367e0f3c7ea77c657914aa3be24a Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 23 May 2018 16:36:43 +0100 Subject: [PATCH 016/104] Fix style issue --- src/Entities/Traits/AccessTokenTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php index cbed3122..501233e9 100644 --- a/src/Entities/Traits/AccessTokenTrait.php +++ b/src/Entities/Traits/AccessTokenTrait.php @@ -20,7 +20,7 @@ use League\OAuth2\Server\Entities\ScopeEntityInterface; trait AccessTokenTrait { /** - * @var CryptKey $privateKey + * @var CryptKey */ private $privateKey; From bd741e9203df090a20a5b06e8249b73833bbdc5f Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 23 May 2018 16:39:55 +0100 Subject: [PATCH 017/104] Update travis to check 8.0.0 branch --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index f900228a..22b7fc4f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,3 +30,4 @@ after_script: branches: only: - master + - 8.0.0 From ef75d1325511c42834a0e96392b199b86cadf653 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 23 May 2018 16:44:00 +0100 Subject: [PATCH 018/104] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20b11e8d..cfdfbcad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Changed -- Replace `convertToJWT()` interface with a more generic `convertToAccessToken` to improve extensibility (PR #874) +- Replace `convertToJWT()` interface with a more generic `__toString()` to improve extensibility (PR #874) - The `invalidClient()` function accepts a PSR-7 compliant `$serverRequest` argument to avoid accessing the `$_SERVER` global variable and improve testing (PR #899) ## [7.1.1] - released 2018-05-21 From ca5fe1093497261cd08586f4eef38926574d1301 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 24 Jun 2018 01:30:15 +0100 Subject: [PATCH 019/104] Fix merge issues --- src/Grant/AbstractGrant.php | 30 +++++++++++++++++++++++++++++- src/Grant/AuthCodeGrant.php | 4 ++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 5210b95b..42b44328 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -190,7 +190,7 @@ abstract class AbstractGrant implements GrantTypeInterface if ($client instanceof ClientEntityInterface === false) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient(); + throw OAuthServerException::invalidClient($request); } // If a redirect URI is provided ensure it matches what is pre-registered @@ -202,6 +202,34 @@ abstract class AbstractGrant implements GrantTypeInterface return $client; } + /** + * Validate redirectUri from the request. + * If a redirect URI is provided ensure it matches what is pre-registered + * + * @param string $redirectUri + * @param ClientEntityInterface $client + * @param ServerRequestInterface $request + * + * @throws OAuthServerException + */ + protected function validateRedirectUri( + string $redirectUri, + ClientEntityInterface $client, + ServerRequestInterface $request + ) { + if (is_string($client->getRedirectUri()) + && (strcmp($client->getRedirectUri(), $redirectUri) !== 0) + ) { + $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); + throw OAuthServerException::invalidClient($request); + } elseif (is_array($client->getRedirectUri()) + && in_array($redirectUri, $client->getRedirectUri(), true) === false + ) { + $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); + throw OAuthServerException::invalidClient($request); + } + } + /** * Validate scopes in the request. * diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index cfa8309b..790315af 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -236,7 +236,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant if ($client instanceof ClientEntityInterface === false) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient(); + throw OAuthServerException::invalidClient($request); } $redirectUri = $this->getQueryStringParameter('redirect_uri', $request); @@ -246,7 +246,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant } elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1 || empty($client->getRedirectUri())) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient(); + throw OAuthServerException::invalidClient($request); } else { $redirectUri = is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] From 7df0dfff9d8dafbadbef619787b935b2eaf4e525 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 24 Jun 2018 13:31:38 +0100 Subject: [PATCH 020/104] Remove double function calls --- src/Grant/AbstractGrant.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index d020c6ad..ed05dfac 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -358,15 +358,9 @@ abstract class AbstractGrant implements GrantTypeInterface $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS; $accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier); - $accessToken->setClient($client); - $accessToken->setUserIdentifier($userIdentifier); $accessToken->setExpiryDateTime((new \DateTime())->add($accessTokenTTL)); $accessToken->setPrivateKey($this->privateKey); - foreach ($scopes as $scope) { - $accessToken->addScope($scope); - } - while ($maxGenerationAttempts-- > 0) { $accessToken->setIdentifier($this->generateUniqueIdentifier()); try { From dad3b1e1c92701c916cf644e159ebdbeb872f0a1 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 24 Jun 2018 13:32:49 +0100 Subject: [PATCH 021/104] Remove unused test --- tests/Grant/AbstractGrantTest.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index a5916de7..64fde4f0 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -2,7 +2,6 @@ namespace LeagueTests\Grant; -use League\Event\Emitter; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\AuthCodeEntityInterface; @@ -24,13 +23,6 @@ use Zend\Diactoros\ServerRequest; class AbstractGrantTest extends TestCase { - public function testGetSet() - { - /** @var AbstractGrant $grantMock */ - $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $grantMock->setEmitter(new Emitter()); - } - public function testHttpBasicWithPassword() { /** @var AbstractGrant $grantMock */ From 574299d862b1dce56e840deeda23d307de0f2695 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 24 Jun 2018 13:38:55 +0100 Subject: [PATCH 022/104] Fix tests --- tests/Grant/ImplicitGrantTest.php | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index 0080548f..df7b6985 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -276,14 +276,20 @@ class ImplicitGrantTest extends TestCase public function testCompleteAuthorizationRequest() { + $client = new ClientEntity(); + $client->setIdentifier('identifier'); + $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); - $authRequest->setClient(new ClientEntity()); + $authRequest->setClient($client); $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); $grant = new ImplicitGrant(new \DateInterval('PT10M')); @@ -318,15 +324,21 @@ class ImplicitGrantTest extends TestCase public function testAccessTokenRepositoryUniqueConstraintCheck() { + $client = new ClientEntity(); + $client->setIdentifier('identifier'); + $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); - $authRequest->setClient(new ClientEntity()); + $authRequest->setClient($client); $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + /** @var AccessTokenRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject $accessTokenRepositoryMock */ $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->expects($this->at(0))->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create()); $accessTokenRepositoryMock->expects($this->at(1))->method('persistNewAccessToken')->willReturnSelf(); From 2fcee76d1376df270b0d43580dff688186ca037a Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 24 Jun 2018 13:39:40 +0100 Subject: [PATCH 023/104] Remove unused stub function --- tests/Stubs/ClientEntity.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/Stubs/ClientEntity.php b/tests/Stubs/ClientEntity.php index 0c6a4f9b..4cb79a0c 100644 --- a/tests/Stubs/ClientEntity.php +++ b/tests/Stubs/ClientEntity.php @@ -14,9 +14,4 @@ class ClientEntity implements ClientEntityInterface { $this->redirectUri = $uri; } - - public function setName($name) - { - $this->name = $name; - } } From 0cdd535f7db3204058a1f854c7959daecf566019 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 24 Jun 2018 13:48:52 +0100 Subject: [PATCH 024/104] Add changes to changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfdfbcad..88d8f16f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - Replace `convertToJWT()` interface with a more generic `__toString()` to improve extensibility (PR #874) -- The `invalidClient()` function accepts a PSR-7 compliant `$serverRequest` argument to avoid accessing the `$_SERVER` global variable and improve testing (PR #899) +- The `invalidClient()` function accepts a PSR-7 compliant `$serverRequest` argument to avoid accessing the `$_SERVER` global variable and improve testing (PR #899) +- `issueAccessToken()` in the Abstract Grant no longer sets access token client, user ID or scopes. These values should already have been set when calling `getNewToken()` (PR #919) ## [7.1.1] - released 2018-05-21 From f49cc65c1353589720f3d5e67def75d6cf499a3d Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 29 Jul 2018 19:56:30 +0100 Subject: [PATCH 025/104] Change to store code challenge and method whenever sent for PKCE --- src/Grant/AuthCodeGrant.php | 7 ++--- tests/Grant/AuthCodeGrantTest.php | 45 +------------------------------ 2 files changed, 3 insertions(+), 49 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 790315af..14aac2a3 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -271,12 +271,9 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $authorizationRequest->setScopes($scopes); - if ($this->enableCodeExchangeProof === true) { - $codeChallenge = $this->getQueryStringParameter('code_challenge', $request); - if ($codeChallenge === null) { - throw OAuthServerException::invalidRequest('code_challenge'); - } + $codeChallenge = $this->getQueryStringParameter('code_challenge', $request); + if ($codeChallenge !== null) { $codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain'); if (in_array($codeChallengeMethod, ['plain', 'S256'], true) === false) { diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 589e488c..6bc2dd7a 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -212,7 +212,7 @@ class AuthCodeGrantTest extends TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); $request = new ServerRequest( @@ -444,49 +444,6 @@ class AuthCodeGrantTest extends TestCase $grant->validateAuthorizationRequest($request); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 3 - */ - public function testValidateAuthorizationRequestMissingCodeChallenge() - { - $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); - $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $clientRepositoryMock->method('getClientEntity')->willReturn($client); - - $scope = new ScopeEntity(); - $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); - $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); - - $grant = new AuthCodeGrant( - $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), - $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') - ); - $grant->enableCodeExchangeProof(); - $grant->setClientRepository($clientRepositoryMock); - $grant->setScopeRepository($scopeRepositoryMock); - $grant->setDefaultScope(self::DEFAULT_SCOPE); - - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - [], - [], - [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', - ] - ); - - $grant->validateAuthorizationRequest($request); - } - /** * @expectedException \League\OAuth2\Server\Exception\OAuthServerException * @expectedExceptionCode 3 From 5ad00b0e3342672c14bbe4b902176ec2a2ff49d6 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 29 Jul 2018 22:34:37 +0100 Subject: [PATCH 026/104] Remove enableCodeExchangeProof function --- src/Grant/AuthCodeGrant.php | 18 ++++++------------ tests/Grant/AuthCodeGrantTest.php | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 14aac2a3..6a2ee795 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -28,11 +28,6 @@ class AuthCodeGrant extends AbstractAuthorizeGrant */ private $authCodeTTL; - /** - * @var bool - */ - private $enableCodeExchangeProof = false; - /** * @param AuthCodeRepositoryInterface $authCodeRepository * @param RefreshTokenRepositoryInterface $refreshTokenRepository @@ -49,11 +44,6 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $this->refreshTokenTTL = new \DateInterval('P1M'); } - public function enableCodeExchangeProof() - { - $this->enableCodeExchangeProof = true; - } - /** * Respond to an access token request. * @@ -81,6 +71,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant // Validate the authorization code try { $authCodePayload = json_decode($this->decrypt($encryptedAuthCode)); + if (time() > $authCodePayload->expire_time) { throw OAuthServerException::invalidRequest('code', 'Authorization code has expired'); } @@ -104,6 +95,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant } $scopes = []; + foreach ($authCodePayload->scopes as $scopeId) { $scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId); @@ -127,9 +119,11 @@ class AuthCodeGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code'); } + // Validate code challenge - if ($this->enableCodeExchangeProof === true) { - $codeVerifier = $this->getRequestParameter('code_verifier', $request, null); + if (!empty($authCodePayload->code_challenge)) { + $codeVerifier = $this->getRequestParameter('code_verifier', $request, null); + if ($codeVerifier === null) { throw OAuthServerException::invalidRequest('code_verifier'); } diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 6bc2dd7a..5ca3c8f1 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -173,7 +173,7 @@ class AuthCodeGrantTest extends TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setDefaultScope(self::DEFAULT_SCOPE); @@ -249,7 +249,7 @@ class AuthCodeGrantTest extends TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); $request = new ServerRequest( @@ -286,7 +286,7 @@ class AuthCodeGrantTest extends TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); $request = new ServerRequest( @@ -464,7 +464,7 @@ class AuthCodeGrantTest extends TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setDefaultScope(self::DEFAULT_SCOPE); @@ -629,7 +629,7 @@ class AuthCodeGrantTest extends TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); @@ -701,7 +701,7 @@ class AuthCodeGrantTest extends TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); @@ -1145,7 +1145,7 @@ class AuthCodeGrantTest extends TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); @@ -1217,7 +1217,7 @@ class AuthCodeGrantTest extends TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); @@ -1289,7 +1289,7 @@ class AuthCodeGrantTest extends TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); @@ -1361,7 +1361,7 @@ class AuthCodeGrantTest extends TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); @@ -1433,7 +1433,7 @@ class AuthCodeGrantTest extends TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); From 972808561d85e1f3e0a7778238d589221ea75323 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 12 Aug 2018 20:06:34 +0100 Subject: [PATCH 027/104] Add optional code challenge check for public clients --- examples/composer.lock | 120 ++++++++++++++---- src/Entities/ClientEntityInterface.php | 7 + src/Grant/AuthCodeGrant.php | 36 +++++- .../ClientRepositoryInterface.php | 9 ++ 4 files changed, 142 insertions(+), 30 deletions(-) diff --git a/examples/composer.lock b/examples/composer.lock index 7210f31e..8603438a 100644 --- a/examples/composer.lock +++ b/examples/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9813ed7c3b6dcf107f44df9392935b8f", + "content-hash": "6701e0eaa09f74e1ebb19c3d61f39068", "packages": [ { "name": "container-interop/container-interop", @@ -82,25 +82,29 @@ }, { "name": "pimple/pimple", - "version": "v3.0.2", + "version": "v3.2.3", "source": { "type": "git", "url": "https://github.com/silexphp/Pimple.git", - "reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a" + "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a30f7d6e57565a2e1a316e1baf2a483f788b258a", - "reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32", + "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=5.3.0", + "psr/container": "^1.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "3.2.x-dev" } }, "autoload": { @@ -124,7 +128,7 @@ "container", "dependency injection" ], - "time": "2015-09-11T15:10:35+00:00" + "time": "2018-01-21T07:42:36+00:00" }, { "name": "psr/container", @@ -295,25 +299,25 @@ "packages-dev": [ { "name": "defuse/php-encryption", - "version": "v2.1.0", + "version": "v2.2.1", "source": { "type": "git", "url": "https://github.com/defuse/php-encryption.git", - "reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689" + "reference": "0f407c43b953d571421e0020ba92082ed5fb7620" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/defuse/php-encryption/zipball/5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689", - "reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689", + "url": "https://api.github.com/repos/defuse/php-encryption/zipball/0f407c43b953d571421e0020ba92082ed5fb7620", + "reference": "0f407c43b953d571421e0020ba92082ed5fb7620", "shasum": "" }, "require": { "ext-openssl": "*", - "paragonie/random_compat": "~2.0", + "paragonie/random_compat": ">= 2", "php": ">=5.4.0" }, "require-dev": { - "nikic/php-parser": "^2.0|^3.0", + "nikic/php-parser": "^2.0|^3.0|^4.0", "phpunit/phpunit": "^4|^5" }, "bin": [ @@ -354,20 +358,20 @@ "security", "symmetric key cryptography" ], - "time": "2017-05-18T21:28:48+00:00" + "time": "2018-07-24T23:27:56+00:00" }, { "name": "lcobucci/jwt", - "version": "3.2.1", + "version": "3.2.4", "source": { "type": "git", "url": "https://github.com/lcobucci/jwt.git", - "reference": "ddce703826f9c5229781933b1a39069e38e6a0f3" + "reference": "c9704b751315d21735dc98d78d4f37bd73596da7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/jwt/zipball/ddce703826f9c5229781933b1a39069e38e6a0f3", - "reference": "ddce703826f9c5229781933b1a39069e38e6a0f3", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/c9704b751315d21735dc98d78d4f37bd73596da7", + "reference": "c9704b751315d21735dc98d78d4f37bd73596da7", "shasum": "" }, "require": { @@ -412,7 +416,7 @@ "JWS", "jwt" ], - "time": "2016-10-31T20:09:32+00:00" + "time": "2018-08-03T11:23:50+00:00" }, { "name": "league/event", @@ -466,16 +470,16 @@ }, { "name": "paragonie/random_compat", - "version": "v2.0.10", + "version": "v2.0.17", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d" + "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d", - "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d", + "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d", "shasum": "" }, "require": { @@ -507,10 +511,74 @@ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "keywords": [ "csprng", + "polyfill", "pseudorandom", "random" ], - "time": "2017-03-13T16:27:32+00:00" + "time": "2018-07-04T16:31:37+00:00" + }, + { + "name": "zendframework/zend-diactoros", + "version": "1.8.4", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-diactoros.git", + "reference": "736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba", + "reference": "736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0", + "psr/http-message": "^1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-dom": "*", + "ext-libxml": "*", + "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", + "zendframework/zend-coding-standard": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8.x-dev", + "dev-develop": "1.9.x-dev", + "dev-release-2.0": "2.0.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions/create_uploaded_file.php", + "src/functions/marshal_headers_from_sapi.php", + "src/functions/marshal_method_from_sapi.php", + "src/functions/marshal_protocol_version_from_sapi.php", + "src/functions/marshal_uri_from_sapi.php", + "src/functions/normalize_server.php", + "src/functions/normalize_uploaded_files.php", + "src/functions/parse_cookie_header.php" + ], + "psr-4": { + "Zend\\Diactoros\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "description": "PSR HTTP Message implementations", + "homepage": "https://github.com/zendframework/zend-diactoros", + "keywords": [ + "http", + "psr", + "psr-7" + ], + "time": "2018-08-01T13:47:49+00:00" } ], "aliases": [], diff --git a/src/Entities/ClientEntityInterface.php b/src/Entities/ClientEntityInterface.php index 80cc70c8..971c6124 100644 --- a/src/Entities/ClientEntityInterface.php +++ b/src/Entities/ClientEntityInterface.php @@ -33,4 +33,11 @@ interface ClientEntityInterface * @return string|string[] */ public function getRedirectUri(); + + /** + * Returns true if the client is confidential. + * + * @return bool + */ + public function isConfidential(); } diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 6a2ee795..406f6ab8 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -28,6 +28,11 @@ class AuthCodeGrant extends AbstractAuthorizeGrant */ private $authCodeTTL; + /** + * @var bool + */ + private $requireCodeChallengeForPublicClients = true; + /** * @param AuthCodeRepositoryInterface $authCodeRepository * @param RefreshTokenRepositoryInterface $refreshTokenRepository @@ -60,8 +65,29 @@ class AuthCodeGrant extends AbstractAuthorizeGrant ResponseTypeInterface $responseType, \DateInterval $accessTokenTTL ) { + $clientId = $this->getRequestParameter('client_id', $request, null); + + if ($clientId === null) { + throw OAuthServerException::invalidRequest('client_id'); + } + + if ($this->clientRepository->isClientConfidential($clientId)) { + $client = $this->validateClient($request); + } else { + $client = $this->clientRepository->getClientEntity( + $clientId, + $this->getIdentifier(), + null, + false + ); + } + // Validate request - $client = $this->validateClient($request); + + // HERE I ONLY WANT TO VALIDATE IF THE CLIENT IS CONFIDENTIAL! + + + $encryptedAuthCode = $this->getRequestParameter('code', $request, null); if ($encryptedAuthCode === null) { @@ -71,7 +97,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant // Validate the authorization code try { $authCodePayload = json_decode($this->decrypt($encryptedAuthCode)); - + if (time() > $authCodePayload->expire_time) { throw OAuthServerException::invalidRequest('code', 'Authorization code has expired'); } @@ -86,6 +112,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant // The redirect URI is required in this request $redirectUri = $this->getRequestParameter('redirect_uri', $request, null); + if (empty($authCodePayload->redirect_uri) === false && $redirectUri === null) { throw OAuthServerException::invalidRequest('redirect_uri'); } @@ -119,10 +146,9 @@ class AuthCodeGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code'); } - // Validate code challenge if (!empty($authCodePayload->code_challenge)) { - $codeVerifier = $this->getRequestParameter('code_verifier', $request, null); + $codeVerifier = $this->getRequestParameter('code_verifier', $request, null); if ($codeVerifier === null) { throw OAuthServerException::invalidRequest('code_verifier'); @@ -164,6 +190,8 @@ class AuthCodeGrant extends AbstractAuthorizeGrant ); // @codeCoverageIgnoreEnd } + } else if ($this->$requireCodeChallengeForPublicClients && !$client->isConfidential()) { + throw OAuthServerException::invalidRequest('code_challenge', 'Code challenge must be provided for public clients'); } // Issue and persist access + refresh tokens diff --git a/src/Repositories/ClientRepositoryInterface.php b/src/Repositories/ClientRepositoryInterface.php index ba0610d5..9e863af8 100644 --- a/src/Repositories/ClientRepositoryInterface.php +++ b/src/Repositories/ClientRepositoryInterface.php @@ -28,4 +28,13 @@ interface ClientRepositoryInterface extends RepositoryInterface * @return ClientEntityInterface */ public function getClientEntity($clientIdentifier, $grantType = null, $clientSecret = null, $mustValidateSecret = true); + + /** + * Check if a client is confidential. + * + * @param string $clientIdentifier The client's identifier + * + * @return bool + */ + public function isClientConfidential($clientIdentifier); } From 838f206832212f5b94b8e4457117b4bd6f6d0d69 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 12 Aug 2018 20:09:55 +0100 Subject: [PATCH 028/104] Tidy up comments --- src/Grant/AuthCodeGrant.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 406f6ab8..e969df8e 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -71,6 +71,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidRequest('client_id'); } + // Only validate the client if it is confidential if ($this->clientRepository->isClientConfidential($clientId)) { $client = $this->validateClient($request); } else { @@ -82,12 +83,6 @@ class AuthCodeGrant extends AbstractAuthorizeGrant ); } - // Validate request - - // HERE I ONLY WANT TO VALIDATE IF THE CLIENT IS CONFIDENTIAL! - - - $encryptedAuthCode = $this->getRequestParameter('code', $request, null); if ($encryptedAuthCode === null) { From d07b5a4a03a8e15fecd34736b04281f15ce9caee Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 12 Aug 2018 20:26:46 +0100 Subject: [PATCH 029/104] Add isConfidential function to client entity trait --- src/Entities/Traits/ClientTrait.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Entities/Traits/ClientTrait.php b/src/Entities/Traits/ClientTrait.php index fec76a6f..f73bc2e0 100644 --- a/src/Entities/Traits/ClientTrait.php +++ b/src/Entities/Traits/ClientTrait.php @@ -21,6 +21,11 @@ trait ClientTrait */ protected $redirectUri; + /** + * @var bool + */ + protected $isConfidential; + /** * Get the client's name. * @@ -43,4 +48,14 @@ trait ClientTrait { return $this->redirectUri; } + + /** + * Returns true if the client is confidential. + * + * @return bool + */ + public function isConfidential() + { + return $this->isConfidential; + } } From 04807a1e2ae155b9d3c9b14b3aa2d4ff79cdfe59 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 12 Aug 2018 20:29:39 +0100 Subject: [PATCH 030/104] Fix incorrect variable reference --- src/Grant/AuthCodeGrant.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index e969df8e..6e38bc09 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -49,6 +49,14 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $this->refreshTokenTTL = new \DateInterval('P1M'); } + /** + * Disable the requirement for a code challenge for public clients. + */ + public function disableCodeChallengeForPublicClients() + { + $this->requireCodeChallengeForPublicClients = false; + } + /** * Respond to an access token request. * @@ -185,7 +193,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant ); // @codeCoverageIgnoreEnd } - } else if ($this->$requireCodeChallengeForPublicClients && !$client->isConfidential()) { + } else if ($this->requireCodeChallengeForPublicClients && !$client->isConfidential()) { throw OAuthServerException::invalidRequest('code_challenge', 'Code challenge must be provided for public clients'); } From abef682031d8e223a2751c4f7ba96cc45543b6cc Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 12 Aug 2018 20:34:58 +0100 Subject: [PATCH 031/104] Add setIsConfidential to client stub for tests --- tests/Grant/AuthCodeGrantTest.php | 3 ++- tests/Stubs/ClientEntity.php | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 5ca3c8f1..70682171 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -540,6 +540,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->setIsConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -629,7 +630,7 @@ class AuthCodeGrantTest extends TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - + $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); diff --git a/tests/Stubs/ClientEntity.php b/tests/Stubs/ClientEntity.php index 4cb79a0c..9d34edb0 100644 --- a/tests/Stubs/ClientEntity.php +++ b/tests/Stubs/ClientEntity.php @@ -14,4 +14,9 @@ class ClientEntity implements ClientEntityInterface { $this->redirectUri = $uri; } + + public function setIsConfidential() + { + $this->isConfidential = true; + } } From 7f2fd7b22cc4ed1fabff57f8122be1192100aa4c Mon Sep 17 00:00:00 2001 From: sephster Date: Mon, 13 Aug 2018 21:21:59 +0100 Subject: [PATCH 032/104] Add set confidential to clients for tests --- tests/Grant/AuthCodeGrantTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 70682171..fc986568 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -1560,6 +1560,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->setIsConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1633,6 +1634,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->setIsConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1705,6 +1707,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->setIsConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); From 491852b521828e4856ddbb5b620afcfd83d05af4 Mon Sep 17 00:00:00 2001 From: sephster Date: Mon, 13 Aug 2018 21:47:53 +0100 Subject: [PATCH 033/104] Move code challenge check to auth code request --- src/Grant/AuthCodeGrant.php | 8 +++---- tests/Grant/AuthCodeGrantTest.php | 40 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 6e38bc09..407b98cf 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -151,7 +151,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant // Validate code challenge if (!empty($authCodePayload->code_challenge)) { - $codeVerifier = $this->getRequestParameter('code_verifier', $request, null); + $codeVerifier = $this->getRequestParameter('code_verifier', $request, null); if ($codeVerifier === null) { throw OAuthServerException::invalidRequest('code_verifier'); @@ -193,8 +193,6 @@ class AuthCodeGrant extends AbstractAuthorizeGrant ); // @codeCoverageIgnoreEnd } - } else if ($this->requireCodeChallengeForPublicClients && !$client->isConfidential()) { - throw OAuthServerException::invalidRequest('code_challenge', 'Code challenge must be provided for public clients'); } // Issue and persist access + refresh tokens @@ -296,7 +294,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $authorizationRequest->setScopes($scopes); - $codeChallenge = $this->getQueryStringParameter('code_challenge', $request); + $codeChallenge = $this->getQueryStringParameter('code_challenge', $request); if ($codeChallenge !== null) { $codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain'); @@ -319,6 +317,8 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $authorizationRequest->setCodeChallenge($codeChallenge); $authorizationRequest->setCodeChallengeMethod($codeChallengeMethod); + } else if ($this->requireCodeChallengeForPublicClients && !$client->isConfidential()) { + throw OAuthServerException::invalidRequest('code_challenge', 'Code challenge must be provided for public clients'); } return $authorizationRequest; diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index fc986568..564389e2 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -1784,4 +1784,44 @@ class AuthCodeGrantTest extends TestCase $grant->completeAuthorizationRequest(new AuthorizationRequest()); } + + public function testPublicClientAuthCodeRequestRejectedWhenCodeChallengeRequiredButNotGiven() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $scope = new ScopeEntity(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + + $grant = new AuthCodeGrant( + $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), + $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), + new \DateInterval('PT10M') + ); + + $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + [], + [], + [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + ] + ); + + $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); + } } From 0105a20126757aa7f38319fdcf40e1ceedd4aef7 Mon Sep 17 00:00:00 2001 From: sephster Date: Mon, 13 Aug 2018 22:00:34 +0100 Subject: [PATCH 034/104] Reverted tests to remove isConfidential check --- tests/Grant/AuthCodeGrantTest.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 564389e2..6f91fc0f 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -540,7 +540,6 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->setIsConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1560,7 +1559,6 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->setIsConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1634,7 +1632,6 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->setIsConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1707,7 +1704,6 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->setIsConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); From 8ab27ede39fcc1d9d4bf9d5b02d4d6230d2cb519 Mon Sep 17 00:00:00 2001 From: sephster Date: Mon, 13 Aug 2018 22:54:12 +0100 Subject: [PATCH 035/104] Add test to ensure public clients are asked to provide a code challenge --- tests/AuthorizationServerTest.php | 1 + tests/Grant/AuthCodeGrantTest.php | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index b003c23f..e0f45d98 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -148,6 +148,7 @@ class AuthorizationServerTest extends TestCase { $client = new ClientEntity(); $client->setRedirectUri('http://foo/bar'); + $client->setIsConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 6f91fc0f..b0652d00 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -84,6 +84,7 @@ class AuthCodeGrantTest extends TestCase { $client = new ClientEntity(); $client->setRedirectUri('http://foo/bar'); + $client->setIsConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -123,6 +124,7 @@ class AuthCodeGrantTest extends TestCase { $client = new ClientEntity(); $client->setRedirectUri(['http://foo/bar']); + $client->setIsConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -198,6 +200,7 @@ class AuthCodeGrantTest extends TestCase } /** + * $this->expectException(OAuth * @expectedException \League\OAuth2\Server\Exception\OAuthServerException */ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooShort() @@ -1818,6 +1821,9 @@ class AuthCodeGrantTest extends TestCase ] ); - $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); + $this->expectException(OAuthServerException::class); + $this->expectExceptionCode(3); + + $grant->validateAuthorizationRequest($request); } } From fb438014582a8b42c9382355b7b14eba63985cb6 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 15 Aug 2018 21:40:41 +0100 Subject: [PATCH 036/104] Change function name to setConfidential() --- tests/AuthorizationServerTest.php | 2 +- tests/Grant/AuthCodeGrantTest.php | 4 ++-- tests/Stubs/ClientEntity.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index e0f45d98..7e83ee5e 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -148,7 +148,7 @@ class AuthorizationServerTest extends TestCase { $client = new ClientEntity(); $client->setRedirectUri('http://foo/bar'); - $client->setIsConfidential(); + $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 b0652d00..867a08d9 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -84,7 +84,7 @@ class AuthCodeGrantTest extends TestCase { $client = new ClientEntity(); $client->setRedirectUri('http://foo/bar'); - $client->setIsConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -124,7 +124,7 @@ class AuthCodeGrantTest extends TestCase { $client = new ClientEntity(); $client->setRedirectUri(['http://foo/bar']); - $client->setIsConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); diff --git a/tests/Stubs/ClientEntity.php b/tests/Stubs/ClientEntity.php index 9d34edb0..d908248b 100644 --- a/tests/Stubs/ClientEntity.php +++ b/tests/Stubs/ClientEntity.php @@ -15,7 +15,7 @@ class ClientEntity implements ClientEntityInterface $this->redirectUri = $uri; } - public function setIsConfidential() + public function setConfidential() { $this->isConfidential = true; } From 74495cac49f20bc697cc4b3100305f409fe36507 Mon Sep 17 00:00:00 2001 From: sephster Date: Thu, 16 Aug 2018 12:59:10 +0100 Subject: [PATCH 037/104] Set proper confidential settings in existing tests --- tests/Grant/AuthCodeGrantTest.php | 102 +++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 867a08d9..a415f3e1 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -543,8 +543,10 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -606,13 +608,85 @@ class AuthCodeGrantTest extends TestCase $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } - public function testRespondToAccessTokenRequestCodeChallengePlain() + public function testRespondToAccessTokenRequestForPublicClient() { $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(false); + + $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' => $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', + ] + ) + ), + ] + ); + + /** @var StubResponseType $response */ + $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + + $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); + $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); + + } + + public function testRespondToAccessTokenRequestCodeChallengePlain() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $client->setRedirectUri('http://foo/bar'); + $client->isConfidential(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -683,8 +757,10 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -758,8 +834,10 @@ class AuthCodeGrantTest extends TestCase { $client = new ClientEntity(); $client->setIdentifier('foo'); + $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), @@ -805,8 +883,10 @@ class AuthCodeGrantTest extends TestCase { $client = new ClientEntity(); $client->setIdentifier('foo'); + $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), @@ -853,8 +933,10 @@ class AuthCodeGrantTest extends TestCase { $client = new ClientEntity(); $client->setRedirectUri('http://foo/bar'); + $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -895,8 +977,10 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); @@ -955,8 +1039,10 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); @@ -1018,8 +1104,10 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); @@ -1078,8 +1166,10 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); @@ -1127,8 +1217,10 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -1199,8 +1291,10 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -1271,8 +1365,10 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -1343,8 +1439,10 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -1415,8 +1513,10 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); + $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); From 46c2f99b06d3b804fa119da309016f05faff1bb2 Mon Sep 17 00:00:00 2001 From: sephster Date: Sat, 1 Sep 2018 13:17:36 +0100 Subject: [PATCH 038/104] Change function name to be more explicit --- src/Grant/AuthCodeGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 407b98cf..a4360bae 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -52,7 +52,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant /** * Disable the requirement for a code challenge for public clients. */ - public function disableCodeChallengeForPublicClients() + public function disableRequireCodeChallengeForPublicClients() { $this->requireCodeChallengeForPublicClients = false; } From 060a0904797b345ae0b78f24ee33e2cfecf337ab Mon Sep 17 00:00:00 2001 From: sephster Date: Sat, 1 Sep 2018 14:26:22 +0100 Subject: [PATCH 039/104] Change tests to use validClient instead of getClientEntity --- src/Grant/AbstractGrant.php | 15 +++++------ .../ClientRepositoryInterface.php | 25 ++++++++++++++----- tests/Exception/OAuthServerExceptionTest.php | 2 +- tests/Grant/AbstractGrantTest.php | 6 ++--- .../AuthorizationServerMiddlewareTest.php | 2 +- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index e34436e5..c34e65cf 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -174,27 +174,24 @@ abstract class AbstractGrant implements GrantTypeInterface list($basicAuthUser, $basicAuthPassword) = $this->getBasicAuthCredentials($request); $clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser); + if (is_null($clientId)) { throw OAuthServerException::invalidRequest('client_id'); } - // If the client is confidential require the client secret $clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword); - $client = $this->clientRepository->getClientEntity( - $clientId, - $this->getIdentifier(), - $clientSecret, - true - ); - - if ($client instanceof ClientEntityInterface === false) { + if ($this->clientRepository->validateClient($clientId, $clientSecret) === false) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); + throw OAuthServerException::invalidClient($request); } + $client = $this->clientRepository->getClientEntity($clientId); + // If a redirect URI is provided ensure it matches what is pre-registered $redirectUri = $this->getRequestParameter('redirect_uri', $request, null); + if ($redirectUri !== null) { $this->validateRedirectUri($redirectUri, $client, $request); } diff --git a/src/Repositories/ClientRepositoryInterface.php b/src/Repositories/ClientRepositoryInterface.php index 9e863af8..d390a5f6 100644 --- a/src/Repositories/ClientRepositoryInterface.php +++ b/src/Repositories/ClientRepositoryInterface.php @@ -19,15 +19,11 @@ interface ClientRepositoryInterface extends RepositoryInterface /** * Get a client. * - * @param string $clientIdentifier The client's identifier - * @param null|string $grantType The grant type used (if sent) - * @param null|string $clientSecret The client's secret (if sent) - * @param bool $mustValidateSecret If true the client must attempt to validate the secret if the client - * is confidential + * @param string $clientIdentifier The client's identifier * * @return ClientEntityInterface */ - public function getClientEntity($clientIdentifier, $grantType = null, $clientSecret = null, $mustValidateSecret = true); + public function getClientEntity($clientIdentifier); /** * Check if a client is confidential. @@ -37,4 +33,21 @@ interface ClientRepositoryInterface extends RepositoryInterface * @return bool */ public function isClientConfidential($clientIdentifier); + + /** + * Validate a client's secret. + * + * @param string $clientIdentifier The client's identifier + * @param null|string $clientSecret The client's secret (if sent) + * + * @return bool + */ + public function validateClient($clientIdentifier, $clientSecret); + + /** + * Check if a client can use a grant type. + * + * @return bool + */ + public function canUseGrant($clientIdentifier, $grantType); } diff --git a/tests/Exception/OAuthServerExceptionTest.php b/tests/Exception/OAuthServerExceptionTest.php index cead7d6a..8efce51f 100644 --- a/tests/Exception/OAuthServerExceptionTest.php +++ b/tests/Exception/OAuthServerExceptionTest.php @@ -52,7 +52,7 @@ class OAuthServerExceptionTest extends TestCase private function issueInvalidClientException($serverRequest) { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $clientRepositoryMock->method('getClientEntity')->willReturn(false); + $clientRepositoryMock->method('validateClient')->willReturn(false); $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setClientRepository($clientRepositoryMock); diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 64fde4f0..d6ddd4b1 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -175,7 +175,7 @@ class AbstractGrantTest extends TestCase public function testValidateClientMissingClientSecret() { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $clientRepositoryMock->method('getClientEntity')->willReturn(null); + $clientRepositoryMock->method('validateClient')->willReturn(false); /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); @@ -200,7 +200,7 @@ class AbstractGrantTest extends TestCase public function testValidateClientInvalidClientSecret() { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $clientRepositoryMock->method('getClientEntity')->willReturn(null); + $clientRepositoryMock->method('validateClient')->willReturn(false); /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); @@ -282,7 +282,7 @@ class AbstractGrantTest extends TestCase public function testValidateClientBadClient() { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $clientRepositoryMock->method('getClientEntity')->willReturn(null); + $clientRepositoryMock->method('validateClient')->willReturn(false); /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); diff --git a/tests/Middleware/AuthorizationServerMiddlewareTest.php b/tests/Middleware/AuthorizationServerMiddlewareTest.php index 99118736..dcfcfbd9 100644 --- a/tests/Middleware/AuthorizationServerMiddlewareTest.php +++ b/tests/Middleware/AuthorizationServerMiddlewareTest.php @@ -66,7 +66,7 @@ class AuthorizationServerMiddlewareTest extends TestCase public function testOAuthErrorResponse() { $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $clientRepository->method('getClientEntity')->willReturn(null); + $clientRepository->method('validateClient')->willReturn(false); $server = new AuthorizationServer( $clientRepository, From cfa9b8d3b4500aaa069c1e3a76c082dda864fec8 Mon Sep 17 00:00:00 2001 From: sephster Date: Sat, 1 Sep 2018 14:38:31 +0100 Subject: [PATCH 040/104] Move grant check for client back to validate method --- src/Grant/AbstractGrant.php | 4 ++-- src/Repositories/ClientRepositoryInterface.php | 10 ++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index c34e65cf..4531a6a3 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -181,7 +181,7 @@ abstract class AbstractGrant implements GrantTypeInterface $clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword); - if ($this->clientRepository->validateClient($clientId, $clientSecret) === false) { + if ($this->clientRepository->validateClient($clientId, $clientSecret, $this->getIdentifier()) === false) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient($request); @@ -191,7 +191,7 @@ abstract class AbstractGrant implements GrantTypeInterface // If a redirect URI is provided ensure it matches what is pre-registered $redirectUri = $this->getRequestParameter('redirect_uri', $request, null); - + if ($redirectUri !== null) { $this->validateRedirectUri($redirectUri, $client, $request); } diff --git a/src/Repositories/ClientRepositoryInterface.php b/src/Repositories/ClientRepositoryInterface.php index d390a5f6..eee03104 100644 --- a/src/Repositories/ClientRepositoryInterface.php +++ b/src/Repositories/ClientRepositoryInterface.php @@ -39,15 +39,9 @@ interface ClientRepositoryInterface extends RepositoryInterface * * @param string $clientIdentifier The client's identifier * @param null|string $clientSecret The client's secret (if sent) + * @param null|string $grantType The type of grant the client is using (if sent) * * @return bool */ - public function validateClient($clientIdentifier, $clientSecret); - - /** - * Check if a client can use a grant type. - * - * @return bool - */ - public function canUseGrant($clientIdentifier, $grantType); + public function validateClient($clientIdentifier, $clientSecret, $grantType); } From 3eabbafe5b2a771dc445ef35f4a0206e4a932557 Mon Sep 17 00:00:00 2001 From: sephster Date: Sat, 1 Sep 2018 14:53:27 +0100 Subject: [PATCH 041/104] Client says if it is confidential instead of repository --- src/Grant/AuthCodeGrant.php | 11 +++-------- src/Repositories/ClientRepositoryInterface.php | 9 --------- tests/Grant/AuthCodeGrantTest.php | 16 ---------------- 3 files changed, 3 insertions(+), 33 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index a4360bae..378f2096 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -79,16 +79,11 @@ class AuthCodeGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidRequest('client_id'); } + $client = $this->clientRepository->getClientEntity($clientId); + // Only validate the client if it is confidential - if ($this->clientRepository->isClientConfidential($clientId)) { + if ($client->isConfidential()) { $client = $this->validateClient($request); - } else { - $client = $this->clientRepository->getClientEntity( - $clientId, - $this->getIdentifier(), - null, - false - ); } $encryptedAuthCode = $this->getRequestParameter('code', $request, null); diff --git a/src/Repositories/ClientRepositoryInterface.php b/src/Repositories/ClientRepositoryInterface.php index eee03104..47b6083a 100644 --- a/src/Repositories/ClientRepositoryInterface.php +++ b/src/Repositories/ClientRepositoryInterface.php @@ -25,15 +25,6 @@ interface ClientRepositoryInterface extends RepositoryInterface */ public function getClientEntity($clientIdentifier); - /** - * Check if a client is confidential. - * - * @param string $clientIdentifier The client's identifier - * - * @return bool - */ - public function isClientConfidential($clientIdentifier); - /** * Validate a client's secret. * diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index a415f3e1..49c6e722 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -546,7 +546,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -615,7 +614,6 @@ class AuthCodeGrantTest extends TestCase $client->setRedirectUri('http://foo/bar'); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(false); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -686,7 +684,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -760,7 +757,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -837,7 +833,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), @@ -886,7 +881,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), @@ -936,7 +930,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -980,7 +973,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); @@ -1042,7 +1034,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); @@ -1107,7 +1098,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); @@ -1169,7 +1159,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); @@ -1220,7 +1209,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -1294,7 +1282,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -1368,7 +1355,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -1442,7 +1428,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -1516,7 +1501,6 @@ class AuthCodeGrantTest extends TestCase $client->isConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $clientRepositoryMock->method('isClientConfidential')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); From de899fbe0a0ca8afa1031b509a92ab25ccd0326b Mon Sep 17 00:00:00 2001 From: sephster Date: Sat, 1 Sep 2018 15:05:12 +0100 Subject: [PATCH 042/104] Fix incorrect usage of isConfidential --- tests/Grant/AuthCodeGrantTest.php | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 49c6e722..632d0e24 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -543,7 +543,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -681,7 +681,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -754,7 +754,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -830,7 +830,7 @@ class AuthCodeGrantTest extends TestCase { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -878,7 +878,7 @@ class AuthCodeGrantTest extends TestCase { $client = new ClientEntity(); $client->setIdentifier('foo'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -927,7 +927,7 @@ class AuthCodeGrantTest extends TestCase { $client = new ClientEntity(); $client->setRedirectUri('http://foo/bar'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -970,7 +970,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1031,7 +1031,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1095,7 +1095,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1156,7 +1156,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1206,7 +1206,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1279,7 +1279,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1352,7 +1352,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1425,7 +1425,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -1498,7 +1498,7 @@ class AuthCodeGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); $client->setRedirectUri('http://foo/bar'); - $client->isConfidential(); + $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); From e85a8e31e8e6cc1bf0ec3e15cc94fdebca898783 Mon Sep 17 00:00:00 2001 From: sephster Date: Sun, 2 Sep 2018 14:58:02 +0100 Subject: [PATCH 043/104] Remove assignment as not needed --- src/Grant/AuthCodeGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 378f2096..3d692758 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -83,7 +83,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant // Only validate the client if it is confidential if ($client->isConfidential()) { - $client = $this->validateClient($request); + $this->validateClient($request); } $encryptedAuthCode = $this->getRequestParameter('code', $request, null); From 5d3d9d95bea6ecb9280947d2b8abb468fed209ec Mon Sep 17 00:00:00 2001 From: sephster Date: Sun, 2 Sep 2018 15:46:59 +0100 Subject: [PATCH 044/104] Remove extra line --- tests/Grant/AuthCodeGrantTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 632d0e24..05975e61 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -673,7 +673,6 @@ class AuthCodeGrantTest extends TestCase $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); - } public function testRespondToAccessTokenRequestCodeChallengePlain() From 07ebe43b91fc2a5758bf892f8d5aa6c412844989 Mon Sep 17 00:00:00 2001 From: sephster Date: Sun, 2 Sep 2018 16:17:34 +0100 Subject: [PATCH 045/104] Change else if to elseif --- src/Grant/AuthCodeGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 3d692758..5ae3990a 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -312,7 +312,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $authorizationRequest->setCodeChallenge($codeChallenge); $authorizationRequest->setCodeChallengeMethod($codeChallengeMethod); - } else if ($this->requireCodeChallengeForPublicClients && !$client->isConfidential()) { + } elseif ($this->requireCodeChallengeForPublicClients && !$client->isConfidential()) { throw OAuthServerException::invalidRequest('code_challenge', 'Code challenge must be provided for public clients'); } From 36bf4ff8f2fa9988633e280a0b6c7d7c0946928a Mon Sep 17 00:00:00 2001 From: sephster Date: Sun, 2 Sep 2018 16:19:47 +0100 Subject: [PATCH 046/104] Fix accidental paste of code --- tests/Grant/AuthCodeGrantTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 05975e61..3ea7105a 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -200,7 +200,6 @@ class AuthCodeGrantTest extends TestCase } /** - * $this->expectException(OAuth * @expectedException \League\OAuth2\Server\Exception\OAuthServerException */ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooShort() From d831868d58556640812f7d1855295c3cd1a553de Mon Sep 17 00:00:00 2001 From: sephster Date: Sun, 2 Sep 2018 16:27:31 +0100 Subject: [PATCH 047/104] Fix getClientEntity parameters --- src/Grant/AuthCodeGrant.php | 7 +------ src/Grant/ImplicitGrant.php | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 5ae3990a..7229940a 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -245,12 +245,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidRequest('client_id'); } - $client = $this->clientRepository->getClientEntity( - $clientId, - $this->getIdentifier(), - null, - false - ); + $client = $this->clientRepository->getClientEntity($clientId); if ($client instanceof ClientEntityInterface === false) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 7caadbc7..6a71ab08 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -123,12 +123,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidRequest('client_id'); } - $client = $this->clientRepository->getClientEntity( - $clientId, - $this->getIdentifier(), - null, - false - ); + $client = $this->clientRepository->getClientEntity($clientId); if ($client instanceof ClientEntityInterface === false) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); From e3e7abf41e9e146fe3f33f7db1dd534285049fb5 Mon Sep 17 00:00:00 2001 From: sephster Date: Mon, 3 Sep 2018 13:09:52 +0100 Subject: [PATCH 048/104] Set default isConfidential to false for client entity --- src/Entities/Traits/ClientTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entities/Traits/ClientTrait.php b/src/Entities/Traits/ClientTrait.php index f73bc2e0..a0078d8d 100644 --- a/src/Entities/Traits/ClientTrait.php +++ b/src/Entities/Traits/ClientTrait.php @@ -24,7 +24,7 @@ trait ClientTrait /** * @var bool */ - protected $isConfidential; + protected $isConfidential = false; /** * Get the client's name. From 6a1645aebc9b0ebc79483f79c54cbc179f31c68c Mon Sep 17 00:00:00 2001 From: sephster Date: Fri, 14 Sep 2018 18:56:22 +0100 Subject: [PATCH 049/104] Start to add code challenge verifier interfaces --- src/Grant/AuthCodeGrant.php | 61 +++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 7229940a..de19e8d1 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -9,6 +9,9 @@ namespace League\OAuth2\Server\Grant; +use League\OAuth2\Server\CodeChallengeVerifiers\CodeChallengeVerifierInterface; +use League\OAuth2\Server\CodeChallengeVerifiers\PlainVerifier; +use League\OAuth2\Server\CodeChallengeVerifiers\S256Verifier; use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; use League\OAuth2\Server\Entities\UserEntityInterface; @@ -33,6 +36,11 @@ class AuthCodeGrant extends AbstractAuthorizeGrant */ private $requireCodeChallengeForPublicClients = true; + /** + * @var CodeChallengeVerifierInterface[] + */ + private $codeChallengeVerifiers = []; + /** * @param AuthCodeRepositoryInterface $authCodeRepository * @param RefreshTokenRepositoryInterface $refreshTokenRepository @@ -47,6 +55,15 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $this->setRefreshTokenRepository($refreshTokenRepository); $this->authCodeTTL = $authCodeTTL; $this->refreshTokenTTL = new \DateInterval('P1M'); + + // SHOULD ONLY DO THIS IS SHA256 is supported + $s256Verifier = new S256Verifier(); + $plainVerifier = new PlainVerifier(); + + $codeChallengeVerifiers = [ + $s256Verifier->getMethod() => $s256Verifier, + $plainVerifier->getMethod() => $plainVerifier, + ]; } /** @@ -161,32 +178,19 @@ class AuthCodeGrant extends AbstractAuthorizeGrant ); } - switch ($authCodePayload->code_challenge_method) { - case 'plain': - if (hash_equals($codeVerifier, $authCodePayload->code_challenge) === false) { - throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.'); - } + if (isset($this->codeChallengeVerifiers[$authCodePayLoad->code_challenge_method])) { + $codeChallengeVerifier = $this->codeChallengeVerifiers[$authCodePayload->code_challenge_method]; - break; - case 'S256': - if ( - hash_equals( - strtr(rtrim(base64_encode(hash('sha256', $codeVerifier, true)), '='), '+/', '-_'), - $authCodePayload->code_challenge - ) === false - ) { - throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.'); - } - // @codeCoverageIgnoreStart - break; - default: - throw OAuthServerException::serverError( - sprintf( - 'Unsupported code challenge method `%s`', - $authCodePayload->code_challenge_method - ) - ); - // @codeCoverageIgnoreEnd + 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 + ) + ); } } @@ -289,10 +293,13 @@ class AuthCodeGrant extends AbstractAuthorizeGrant if ($codeChallenge !== null) { $codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain'); - if (in_array($codeChallengeMethod, ['plain', 'S256'], true) === false) { + if (array_key_exitst($codeChallengeMethod, $this->codeChallengeVerifiers) === false) { throw OAuthServerException::invalidRequest( 'code_challenge_method', - 'Code challenge method must be `plain` or `S256`' + 'Code challenge method must be one of ' . implode(', ', array_map( + function ($method) { return '`' . $method . '`'; }, + array_keys($this->codeChallengeVerifiers) + )) ); } From 970df8f34b78a54e9360e3d598752e1a9adc15eb Mon Sep 17 00:00:00 2001 From: sephster Date: Mon, 17 Sep 2018 12:48:32 +0100 Subject: [PATCH 050/104] Add code challenge verifiers --- .../CodeChallengeVerifierInterface.php | 30 ++++++++++++++ src/CodeChallengeVerifiers/PlainVerifier.php | 36 +++++++++++++++++ src/CodeChallengeVerifiers/S256Verifier.php | 39 +++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php create mode 100644 src/CodeChallengeVerifiers/PlainVerifier.php create mode 100644 src/CodeChallengeVerifiers/S256Verifier.php diff --git a/src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php b/src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php new file mode 100644 index 00000000..3d7ad59c --- /dev/null +++ b/src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php @@ -0,0 +1,30 @@ + + * @copyright Copyright (c) Lukáš Unger + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\CodeChallengeVerifiers; + +interface CodeChallengeVerifierInterface +{ + /** + * Return code challenge method. + * + * @return string + */ + public function getMethod(); + + /** + * Verify the code challenge. + * + * @param string $codeVerifier + * @param string $codeChallenge + * + * @return bool + */ + public function verifyCodeChallenge($codeVerifier, $codeChallenge); +} diff --git a/src/CodeChallengeVerifiers/PlainVerifier.php b/src/CodeChallengeVerifiers/PlainVerifier.php new file mode 100644 index 00000000..71749c97 --- /dev/null +++ b/src/CodeChallengeVerifiers/PlainVerifier.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) Lukáš Unger + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\CodeChallengeVerifiers; + +class PlainVerifier implements CodeChallengeVerifierInterface +{ + /** + * Return code challenge method. + * + * @return string + */ + public function getMethod() + { + return 'plain'; + } + + /** + * Verify the code challenge. + * + * @param string $codeVerifier + * @param string $codeChallenge + * + * @return bool + */ + public function verifyCodeChallenge($codeVerifier, $codeChallenge) + { + return hash_equals($codeVerifier, $codeChallenge); + } +} diff --git a/src/CodeChallengeVerifiers/S256Verifier.php b/src/CodeChallengeVerifiers/S256Verifier.php new file mode 100644 index 00000000..3b37af3a --- /dev/null +++ b/src/CodeChallengeVerifiers/S256Verifier.php @@ -0,0 +1,39 @@ + + * @copyright Copyright (c) Lukáš Unger + * @license http://mit-license.org/ + * + * @link https://github.com/thephpleague/oauth2-server + */ + +namespace League\OAuth2\Server\CodeChallengeVerifiers; + +class S256Verifier implements CodeChallengeVerifierInterface +{ + /** + * Return code challenge method. + * + * @return string + */ + public function getMethod() + { + return 'S256'; + } + + /** + * Verify the code challenge. + * + * @param string $codeVerifier + * @param string $codeChallenge + * + * @return bool + */ + public function verifyCodeChallenge($codeVerifier, $codeChallenge) + { + return hash_equals( + strtr(rtrim(base64_encode(hash('sha256', $codeVerifier, true)), '='), '+/', '-_'), + $codeChallenge + ); + } +} From 4a464dd336d9678bc4c4cea13da698fee68042b0 Mon Sep 17 00:00:00 2001 From: sephster Date: Mon, 17 Sep 2018 12:49:37 +0100 Subject: [PATCH 051/104] Fix coding standard issue --- src/Grant/AuthCodeGrant.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index de19e8d1..0514137d 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -297,7 +297,9 @@ class AuthCodeGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidRequest( 'code_challenge_method', 'Code challenge method must be one of ' . implode(', ', array_map( - function ($method) { return '`' . $method . '`'; }, + function ($method) { + return '`' . $method . '`'; + }, array_keys($this->codeChallengeVerifiers) )) ); From 592dd2f433ba87058810f32a7ce91cabc511cf78 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 17 Sep 2018 20:10:26 +0100 Subject: [PATCH 052/104] Fix typo in function name --- src/Grant/AuthCodeGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 0514137d..f2db55f0 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -293,7 +293,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant if ($codeChallenge !== null) { $codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain'); - if (array_key_exitst($codeChallengeMethod, $this->codeChallengeVerifiers) === false) { + if (array_key_exists($codeChallengeMethod, $this->codeChallengeVerifiers) === false) { throw OAuthServerException::invalidRequest( 'code_challenge_method', 'Code challenge method must be one of ' . implode(', ', array_map( From 133d9cc97a791b9626c9316ec21a587da1fd5438 Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 18 Sep 2018 17:51:11 +0100 Subject: [PATCH 053/104] Fix missing --- src/Grant/AuthCodeGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index f2db55f0..b72b3a5d 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -60,7 +60,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $s256Verifier = new S256Verifier(); $plainVerifier = new PlainVerifier(); - $codeChallengeVerifiers = [ + $this->codeChallengeVerifiers = [ $s256Verifier->getMethod() => $s256Verifier, $plainVerifier->getMethod() => $plainVerifier, ]; From fcd6eb8a3cc7b13b7bdc26d3ccc46cee0555a6f8 Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 18 Sep 2018 18:01:24 +0100 Subject: [PATCH 054/104] Fix variable name --- src/Grant/AuthCodeGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index b72b3a5d..974f8373 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -178,7 +178,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant ); } - if (isset($this->codeChallengeVerifiers[$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) { From 1ddc27e792ef2dfdff06685700afee451d276d28 Mon Sep 17 00:00:00 2001 From: sephster Date: Fri, 21 Sep 2018 20:12:05 +0100 Subject: [PATCH 055/104] Add code challenge verifier tests --- .../CodeChallengeVerifiers/PlainVerifier.php | 24 ++++++++++++ tests/CodeChallengeVerifiers/S256Verifier.php | 37 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 tests/CodeChallengeVerifiers/PlainVerifier.php create mode 100644 tests/CodeChallengeVerifiers/S256Verifier.php diff --git a/tests/CodeChallengeVerifiers/PlainVerifier.php b/tests/CodeChallengeVerifiers/PlainVerifier.php new file mode 100644 index 00000000..4939c88c --- /dev/null +++ b/tests/CodeChallengeVerifiers/PlainVerifier.php @@ -0,0 +1,24 @@ +assertEquals('plain', $verifier->getMethod()); + } + + public function testVerifyCodeChallenge() + { + $verifier = new PlainVerifier(); + + $this->assertTrue($verifier->verifyCodeChallenge('foo', 'foo'); + $this->assertFalse($verifier->verifyCodeChallenge('foo', 'bar'); + } +} diff --git a/tests/CodeChallengeVerifiers/S256Verifier.php b/tests/CodeChallengeVerifiers/S256Verifier.php new file mode 100644 index 00000000..2bf14353 --- /dev/null +++ b/tests/CodeChallengeVerifiers/S256Verifier.php @@ -0,0 +1,37 @@ +assertEquals('S256', $verifier->getMethod()); + } + + public function testVerifyCodeChallengeSucceeds() + { + $codeChallenge = $this->createCodeChallenge('foo'); + $verifier = new S256Verifier(); + + $this->assertTrue($verifier->verifyCodeChallenge('foo', $codeChallenge)); + } + + public function testVerifyCodeChallengeFails() + { + $codeChallenge = $this->createCodeChallenge('bar'); + $verifier = new S256Verifier(); + + $this->assertFalse($verifier->verifyCodeChallenge('foo', $codeChallenge)); + } + + private function createCodeChallenge($codeVerifier) + { + return strtr(rtrim(base64_encode(hash('sha256', $codeVerifier, true)), '='), '+/', '-_'); + } +} From efb5ce5e2a3222da2ee17974c05a0eb40efd0f75 Mon Sep 17 00:00:00 2001 From: sephster Date: Fri, 21 Sep 2018 20:29:27 +0100 Subject: [PATCH 056/104] Update changelog --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ad6e3f1..4ff27318 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added +- Flag, `requireCodeChallengeForPublicClients`, used to reject public clients that do not provide a code challenge for the Auth Code Grant (PR #938) +- Public clients can now use the Auth Code Grant (PR #938) +- `isConfidential` property added to `ClientEntity` to identify type of client (PR #938) + ### Changed - Replace `convertToJWT()` interface with a more generic `__toString()` to improve extensibility (PR #874) - The `invalidClient()` function accepts a PSR-7 compliant `$serverRequest` argument to avoid accessing the `$_SERVER` global variable and improve testing (PR #899) - `issueAccessToken()` in the Abstract Grant no longer sets access token client, user ID or scopes. These values should already have been set when calling `getNewToken()` (PR #919) +- No longer need to enable PKCE with `enableCodeExchangeProof` flag. Any client sending a code challenge will initiate PKCE checks. (PR #938) + +### Removed +- `enableCodeExchangeProof` flag (PR #938) ## [7.2.0] - released 2018-06-23 From da2742bea7a784de7680a999a2187e403093c548 Mon Sep 17 00:00:00 2001 From: sephster Date: Fri, 21 Sep 2018 20:32:47 +0100 Subject: [PATCH 057/104] Add details on client validation changes --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ff27318..b8b6ecba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,12 +10,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Flag, `requireCodeChallengeForPublicClients`, used to reject public clients that do not provide a code challenge for the Auth Code Grant (PR #938) - Public clients can now use the Auth Code Grant (PR #938) - `isConfidential` property added to `ClientEntity` to identify type of client (PR #938) +- Function `validateClient()` added to validate clients which was previously performed by the `getClientEntity()` function (PR #938) ### Changed - Replace `convertToJWT()` interface with a more generic `__toString()` to improve extensibility (PR #874) - The `invalidClient()` function accepts a PSR-7 compliant `$serverRequest` argument to avoid accessing the `$_SERVER` global variable and improve testing (PR #899) - `issueAccessToken()` in the Abstract Grant no longer sets access token client, user ID or scopes. These values should already have been set when calling `getNewToken()` (PR #919) - No longer need to enable PKCE with `enableCodeExchangeProof` flag. Any client sending a code challenge will initiate PKCE checks. (PR #938) +- Function `getClientEntity()` no longer performs client validation (PR #938) ### Removed - `enableCodeExchangeProof` flag (PR #938) From 9645119ccb65bf4e194b3f8c417acc21fe3dba57 Mon Sep 17 00:00:00 2001 From: sephster Date: Fri, 21 Sep 2018 20:35:04 +0100 Subject: [PATCH 058/104] Fix missing comma --- tests/CodeChallengeVerifiers/PlainVerifier.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/CodeChallengeVerifiers/PlainVerifier.php b/tests/CodeChallengeVerifiers/PlainVerifier.php index 4939c88c..437c0c2f 100644 --- a/tests/CodeChallengeVerifiers/PlainVerifier.php +++ b/tests/CodeChallengeVerifiers/PlainVerifier.php @@ -18,7 +18,7 @@ class PlainVerifierTest extends TestCase { $verifier = new PlainVerifier(); - $this->assertTrue($verifier->verifyCodeChallenge('foo', 'foo'); - $this->assertFalse($verifier->verifyCodeChallenge('foo', 'bar'); + $this->assertTrue($verifier->verifyCodeChallenge('foo', 'foo')); + $this->assertFalse($verifier->verifyCodeChallenge('foo', 'bar')); } } From 0c2356a508c48be19340d7752509e16d0752a199 Mon Sep 17 00:00:00 2001 From: sephster Date: Fri, 21 Sep 2018 20:43:04 +0100 Subject: [PATCH 059/104] Fix file names for code challenge verifier tests --- .../{PlainVerifier.php => PlainVerifierTest.php} | 0 .../{S256Verifier.php => S256VerifierTest.php} | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/CodeChallengeVerifiers/{PlainVerifier.php => PlainVerifierTest.php} (100%) rename tests/CodeChallengeVerifiers/{S256Verifier.php => S256VerifierTest.php} (96%) diff --git a/tests/CodeChallengeVerifiers/PlainVerifier.php b/tests/CodeChallengeVerifiers/PlainVerifierTest.php similarity index 100% rename from tests/CodeChallengeVerifiers/PlainVerifier.php rename to tests/CodeChallengeVerifiers/PlainVerifierTest.php diff --git a/tests/CodeChallengeVerifiers/S256Verifier.php b/tests/CodeChallengeVerifiers/S256VerifierTest.php similarity index 96% rename from tests/CodeChallengeVerifiers/S256Verifier.php rename to tests/CodeChallengeVerifiers/S256VerifierTest.php index 2bf14353..cb96c000 100644 --- a/tests/CodeChallengeVerifiers/S256Verifier.php +++ b/tests/CodeChallengeVerifiers/S256VerifierTest.php @@ -5,7 +5,7 @@ namespace LeagueTests\CodeChallengeVerifiers; use League\OAuth2\Server\CodeChallengeVerifiers\S256Verifier; use PHPUnit\Framework\TestCase; -class S256Verifier extends TestCase +class S256VerifierTest extends TestCase { public function testGetMethod() { From 16f9de86f2891cc89a8f26062cff2708d6ed5447 Mon Sep 17 00:00:00 2001 From: Marc Bennewitz Date: Thu, 8 Nov 2018 10:41:01 +0100 Subject: [PATCH 060/104] cleanup DateTime handline * DateTime -> DateTimeImmutable * DateTime::format('U') -> DateTime::getTimestamp() * (new DateTime())->getTimestamp() -> time() --- src/AuthorizationServer.php | 11 +- src/Entities/RefreshTokenEntityInterface.php | 10 +- src/Entities/TokenInterface.php | 10 +- src/Entities/Traits/AccessTokenTrait.php | 5 +- src/Entities/Traits/RefreshTokenTrait.php | 11 +- src/Entities/Traits/TokenEntityTrait.php | 11 +- src/Grant/AbstractGrant.php | 20 +-- src/Grant/AuthCodeGrant.php | 22 ++-- src/Grant/ClientCredentialsGrant.php | 3 +- src/Grant/GrantTypeInterface.php | 9 +- src/Grant/ImplicitGrant.php | 19 +-- src/Grant/PasswordGrant.php | 5 +- src/Grant/RefreshTokenGrant.php | 5 +- src/ResponseTypes/BearerTokenResponse.php | 2 +- tests/AuthorizationServerTest.php | 11 +- tests/Grant/AbstractGrantTest.php | 7 +- tests/Grant/AuthCodeGrantTest.php | 117 +++++++++--------- tests/Grant/ClientCredentialsGrantTest.php | 3 +- tests/Grant/ImplicitGrantTest.php | 41 +++--- tests/Grant/PasswordGrantTest.php | 9 +- tests/Grant/RefreshTokenGrantTest.php | 17 +-- .../AuthorizationServerMiddlewareTest.php | 3 +- .../ResourceServerMiddlewareTest.php | 6 +- .../ResponseTypes/BearerResponseTypeTest.php | 22 ++-- 24 files changed, 202 insertions(+), 177 deletions(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index f1e96146..81973b24 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -9,6 +9,7 @@ namespace League\OAuth2\Server; +use DateInterval; use Defuse\Crypto\Key; use League\Event\EmitterAwareInterface; use League\Event\EmitterAwareTrait; @@ -34,7 +35,7 @@ class AuthorizationServer implements EmitterAwareInterface protected $enabledGrantTypes = []; /** - * @var \DateInterval[] + * @var DateInterval[] */ protected $grantTypeAccessTokenTTL = []; @@ -112,12 +113,12 @@ class AuthorizationServer implements EmitterAwareInterface * Enable a grant type on the server. * * @param GrantTypeInterface $grantType - * @param null|\DateInterval $accessTokenTTL + * @param null|DateInterval $accessTokenTTL */ - public function enableGrantType(GrantTypeInterface $grantType, \DateInterval $accessTokenTTL = null) + public function enableGrantType(GrantTypeInterface $grantType, DateInterval $accessTokenTTL = null) { - if ($accessTokenTTL instanceof \DateInterval === false) { - $accessTokenTTL = new \DateInterval('PT1H'); + if ($accessTokenTTL === null) { + $accessTokenTTL = new DateInterval('PT1H'); } $grantType->setAccessTokenRepository($this->accessTokenRepository); diff --git a/src/Entities/RefreshTokenEntityInterface.php b/src/Entities/RefreshTokenEntityInterface.php index e4f10400..a197ce5e 100644 --- a/src/Entities/RefreshTokenEntityInterface.php +++ b/src/Entities/RefreshTokenEntityInterface.php @@ -9,6 +9,8 @@ namespace League\OAuth2\Server\Entities; +use DateTimeImmutable; + interface RefreshTokenEntityInterface { /** @@ -28,16 +30,16 @@ interface RefreshTokenEntityInterface /** * Get the token's expiry date time. * - * @return \DateTime + * @return DateTimeImmutable */ - public function getExpiryDateTime(); + public function getExpiryDateTime(): DateTimeImmutable; /** * Set the date time when the token expires. * - * @param \DateTime $dateTime + * @param DateTimeImmutable $dateTime */ - public function setExpiryDateTime(\DateTime $dateTime); + public function setExpiryDateTime(DateTimeImmutable $dateTime); /** * Set the access token that the refresh token was associated with. diff --git a/src/Entities/TokenInterface.php b/src/Entities/TokenInterface.php index 378adbdc..a8d1496d 100644 --- a/src/Entities/TokenInterface.php +++ b/src/Entities/TokenInterface.php @@ -9,6 +9,8 @@ namespace League\OAuth2\Server\Entities; +use DateTimeImmutable; + interface TokenInterface { /** @@ -28,16 +30,16 @@ interface TokenInterface /** * Get the token's expiry date time. * - * @return \DateTime + * @return DateTimeImmutable */ - public function getExpiryDateTime(); + public function getExpiryDateTime(): DateTimeImmutable; /** * Set the date time when the token expires. * - * @param \DateTime $dateTime + * @param DateTimeImmutable $dateTime */ - public function setExpiryDateTime(\DateTime $dateTime); + public function setExpiryDateTime(DateTimeImmutable $dateTime); /** * Set the identifier of the user associated with the token. diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php index 501233e9..4fa7193a 100644 --- a/src/Entities/Traits/AccessTokenTrait.php +++ b/src/Entities/Traits/AccessTokenTrait.php @@ -9,6 +9,7 @@ namespace League\OAuth2\Server\Entities\Traits; +use DateTimeImmutable; use Lcobucci\JWT\Builder; use Lcobucci\JWT\Signer\Key; use Lcobucci\JWT\Signer\Rsa\Sha256; @@ -67,9 +68,9 @@ trait AccessTokenTrait abstract public function getClient(); /** - * @return \DateTime + * @return DateTimeImmutable */ - abstract public function getExpiryDateTime(); + abstract public function getExpiryDateTime(): DateTimeImmutable; /** * @return string|int diff --git a/src/Entities/Traits/RefreshTokenTrait.php b/src/Entities/Traits/RefreshTokenTrait.php index fb9dbc68..e79cfdaa 100644 --- a/src/Entities/Traits/RefreshTokenTrait.php +++ b/src/Entities/Traits/RefreshTokenTrait.php @@ -9,6 +9,7 @@ namespace League\OAuth2\Server\Entities\Traits; +use DateTimeImmutable; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; trait RefreshTokenTrait @@ -19,7 +20,7 @@ trait RefreshTokenTrait protected $accessToken; /** - * @var \DateTime + * @var DateTimeImmutable */ protected $expiryDateTime; @@ -42,9 +43,9 @@ trait RefreshTokenTrait /** * Get the token's expiry date time. * - * @return \DateTime + * @return DateTimeImmutable */ - public function getExpiryDateTime() + public function getExpiryDateTime(): DateTimeImmutable { return $this->expiryDateTime; } @@ -52,9 +53,9 @@ trait RefreshTokenTrait /** * Set the date time when the token expires. * - * @param \DateTime $dateTime + * @param DateTimeImmutable $dateTime */ - public function setExpiryDateTime(\DateTime $dateTime) + public function setExpiryDateTime(DateTimeImmutable $dateTime) { $this->expiryDateTime = $dateTime; } diff --git a/src/Entities/Traits/TokenEntityTrait.php b/src/Entities/Traits/TokenEntityTrait.php index c6653cce..8ed2b63a 100644 --- a/src/Entities/Traits/TokenEntityTrait.php +++ b/src/Entities/Traits/TokenEntityTrait.php @@ -9,6 +9,7 @@ namespace League\OAuth2\Server\Entities\Traits; +use DateTimeImmutable; use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; @@ -20,7 +21,7 @@ trait TokenEntityTrait protected $scopes = []; /** - * @var \DateTime + * @var DateTimeImmutable */ protected $expiryDateTime; @@ -57,9 +58,9 @@ trait TokenEntityTrait /** * Get the token's expiry date time. * - * @return \DateTime + * @return DateTimeImmutable */ - public function getExpiryDateTime() + public function getExpiryDateTime(): DateTimeImmutable { return $this->expiryDateTime; } @@ -67,9 +68,9 @@ trait TokenEntityTrait /** * Set the date time when the token expires. * - * @param \DateTime $dateTime + * @param DateTimeImmutable $dateTime */ - public function setExpiryDateTime(\DateTime $dateTime) + public function setExpiryDateTime(DateTimeImmutable $dateTime) { $this->expiryDateTime = $dateTime; } diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 4531a6a3..30f5448c 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -10,6 +10,8 @@ */ namespace League\OAuth2\Server\Grant; +use DateInterval; +use DateTimeImmutable; use League\Event\EmitterAwareTrait; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\CryptTrait; @@ -72,7 +74,7 @@ abstract class AbstractGrant implements GrantTypeInterface protected $userRepository; /** - * @var \DateInterval + * @var DateInterval */ protected $refreshTokenTTL; @@ -137,7 +139,7 @@ abstract class AbstractGrant implements GrantTypeInterface /** * {@inheritdoc} */ - public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL) + public function setRefreshTokenTTL(DateInterval $refreshTokenTTL) { $this->refreshTokenTTL = $refreshTokenTTL; } @@ -352,7 +354,7 @@ abstract class AbstractGrant implements GrantTypeInterface /** * Issue an access token. * - * @param \DateInterval $accessTokenTTL + * @param DateInterval $accessTokenTTL * @param ClientEntityInterface $client * @param string|null $userIdentifier * @param ScopeEntityInterface[] $scopes @@ -363,7 +365,7 @@ abstract class AbstractGrant implements GrantTypeInterface * @return AccessTokenEntityInterface */ protected function issueAccessToken( - \DateInterval $accessTokenTTL, + DateInterval $accessTokenTTL, ClientEntityInterface $client, $userIdentifier, array $scopes = [] @@ -371,7 +373,7 @@ abstract class AbstractGrant implements GrantTypeInterface $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS; $accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier); - $accessToken->setExpiryDateTime((new \DateTime())->add($accessTokenTTL)); + $accessToken->setExpiryDateTime((new DateTimeImmutable())->add($accessTokenTTL)); $accessToken->setPrivateKey($this->privateKey); while ($maxGenerationAttempts-- > 0) { @@ -391,7 +393,7 @@ abstract class AbstractGrant implements GrantTypeInterface /** * Issue an auth code. * - * @param \DateInterval $authCodeTTL + * @param DateInterval $authCodeTTL * @param ClientEntityInterface $client * @param string $userIdentifier * @param string|null $redirectUri @@ -403,7 +405,7 @@ abstract class AbstractGrant implements GrantTypeInterface * @return AuthCodeEntityInterface */ protected function issueAuthCode( - \DateInterval $authCodeTTL, + DateInterval $authCodeTTL, ClientEntityInterface $client, $userIdentifier, $redirectUri, @@ -412,7 +414,7 @@ abstract class AbstractGrant implements GrantTypeInterface $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS; $authCode = $this->authCodeRepository->getNewAuthCode(); - $authCode->setExpiryDateTime((new \DateTime())->add($authCodeTTL)); + $authCode->setExpiryDateTime((new DateTimeImmutable())->add($authCodeTTL)); $authCode->setClient($client); $authCode->setUserIdentifier($userIdentifier); @@ -451,7 +453,7 @@ abstract class AbstractGrant implements GrantTypeInterface $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS; $refreshToken = $this->refreshTokenRepository->getNewRefreshToken(); - $refreshToken->setExpiryDateTime((new \DateTime())->add($this->refreshTokenTTL)); + $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add($this->refreshTokenTTL)); $refreshToken->setAccessToken($accessToken); while ($maxGenerationAttempts-- > 0) { diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 974f8373..9758744d 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -9,6 +9,8 @@ namespace League\OAuth2\Server\Grant; +use DateInterval; +use DateTimeImmutable; use League\OAuth2\Server\CodeChallengeVerifiers\CodeChallengeVerifierInterface; use League\OAuth2\Server\CodeChallengeVerifiers\PlainVerifier; use League\OAuth2\Server\CodeChallengeVerifiers\S256Verifier; @@ -27,7 +29,7 @@ use Psr\Http\Message\ServerRequestInterface; class AuthCodeGrant extends AbstractAuthorizeGrant { /** - * @var \DateInterval + * @var DateInterval */ private $authCodeTTL; @@ -44,17 +46,17 @@ class AuthCodeGrant extends AbstractAuthorizeGrant /** * @param AuthCodeRepositoryInterface $authCodeRepository * @param RefreshTokenRepositoryInterface $refreshTokenRepository - * @param \DateInterval $authCodeTTL + * @param DateInterval $authCodeTTL */ public function __construct( AuthCodeRepositoryInterface $authCodeRepository, RefreshTokenRepositoryInterface $refreshTokenRepository, - \DateInterval $authCodeTTL + DateInterval $authCodeTTL ) { $this->setAuthCodeRepository($authCodeRepository); $this->setRefreshTokenRepository($refreshTokenRepository); $this->authCodeTTL = $authCodeTTL; - $this->refreshTokenTTL = new \DateInterval('P1M'); + $this->refreshTokenTTL = new DateInterval('P1M'); // SHOULD ONLY DO THIS IS SHA256 is supported $s256Verifier = new S256Verifier(); @@ -79,7 +81,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant * * @param ServerRequestInterface $request * @param ResponseTypeInterface $responseType - * @param \DateInterval $accessTokenTTL + * @param DateInterval $accessTokenTTL * * @throws OAuthServerException * @@ -88,7 +90,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant public function respondToAccessTokenRequest( ServerRequestInterface $request, ResponseTypeInterface $responseType, - \DateInterval $accessTokenTTL + DateInterval $accessTokenTTL ) { $clientId = $this->getRequestParameter('client_id', $request, null); @@ -354,7 +356,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant 'auth_code_id' => $authCode->getIdentifier(), 'scopes' => $authCode->getScopes(), 'user_id' => $authCode->getUserIdentifier(), - 'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'), + 'expire_time' => (new DateTimeImmutable())->add($this->authCodeTTL)->getTimestamp(), 'code_challenge' => $authorizationRequest->getCodeChallenge(), 'code_challenge_method' => $authorizationRequest->getCodeChallengeMethod(), ]; @@ -364,11 +366,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $this->makeRedirectUri( $finalRedirectUri, [ - 'code' => $this->encrypt( - json_encode( - $payload - ) - ), + 'code' => $this->encrypt(json_encode($payload)), 'state' => $authorizationRequest->getState(), ] ) diff --git a/src/Grant/ClientCredentialsGrant.php b/src/Grant/ClientCredentialsGrant.php index 026ce5e5..9f647965 100644 --- a/src/Grant/ClientCredentialsGrant.php +++ b/src/Grant/ClientCredentialsGrant.php @@ -11,6 +11,7 @@ namespace League\OAuth2\Server\Grant; +use DateInterval; use League\OAuth2\Server\RequestEvent; use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface; use Psr\Http\Message\ServerRequestInterface; @@ -26,7 +27,7 @@ class ClientCredentialsGrant extends AbstractGrant public function respondToAccessTokenRequest( ServerRequestInterface $request, ResponseTypeInterface $responseType, - \DateInterval $accessTokenTTL + DateInterval $accessTokenTTL ) { // Validate request $client = $this->validateClient($request); diff --git a/src/Grant/GrantTypeInterface.php b/src/Grant/GrantTypeInterface.php index 2aee367f..41ebeb5f 100644 --- a/src/Grant/GrantTypeInterface.php +++ b/src/Grant/GrantTypeInterface.php @@ -11,6 +11,7 @@ namespace League\OAuth2\Server\Grant; +use DateInterval; use Defuse\Crypto\Key; use League\Event\EmitterAwareInterface; use League\OAuth2\Server\CryptKey; @@ -29,9 +30,9 @@ interface GrantTypeInterface extends EmitterAwareInterface /** * Set refresh token TTL. * - * @param \DateInterval $refreshTokenTTL + * @param DateInterval $refreshTokenTTL */ - public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL); + public function setRefreshTokenTTL(DateInterval $refreshTokenTTL); /** * Return the grant identifier that can be used in matching up requests. @@ -45,14 +46,14 @@ interface GrantTypeInterface extends EmitterAwareInterface * * @param ServerRequestInterface $request * @param ResponseTypeInterface $responseType - * @param \DateInterval $accessTokenTTL + * @param DateInterval $accessTokenTTL * * @return ResponseTypeInterface */ public function respondToAccessTokenRequest( ServerRequestInterface $request, ResponseTypeInterface $responseType, - \DateInterval $accessTokenTTL + DateInterval $accessTokenTTL ); /** diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 6a71ab08..5fc08cf1 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -9,6 +9,7 @@ namespace League\OAuth2\Server\Grant; +use DateInterval; use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\UserEntityInterface; use League\OAuth2\Server\Exception\OAuthServerException; @@ -22,7 +23,7 @@ use Psr\Http\Message\ServerRequestInterface; class ImplicitGrant extends AbstractAuthorizeGrant { /** - * @var \DateInterval + * @var DateInterval */ private $accessTokenTTL; @@ -32,21 +33,21 @@ class ImplicitGrant extends AbstractAuthorizeGrant private $queryDelimiter; /** - * @param \DateInterval $accessTokenTTL - * @param string $queryDelimiter + * @param DateInterval $accessTokenTTL + * @param string $queryDelimiter */ - public function __construct(\DateInterval $accessTokenTTL, $queryDelimiter = '#') + public function __construct(DateInterval $accessTokenTTL, $queryDelimiter = '#') { $this->accessTokenTTL = $accessTokenTTL; $this->queryDelimiter = $queryDelimiter; } /** - * @param \DateInterval $refreshTokenTTL + * @param DateInterval $refreshTokenTTL * * @throw \LogicException */ - public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL) + public function setRefreshTokenTTL(DateInterval $refreshTokenTTL) { throw new \LogicException('The Implicit Grant does not return refresh tokens'); } @@ -84,14 +85,14 @@ class ImplicitGrant extends AbstractAuthorizeGrant * * @param ServerRequestInterface $request * @param ResponseTypeInterface $responseType - * @param \DateInterval $accessTokenTTL + * @param DateInterval $accessTokenTTL * * @return ResponseTypeInterface */ public function respondToAccessTokenRequest( ServerRequestInterface $request, ResponseTypeInterface $responseType, - \DateInterval $accessTokenTTL + DateInterval $accessTokenTTL ) { throw new \LogicException('This grant does not used this method'); } @@ -203,7 +204,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant [ 'access_token' => (string) $accessToken, 'token_type' => 'Bearer', - 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(), + 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - \time(), 'state' => $authorizationRequest->getState(), ], $this->queryDelimiter diff --git a/src/Grant/PasswordGrant.php b/src/Grant/PasswordGrant.php index 1d00998b..4b68ad44 100644 --- a/src/Grant/PasswordGrant.php +++ b/src/Grant/PasswordGrant.php @@ -11,6 +11,7 @@ namespace League\OAuth2\Server\Grant; +use DateInterval; use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\UserEntityInterface; use League\OAuth2\Server\Exception\OAuthServerException; @@ -36,7 +37,7 @@ class PasswordGrant extends AbstractGrant $this->setUserRepository($userRepository); $this->setRefreshTokenRepository($refreshTokenRepository); - $this->refreshTokenTTL = new \DateInterval('P1M'); + $this->refreshTokenTTL = new DateInterval('P1M'); } /** @@ -45,7 +46,7 @@ class PasswordGrant extends AbstractGrant public function respondToAccessTokenRequest( ServerRequestInterface $request, ResponseTypeInterface $responseType, - \DateInterval $accessTokenTTL + DateInterval $accessTokenTTL ) { // Validate request $client = $this->validateClient($request); diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php index 519954be..cde4beee 100644 --- a/src/Grant/RefreshTokenGrant.php +++ b/src/Grant/RefreshTokenGrant.php @@ -11,6 +11,7 @@ namespace League\OAuth2\Server\Grant; +use DateInterval; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface; use League\OAuth2\Server\RequestEvent; @@ -29,7 +30,7 @@ class RefreshTokenGrant extends AbstractGrant { $this->setRefreshTokenRepository($refreshTokenRepository); - $this->refreshTokenTTL = new \DateInterval('P1M'); + $this->refreshTokenTTL = new DateInterval('P1M'); } /** @@ -38,7 +39,7 @@ class RefreshTokenGrant extends AbstractGrant public function respondToAccessTokenRequest( ServerRequestInterface $request, ResponseTypeInterface $responseType, - \DateInterval $accessTokenTTL + DateInterval $accessTokenTTL ) { // Validate request $client = $this->validateClient($request); diff --git a/src/ResponseTypes/BearerTokenResponse.php b/src/ResponseTypes/BearerTokenResponse.php index 2e658215..ddcadd63 100644 --- a/src/ResponseTypes/BearerTokenResponse.php +++ b/src/ResponseTypes/BearerTokenResponse.php @@ -26,7 +26,7 @@ class BearerTokenResponse extends AbstractResponseType $responseParams = [ 'token_type' => 'Bearer', - 'expires_in' => $expireDateTime - (new \DateTime())->getTimestamp(), + 'expires_in' => $expireDateTime - \time(), 'access_token' => (string) $this->accessToken, ]; diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 7e83ee5e..9fe1a020 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -2,6 +2,7 @@ namespace LeagueTests; +use DateInterval; use League\OAuth2\Server\AuthorizationServer; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Grant\AuthCodeGrant; @@ -48,7 +49,7 @@ class AuthorizationServerTest extends TestCase new StubResponseType() ); - $server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M')); + $server->enableGrantType(new ClientCredentialsGrant(), new DateInterval('PT1M')); try { $server->respondToAccessTokenRequest(ServerRequestFactory::fromGlobals(), new Response); @@ -81,7 +82,7 @@ class AuthorizationServerTest extends TestCase ); $server->setDefaultScope(self::DEFAULT_SCOPE); - $server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M')); + $server->enableGrantType(new ClientCredentialsGrant(), new DateInterval('PT1M')); $_POST['grant_type'] = 'client_credentials'; $_POST['client_id'] = 'foo'; @@ -127,7 +128,7 @@ class AuthorizationServerTest extends TestCase $grant = new AuthCodeGrant( $authCodeRepository, $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $server->enableGrantType($grant); @@ -159,7 +160,7 @@ class AuthorizationServerTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -200,7 +201,7 @@ class AuthorizationServerTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index d6ddd4b1..33103130 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -2,6 +2,7 @@ namespace LeagueTests\Grant; +use DateInterval; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\AuthCodeEntityInterface; @@ -325,7 +326,7 @@ class AbstractGrantTest extends TestCase /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $grantMock->setRefreshTokenTTL(new \DateInterval('PT1M')); + $grantMock->setRefreshTokenTTL(new DateInterval('PT1M')); $grantMock->setRefreshTokenRepository($refreshTokenRepoMock); $abstractGrantReflection = new \ReflectionClass($grantMock); @@ -356,7 +357,7 @@ class AbstractGrantTest extends TestCase /** @var AccessTokenEntityInterface $accessToken */ $accessToken = $issueAccessTokenMethod->invoke( $grantMock, - new \DateInterval('PT1H'), + new DateInterval('PT1H'), new ClientEntity(), 123, [new ScopeEntity()] @@ -381,7 +382,7 @@ class AbstractGrantTest extends TestCase AuthCodeEntityInterface::class, $issueAuthCodeMethod->invoke( $grantMock, - new \DateInterval('PT1H'), + new DateInterval('PT1H'), new ClientEntity(), 123, 'http://foo/bar', diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 3ea7105a..ed702e8a 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -2,6 +2,7 @@ namespace LeagueTests\Grant; +use DateInterval; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; @@ -49,7 +50,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $this->assertEquals('authorization_code', $grant->getIdentifier()); @@ -60,7 +61,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $request = new ServerRequest( @@ -96,7 +97,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); @@ -135,7 +136,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); @@ -173,7 +174,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -212,7 +213,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -249,7 +250,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -286,7 +287,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -321,7 +322,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -353,7 +354,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -388,7 +389,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -424,7 +425,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -464,7 +465,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -505,7 +506,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $authCodeRepository, $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setEncryptionKey($this->cryptStub->getKey()); @@ -530,7 +531,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $authCodeRepository, $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setEncryptionKey($this->cryptStub->getKey()); @@ -562,7 +563,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); @@ -600,7 +601,7 @@ class AuthCodeGrantTest extends TestCase ); /** @var StubResponseType $response */ - $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); @@ -630,7 +631,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); @@ -668,7 +669,7 @@ class AuthCodeGrantTest extends TestCase ); /** @var StubResponseType $response */ - $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); @@ -699,7 +700,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -741,7 +742,7 @@ class AuthCodeGrantTest extends TestCase ); /** @var StubResponseType $response */ - $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); @@ -772,7 +773,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -814,7 +815,7 @@ class AuthCodeGrantTest extends TestCase ); /** @var StubResponseType $response */ - $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); @@ -835,7 +836,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); @@ -865,7 +866,7 @@ class AuthCodeGrantTest extends TestCase ] ); - $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } /** @@ -883,7 +884,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); @@ -914,7 +915,7 @@ class AuthCodeGrantTest extends TestCase ] ); - $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } /** @@ -935,7 +936,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); @@ -960,7 +961,7 @@ class AuthCodeGrantTest extends TestCase ); /* @var StubResponseType $response */ - $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } public function testRespondToAccessTokenRequestExpiredCode() @@ -981,7 +982,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); @@ -1018,7 +1019,7 @@ class AuthCodeGrantTest extends TestCase try { /* @var StubResponseType $response */ - $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { $this->assertEquals($e->getHint(), 'Authorization code has expired'); } @@ -1045,7 +1046,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $authCodeRepositoryMock, $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); @@ -1082,7 +1083,7 @@ class AuthCodeGrantTest extends TestCase try { /* @var StubResponseType $response */ - $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { $this->assertEquals($e->getHint(), 'Authorization code has been revoked'); } @@ -1106,7 +1107,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); @@ -1143,7 +1144,7 @@ class AuthCodeGrantTest extends TestCase try { /* @var StubResponseType $response */ - $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { $this->assertEquals($e->getHint(), 'Authorization code was not issued to this client'); } @@ -1167,7 +1168,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); @@ -1193,7 +1194,7 @@ class AuthCodeGrantTest extends TestCase try { /* @var StubResponseType $response */ - $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { $this->assertEquals($e->getHint(), 'Cannot decrypt the authorization code'); } @@ -1224,7 +1225,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -1266,7 +1267,7 @@ class AuthCodeGrantTest extends TestCase try { /* @var StubResponseType $response */ - $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { $this->assertEquals($e->getHint(), 'Failed to verify `code_verifier`.'); } @@ -1297,7 +1298,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -1339,7 +1340,7 @@ class AuthCodeGrantTest extends TestCase try { /* @var StubResponseType $response */ - $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { $this->assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.'); } @@ -1370,7 +1371,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -1412,7 +1413,7 @@ class AuthCodeGrantTest extends TestCase try { /* @var StubResponseType $response */ - $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { $this->assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.'); } @@ -1443,7 +1444,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -1485,7 +1486,7 @@ class AuthCodeGrantTest extends TestCase try { /* @var StubResponseType $response */ - $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { $this->assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.'); } @@ -1516,7 +1517,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -1557,7 +1558,7 @@ class AuthCodeGrantTest extends TestCase try { /* @var StubResponseType $response */ - $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { $this->assertEquals($e->getHint(), 'Check the `code_verifier` parameter'); } @@ -1580,7 +1581,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $authCodeRepository, $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); @@ -1607,7 +1608,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $authCodeRepository, $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setEncryptionKey($this->cryptStub->getKey()); @@ -1633,7 +1634,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $authCodeRepository, $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); @@ -1664,7 +1665,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); @@ -1702,7 +1703,7 @@ class AuthCodeGrantTest extends TestCase ); /** @var StubResponseType $response */ - $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); @@ -1736,7 +1737,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); @@ -1774,7 +1775,7 @@ class AuthCodeGrantTest extends TestCase ); /** @var StubResponseType $response */ - $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); @@ -1808,7 +1809,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); @@ -1846,7 +1847,7 @@ class AuthCodeGrantTest extends TestCase ); /** @var StubResponseType $response */ - $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); @@ -1860,7 +1861,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->completeAuthorizationRequest(new AuthorizationRequest()); @@ -1881,7 +1882,7 @@ class AuthCodeGrantTest extends TestCase $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index dfd78b41..b02f1190 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -2,6 +2,7 @@ namespace LeagueTests\Grant; +use DateInterval; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Grant\ClientCredentialsGrant; @@ -56,7 +57,7 @@ class ClientCredentialsGrantTest extends TestCase ); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); } diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index df7b6985..5e702e01 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -2,6 +2,7 @@ namespace LeagueTests\Grant; +use DateInterval; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException; @@ -37,13 +38,13 @@ class ImplicitGrantTest extends TestCase public function testGetIdentifier() { - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $this->assertEquals('implicit', $grant->getIdentifier()); } public function testCanRespondToAccessTokenRequest() { - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $this->assertFalse( $grant->canRespondToAccessTokenRequest(new ServerRequest()) @@ -55,17 +56,17 @@ class ImplicitGrantTest extends TestCase */ public function testRespondToAccessTokenRequest() { - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->respondToAccessTokenRequest( new ServerRequest(), new StubResponseType(), - new \DateInterval('PT10M') + new DateInterval('PT10M') ); } public function testCanRespondToAuthorizationRequest() { - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $request = new ServerRequest( [], @@ -96,7 +97,7 @@ class ImplicitGrantTest extends TestCase $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setDefaultScope(self::DEFAULT_SCOPE); @@ -131,7 +132,7 @@ class ImplicitGrantTest extends TestCase $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setDefaultScope(self::DEFAULT_SCOPE); @@ -162,7 +163,7 @@ class ImplicitGrantTest extends TestCase { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setClientRepository($clientRepositoryMock); $request = new ServerRequest( @@ -190,7 +191,7 @@ class ImplicitGrantTest extends TestCase $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn(null); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setClientRepository($clientRepositoryMock); $request = new ServerRequest( @@ -221,7 +222,7 @@ class ImplicitGrantTest extends TestCase $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setClientRepository($clientRepositoryMock); $request = new ServerRequest( @@ -253,7 +254,7 @@ class ImplicitGrantTest extends TestCase $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setClientRepository($clientRepositoryMock); $request = new ServerRequest( @@ -292,7 +293,7 @@ class ImplicitGrantTest extends TestCase $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); @@ -315,7 +316,7 @@ class ImplicitGrantTest extends TestCase $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); @@ -342,7 +343,7 @@ class ImplicitGrantTest extends TestCase $accessTokenRepositoryMock->expects($this->at(0))->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create()); $accessTokenRepositoryMock->expects($this->at(1))->method('persistNewAccessToken')->willReturnSelf(); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); @@ -366,7 +367,7 @@ class ImplicitGrantTest extends TestCase $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); $accessTokenRepositoryMock->method('persistNewAccessToken')->willThrowException(OAuthServerException::serverError('something bad happened')); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); @@ -390,7 +391,7 @@ class ImplicitGrantTest extends TestCase $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); $accessTokenRepositoryMock->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create()); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); @@ -402,8 +403,8 @@ class ImplicitGrantTest extends TestCase */ public function testSetRefreshTokenTTL() { - $grant = new ImplicitGrant(new \DateInterval('PT10M')); - $grant->setRefreshTokenTTL(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); + $grant->setRefreshTokenTTL(new DateInterval('PT10M')); } /** @@ -411,7 +412,7 @@ class ImplicitGrantTest extends TestCase */ public function testSetRefreshTokenRepository() { - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); } @@ -421,7 +422,7 @@ class ImplicitGrantTest extends TestCase */ public function testCompleteAuthorizationRequestNoUser() { - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->completeAuthorizationRequest(new AuthorizationRequest()); } } diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index c90a83db..56a278df 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -2,6 +2,7 @@ namespace LeagueTests\Grant; +use DateInterval; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; @@ -74,7 +75,7 @@ class PasswordGrantTest extends TestCase ); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); @@ -108,7 +109,7 @@ class PasswordGrantTest extends TestCase ); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } /** @@ -140,7 +141,7 @@ class PasswordGrantTest extends TestCase ); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } /** @@ -174,6 +175,6 @@ class PasswordGrantTest extends TestCase ); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } } diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index 89598115..6d1cbdf2 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -2,6 +2,7 @@ namespace LeagueTests\Grant; +use DateInterval; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; @@ -88,7 +89,7 @@ class RefreshTokenGrantTest extends TestCase ]); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); @@ -145,7 +146,7 @@ class RefreshTokenGrantTest extends TestCase ); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); @@ -204,7 +205,7 @@ class RefreshTokenGrantTest extends TestCase ); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } /** @@ -236,7 +237,7 @@ class RefreshTokenGrantTest extends TestCase ); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } /** @@ -271,7 +272,7 @@ class RefreshTokenGrantTest extends TestCase ); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } /** @@ -320,7 +321,7 @@ class RefreshTokenGrantTest extends TestCase ); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } /** @@ -366,7 +367,7 @@ class RefreshTokenGrantTest extends TestCase ); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } /** @@ -413,6 +414,6 @@ class RefreshTokenGrantTest extends TestCase ); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } } diff --git a/tests/Middleware/AuthorizationServerMiddlewareTest.php b/tests/Middleware/AuthorizationServerMiddlewareTest.php index dcfcfbd9..a7ef803d 100644 --- a/tests/Middleware/AuthorizationServerMiddlewareTest.php +++ b/tests/Middleware/AuthorizationServerMiddlewareTest.php @@ -2,6 +2,7 @@ namespace LeagueTests\Middleware; +use DateInterval; use League\OAuth2\Server\AuthorizationServer; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Grant\ClientCredentialsGrant; @@ -77,7 +78,7 @@ class AuthorizationServerMiddlewareTest extends TestCase new StubResponseType() ); - $server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M')); + $server->enableGrantType(new ClientCredentialsGrant(), new DateInterval('PT1M')); $_POST['grant_type'] = 'client_credentials'; $_POST['client_id'] = 'foo'; diff --git a/tests/Middleware/ResourceServerMiddlewareTest.php b/tests/Middleware/ResourceServerMiddlewareTest.php index d1a96042..7210567b 100644 --- a/tests/Middleware/ResourceServerMiddlewareTest.php +++ b/tests/Middleware/ResourceServerMiddlewareTest.php @@ -2,6 +2,8 @@ namespace LeagueTests\Middleware; +use DateInterval; +use DateTimeImmutable; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Middleware\ResourceServerMiddleware; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; @@ -27,7 +29,7 @@ class ResourceServerMiddlewareTest extends TestCase $accessToken = new AccessTokenEntity(); $accessToken->setIdentifier('test'); $accessToken->setUserIdentifier(123); - $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); $accessToken->setClient($client); $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); @@ -63,7 +65,7 @@ class ResourceServerMiddlewareTest extends TestCase $accessToken = new AccessTokenEntity(); $accessToken->setIdentifier('test'); $accessToken->setUserIdentifier(123); - $accessToken->setExpiryDateTime((new \DateTime())->sub(new \DateInterval('PT1H'))); + $accessToken->setExpiryDateTime((new DateTimeImmutable())->sub(new DateInterval('PT1H'))); $accessToken->setClient($client); $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 2eb87238..5eddfdef 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -2,6 +2,8 @@ namespace LeagueTests\ResponseTypes; +use DateInterval; +use DateTimeImmutable; use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Exception\OAuthServerException; @@ -32,7 +34,7 @@ class BearerResponseTypeTest extends TestCase $accessToken = new AccessTokenEntity(); $accessToken->setIdentifier('abcdef'); - $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); $accessToken->setClient($client); $accessToken->addScope($scope); $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); @@ -40,7 +42,7 @@ class BearerResponseTypeTest extends TestCase $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); $refreshToken->setAccessToken($accessToken); - $refreshToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); $responseType->setAccessToken($accessToken); $responseType->setRefreshToken($refreshToken); @@ -75,7 +77,7 @@ class BearerResponseTypeTest extends TestCase $accessToken = new AccessTokenEntity(); $accessToken->setIdentifier('abcdef'); - $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); $accessToken->setClient($client); $accessToken->addScope($scope); $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); @@ -83,7 +85,7 @@ class BearerResponseTypeTest extends TestCase $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); $refreshToken->setAccessToken($accessToken); - $refreshToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); $responseType->setAccessToken($accessToken); $responseType->setRefreshToken($refreshToken); @@ -119,14 +121,14 @@ class BearerResponseTypeTest extends TestCase $accessToken = new AccessTokenEntity(); $accessToken->setIdentifier('abcdef'); $accessToken->setUserIdentifier(123); - $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); $accessToken->setClient($client); $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); $refreshToken->setAccessToken($accessToken); - $refreshToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); $responseType->setAccessToken($accessToken); $responseType->setRefreshToken($refreshToken); @@ -165,14 +167,14 @@ class BearerResponseTypeTest extends TestCase $accessToken = new AccessTokenEntity(); $accessToken->setIdentifier('abcdef'); $accessToken->setUserIdentifier(123); - $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); $accessToken->setClient($client); $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); $refreshToken->setAccessToken($accessToken); - $refreshToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); $responseType->setAccessToken($accessToken); $responseType->setRefreshToken($refreshToken); @@ -208,14 +210,14 @@ class BearerResponseTypeTest extends TestCase $accessToken = new AccessTokenEntity(); $accessToken->setIdentifier('abcdef'); $accessToken->setUserIdentifier(123); - $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); $accessToken->setClient($client); $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); $refreshToken->setAccessToken($accessToken); - $refreshToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); + $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); $responseType->setAccessToken($accessToken); $responseType->setRefreshToken($refreshToken); From 2b4974b697ca937d37f407c429d792c467af7ec5 Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 13 Nov 2018 18:18:07 +0000 Subject: [PATCH 061/104] Change to use invalid_grant --- src/Grant/PasswordGrant.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Grant/PasswordGrant.php b/src/Grant/PasswordGrant.php index 1d00998b..412ac117 100644 --- a/src/Grant/PasswordGrant.php +++ b/src/Grant/PasswordGrant.php @@ -81,11 +81,13 @@ class PasswordGrant extends AbstractGrant protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client) { $username = $this->getRequestParameter('username', $request); + if (is_null($username)) { throw OAuthServerException::invalidRequest('username'); } $password = $this->getRequestParameter('password', $request); + if (is_null($password)) { throw OAuthServerException::invalidRequest('password'); } @@ -96,10 +98,11 @@ class PasswordGrant extends AbstractGrant $this->getIdentifier(), $client ); + if ($user instanceof UserEntityInterface === false) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidCredentials(); + throw OAuthServerException::invalidGrant(); } return $user; From 685dc6edea755e587675eae6bcfa43f8ec959d8f Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 13 Nov 2018 18:19:20 +0000 Subject: [PATCH 062/104] Update test --- tests/Grant/PasswordGrantTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index c90a83db..378fee6f 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -145,6 +145,7 @@ class PasswordGrantTest extends TestCase /** * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 10 */ public function testRespondToRequestBadCredentials() { From a93696271625a33ab60940d192e2f13dd38b7c11 Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 13 Nov 2018 18:27:03 +0000 Subject: [PATCH 063/104] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8b6ecba..0cb7da30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `issueAccessToken()` in the Abstract Grant no longer sets access token client, user ID or scopes. These values should already have been set when calling `getNewToken()` (PR #919) - No longer need to enable PKCE with `enableCodeExchangeProof` flag. Any client sending a code challenge will initiate PKCE checks. (PR #938) - Function `getClientEntity()` no longer performs client validation (PR #938) +- Password Grant now returns an invalid_grant error instead of invalid_credentials if a user cannot be validated (PR #967) ### Removed - `enableCodeExchangeProof` flag (PR #938) From ec8a663a8188a32ecc44afba069a95bd5674632b Mon Sep 17 00:00:00 2001 From: Chris Tanaskoski Date: Thu, 29 Nov 2018 09:28:36 +0100 Subject: [PATCH 064/104] Added test for respondToAccessTokenRequest using Http Basic Auth for client credentials --- tests/Grant/AuthCodeGrantTest.php | 70 +++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 3ea7105a..ff051087 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -606,6 +606,76 @@ class AuthCodeGrantTest extends TestCase $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } + public function testRespondToAccessTokenRequestUsingHttpBasicAuth() + { + $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', + [ + 'Authorization' => 'Basic Zm9vOmJhcg==', + ], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'redirect_uri' => 'http://foo/bar', + 'code' => $this->cryptStub->doEncrypt( + json_encode( + [ + 'auth_code_id' => uniqid(), + 'client_id' => 'foo', + 'expire_time' => time() + 3600, + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => 'http://foo/bar', + ] + ) + ), + ] + ); + + /** @var StubResponseType $response */ + $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + + $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); + $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); + } + public function testRespondToAccessTokenRequestForPublicClient() { $client = new ClientEntity(); From b6955a6c65c84e4b4c4faa360ae70ea9477ecd7c Mon Sep 17 00:00:00 2001 From: Chris Tanaskoski Date: Thu, 29 Nov 2018 09:33:12 +0100 Subject: [PATCH 065/104] Fixed respondToAccessTokenRequest such that it accepts client_id through request body and Http Basic Auth --- src/Grant/AbstractGrant.php | 33 ++++++++++++++++++++++++--------- src/Grant/AuthCodeGrant.php | 6 +----- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 4531a6a3..65ab16cf 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -171,15 +171,7 @@ abstract class AbstractGrant implements GrantTypeInterface */ protected function validateClient(ServerRequestInterface $request) { - list($basicAuthUser, $basicAuthPassword) = $this->getBasicAuthCredentials($request); - - $clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser); - - if (is_null($clientId)) { - throw OAuthServerException::invalidRequest('client_id'); - } - - $clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword); + list($clientId, $clientSecret) = $this->getClientCredentials($request); if ($this->clientRepository->validateClient($clientId, $clientSecret, $this->getIdentifier()) === false) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); @@ -199,6 +191,29 @@ abstract class AbstractGrant implements GrantTypeInterface return $client; } + /** + * Gets the client credentials from the request from the request body or + * the Http Basic Authorization header + * + * @param ServerRequestInterface $request + * + * @return array + */ + protected function getClientCredentials(ServerRequestInterface $request) + { + list($basicAuthUser, $basicAuthPassword) = $this->getBasicAuthCredentials($request); + + $clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser); + + if (is_null($clientId)) { + throw OAuthServerException::invalidRequest('client_id'); + } + + $clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword); + + return [$clientId, $clientSecret]; + } + /** * Validate redirectUri from the request. * If a redirect URI is provided ensure it matches what is pre-registered diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 974f8373..c340fe96 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -90,11 +90,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant ResponseTypeInterface $responseType, \DateInterval $accessTokenTTL ) { - $clientId = $this->getRequestParameter('client_id', $request, null); - - if ($clientId === null) { - throw OAuthServerException::invalidRequest('client_id'); - } + list($clientId) = $this->getClientCredentials($request); $client = $this->clientRepository->getClientEntity($clientId); From fd65bf9e544262949baa46fa2ad9ca2f4e75e787 Mon Sep 17 00:00:00 2001 From: sephster Date: Mon, 10 Dec 2018 22:51:58 +0000 Subject: [PATCH 066/104] Streamline tests --- tests/Grant/AuthCodeGrantTest.php | 42 ++++++++++--------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index ff051087..3abf232c 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -610,35 +610,30 @@ class AuthCodeGrantTest extends TestCase { $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('getScopeEntityByIdentifier')->willReturn(new 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( + $authCodeGrant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), - $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), + $refreshTokenRepositoryMock, 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')); + + $authCodeGrant->setClientRepository($clientRepositoryMock); + $authCodeGrant->setScopeRepository($scopeRepositoryMock); + $authCodeGrant->setAccessTokenRepository($accessTokenRepositoryMock); + $authCodeGrant->setEncryptionKey($this->cryptStub->getKey()); + $authCodeGrant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest( [], @@ -647,7 +642,7 @@ class AuthCodeGrantTest extends TestCase 'POST', 'php://input', [ - 'Authorization' => 'Basic Zm9vOmJhcg==', + //'Authorization' => 'Basic Zm9vOmJhcg==', ], [], [], @@ -670,7 +665,7 @@ class AuthCodeGrantTest extends TestCase ); /** @var StubResponseType $response */ - $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $response = $authCodeGrant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); @@ -1035,27 +1030,16 @@ class AuthCodeGrantTest extends TestCase public function testRespondToAccessTokenRequestExpiredCode() { - $client = new ClientEntity(); - $client->setIdentifier('foo'); - $client->setRedirectUri('http://foo/bar'); - $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $clientRepositoryMock->method('getClientEntity')->willReturn($client); - - $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); - - $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); - $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + $clientRepositoryMock->method('getClientEntity')->willReturn(new ClientEntity()); $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); + $grant->setClientRepository($clientRepositoryMock); - $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( From 894724c45b903c1865143c2f672a1b21cb8dd07a Mon Sep 17 00:00:00 2001 From: sephster Date: Mon, 10 Dec 2018 23:01:45 +0000 Subject: [PATCH 067/104] Remove invalid commenting --- 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 3abf232c..c7f08a1a 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -642,7 +642,7 @@ class AuthCodeGrantTest extends TestCase 'POST', 'php://input', [ - //'Authorization' => 'Basic Zm9vOmJhcg==', + 'Authorization' => 'Basic Zm9vOmJhcg==', ], [], [], From c2cd12e0b89b698285dafeb6a68367018827c60f Mon Sep 17 00:00:00 2001 From: sephster Date: Wed, 19 Dec 2018 12:54:26 +0000 Subject: [PATCH 068/104] Remove return types --- src/Entities/RefreshTokenEntityInterface.php | 2 +- src/Entities/TokenInterface.php | 2 +- src/Entities/Traits/AccessTokenTrait.php | 2 +- src/Entities/Traits/RefreshTokenTrait.php | 2 +- src/Entities/Traits/TokenEntityTrait.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Entities/RefreshTokenEntityInterface.php b/src/Entities/RefreshTokenEntityInterface.php index a197ce5e..551ad052 100644 --- a/src/Entities/RefreshTokenEntityInterface.php +++ b/src/Entities/RefreshTokenEntityInterface.php @@ -32,7 +32,7 @@ interface RefreshTokenEntityInterface * * @return DateTimeImmutable */ - public function getExpiryDateTime(): DateTimeImmutable; + public function getExpiryDateTime(); /** * Set the date time when the token expires. diff --git a/src/Entities/TokenInterface.php b/src/Entities/TokenInterface.php index a8d1496d..7b063e13 100644 --- a/src/Entities/TokenInterface.php +++ b/src/Entities/TokenInterface.php @@ -32,7 +32,7 @@ interface TokenInterface * * @return DateTimeImmutable */ - public function getExpiryDateTime(): DateTimeImmutable; + public function getExpiryDateTime(); /** * Set the date time when the token expires. diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php index 4fa7193a..872e8c18 100644 --- a/src/Entities/Traits/AccessTokenTrait.php +++ b/src/Entities/Traits/AccessTokenTrait.php @@ -70,7 +70,7 @@ trait AccessTokenTrait /** * @return DateTimeImmutable */ - abstract public function getExpiryDateTime(): DateTimeImmutable; + abstract public function getExpiryDateTime(); /** * @return string|int diff --git a/src/Entities/Traits/RefreshTokenTrait.php b/src/Entities/Traits/RefreshTokenTrait.php index e79cfdaa..f0f15444 100644 --- a/src/Entities/Traits/RefreshTokenTrait.php +++ b/src/Entities/Traits/RefreshTokenTrait.php @@ -45,7 +45,7 @@ trait RefreshTokenTrait * * @return DateTimeImmutable */ - public function getExpiryDateTime(): DateTimeImmutable + public function getExpiryDateTime() { return $this->expiryDateTime; } diff --git a/src/Entities/Traits/TokenEntityTrait.php b/src/Entities/Traits/TokenEntityTrait.php index 8ed2b63a..5275c462 100644 --- a/src/Entities/Traits/TokenEntityTrait.php +++ b/src/Entities/Traits/TokenEntityTrait.php @@ -60,7 +60,7 @@ trait TokenEntityTrait * * @return DateTimeImmutable */ - public function getExpiryDateTime(): DateTimeImmutable + public function getExpiryDateTime() { return $this->expiryDateTime; } From 5ed8e59ef3fd9bf57e4d456be3957ef97d5f2469 Mon Sep 17 00:00:00 2001 From: sephster Date: Wed, 19 Dec 2018 12:58:11 +0000 Subject: [PATCH 069/104] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8b6ecba..dfe3c16f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `issueAccessToken()` in the Abstract Grant no longer sets access token client, user ID or scopes. These values should already have been set when calling `getNewToken()` (PR #919) - No longer need to enable PKCE with `enableCodeExchangeProof` flag. Any client sending a code challenge will initiate PKCE checks. (PR #938) - Function `getClientEntity()` no longer performs client validation (PR #938) +- Use `DateTimeImmutable()` instead of `DateTime()`, `time()` instead of `(new DateTime())->getTimeStamp()`, and `DateTime::getTimeStamp()` instead of `DateTime::format('U')` (PR #963) ### Removed - `enableCodeExchangeProof` flag (PR #938) From 42df2d9c471b3579c3d783b62b641e616c35d220 Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Sat, 11 May 2019 13:35:24 -0500 Subject: [PATCH 070/104] Add typehints to OAuthServerException calls --- src/Exception/OAuthServerException.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 264f0e87..7c2ddae0 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -95,7 +95,7 @@ class OAuthServerException extends \Exception * * @param ServerRequestInterface $serverRequest */ - public function setServerRequest($serverRequest) + public function setServerRequest(ServerRequestInterface $serverRequest) { $this->serverRequest = $serverRequest; } @@ -137,7 +137,7 @@ class OAuthServerException extends \Exception * * @return static */ - public static function invalidClient($serverRequest) + public static function invalidClient(ServerRequestInterface $serverRequest) { $exception = new static('Client authentication failed', 4, 'invalid_client', 401); From 3fdfbe11f66ae109c507390345ff1c21e387726f Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Sat, 11 May 2019 13:37:22 -0500 Subject: [PATCH 071/104] Explicitly require ext-json Makes phpstorm happier; take or leave --- README.md | 2 +- composer.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d5fd215..85a5c590 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ The following versions of PHP are supported: * PHP 7.1 * PHP 7.2 -The `openssl` extension is also required. +The `openssl` and `json` extensions are also required. All HTTP messages passed to the server should be [PSR-7 compliant](https://www.php-fig.org/psr/psr-7/). This ensures interoperability with other packages and frameworks. diff --git a/composer.json b/composer.json index 48a95701..f8a2160f 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,8 @@ "league/event": "^2.1", "lcobucci/jwt": "^3.2.2", "psr/http-message": "^1.0.1", - "defuse/php-encryption": "^2.1" + "defuse/php-encryption": "^2.1", + "ext-json": "*" }, "require-dev": { "phpunit/phpunit": "^6.3 || ^7.0", From 4ecd3131c1e06d08d4405bd8ad72cefdd571fd51 Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Sat, 11 May 2019 13:44:58 -0500 Subject: [PATCH 072/104] Skip SHA256 verifier if system doesn't support sha256 --- src/Grant/AuthCodeGrant.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index a26ea6ec..4a11b59e 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -58,14 +58,13 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $this->authCodeTTL = $authCodeTTL; $this->refreshTokenTTL = new DateInterval('P1M'); - // SHOULD ONLY DO THIS IS SHA256 is supported - $s256Verifier = new S256Verifier(); - $plainVerifier = new PlainVerifier(); + if (in_array('sha256', hash_algos(), true)) { + $s256Verifier = new S256Verifier(); + $this->codeChallengeVerifiers[$s256Verifier->getMethod()] = $s256Verifier; + } - $this->codeChallengeVerifiers = [ - $s256Verifier->getMethod() => $s256Verifier, - $plainVerifier->getMethod() => $plainVerifier, - ]; + $plainVerifier = new PlainVerifier(); + $this->codeChallengeVerifiers[$plainVerifier->getMethod()] = $plainVerifier; } /** From 27d5c5ed8da27586de511cf40b8a44170a4b3ab7 Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Sat, 11 May 2019 14:05:39 -0500 Subject: [PATCH 073/104] Ensure unvalidated ClientEntity gets throw/emit if they return null In many cases, we validate client info before pulling from client itself from the repository, in which case it's safe to assume that you can grab the client once validation passes. However on implicit/auth code grants we don't have this guarantee due to non-confidential clients that just reference the client ID. In those cases the client may supply a client ID that doesn't exist, and we don't do a validation step before pulling it from the repo. The issue with that is that ClientRepository doesn't actually enforce returning a ClientInterface via typehint, nor does it even suggest an exception to throw if the client doesn't exist. So in most places we do an instanceof check after the repository returns and throw/emit an error event if the client doesn't exist. This approach ends up being a bit error-prone; we missed one case where we should've been doing this check: in the access token request on an auth code grant. We don't do enough validation beforehand to assume that the incoming request has an accurate client ID, so L96 could absolutely be a method call on a non-object. This commit centralizes the return-check-emit-throw logic so it's a one-liner for wherever we need it, including the access token request processor for auth code grants. --- src/Grant/AbstractGrant.php | 27 +++++++++++++++++++++++++++ src/Grant/AuthCodeGrant.php | 10 ++-------- src/Grant/ImplicitGrant.php | 8 +------- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index d64c9697..4836e8dd 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -193,6 +193,33 @@ abstract class AbstractGrant implements GrantTypeInterface return $client; } + /** + * Wrapper around ClientRepository::getClientEntity() that ensures we emit + * an event and throw an exception if the repo doesn't return a client + * entity. + * + * This is a bit of defensive coding because the interface contract + * doesn't actually enforce non-null returns/exception-on-no-client so + * getClientEntity might return null. By contrast, this method will + * always either return a ClientEntityInterface or throw. + * + * @param string $clientId + * @param ServerRequestInterface $request + * + * @return ClientEntityInterface + */ + protected function getClientEntityOrFail($clientId, ServerRequestInterface $request) + { + $client = $this->clientRepository->getClientEntity($clientId); + + if ($client instanceof ClientEntityInterface === false) { + $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); + throw OAuthServerException::invalidClient($request); + } + + return $client; + } + /** * Gets the client credentials from the request from the request body or * the Http Basic Authorization header diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index a26ea6ec..ea9cf37e 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -14,7 +14,6 @@ use DateTimeImmutable; use League\OAuth2\Server\CodeChallengeVerifiers\CodeChallengeVerifierInterface; use League\OAuth2\Server\CodeChallengeVerifiers\PlainVerifier; use League\OAuth2\Server\CodeChallengeVerifiers\S256Verifier; -use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; use League\OAuth2\Server\Entities\UserEntityInterface; use League\OAuth2\Server\Exception\OAuthServerException; @@ -94,7 +93,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant ) { list($clientId) = $this->getClientCredentials($request); - $client = $this->clientRepository->getClientEntity($clientId); + $client = $this->getClientEntityOrFail($clientId, $request); // Only validate the client if it is confidential if ($client->isConfidential()) { @@ -247,12 +246,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidRequest('client_id'); } - $client = $this->clientRepository->getClientEntity($clientId); - - if ($client instanceof ClientEntityInterface === false) { - $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient($request); - } + $client = $this->getClientEntityOrFail($clientId, $request); $redirectUri = $this->getQueryStringParameter('redirect_uri', $request); diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 5fc08cf1..8de22c36 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -10,7 +10,6 @@ namespace League\OAuth2\Server\Grant; use DateInterval; -use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\UserEntityInterface; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface; @@ -124,12 +123,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidRequest('client_id'); } - $client = $this->clientRepository->getClientEntity($clientId); - - if ($client instanceof ClientEntityInterface === false) { - $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient($request); - } + $client = $this->getClientEntityOrFail($clientId, $request); $redirectUri = $this->getQueryStringParameter('redirect_uri', $request); From 86d1581cd965973665ab2391bfee45d3adfb0732 Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 14 May 2019 15:57:13 +0100 Subject: [PATCH 074/104] Remove unused imports --- src/Grant/ImplicitGrant.php | 1 - src/ResponseTypes/BearerTokenResponse.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index c84a7c4d..dd6ef406 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -10,7 +10,6 @@ namespace League\OAuth2\Server\Grant; use DateInterval; -use DateTime; use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\UserEntityInterface; use League\OAuth2\Server\Exception\OAuthServerException; diff --git a/src/ResponseTypes/BearerTokenResponse.php b/src/ResponseTypes/BearerTokenResponse.php index 84f8939a..ddcadd63 100644 --- a/src/ResponseTypes/BearerTokenResponse.php +++ b/src/ResponseTypes/BearerTokenResponse.php @@ -11,7 +11,6 @@ namespace League\OAuth2\Server\ResponseTypes; -use DateTime; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; use Psr\Http\Message\ResponseInterface; From a1cf22a3a9868b2abe241fc6e874e007a743b1ec Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 14 May 2019 16:11:34 +0100 Subject: [PATCH 075/104] Remove duplicate setting of expirydatetime --- 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 a3870834..ec317b31 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -484,12 +484,12 @@ abstract class AbstractGrant implements GrantTypeInterface protected function issueRefreshToken(AccessTokenEntityInterface $accessToken) { $refreshToken = $this->refreshTokenRepository->getNewRefreshToken(); - $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add($this->refreshTokenTTL)); + if ($refreshToken === null) { return null; } - $refreshToken->setExpiryDateTime((new DateTime())->add($this->refreshTokenTTL)); + $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add($this->refreshTokenTTL)); $refreshToken->setAccessToken($accessToken); $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS; From 17923634bf33870b555e520ebede7b689181eff8 Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 14 May 2019 20:56:54 +0100 Subject: [PATCH 076/104] Set private keys in tests --- tests/Grant/AuthCodeGrantTest.php | 1 + tests/Grant/PasswordGrantTest.php | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 4ea0a5d0..07b220c5 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -771,6 +771,7 @@ class AuthCodeGrantTest extends TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest( [], diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index e8843c74..7fc99e83 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -108,6 +108,7 @@ class PasswordGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setDefaultScope(self::DEFAULT_SCOPE); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = new ServerRequest(); $serverRequest = $serverRequest->withParsedBody( From bd483d701bcc10669d86bf0c2dbfab2df5cf0981 Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 14 May 2019 21:26:17 +0100 Subject: [PATCH 077/104] Remove support for PHP 7.0 --- .travis.yml | 1 - CHANGELOG.md | 1 + README.md | 1 - composer.json | 2 +- 4 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 106cfdb4..b773c2aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,6 @@ env: - DEPENDENCIES="--prefer-lowest --prefer-stable" php: - - 7.0 - 7.1 - 7.2 - 7.3 diff --git a/CHANGELOG.md b/CHANGELOG.md index bf05c965..369e67ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Removed - `enableCodeExchangeProof` flag (PR #938) +- Support for PHP 7.0 ## [7.4.0] - released 2019-05-05 diff --git a/README.md b/README.md index 1c4b01c6..b51a18f3 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,6 @@ This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](ht The following versions of PHP are supported: -* PHP 7.0 * PHP 7.1 * PHP 7.2 * PHP 7.3 diff --git a/composer.json b/composer.json index 2df95811..8f7f2dca 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "homepage": "https://oauth2.thephpleague.com/", "license": "MIT", "require": { - "php": ">=7.0.0", + "php": ">=7.1.0", "ext-openssl": "*", "league/event": "^2.1", "lcobucci/jwt": "^3.2.2", From 28709f300fb8256e5d23b63eb81a6e68dc17e902 Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 14 May 2019 21:30:30 +0100 Subject: [PATCH 078/104] Add pull request number to changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 369e67ab..6add5685 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Removed - `enableCodeExchangeProof` flag (PR #938) -- Support for PHP 7.0 +- Support for PHP 7.0 (PR #1014) ## [7.4.0] - released 2019-05-05 From c5cfc0a371663d4156461d773f3c9ab3db9f0dd2 Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Sat, 11 May 2019 13:21:29 -0500 Subject: [PATCH 079/104] Remove dead Commercial Support link --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index b51a18f3..4c031453 100644 --- a/README.md +++ b/README.md @@ -85,10 +85,6 @@ Bugs and feature request are tracked on [GitHub](https://github.com/thephpleague If you have any questions about OAuth _please_ open a ticket here; please **don't** email the address below. -## Commercial Support - -If you would like help implementing this library into your existing platform, or would be interested in OAuth advice or training for you and your team please get in touch with [Glynde Labs](https://glyndelabs.com). - ## Security If you discover any security related issues, please email `hello@alexbilbie.com` instead of using the issue tracker. From bf75596989e57a6241071fa0d9b709416c5a90b4 Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Sat, 11 May 2019 13:22:29 -0500 Subject: [PATCH 080/104] Update security contact email to current maintainer --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c031453..48656296 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ If you have any questions about OAuth _please_ open a ticket here; please **don' ## Security -If you discover any security related issues, please email `hello@alexbilbie.com` instead of using the issue tracker. +If you discover any security related issues, please email `andrew@noexceptions.io` instead of using the issue tracker. ## License From 048e45d8cd719c0784202b7c65edc1200545c0ea Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Sat, 11 May 2019 13:28:02 -0500 Subject: [PATCH 081/104] Add more recent 7.x releases to changelog --- CHANGELOG.md | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6add5685..02646cdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,41 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed - Fix issue where AuthorizationServer is not stateless as ResponseType could store state of a previous request (PR #960) +## [7.4.0] - released 2019-05-05 + +### Changed +- RefreshTokenRepository can now return null, allowing refresh tokens to be optional. (PR #649) + +## [7.3.3] - released 2019-03-29 + +### Added +- Added `error_description` to the error payload to improve standards compliance. The contents of this are copied from the existing `message` value. (PR #1006) + +### Deprecated +- Error payload will not issue `message` value in the next major release (PR #1006) + +## [7.3.2] - released 2018-11-21 + +### Fixed +- Revert setting keys on response type to be inside `getResponseType()` function instead of AuthorizationServer constructor (PR #969) + +## [7.3.1] - released 2018-11-15 + +### Fixed +- Fix issue with previous release where interface had changed for the AuthorizationServer. Reverted to the previous interface while maintaining functionality changes (PR #970) + +## [7.3.0] - released 2018-11-13 + +### Changed +- Moved the `finalizeScopes()` call from `validateAuthorizationRequest` method to the `completeAuthorizationRequest` method so it is called just before the access token is issued (PR #923) + +### Added +- Added a ScopeTrait to provide an implementation for jsonSerialize (PR #952) +- Ability to nest exceptions (PR #965) + +### Fixed +- Fix issue where AuthorizationServer is not stateless as ResponseType could store state of a previous request (PR #960) + ## [7.2.0] - released 2018-06-23 ### Changed @@ -460,7 +495,8 @@ Version 5 is a complete code rewrite. - First major release -[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/7.4.0...HEAD +[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.0.0...HEAD +[8.0.0]: https://github.com/thephpleague/oauth2-server/compare/7.4.0...8.0.0 [7.4.0]: https://github.com/thephpleague/oauth2-server/compare/7.3.3...7.4.0 [7.3.3]: https://github.com/thephpleague/oauth2-server/compare/7.3.2...7.3.3 [7.3.2]: https://github.com/thephpleague/oauth2-server/compare/7.3.1...7.3.2 From 8b5841870f722cbc166de9262439d27d5820c97b Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Sat, 11 May 2019 13:32:11 -0500 Subject: [PATCH 082/104] Add more detail/precision to 8.0.0 changelog around breaking changes --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02646cdd..83f54cd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added -- Flag, `requireCodeChallengeForPublicClients`, used to reject public clients that do not provide a code challenge for the Auth Code Grant (PR #938) +- Flag, `requireCodeChallengeForPublicClients`, used to reject public clients that do not provide a code challenge for the Auth Code Grant; use AuthCodeGrant::disableRequireCodeCallengeForPublicClients() to turn off this requirement (PR #938) - Public clients can now use the Auth Code Grant (PR #938) -- `isConfidential` property added to `ClientEntity` to identify type of client (PR #938) +- `isConfidential` getter added to `ClientEntity` to identify type of client (PR #938) - Function `validateClient()` added to validate clients which was previously performed by the `getClientEntity()` function (PR #938) ### Changed -- Replace `convertToJWT()` interface with a more generic `__toString()` to improve extensibility (PR #874) +- Replace `convertToJWT()` interface with a more generic `__toString()` to improve extensibility; AccessTokenEntityInterface now requires `setPrivateKey(CryptKey $privateKey)` so `__toString()` has everything it needs to work (PR #874) - The `invalidClient()` function accepts a PSR-7 compliant `$serverRequest` argument to avoid accessing the `$_SERVER` global variable and improve testing (PR #899) - `issueAccessToken()` in the Abstract Grant no longer sets access token client, user ID or scopes. These values should already have been set when calling `getNewToken()` (PR #919) - No longer need to enable PKCE with `enableCodeExchangeProof` flag. Any client sending a code challenge will initiate PKCE checks. (PR #938) From e0ee2445060f4f0213b3823a4e15340defb80d72 Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 18 Jun 2019 22:30:17 +0100 Subject: [PATCH 083/104] Remove duplicate entries from changelog --- CHANGELOG.md | 38 +------------------------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83f54cd2..8afc6606 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,41 +60,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed - Fix issue where AuthorizationServer is not stateless as ResponseType could store state of a previous request (PR #960) -## [7.4.0] - released 2019-05-05 - -### Changed -- RefreshTokenRepository can now return null, allowing refresh tokens to be optional. (PR #649) - -## [7.3.3] - released 2019-03-29 - -### Added -- Added `error_description` to the error payload to improve standards compliance. The contents of this are copied from the existing `message` value. (PR #1006) - -### Deprecated -- Error payload will not issue `message` value in the next major release (PR #1006) - -## [7.3.2] - released 2018-11-21 - -### Fixed -- Revert setting keys on response type to be inside `getResponseType()` function instead of AuthorizationServer constructor (PR #969) - -## [7.3.1] - released 2018-11-15 - -### Fixed -- Fix issue with previous release where interface had changed for the AuthorizationServer. Reverted to the previous interface while maintaining functionality changes (PR #970) - -## [7.3.0] - released 2018-11-13 - -### Changed -- Moved the `finalizeScopes()` call from `validateAuthorizationRequest` method to the `completeAuthorizationRequest` method so it is called just before the access token is issued (PR #923) - -### Added -- Added a ScopeTrait to provide an implementation for jsonSerialize (PR #952) -- Ability to nest exceptions (PR #965) - -### Fixed -- Fix issue where AuthorizationServer is not stateless as ResponseType could store state of a previous request (PR #960) - ## [7.2.0] - released 2018-06-23 ### Changed @@ -495,8 +460,7 @@ Version 5 is a complete code rewrite. - First major release -[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.0.0...HEAD -[8.0.0]: https://github.com/thephpleague/oauth2-server/compare/7.4.0...8.0.0 +[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/7.4.0...HEAD [7.4.0]: https://github.com/thephpleague/oauth2-server/compare/7.3.3...7.4.0 [7.3.3]: https://github.com/thephpleague/oauth2-server/compare/7.3.2...7.3.3 [7.3.2]: https://github.com/thephpleague/oauth2-server/compare/7.3.1...7.3.2 From c7d047f7f56b9e594401146e69ac061648a44551 Mon Sep 17 00:00:00 2001 From: sephster Date: Sun, 23 Jun 2019 17:35:24 +0100 Subject: [PATCH 084/104] Remove extra line spaces --- src/Grant/AuthCodeGrant.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 51c9e90e..e1b2b981 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -112,11 +112,8 @@ class AuthCodeGrant extends AbstractAuthorizeGrant try { $authCodePayload = json_decode($this->decrypt($encryptedAuthCode)); - $this->validateAuthorizationCode($authCodePayload, $client, $request); - - $scopes = $this->scopeRepository->finalizeScopes( $this->validateScopes($authCodePayload->scopes), $this->getIdentifier(), From 0db54cf1e58a45bc78d23b973fd37cca547c6e27 Mon Sep 17 00:00:00 2001 From: sephster Date: Sun, 23 Jun 2019 17:40:39 +0100 Subject: [PATCH 085/104] Reinstate use for ClientEntityInterface --- src/Grant/AuthCodeGrant.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index e1b2b981..a11828d7 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -15,6 +15,7 @@ use Exception; use League\OAuth2\Server\CodeChallengeVerifiers\CodeChallengeVerifierInterface; use League\OAuth2\Server\CodeChallengeVerifiers\PlainVerifier; use League\OAuth2\Server\CodeChallengeVerifiers\S256Verifier; +use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\UserEntityInterface; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface; From 012808f094d2f7f2852b103078b16e021c792f9b Mon Sep 17 00:00:00 2001 From: sephster Date: Sun, 23 Jun 2019 17:56:32 +0100 Subject: [PATCH 086/104] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8afc6606..751379cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Public clients can now use the Auth Code Grant (PR #938) - `isConfidential` getter added to `ClientEntity` to identify type of client (PR #938) - Function `validateClient()` added to validate clients which was previously performed by the `getClientEntity()` function (PR #938) +- Add a new function to the AbstractGrant class called `getClientEntityOrFail()`. This is a wrapper around the `getClientEntity()` function that ensures we emit and throw an exception if the repo doesn't return a client entity. (PR #1010) ### Changed - Replace `convertToJWT()` interface with a more generic `__toString()` to improve extensibility; AccessTokenEntityInterface now requires `setPrivateKey(CryptKey $privateKey)` so `__toString()` has everything it needs to work (PR #874) From e3b23fa8265994e42cf975729436bc4212fb7b56 Mon Sep 17 00:00:00 2001 From: sephster Date: Thu, 27 Jun 2019 12:54:22 +0100 Subject: [PATCH 087/104] Update dependencies and fix PHPUnit tests --- composer.json | 16 +- tests/AuthorizationServerTest.php | 26 +- .../BearerTokenValidatorTest.php | 7 +- tests/Grant/AbstractGrantTest.php | 53 ++-- tests/Grant/AuthCodeGrantTest.php | 289 ++++++------------ tests/Grant/ImplicitGrantTest.php | 199 ++++-------- tests/Grant/PasswordGrantTest.php | 59 ++-- tests/Grant/RefreshTokenGrantTest.php | 128 ++++---- .../ResponseTypes/BearerResponseTypeTest.php | 6 +- tests/Utils/CryptKeyTest.php | 5 +- tests/Utils/CryptTraitTest.php | 2 +- 11 files changed, 288 insertions(+), 502 deletions(-) diff --git a/composer.json b/composer.json index 8f7f2dca..2635dcee 100644 --- a/composer.json +++ b/composer.json @@ -6,18 +6,18 @@ "require": { "php": ">=7.1.0", "ext-openssl": "*", - "league/event": "^2.1", - "lcobucci/jwt": "^3.2.2", + "league/event": "^2.2", + "lcobucci/jwt": "^3.3.1", "psr/http-message": "^1.0.1", - "defuse/php-encryption": "^2.1", + "defuse/php-encryption": "^2.2.1", "ext-json": "*" }, "require-dev": { - "phpunit/phpunit": "^6.3 || ^7.0", - "zendframework/zend-diactoros": "^1.3.2", - "phpstan/phpstan": "^0.9.2", - "phpstan/phpstan-phpunit": "^0.9.4", - "phpstan/phpstan-strict-rules": "^0.9.0", + "phpunit/phpunit": "^7.5.13 || ^8.2.3", + "zendframework/zend-diactoros": "^2.1.2", + "phpstan/phpstan": "^0.11.8", + "phpstan/phpstan-phpunit": "^0.11.2", + "phpstan/phpstan-strict-rules": "^0.11.1", "roave/security-advisories": "dev-master" }, "repositories": [ diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index f88cd733..9ba15749 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -31,7 +31,7 @@ class AuthorizationServerTest extends TestCase { const DEFAULT_SCOPE = 'basic'; - public function setUp() + public function setUp(): void { // Make sure the keys have the correct permissions. chmod(__DIR__ . '/Stubs/private.key', 0600); @@ -326,10 +326,6 @@ class AuthorizationServerTest extends TestCase } } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 2 - */ public function testValidateAuthorizationRequestUnregistered() { $server = new AuthorizationServer( @@ -340,19 +336,13 @@ class AuthorizationServerTest extends TestCase 'file://' . __DIR__ . '/Stubs/public.key' ); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - $headers = [], - $cookies = [], - $queryParams = [ - 'response_type' => 'code', - 'client_id' => 'foo', - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + 'client_id' => 'foo', + ]); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(2); $server->validateAuthorizationRequest($request); } diff --git a/tests/AuthorizationValidators/BearerTokenValidatorTest.php b/tests/AuthorizationValidators/BearerTokenValidatorTest.php index 801846cb..25c7b188 100644 --- a/tests/AuthorizationValidators/BearerTokenValidatorTest.php +++ b/tests/AuthorizationValidators/BearerTokenValidatorTest.php @@ -11,10 +11,6 @@ use Zend\Diactoros\ServerRequest; class BearerTokenValidatorTest extends TestCase { - /** - * @expectedException League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 9 - */ public function testThrowExceptionWhenAccessTokenIsNotSigned() { $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -35,6 +31,9 @@ class BearerTokenValidatorTest extends TestCase $request = new ServerRequest(); $request = $request->withHeader('authorization', sprintf('Bearer %s', $unsignedJwt)); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(9); + $bearerTokenValidator->validateAuthorization($request); } } diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index ec8d3f47..ea0afbca 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -148,9 +148,7 @@ class AbstractGrantTest extends TestCase $this->assertEquals($client, $result); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - */ + public function testValidateClientMissingClientId() { $client = new ClientEntity(); @@ -167,12 +165,11 @@ class AbstractGrantTest extends TestCase $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $validateClientMethod->invoke($grantMock, $serverRequest, true, true); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - */ public function testValidateClientMissingClientSecret() { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -192,12 +189,11 @@ class AbstractGrantTest extends TestCase $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $validateClientMethod->invoke($grantMock, $serverRequest, true, true); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - */ public function testValidateClientInvalidClientSecret() { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -218,12 +214,11 @@ class AbstractGrantTest extends TestCase $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $validateClientMethod->invoke($grantMock, $serverRequest, true, true); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - */ public function testValidateClientInvalidRedirectUri() { $client = new ClientEntity(); @@ -246,12 +241,11 @@ class AbstractGrantTest extends TestCase $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $validateClientMethod->invoke($grantMock, $serverRequest, true, true); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - */ public function testValidateClientInvalidRedirectUriArray() { $client = new ClientEntity(); @@ -274,12 +268,11 @@ class AbstractGrantTest extends TestCase $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $validateClientMethod->invoke($grantMock, $serverRequest, true, true); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - */ public function testValidateClientBadClient() { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -300,6 +293,8 @@ class AbstractGrantTest extends TestCase $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $validateClientMethod->invoke($grantMock, $serverRequest, true); } @@ -439,8 +434,7 @@ class AbstractGrantTest extends TestCase $method = $abstractGrantReflection->getMethod('getQueryStringParameter'); $method->setAccessible(true); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withQueryParams([ + $serverRequest = (new ServerRequest())->withQueryParams([ 'foo' => 'bar', ]); @@ -461,9 +455,6 @@ class AbstractGrantTest extends TestCase $this->assertEquals([$scope], $grantMock->validateScopes('basic ')); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - */ public function testValidateScopesBadScope() { $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); @@ -473,6 +464,8 @@ class AbstractGrantTest extends TestCase $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setScopeRepository($scopeRepositoryMock); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $grantMock->validateScopes('basic '); } @@ -484,7 +477,7 @@ class AbstractGrantTest extends TestCase $method = $abstractGrantReflection->getMethod('generateUniqueIdentifier'); $method->setAccessible(true); - $this->assertInternalType('string', $method->invoke($grantMock)); + $this->assertIsString($method->invoke($grantMock)); } public function testCanRespondToAuthorizationRequest() @@ -493,21 +486,21 @@ class AbstractGrantTest extends TestCase $this->assertFalse($grantMock->canRespondToAuthorizationRequest(new ServerRequest())); } - /** - * @expectedException \LogicException - */ public function testValidateAuthorizationRequest() { $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + + $this->expectException(\LogicException::class); + $grantMock->validateAuthorizationRequest(new ServerRequest()); } - /** - * @expectedException \LogicException - */ public function testCompleteAuthorizationRequest() { $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + + $this->expectException(\LogicException::class); + $grantMock->completeAuthorizationRequest(new AuthorizationRequest()); } } diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 07b220c5..2d3098c4 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -26,6 +26,7 @@ use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; +use Zend\Diactoros\ServerRequestFactory; class AuthCodeGrantTest extends TestCase { @@ -40,7 +41,7 @@ class AuthCodeGrantTest extends TestCase const CODE_CHALLENGE = 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM'; - public function setUp() + public function setUp(): void { $this->cryptStub = new CryptTraitStub(); } @@ -200,9 +201,6 @@ class AuthCodeGrantTest extends TestCase $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - */ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooShort() { $client = new ClientEntity(); @@ -218,28 +216,18 @@ class AuthCodeGrantTest extends TestCase $grant->setClientRepository($clientRepositoryMock); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - [], - [], - [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => str_repeat('A', 42), - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code_challenge' => str_repeat('A', 42), + ]); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $grant->validateAuthorizationRequest($request); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - */ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLong() { $client = new ClientEntity(); @@ -255,28 +243,18 @@ class AuthCodeGrantTest extends TestCase $grant->setClientRepository($clientRepositoryMock); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - [], - [], - [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => str_repeat('A', 129), - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code_challenge' => str_repeat('A', 129), + ]); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $grant->validateAuthorizationRequest($request); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - */ public function testValidateAuthorizationRequestCodeChallengeInvalidCharacters() { $client = new ClientEntity(); @@ -292,29 +270,18 @@ class AuthCodeGrantTest extends TestCase $grant->setClientRepository($clientRepositoryMock); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - [], - [], - [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => str_repeat('A', 42) . '!', - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code_challenge' => str_repeat('A', 42) . '!', + ]); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $grant->validateAuthorizationRequest($request); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 3 - */ public function testValidateAuthorizationRequestMissingClientId() { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -326,26 +293,16 @@ class AuthCodeGrantTest extends TestCase ); $grant->setClientRepository($clientRepositoryMock); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - $headers = [], - $cookies = [], - $queryParams = [ - 'response_type' => 'code', - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + ]); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(3); $grant->validateAuthorizationRequest($request); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 4 - */ public function testValidateAuthorizationRequestInvalidClientId() { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -358,27 +315,17 @@ class AuthCodeGrantTest extends TestCase ); $grant->setClientRepository($clientRepositoryMock); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - $headers = [], - $cookies = [], - $queryParams = [ - 'response_type' => 'code', - 'client_id' => 'foo', - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + 'client_id' => 'foo', + ]); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 4 - */ public function testValidateAuthorizationRequestBadRedirectUriString() { $client = new ClientEntity(); @@ -393,28 +340,18 @@ class AuthCodeGrantTest extends TestCase ); $grant->setClientRepository($clientRepositoryMock); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - [], - [], - [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://bar', - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar', + ]); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 4 - */ public function testValidateAuthorizationRequestBadRedirectUriArray() { $client = new ClientEntity(); @@ -429,28 +366,18 @@ class AuthCodeGrantTest extends TestCase ); $grant->setClientRepository($clientRepositoryMock); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - [], - [], - [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://bar', - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar', + ]); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 3 - */ public function testValidateAuthorizationRequestInvalidCodeChallengeMethod() { $client = new ClientEntity(); @@ -472,22 +399,16 @@ class AuthCodeGrantTest extends TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setDefaultScope(self::DEFAULT_SCOPE); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - [], - [], - [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => 'foobar', - 'code_challenge_method' => 'foo', - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code_challenge' => 'foobar', + 'code_challenge_method' => 'foo', + ]); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(3); $grant->validateAuthorizationRequest($request); } @@ -513,10 +434,6 @@ class AuthCodeGrantTest extends TestCase $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 9 - */ public function testCompleteAuthorizationRequestDenied() { $authRequest = new AuthorizationRequest(); @@ -535,6 +452,9 @@ class AuthCodeGrantTest extends TestCase ); $grant->setEncryptionKey($this->cryptStub->getKey()); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(9); + $grant->completeAuthorizationRequest($authRequest); } @@ -954,10 +874,6 @@ class AuthCodeGrantTest extends TestCase $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 3 - */ public function testRespondToAccessTokenRequestMissingRedirectUri() { $client = new ClientEntity(); @@ -999,13 +915,12 @@ class AuthCodeGrantTest extends TestCase ] ); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(3); + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 3 - */ public function testRespondToAccessTokenRequestRedirectUriMismatch() { $client = new ClientEntity(); @@ -1048,13 +963,12 @@ class AuthCodeGrantTest extends TestCase ] ); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(3); + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 3 - */ public function testRespondToAccessTokenRequestMissingCode() { $client = new ClientEntity(); @@ -1093,6 +1007,9 @@ class AuthCodeGrantTest extends TestCase ] ); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(3); + /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } @@ -1711,10 +1628,6 @@ class AuthCodeGrantTest extends TestCase $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 7 - */ public function testAuthCodeRepositoryFailToPersist() { $authRequest = new AuthorizationRequest(); @@ -1734,13 +1647,12 @@ class AuthCodeGrantTest extends TestCase ); $grant->setEncryptionKey($this->cryptStub->getKey()); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(7); + $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } - /** - * @expectedException \League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException - * @expectedExceptionCode 100 - */ public function testAuthCodeRepositoryFailToPersistUniqueNoInfiniteLoop() { $authRequest = new AuthorizationRequest(); @@ -1759,6 +1671,9 @@ class AuthCodeGrantTest extends TestCase new DateInterval('PT10M') ); + $this->expectException(\League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException::class); + $this->expectExceptionCode(100); + $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } @@ -1831,10 +1746,6 @@ class AuthCodeGrantTest extends TestCase $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 7 - */ public function testRefreshTokenRepositoryFailToPersist() { $client = new ClientEntity(); @@ -1896,6 +1807,9 @@ class AuthCodeGrantTest extends TestCase ] ); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(7); + /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); @@ -1903,10 +1817,6 @@ class AuthCodeGrantTest extends TestCase $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } - /** - * @expectedException \League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException - * @expectedExceptionCode 100 - */ public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop() { $client = new ClientEntity(); @@ -1968,6 +1878,9 @@ class AuthCodeGrantTest extends TestCase ] ); + $this->expectException(\League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException::class); + $this->expectExceptionCode(100); + /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); @@ -1975,9 +1888,6 @@ class AuthCodeGrantTest extends TestCase $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } - /** - * @expectedException \LogicException - */ public function testCompleteAuthorizationRequestNoUser() { $grant = new AuthCodeGrant( @@ -1986,6 +1896,8 @@ class AuthCodeGrantTest extends TestCase new DateInterval('PT10M') ); + $this->expectException(\LogicException::class); + $grant->completeAuthorizationRequest(new AuthorizationRequest()); } @@ -2011,20 +1923,11 @@ class AuthCodeGrantTest extends TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setDefaultScope(self::DEFAULT_SCOPE); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - [], - [], - [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + ]); $this->expectException(OAuthServerException::class); $this->expectExceptionCode(3); diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index b63c3ffa..558923b5 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -31,7 +31,7 @@ class ImplicitGrantTest extends TestCase */ protected $cryptStub; - public function setUp() + public function setUp(): void { $this->cryptStub = new CryptTraitStub(); } @@ -51,12 +51,12 @@ class ImplicitGrantTest extends TestCase ); } - /** - * @expectedException \LogicException - */ public function testRespondToAccessTokenRequest() { $grant = new ImplicitGrant(new DateInterval('PT10M')); + + $this->expectException(\LogicException::class); + $grant->respondToAccessTokenRequest( new ServerRequest(), new StubResponseType(), @@ -68,19 +68,10 @@ class ImplicitGrantTest extends TestCase { $grant = new ImplicitGrant(new DateInterval('PT10M')); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - $headers = [], - $cookies = [], - $queryParams = [ - 'response_type' => 'token', - 'client_id' => 'foo', - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'token', + 'client_id' => 'foo', + ]); $this->assertTrue($grant->canRespondToAuthorizationRequest($request)); } @@ -101,20 +92,11 @@ class ImplicitGrantTest extends TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setDefaultScope(self::DEFAULT_SCOPE); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - $headers = [], - $cookies = [], - $queryParams = [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + ]); $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); } @@ -135,28 +117,15 @@ class ImplicitGrantTest extends TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setDefaultScope(self::DEFAULT_SCOPE); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - $headers = [], - $cookies = [], - $queryParams = [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + ]); $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 3 - */ public function testValidateAuthorizationRequestMissingClientId() { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -164,26 +133,14 @@ class ImplicitGrantTest extends TestCase $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setClientRepository($clientRepositoryMock); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - $headers = [], - $cookies = [], - $queryParams = [ - 'response_type' => 'code', - ] - ); + $request = (new ServerRequest())->withQueryParams(['response_type' => 'code']); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(3); $grant->validateAuthorizationRequest($request); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 4 - */ public function testValidateAuthorizationRequestInvalidClientId() { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -192,27 +149,17 @@ class ImplicitGrantTest extends TestCase $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setClientRepository($clientRepositoryMock); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - $headers = [], - $cookies = [], - $queryParams = [ - 'response_type' => 'code', - 'client_id' => 'foo', - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + 'client_id' => 'foo', + ]); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 4 - */ public function testValidateAuthorizationRequestBadRedirectUriString() { $client = new ClientEntity(); @@ -223,28 +170,18 @@ class ImplicitGrantTest extends TestCase $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setClientRepository($clientRepositoryMock); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - $headers = [], - $cookies = [], - $queryParams = [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://bar', - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar', + ]); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 4 - */ public function testValidateAuthorizationRequestBadRedirectUriArray() { $client = new ClientEntity(); @@ -255,20 +192,14 @@ class ImplicitGrantTest extends TestCase $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setClientRepository($clientRepositoryMock); - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - $headers = [], - $cookies = [], - $queryParams = [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://bar', - ] - ); + $request = (new ServerRequest())->withQueryParams([ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar', + ]); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); } @@ -302,10 +233,6 @@ class ImplicitGrantTest extends TestCase $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 9 - */ public function testCompleteAuthorizationRequestDenied() { $authRequest = new AuthorizationRequest(); @@ -326,6 +253,9 @@ class ImplicitGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(9); + $grant->completeAuthorizationRequest($authRequest); } @@ -360,10 +290,6 @@ class ImplicitGrantTest extends TestCase $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 7 - */ public function testAccessTokenRepositoryFailToPersist() { $authRequest = new AuthorizationRequest(); @@ -385,13 +311,12 @@ class ImplicitGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(7); + $grant->completeAuthorizationRequest($authRequest); } - /** - * @expectedException \League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException - * @expectedExceptionCode 100 - */ public function testAccessTokenRepositoryFailToPersistUniqueNoInfiniteLoop() { $authRequest = new AuthorizationRequest(); @@ -413,34 +338,38 @@ class ImplicitGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); + $this->expectException(\League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException::class); + $this->expectExceptionCode(100); + $grant->completeAuthorizationRequest($authRequest); } - /** - * @expectedException \LogicException - */ public function testSetRefreshTokenTTL() { $grant = new ImplicitGrant(new DateInterval('PT10M')); + + $this->expectException(\LogicException::class); + $grant->setRefreshTokenTTL(new DateInterval('PT10M')); } - /** - * @expectedException \LogicException - */ public function testSetRefreshTokenRepository() { $grant = new ImplicitGrant(new DateInterval('PT10M')); + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + + $this->expectException(\LogicException::class); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); } - /** - * @expectedException \LogicException - */ public function testCompleteAuthorizationRequestNoUser() { $grant = new ImplicitGrant(new DateInterval('PT10M')); + + $this->expectException(\LogicException::class); + $grant->completeAuthorizationRequest(new AuthorizationRequest()); } } diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index 7fc99e83..4b7de98a 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -127,9 +127,6 @@ class PasswordGrantTest extends TestCase $this->assertNull($responseType->getRefreshToken()); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - */ public function testRespondToRequestMissingUsername() { $client = new ClientEntity(); @@ -146,21 +143,18 @@ class PasswordGrantTest extends TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - ] - ); + $serverRequest = (new ServerRequest())->withQueryParams([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + ]); $responseType = new StubResponseType(); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - */ public function testRespondToRequestMissingPassword() { $client = new ClientEntity(); @@ -177,23 +171,19 @@ class PasswordGrantTest extends TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'username' => 'alex', - ] - ); + $serverRequest = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'username' => 'alex', + ]); $responseType = new StubResponseType(); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 10 - */ public function testRespondToRequestBadCredentials() { $client = new ClientEntity(); @@ -211,17 +201,18 @@ class PasswordGrantTest extends TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'username' => 'alex', - 'password' => 'whisky', - ] - ); + $serverRequest = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'username' => 'alex', + 'password' => 'whisky', + ]); $responseType = new StubResponseType(); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(10); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } } diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index e895f16b..632cbe80 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -27,7 +27,7 @@ class RefreshTokenGrantTest extends TestCase */ protected $cryptStub; - public function setUp() + public function setUp(): void { $this->cryptStub = new CryptTraitStub(); } @@ -209,10 +209,6 @@ class RefreshTokenGrantTest extends TestCase $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 5 - */ public function testRespondToUnexpectedScope() { $client = new ClientEntity(); @@ -251,24 +247,21 @@ class RefreshTokenGrantTest extends TestCase ) ); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, - 'scope' => 'foobar', - ] - ); + $serverRequest = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + 'scope' => 'foobar', + ]); $responseType = new StubResponseType(); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(5); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 3 - */ public function testRespondToRequestMissingOldToken() { $client = new ClientEntity(); @@ -285,22 +278,19 @@ class RefreshTokenGrantTest extends TestCase $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - ] - ); + $serverRequest = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + ]); $responseType = new StubResponseType(); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(3); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 8 - */ public function testRespondToRequestInvalidOldToken() { $client = new ClientEntity(); @@ -319,23 +309,20 @@ class RefreshTokenGrantTest extends TestCase $oldRefreshToken = 'foobar'; - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, - ] - ); + $serverRequest = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + ]); $responseType = new StubResponseType(); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(8); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 8 - */ public function testRespondToRequestClientMismatch() { $client = new ClientEntity(); @@ -368,23 +355,20 @@ class RefreshTokenGrantTest extends TestCase ) ); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, - ] - ); + $serverRequest = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + ]); $responseType = new StubResponseType(); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(8); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 8 - */ public function testRespondToRequestExpiredToken() { $client = new ClientEntity(); @@ -414,23 +398,20 @@ class RefreshTokenGrantTest extends TestCase ) ); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, - ] - ); + $serverRequest = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + ]); $responseType = new StubResponseType(); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(8); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 8 - */ public function testRespondToRequestRevokedToken() { $client = new ClientEntity(); @@ -461,16 +442,17 @@ class RefreshTokenGrantTest extends TestCase ) ); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, - ] - ); + $serverRequest = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + ]); $responseType = new StubResponseType(); + + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectExceptionCode(8); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } } diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 5eddfdef..97bc37aa 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -57,7 +57,7 @@ class BearerResponseTypeTest extends TestCase $response->getBody()->rewind(); $json = json_decode($response->getBody()->getContents()); - $this->assertAttributeEquals('Bearer', 'token_type', $json); + $this->assertEquals('Bearer', $json->token_type); $this->assertObjectHasAttribute('expires_in', $json); $this->assertObjectHasAttribute('access_token', $json); $this->assertObjectHasAttribute('refresh_token', $json); @@ -100,13 +100,13 @@ class BearerResponseTypeTest extends TestCase $response->getBody()->rewind(); $json = json_decode($response->getBody()->getContents()); - $this->assertAttributeEquals('Bearer', 'token_type', $json); + $this->assertEquals('Bearer', $json->token_type); $this->assertObjectHasAttribute('expires_in', $json); $this->assertObjectHasAttribute('access_token', $json); $this->assertObjectHasAttribute('refresh_token', $json); $this->assertObjectHasAttribute('foo', $json); - $this->assertAttributeEquals('bar', 'foo', $json); + $this->assertEquals('bar', $json->foo); } public function testDetermineAccessTokenInHeaderValidToken() diff --git a/tests/Utils/CryptKeyTest.php b/tests/Utils/CryptKeyTest.php index 9f3f337c..1fe79d20 100644 --- a/tests/Utils/CryptKeyTest.php +++ b/tests/Utils/CryptKeyTest.php @@ -7,11 +7,10 @@ use PHPUnit\Framework\TestCase; class CryptKeyTest extends TestCase { - /** - * @expectedException \LogicException - */ public function testNoFile() { + $this->expectException(\LogicException::class); + new CryptKey('undefined file'); } diff --git a/tests/Utils/CryptTraitTest.php b/tests/Utils/CryptTraitTest.php index c517cec2..06d0b219 100644 --- a/tests/Utils/CryptTraitTest.php +++ b/tests/Utils/CryptTraitTest.php @@ -10,7 +10,7 @@ class CryptTraitTest extends TestCase { protected $cryptStub; - protected function setUp() + protected function setUp(): void { $this->cryptStub = new CryptTraitStub(); } From 51b97f87c17c57e00ad15733bdcc1d2ddcf6cd1d Mon Sep 17 00:00:00 2001 From: sephster Date: Thu, 27 Jun 2019 13:15:37 +0100 Subject: [PATCH 088/104] Fix issues setting attributes on requests --- .../BearerTokenValidatorTest.php | 3 +- tests/Grant/AbstractGrantTest.php | 61 +++++++------------ tests/Grant/ClientCredentialsGrantTest.php | 11 ++-- tests/Grant/PasswordGrantTest.php | 30 ++++----- tests/Grant/RefreshTokenGrantTest.php | 21 +++---- .../ResourceServerMiddlewareTest.php | 9 +-- .../ResponseTypes/BearerResponseTypeTest.php | 15 ++--- 7 files changed, 55 insertions(+), 95 deletions(-) diff --git a/tests/AuthorizationValidators/BearerTokenValidatorTest.php b/tests/AuthorizationValidators/BearerTokenValidatorTest.php index 25c7b188..69e56995 100644 --- a/tests/AuthorizationValidators/BearerTokenValidatorTest.php +++ b/tests/AuthorizationValidators/BearerTokenValidatorTest.php @@ -28,8 +28,7 @@ class BearerTokenValidatorTest extends TestCase ->set('scopes', 'scope1 scope2 scope3 scope4') ->getToken(); - $request = new ServerRequest(); - $request = $request->withHeader('authorization', sprintf('Bearer %s', $unsignedJwt)); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $unsignedJwt)); $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(9); diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index ea0afbca..a4b94264 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -30,8 +30,7 @@ class AbstractGrantTest extends TestCase $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $abstractGrantReflection = new \ReflectionClass($grantMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withHeader('Authorization', 'Basic ' . base64_encode('Open:Sesame')); + $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . base64_encode('Open:Sesame')); $basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials'); $basicAuthMethod->setAccessible(true); @@ -44,8 +43,7 @@ class AbstractGrantTest extends TestCase $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $abstractGrantReflection = new \ReflectionClass($grantMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withHeader('Authorization', 'Basic ' . base64_encode('Open:')); + $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . base64_encode('Open:')); $basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials'); $basicAuthMethod->setAccessible(true); @@ -58,8 +56,7 @@ class AbstractGrantTest extends TestCase $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $abstractGrantReflection = new \ReflectionClass($grantMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withHeader('Authorization', 'Foo ' . base64_encode('Open:Sesame')); + $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Foo ' . base64_encode('Open:Sesame')); $basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials'); $basicAuthMethod->setAccessible(true); @@ -72,8 +69,7 @@ class AbstractGrantTest extends TestCase $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $abstractGrantReflection = new \ReflectionClass($grantMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withHeader('Authorization', 'Basic ||'); + $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ||'); $basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials'); $basicAuthMethod->setAccessible(true); @@ -86,8 +82,7 @@ class AbstractGrantTest extends TestCase $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $abstractGrantReflection = new \ReflectionClass($grantMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withHeader('Authorization', 'Basic ' . base64_encode('OpenSesame')); + $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . base64_encode('OpenSesame')); $basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials'); $basicAuthMethod->setAccessible(true); @@ -107,12 +102,10 @@ class AbstractGrantTest extends TestCase $abstractGrantReflection = new \ReflectionClass($grantMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - ] - ); + $serverRequest = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + ]); + $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); @@ -133,14 +126,12 @@ class AbstractGrantTest extends TestCase $abstractGrantReflection = new \ReflectionClass($grantMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'redirect_uri' => 'http://foo/bar', - ] - ); + $serverRequest = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'redirect_uri' => 'http://foo/bar', + ]); + $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); @@ -148,7 +139,6 @@ class AbstractGrantTest extends TestCase $this->assertEquals($client, $result); } - public function testValidateClientMissingClientId() { $client = new ClientEntity(); @@ -181,8 +171,7 @@ class AbstractGrantTest extends TestCase $abstractGrantReflection = new \ReflectionClass($grantMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody([ + $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', ]); @@ -205,8 +194,7 @@ class AbstractGrantTest extends TestCase $abstractGrantReflection = new \ReflectionClass($grantMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody([ + $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'client_secret' => 'foo', ]); @@ -232,8 +220,7 @@ class AbstractGrantTest extends TestCase $abstractGrantReflection = new \ReflectionClass($grantMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody([ + $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'redirect_uri' => 'http://bar/foo', ]); @@ -259,8 +246,7 @@ class AbstractGrantTest extends TestCase $abstractGrantReflection = new \ReflectionClass($grantMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody([ + $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'redirect_uri' => 'http://bar/foo', ]); @@ -284,8 +270,7 @@ class AbstractGrantTest extends TestCase $abstractGrantReflection = new \ReflectionClass($grantMock); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody([ + $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'client_secret' => 'bar', ]); @@ -303,8 +288,7 @@ class AbstractGrantTest extends TestCase $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->method('getIdentifier')->willReturn('foobar'); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody([ + $serverRequest = (new ServerRequest())->withParsedBody([ 'grant_type' => 'foobar', ]); @@ -416,8 +400,7 @@ class AbstractGrantTest extends TestCase $method = $abstractGrantReflection->getMethod('getCookieParameter'); $method->setAccessible(true); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withCookieParams([ + $serverRequest = (new ServerRequest())->withCookieParams([ 'foo' => 'bar', ]); diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index b02f1190..54be52fd 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -48,13 +48,10 @@ class ClientCredentialsGrantTest extends TestCase $grant->setDefaultScope(self::DEFAULT_SCOPE); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - ] - ); + $serverRequest = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + ]); $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index 4b7de98a..6568d06a 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -64,15 +64,12 @@ class PasswordGrantTest extends TestCase $grant->setDefaultScope(self::DEFAULT_SCOPE); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'username' => 'foo', - 'password' => 'bar', - ] - ); + $serverRequest = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'username' => 'foo', + 'password' => 'bar', + ]); $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); @@ -110,15 +107,12 @@ class PasswordGrantTest extends TestCase $grant->setDefaultScope(self::DEFAULT_SCOPE); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'username' => 'foo', - 'password' => 'bar', - ] - ); + $serverRequest = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'username' => 'foo', + 'password' => 'bar', + ]); $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index 632cbe80..21a5eeb8 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -80,8 +80,7 @@ class RefreshTokenGrantTest extends TestCase ) ); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody([ + $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, @@ -137,8 +136,7 @@ class RefreshTokenGrantTest extends TestCase ) ); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody([ + $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, @@ -192,15 +190,12 @@ class RefreshTokenGrantTest extends TestCase ) ); - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, - 'scope' => 'foo', - ] - ); + $serverRequest = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + 'scope' => 'foo', + ]); $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); diff --git a/tests/Middleware/ResourceServerMiddlewareTest.php b/tests/Middleware/ResourceServerMiddlewareTest.php index 7210567b..4f2d3079 100644 --- a/tests/Middleware/ResourceServerMiddlewareTest.php +++ b/tests/Middleware/ResourceServerMiddlewareTest.php @@ -35,8 +35,7 @@ class ResourceServerMiddlewareTest extends TestCase $token = (string) $accessToken; - $request = new ServerRequest(); - $request = $request->withHeader('authorization', sprintf('Bearer %s', $token)); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $token)); $middleware = new ResourceServerMiddleware($server); $response = $middleware->__invoke( @@ -71,8 +70,7 @@ class ResourceServerMiddlewareTest extends TestCase $token = (string) $accessToken; - $request = new ServerRequest(); - $request = $request->withHeader('authorization', sprintf('Bearer %s', $token)); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $token)); $middleware = new ResourceServerMiddleware($server); $response = $middleware->__invoke( @@ -95,8 +93,7 @@ class ResourceServerMiddlewareTest extends TestCase 'file://' . __DIR__ . '/../Stubs/public.key' ); - $request = new ServerRequest(); - $request = $request->withHeader('authorization', ''); + $request = (new ServerRequest())->withHeader('authorization', ''); $middleware = new ResourceServerMiddleware($server); $response = $middleware->__invoke( diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 97bc37aa..0c4a46fc 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -142,8 +142,7 @@ class BearerResponseTypeTest extends TestCase $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $request = new ServerRequest(); - $request = $request->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); $request = $authorizationValidator->validateAuthorization($request); @@ -185,8 +184,7 @@ class BearerResponseTypeTest extends TestCase $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $request = new ServerRequest(); - $request = $request->withHeader('authorization', sprintf('Bearer %s', $json->access_token . 'foo')); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token . 'foo')); try { $authorizationValidator->validateAuthorization($request); @@ -231,8 +229,7 @@ class BearerResponseTypeTest extends TestCase $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $request = new ServerRequest(); - $request = $request->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); try { $authorizationValidator->validateAuthorization($request); @@ -255,8 +252,7 @@ class BearerResponseTypeTest extends TestCase $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $request = new ServerRequest(); - $request = $request->withHeader('authorization', 'Bearer blah'); + $request = (new ServerRequest())->withHeader('authorization', 'Bearer blah'); try { $authorizationValidator->validateAuthorization($request); @@ -279,8 +275,7 @@ class BearerResponseTypeTest extends TestCase $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $request = new ServerRequest(); - $request = $request->withHeader('authorization', 'Bearer blah.blah.blah'); + $request = (new ServerRequest())->withHeader('authorization', 'Bearer blah.blah.blah'); try { $authorizationValidator->validateAuthorization($request); From 7bc1ec643ec4472b96b2e987b89d3683597eeee9 Mon Sep 17 00:00:00 2001 From: sephster Date: Thu, 27 Jun 2019 13:24:58 +0100 Subject: [PATCH 089/104] Remove unused import --- tests/Grant/AuthCodeGrantTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 2d3098c4..b2dc122a 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -26,7 +26,6 @@ use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; -use Zend\Diactoros\ServerRequestFactory; class AuthCodeGrantTest extends TestCase { From c4c354e2df792be41a9674344c7700ee716bb19b Mon Sep 17 00:00:00 2001 From: sephster Date: Mon, 1 Jul 2019 19:17:43 +0100 Subject: [PATCH 090/104] Fix phpstan issues --- phpstan.neon | 2 -- .../BearerTokenValidator.php | 2 +- src/CryptTrait.php | 18 +++++++--- src/Entities/Traits/AccessTokenTrait.php | 2 +- src/Exception/OAuthServerException.php | 4 ++- src/Grant/AbstractGrant.php | 2 +- src/Grant/AuthCodeGrant.php | 32 ++++++++++------- .../ClientRepositoryInterface.php | 2 +- src/Repositories/ScopeRepositoryInterface.php | 2 +- src/Repositories/UserRepositoryInterface.php | 2 +- src/RequestTypes/AuthorizationRequest.php | 2 +- src/ResponseTypes/BearerTokenResponse.php | 35 +++++++++++-------- tests/Exception/OAuthServerExceptionTest.php | 4 ++- tests/Grant/ImplicitGrantTest.php | 6 ++-- tests/Utils/CryptKeyTest.php | 10 ++++++ 15 files changed, 79 insertions(+), 46 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 5cd9d80d..ba1fb491 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,8 +1,6 @@ includes: - vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-phpunit/rules.neon - - vendor/phpstan/phpstan-phpunit/strictRules.neon - - vendor/phpstan/phpstan-strict-rules/rules.neon services: - class: LeagueTests\PHPStan\AbstractGrantExtension diff --git a/src/AuthorizationValidators/BearerTokenValidator.php b/src/AuthorizationValidators/BearerTokenValidator.php index b2035ccc..7218f413 100644 --- a/src/AuthorizationValidators/BearerTokenValidator.php +++ b/src/AuthorizationValidators/BearerTokenValidator.php @@ -63,7 +63,7 @@ class BearerTokenValidator implements AuthorizationValidatorInterface } $header = $request->getHeader('authorization'); - $jwt = trim(preg_replace('/^(?:\s+)?Bearer\s/', '', $header[0])); + $jwt = trim((string) preg_replace('/^(?:\s+)?Bearer\s/', '', $header[0])); try { // Attempt to parse and validate the JWT diff --git a/src/CryptTrait.php b/src/CryptTrait.php index 1196e9dc..5709940d 100644 --- a/src/CryptTrait.php +++ b/src/CryptTrait.php @@ -19,7 +19,7 @@ use LogicException; trait CryptTrait { /** - * @var string|Key + * @var string|Key|null */ protected $encryptionKey; @@ -39,9 +39,13 @@ trait CryptTrait return Crypto::encrypt($unencryptedData, $this->encryptionKey); } - return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey); + if (is_string($this->encryptionKey)) { + return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey); + } + + throw new LogicException('Encryption key not set when attempting to encrypt'); } catch (Exception $e) { - throw new LogicException($e->getMessage(), null, $e); + throw new LogicException($e->getMessage(), 0, $e); } } @@ -61,9 +65,13 @@ trait CryptTrait return Crypto::decrypt($encryptedData, $this->encryptionKey); } - return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey); + if (is_string($this->encryptionKey)) { + return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey); + } + + throw new LogicException('Encryption key not set when attempting to decrypt'); } catch (Exception $e) { - throw new LogicException($e->getMessage(), null, $e); + throw new LogicException($e->getMessage(), 0, $e); } } diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php index 872e8c18..e9757264 100644 --- a/src/Entities/Traits/AccessTokenTrait.php +++ b/src/Entities/Traits/AccessTokenTrait.php @@ -48,7 +48,7 @@ trait AccessTokenTrait ->setIssuedAt(time()) ->setNotBefore(time()) ->setExpiration($this->getExpiryDateTime()->getTimestamp()) - ->setSubject($this->getUserIdentifier()) + ->setSubject((string) $this->getUserIdentifier()) ->set('scopes', $this->getScopes()) ->sign(new Sha256(), new Key($privateKey->getKeyPath(), $privateKey->getPassPhrase())) ->getToken(); diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 3c3c7129..8e628baa 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -308,7 +308,9 @@ class OAuthServerException extends Exception $response = $response->withHeader($header, $content); } - $response->getBody()->write(json_encode($payload, $jsonOptions)); + $responseBody = json_encode($payload, $jsonOptions) ?: 'JSON encoding of payload failed'; + + $response->getBody()->write($responseBody); return $response->withStatus($this->getHttpStatusCode()); } diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index aed0e6c7..0ac9e395 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -185,7 +185,7 @@ abstract class AbstractGrant implements GrantTypeInterface throw OAuthServerException::invalidClient($request); } - $client = $this->clientRepository->getClientEntity($clientId); + $client = $this->getClientEntityOrFail($clientId, $request); // If a redirect URI is provided ensure it matches what is pre-registered $redirectUri = $this->getRequestParameter('redirect_uri', $request, null); diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index a11828d7..f85a0898 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -142,19 +142,21 @@ class AuthCodeGrant extends AbstractAuthorizeGrant ); } - if (isset($this->codeChallengeVerifiers[$authCodePayload->code_challenge_method])) { - $codeChallengeVerifier = $this->codeChallengeVerifiers[$authCodePayload->code_challenge_method]; + 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`.'); + 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 + ) + ); } - } else { - throw OAuthServerException::serverError( - sprintf( - 'Unsupported code challenge method `%s`', - $authCodePayload->code_challenge_method - ) - ); } } @@ -351,12 +353,18 @@ class AuthCodeGrant extends AbstractAuthorizeGrant 'code_challenge_method' => $authorizationRequest->getCodeChallengeMethod(), ]; + $jsonPayload = json_encode($payload); + + if ($jsonPayload === false) { + throw new LogicException('An error was encountered when JSON encoding the authorization request response'); + } + $response = new RedirectResponse(); $response->setRedirectUri( $this->makeRedirectUri( $finalRedirectUri, [ - 'code' => $this->encrypt(json_encode($payload)), + 'code' => $this->encrypt($jsonPayload), 'state' => $authorizationRequest->getState(), ] ) diff --git a/src/Repositories/ClientRepositoryInterface.php b/src/Repositories/ClientRepositoryInterface.php index 47b6083a..7eef494f 100644 --- a/src/Repositories/ClientRepositoryInterface.php +++ b/src/Repositories/ClientRepositoryInterface.php @@ -21,7 +21,7 @@ interface ClientRepositoryInterface extends RepositoryInterface * * @param string $clientIdentifier The client's identifier * - * @return ClientEntityInterface + * @return ClientEntityInterface|null */ public function getClientEntity($clientIdentifier); diff --git a/src/Repositories/ScopeRepositoryInterface.php b/src/Repositories/ScopeRepositoryInterface.php index 52db05de..997aac2c 100644 --- a/src/Repositories/ScopeRepositoryInterface.php +++ b/src/Repositories/ScopeRepositoryInterface.php @@ -22,7 +22,7 @@ interface ScopeRepositoryInterface extends RepositoryInterface * * @param string $identifier The scope identifier * - * @return ScopeEntityInterface + * @return ScopeEntityInterface|null */ public function getScopeEntityByIdentifier($identifier); diff --git a/src/Repositories/UserRepositoryInterface.php b/src/Repositories/UserRepositoryInterface.php index 0a9efef0..8ad49aa7 100644 --- a/src/Repositories/UserRepositoryInterface.php +++ b/src/Repositories/UserRepositoryInterface.php @@ -22,7 +22,7 @@ interface UserRepositoryInterface extends RepositoryInterface * @param string $grantType The grant type used * @param ClientEntityInterface $clientEntity * - * @return UserEntityInterface + * @return UserEntityInterface|null */ public function getUserEntityByUserCredentials( $username, diff --git a/src/RequestTypes/AuthorizationRequest.php b/src/RequestTypes/AuthorizationRequest.php index 5faa45d4..6441e144 100644 --- a/src/RequestTypes/AuthorizationRequest.php +++ b/src/RequestTypes/AuthorizationRequest.php @@ -111,7 +111,7 @@ class AuthorizationRequest } /** - * @return UserEntityInterface + * @return UserEntityInterface|null */ public function getUser() { diff --git a/src/ResponseTypes/BearerTokenResponse.php b/src/ResponseTypes/BearerTokenResponse.php index ddcadd63..41a10170 100644 --- a/src/ResponseTypes/BearerTokenResponse.php +++ b/src/ResponseTypes/BearerTokenResponse.php @@ -14,6 +14,7 @@ namespace League\OAuth2\Server\ResponseTypes; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; use Psr\Http\Message\ResponseInterface; +use \LogicException; class BearerTokenResponse extends AbstractResponseType { @@ -31,23 +32,27 @@ class BearerTokenResponse extends AbstractResponseType ]; if ($this->refreshToken instanceof RefreshTokenEntityInterface) { - $refreshToken = $this->encrypt( - json_encode( - [ - 'client_id' => $this->accessToken->getClient()->getIdentifier(), - 'refresh_token_id' => $this->refreshToken->getIdentifier(), - 'access_token_id' => $this->accessToken->getIdentifier(), - 'scopes' => $this->accessToken->getScopes(), - 'user_id' => $this->accessToken->getUserIdentifier(), - 'expire_time' => $this->refreshToken->getExpiryDateTime()->getTimestamp(), - ] - ) - ); + $refreshTokenPayload = json_encode([ + 'client_id' => $this->accessToken->getClient()->getIdentifier(), + 'refresh_token_id' => $this->refreshToken->getIdentifier(), + 'access_token_id' => $this->accessToken->getIdentifier(), + 'scopes' => $this->accessToken->getScopes(), + 'user_id' => $this->accessToken->getUserIdentifier(), + 'expire_time' => $this->refreshToken->getExpiryDateTime()->getTimestamp(), + ]); - $responseParams['refresh_token'] = $refreshToken; + if ($refreshTokenPayload === false) { + throw new LogicException('Error encountered JSON encoding the refresh token payload'); + } + + $responseParams['refresh_token'] = $this->encrypt($refreshTokenPayload); } - $responseParams = array_merge($this->getExtraParams($this->accessToken), $responseParams); + $responseParams = json_encode(array_merge($this->getExtraParams($this->accessToken), $responseParams)); + + if ($responseParams === false) { + throw new LogicException('Error encountered JSON encoding response parameters'); + } $response = $response ->withStatus(200) @@ -55,7 +60,7 @@ class BearerTokenResponse extends AbstractResponseType ->withHeader('cache-control', 'no-store') ->withHeader('content-type', 'application/json; charset=UTF-8'); - $response->getBody()->write(json_encode($responseParams)); + $response->getBody()->write($responseParams); return $response; } diff --git a/tests/Exception/OAuthServerExceptionTest.php b/tests/Exception/OAuthServerExceptionTest.php index 82ecef02..eb2b2ad3 100644 --- a/tests/Exception/OAuthServerExceptionTest.php +++ b/tests/Exception/OAuthServerExceptionTest.php @@ -85,7 +85,9 @@ class OAuthServerExceptionTest extends TestCase $previous = new Exception('This is the previous'); $exceptionWithPrevious = OAuthServerException::accessDenied(null, null, $previous); - $this->assertSame('This is the previous', $exceptionWithPrevious->getPrevious()->getMessage()); + $previousMessage = $exceptionWithPrevious->getPrevious() !== null ? $exceptionWithPrevious->getPrevious()->getMessage() : null; + + $this->assertSame('This is the previous', $previousMessage); } public function testDoesNotHavePrevious() diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index 558923b5..e9523669 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -273,7 +273,7 @@ class ImplicitGrantTest extends TestCase $accessToken = new AccessTokenEntity(); $accessToken->setClient($client); - /** @var AccessTokenRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject $accessTokenRepositoryMock */ + /** @var AccessTokenRepositoryInterface|\PHPUnit\Framework\MockObject\MockObject $accessTokenRepositoryMock */ $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->expects($this->at(0))->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create()); @@ -298,7 +298,7 @@ class ImplicitGrantTest extends TestCase $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); - /** @var AccessTokenRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject $accessTokenRepositoryMock */ + /** @var AccessTokenRepositoryInterface|\PHPUnit\Framework\MockObject\MockObject $accessTokenRepositoryMock */ $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); $accessTokenRepositoryMock->method('persistNewAccessToken')->willThrowException(OAuthServerException::serverError('something bad happened')); @@ -325,7 +325,7 @@ class ImplicitGrantTest extends TestCase $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); - /** @var AccessTokenRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject $accessTokenRepositoryMock */ + /** @var AccessTokenRepositoryInterface|\PHPUnit\Framework\MockObject\MockObject $accessTokenRepositoryMock */ $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); $accessTokenRepositoryMock->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create()); diff --git a/tests/Utils/CryptKeyTest.php b/tests/Utils/CryptKeyTest.php index 1fe79d20..e9799c08 100644 --- a/tests/Utils/CryptKeyTest.php +++ b/tests/Utils/CryptKeyTest.php @@ -26,6 +26,11 @@ class CryptKeyTest extends TestCase public function testKeyFileCreation() { $keyContent = file_get_contents(__DIR__ . '/../Stubs/public.key'); + + if (!is_string($keyContent)) { + $this->fail('The public key stub is not a string'); + } + $key = new CryptKey($keyContent); $this->assertEquals( @@ -34,6 +39,11 @@ class CryptKeyTest extends TestCase ); $keyContent = file_get_contents(__DIR__ . '/../Stubs/private.key.crlf'); + + if (!is_string($keyContent)) { + $this->fail('The private key (crlf) stub is not a string'); + } + $key = new CryptKey($keyContent); $this->assertEquals( From a92a274d1538ffdfa4e28f09e3b0075768745a9a Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 2 Jul 2019 19:09:47 +0100 Subject: [PATCH 091/104] Use reflection instead of extension in test --- tests/AuthorizationServerTest.php | 40 ++++++++++++++----------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 9ba15749..bcd87b5b 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -117,35 +117,31 @@ class AuthorizationServerTest extends TestCase $privateKey = 'file://' . __DIR__ . '/Stubs/private.key'; $encryptionKey = 'file://' . __DIR__ . '/Stubs/public.key'; - $server = new class($clientRepository, $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), $privateKey, $encryptionKey) extends AuthorizationServer { - protected function getResponseType() - { - $this->responseType = new class extends BearerTokenResponse { - /* @return null|CryptKey */ - public function getPrivateKey() - { - return $this->privateKey; - } - - public function getEncryptionKey() - { - return $this->encryptionKey; - } - }; - - return parent::getResponseType(); - } - }; + $server = new AuthorizationServer( + $clientRepository, + $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), + $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), + 'file://' . __DIR__ . '/Stubs/private.key', + 'file://' . __DIR__ . '/Stubs/public.key' + ); $abstractGrantReflection = new \ReflectionClass($server); $method = $abstractGrantReflection->getMethod('getResponseType'); $method->setAccessible(true); + $responseType = $method->invoke($server); - $this->assertInstanceOf(BearerTokenResponse::class, $responseType); + $responseTypeReflection = new \ReflectionClass($responseType); + + $privateKeyProperty = $responseTypeReflection->getProperty('privateKey'); + $privateKeyProperty->setAccessible(true); + + $encryptionKeyProperty = $responseTypeReflection->getProperty('encryptionKey'); + $encryptionKeyProperty->setAccessible(true); + // generated instances should have keys setup - $this->assertSame($privateKey, $responseType->getPrivateKey()->getKeyPath()); - $this->assertSame($encryptionKey, $responseType->getEncryptionKey()); + $this->assertSame($privateKey, $privateKeyProperty->getValue($responseType)->getKeyPath()); + $this->assertSame($encryptionKey, $encryptionKeyProperty->getValue($responseType)); } public function testMultipleRequestsGetDifferentResponseTypeInstances() From 46c86ed5b1a3cdddc1f3541a54865c1d12fda3f6 Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 2 Jul 2019 19:21:13 +0100 Subject: [PATCH 092/104] Apply style fix --- src/ResponseTypes/BearerTokenResponse.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ResponseTypes/BearerTokenResponse.php b/src/ResponseTypes/BearerTokenResponse.php index 41a10170..6cdf0c59 100644 --- a/src/ResponseTypes/BearerTokenResponse.php +++ b/src/ResponseTypes/BearerTokenResponse.php @@ -14,7 +14,7 @@ namespace League\OAuth2\Server\ResponseTypes; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; use Psr\Http\Message\ResponseInterface; -use \LogicException; +use LogicException; class BearerTokenResponse extends AbstractResponseType { From 1a6ebdf81c9561bb2fff3c40abff8f519c68c611 Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 2 Jul 2019 19:24:19 +0100 Subject: [PATCH 093/104] Fix order of imports --- src/ResponseTypes/BearerTokenResponse.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ResponseTypes/BearerTokenResponse.php b/src/ResponseTypes/BearerTokenResponse.php index 6cdf0c59..f24a9485 100644 --- a/src/ResponseTypes/BearerTokenResponse.php +++ b/src/ResponseTypes/BearerTokenResponse.php @@ -13,8 +13,8 @@ namespace League\OAuth2\Server\ResponseTypes; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; -use Psr\Http\Message\ResponseInterface; use LogicException; +use Psr\Http\Message\ResponseInterface; class BearerTokenResponse extends AbstractResponseType { From 6b2a3db1852ad6e2770449eb536549e18cc388de Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 2 Jul 2019 22:10:17 +0100 Subject: [PATCH 094/104] Removing php stan strict rules --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 2635dcee..7991a0cc 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,6 @@ "zendframework/zend-diactoros": "^2.1.2", "phpstan/phpstan": "^0.11.8", "phpstan/phpstan-phpunit": "^0.11.2", - "phpstan/phpstan-strict-rules": "^0.11.1", "roave/security-advisories": "dev-master" }, "repositories": [ From a6a499f8fbe8b024a9ba1ab7f6ddbd081acaaa2e Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 2 Jul 2019 22:20:37 +0100 Subject: [PATCH 095/104] Remove Simon Hamp from README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48656296..92a12f3e 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ This package is released under the MIT License. See the bundled [LICENSE](https: ## Credits -This code is principally developed and maintained by [Andy Millington](https://twitter.com/Sephster) and [Simon Hamp](https://twitter.com/simonhamp). +This code is principally developed and maintained by [Andy Millington](https://twitter.com/Sephster). Between 2012 and 2017 this library was developed and maintained by [Alex Bilbie](https://alexbilbie.com/). From cb9aa25c890a8847788f89b174d26dcab43979b2 Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 2 Jul 2019 22:51:12 +0100 Subject: [PATCH 096/104] Re-add removed changelog instances --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 751379cc..3a531001 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -169,6 +169,10 @@ To address feedback from the security release the following change has been made - Fixed `finalizeScopes` call (Issue #650) +# [4.1.6] - 2016-09-13 + +- Less restrictive on Authorization header check (Issue #652) + ## [5.1.1] - 2016-07-26 - Improved test suite (Issue #614) @@ -467,6 +471,7 @@ Version 5 is a complete code rewrite. [7.3.2]: https://github.com/thephpleague/oauth2-server/compare/7.3.1...7.3.2 [7.3.1]: https://github.com/thephpleague/oauth2-server/compare/7.3.0...7.3.1 [7.3.0]: https://github.com/thephpleague/oauth2-server/compare/7.2.0...7.3.0 +[7.2.0]: https://github.com/thephpleague/oauth2-server/compare/7.1.1...7.2.0 [7.1.1]: https://github.com/thephpleague/oauth2-server/compare/7.1.0...7.1.1 [7.1.0]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...7.1.0 [7.0.0]: https://github.com/thephpleague/oauth2-server/compare/6.1.1...7.0.0 @@ -486,6 +491,8 @@ Version 5 is a complete code rewrite. [5.0.0]: https://github.com/thephpleague/oauth2-server/compare/5.0.0-RC2...5.0.0 [5.0.0-RC2]: https://github.com/thephpleague/oauth2-server/compare/5.0.0-RC1...5.0.0-RC2 [5.0.0-RC1]: https://github.com/thephpleague/oauth2-server/compare/4.1.5...5.0.0-RC1 +[4.1.7]: https://github.com/thephpleague/oauth2-server/compare/4.1.6...4.1.7 +[4.1.6]: https://github.com/thephpleague/oauth2-server/compare/4.1.5...4.1.6 [4.1.5]: https://github.com/thephpleague/oauth2-server/compare/4.1.4...4.1.5 [4.1.4]: https://github.com/thephpleague/oauth2-server/compare/4.1.3...4.1.4 [4.1.3]: https://github.com/thephpleague/oauth2-server/compare/4.1.2...4.1.3 From 7f0879b8b4efd474348cf378daa9fcf3d8c156d0 Mon Sep 17 00:00:00 2001 From: sephster Date: Tue, 2 Jul 2019 22:52:13 +0100 Subject: [PATCH 097/104] Change header type --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a531001..446b920f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -169,7 +169,7 @@ To address feedback from the security release the following change has been made - Fixed `finalizeScopes` call (Issue #650) -# [4.1.6] - 2016-09-13 +## [4.1.6] - 2016-09-13 - Less restrictive on Authorization header check (Issue #652) From aba5353257e1983bdba820d179861e0c4d137232 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 13 Jul 2019 17:31:09 +0100 Subject: [PATCH 098/104] Add validateClient() function to ClientRepository --- .../src/Repositories/ClientRepository.php | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/examples/src/Repositories/ClientRepository.php b/examples/src/Repositories/ClientRepository.php index eaa4e5c2..404a27fc 100644 --- a/examples/src/Repositories/ClientRepository.php +++ b/examples/src/Repositories/ClientRepository.php @@ -14,16 +14,33 @@ use OAuth2ServerExamples\Entities\ClientEntity; class ClientRepository implements ClientRepositoryInterface { + const CLIENT_NAME = 'My Awesome App'; + const REDIRECT_URI = 'http://foo/bar'; + /** * {@inheritdoc} */ public function getClientEntity($clientIdentifier, $grantType = null, $clientSecret = null, $mustValidateSecret = true) + { + $client = new ClientEntity(); + + $client->setIdentifier($clientIdentifier); + $client->setName(self::CLIENT_NAME); + $client->setRedirectUri(self::REDIRECT_URI); + + return $client; + } + + /** + * {@inheritDoc} + */ + public function validateClient($clientIdentifier, $clientSecret, $grantType) { $clients = [ 'myawesomeapp' => [ 'secret' => password_hash('abc123', PASSWORD_BCRYPT), - 'name' => 'My Awesome App', - 'redirect_uri' => 'http://foo/bar', + 'name' => self::CLIENT_NAME, + 'redirect_uri' => self::REDIRECT_URI, 'is_confidential' => true, ], ]; @@ -40,12 +57,5 @@ class ClientRepository implements ClientRepositoryInterface ) { return; } - - $client = new ClientEntity(); - $client->setIdentifier($clientIdentifier); - $client->setName($clients[$clientIdentifier]['name']); - $client->setRedirectUri($clients[$clientIdentifier]['redirect_uri']); - - return $client; } } From 4be97e6fd0aa1f47e080e35c8306a9962e76ee8a Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 13 Jul 2019 17:37:45 +0100 Subject: [PATCH 099/104] Update composer dependencies and remove mustValidateSecret --- examples/composer.json | 6 +- examples/composer.lock | 67 +++++++++---------- .../src/Repositories/ClientRepository.php | 3 +- 3 files changed, 36 insertions(+), 40 deletions(-) diff --git a/examples/composer.json b/examples/composer.json index a3ef9b7a..ab0832fe 100644 --- a/examples/composer.json +++ b/examples/composer.json @@ -3,11 +3,11 @@ "slim/slim": "^3.0.0" }, "require-dev": { - "league/event": "^2.1", - "lcobucci/jwt": "^3.2", + "league/event": "^2.2", + "lcobucci/jwt": "^3.3", "psr/http-message": "^1.0", "defuse/php-encryption": "^2.2", - "zendframework/zend-diactoros": "^2.0.0" + "zendframework/zend-diactoros": "^2.1.0" }, "autoload": { "psr-4": { diff --git a/examples/composer.lock b/examples/composer.lock index 1611f94b..d557b685 100644 --- a/examples/composer.lock +++ b/examples/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "97f2878428e37d1d8e5418cc85cbfa3d", + "content-hash": "a7f5c3fdcadb17399bbd97f15e1b11f1", "packages": [ { "name": "container-interop/container-interop", @@ -234,16 +234,16 @@ }, { "name": "slim/slim", - "version": "3.11.0", + "version": "3.12.1", "source": { "type": "git", "url": "https://github.com/slimphp/Slim.git", - "reference": "d378e70431e78ee92ee32ddde61ecc72edf5dc0a" + "reference": "eaee12ef8d0750db62b8c548016d82fb33addb6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim/zipball/d378e70431e78ee92ee32ddde61ecc72edf5dc0a", - "reference": "d378e70431e78ee92ee32ddde61ecc72edf5dc0a", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/eaee12ef8d0750db62b8c548016d82fb33addb6b", + "reference": "eaee12ef8d0750db62b8c548016d82fb33addb6b", "shasum": "" }, "require": { @@ -301,7 +301,7 @@ "micro", "router" ], - "time": "2018-09-16T10:54:21+00:00" + "time": "2019-04-16T16:47:29+00:00" } ], "packages-dev": [ @@ -370,33 +370,30 @@ }, { "name": "lcobucci/jwt", - "version": "3.2.4", + "version": "3.3.1", "source": { "type": "git", "url": "https://github.com/lcobucci/jwt.git", - "reference": "c9704b751315d21735dc98d78d4f37bd73596da7" + "reference": "a11ec5f4b4d75d1fcd04e133dede4c317aac9e18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/jwt/zipball/c9704b751315d21735dc98d78d4f37bd73596da7", - "reference": "c9704b751315d21735dc98d78d4f37bd73596da7", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/a11ec5f4b4d75d1fcd04e133dede4c317aac9e18", + "reference": "a11ec5f4b4d75d1fcd04e133dede4c317aac9e18", "shasum": "" }, "require": { + "ext-mbstring": "*", "ext-openssl": "*", - "php": ">=5.5" + "php": "^5.6 || ^7.0" }, "require-dev": { - "mdanter/ecc": "~0.3.1", "mikey179/vfsstream": "~1.5", "phpmd/phpmd": "~2.2", "phpunit/php-invoker": "~1.1", - "phpunit/phpunit": "~4.5", + "phpunit/phpunit": "^5.7 || ^7.3", "squizlabs/php_codesniffer": "~2.3" }, - "suggest": { - "mdanter/ecc": "Required to use Elliptic Curves based algorithms." - }, "type": "library", "extra": { "branch-alias": { @@ -424,20 +421,20 @@ "JWS", "jwt" ], - "time": "2018-08-03T11:23:50+00:00" + "time": "2019-05-24T18:30:49+00:00" }, { "name": "league/event", - "version": "2.1.2", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/thephpleague/event.git", - "reference": "e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd" + "reference": "d2cc124cf9a3fab2bb4ff963307f60361ce4d119" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/event/zipball/e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd", - "reference": "e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd", + "url": "https://api.github.com/repos/thephpleague/event/zipball/d2cc124cf9a3fab2bb4ff963307f60361ce4d119", + "reference": "d2cc124cf9a3fab2bb4ff963307f60361ce4d119", "shasum": "" }, "require": { @@ -445,7 +442,7 @@ }, "require-dev": { "henrikbjorn/phpspec-code-coverage": "~1.0.1", - "phpspec/phpspec": "~2.0.0" + "phpspec/phpspec": "^2.2" }, "type": "library", "extra": { @@ -474,7 +471,7 @@ "event", "listener" ], - "time": "2015-05-21T12:24:47+00:00" + "time": "2018-11-26T11:52:41+00:00" }, { "name": "paragonie/random_compat", @@ -523,16 +520,16 @@ }, { "name": "psr/http-factory", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", - "reference": "378bfe27931ecc54ff824a20d6f6bfc303bbd04c" + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/378bfe27931ecc54ff824a20d6f6bfc303bbd04c", - "reference": "378bfe27931ecc54ff824a20d6f6bfc303bbd04c", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", "shasum": "" }, "require": { @@ -571,20 +568,20 @@ "request", "response" ], - "time": "2018-07-30T21:54:04+00:00" + "time": "2019-04-30T12:38:16+00:00" }, { "name": "zendframework/zend-diactoros", - "version": "2.0.0", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "0bae78192e634774b5584f0210c1232da82cb1ff" + "reference": "279723778c40164bcf984a2df12ff2c6ec5e61c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/0bae78192e634774b5584f0210c1232da82cb1ff", - "reference": "0bae78192e634774b5584f0210c1232da82cb1ff", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/279723778c40164bcf984a2df12ff2c6ec5e61c1", + "reference": "279723778c40164bcf984a2df12ff2c6ec5e61c1", "shasum": "" }, "require": { @@ -607,8 +604,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev", - "dev-develop": "2.1.x-dev", + "dev-master": "2.1.x-dev", + "dev-develop": "2.2.x-dev", "dev-release-1.8": "1.8.x-dev" } }, @@ -637,7 +634,7 @@ "psr", "psr-7" ], - "time": "2018-09-27T19:49:04+00:00" + "time": "2019-07-10T16:13:25+00:00" } ], "aliases": [], diff --git a/examples/src/Repositories/ClientRepository.php b/examples/src/Repositories/ClientRepository.php index 404a27fc..e0f1f12a 100644 --- a/examples/src/Repositories/ClientRepository.php +++ b/examples/src/Repositories/ClientRepository.php @@ -51,8 +51,7 @@ class ClientRepository implements ClientRepositoryInterface } if ( - $mustValidateSecret === true - && $clients[$clientIdentifier]['is_confidential'] === true + $clients[$clientIdentifier]['is_confidential'] === true && password_verify($clientSecret, $clients[$clientIdentifier]['secret']) === false ) { return; From 3f95c0d11e338495366e59ec698451832760932b Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 13 Jul 2019 17:40:38 +0100 Subject: [PATCH 100/104] Update validateClient arguments list --- examples/src/Repositories/ClientRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/Repositories/ClientRepository.php b/examples/src/Repositories/ClientRepository.php index e0f1f12a..da7078e6 100644 --- a/examples/src/Repositories/ClientRepository.php +++ b/examples/src/Repositories/ClientRepository.php @@ -20,7 +20,7 @@ class ClientRepository implements ClientRepositoryInterface /** * {@inheritdoc} */ - public function getClientEntity($clientIdentifier, $grantType = null, $clientSecret = null, $mustValidateSecret = true) + public function getClientEntity($clientIdentifier) { $client = new ClientEntity(); From 2b7923c593c30d33d9889487121fb00fc50b610f Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 13 Jul 2019 17:49:26 +0100 Subject: [PATCH 101/104] Fix inheritdoc case --- examples/src/Repositories/ClientRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/Repositories/ClientRepository.php b/examples/src/Repositories/ClientRepository.php index da7078e6..47db508c 100644 --- a/examples/src/Repositories/ClientRepository.php +++ b/examples/src/Repositories/ClientRepository.php @@ -32,7 +32,7 @@ class ClientRepository implements ClientRepositoryInterface } /** - * {@inheritDoc} + * {@inheritdoc} */ public function validateClient($clientIdentifier, $clientSecret, $grantType) { From f5e910e6ec98cc4a221e677d75614eb6e27ce564 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 13 Jul 2019 17:51:56 +0100 Subject: [PATCH 102/104] Remove jti replication from JWT Header --- src/Entities/Traits/AccessTokenTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php index e9757264..44276de1 100644 --- a/src/Entities/Traits/AccessTokenTrait.php +++ b/src/Entities/Traits/AccessTokenTrait.php @@ -44,7 +44,7 @@ trait AccessTokenTrait { return (new Builder()) ->setAudience($this->getClient()->getIdentifier()) - ->setId($this->getIdentifier(), true) + ->setId($this->getIdentifier()) ->setIssuedAt(time()) ->setNotBefore(time()) ->setExpiration($this->getExpiryDateTime()->getTimestamp()) From dc3c74601ab8ed6391211c35cfe3bfcdc4d9f702 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 13 Jul 2019 17:52:35 +0100 Subject: [PATCH 103/104] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 446b920f..d4d7921a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Removed - `enableCodeExchangeProof` flag (PR #938) - Support for PHP 7.0 (PR #1014) +- Remove JTI claim from JWT header (PR #) ## [7.4.0] - released 2019-05-05 From c7f998ee0203d2f065bc39c2f7c89459a862ec31 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 13 Jul 2019 18:03:24 +0100 Subject: [PATCH 104/104] Add PR number for JTI PR to changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4d7921a..acfe9c1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Removed - `enableCodeExchangeProof` flag (PR #938) - Support for PHP 7.0 (PR #1014) -- Remove JTI claim from JWT header (PR #) +- Remove JTI claim from JWT header (PR #1031) ## [7.4.0] - released 2019-05-05