From 7285ede563476400790db523ab3159f182ccf248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=CC=87smail=20BASKIN?= Date: Wed, 4 May 2016 13:34:37 +0300 Subject: [PATCH 001/191] Include redirect_uri check on authorization endpoint --- src/Grant/AuthCodeGrant.php | 5 ++++ tests/AuthorizationServerTest.php | 45 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 9216e167..fe4c5933 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -195,6 +195,11 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient(); } + } elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1 + || empty($client->getRedirectUri()) + ) { + $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); + throw OAuthServerException::invalidClient(); } $scopes = $this->validateScopes( diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 2303b713..67c924a8 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -135,6 +135,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase public function testValidateAuthorizationRequest() { $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); @@ -171,6 +172,50 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase $this->assertTrue($server->validateAuthorizationRequest($request) instanceof AuthorizationRequest); } + public function testValidateAuthorizationRequestWithMissingRedirectUri() + { + $client = new ClientEntity(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + + $server = new AuthorizationServer( + $clientRepositoryMock, + $this->getMock(AccessTokenRepositoryInterface::class), + $this->getMock(ScopeRepositoryInterface::class), + 'file://' . __DIR__ . '/Stubs/private.key', + 'file://' . __DIR__ . '/Stubs/public.key' + ); + $server->enableGrantType($grant); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + ] + ); + + try { + $server->validateAuthorizationRequest($request); + } catch (OAuthServerException $e) { + $this->assertEquals('invalid_client', $e->getErrorType()); + $this->assertEquals(401, $e->getHttpStatusCode()); + } + } + /** * @expectedException \League\OAuth2\Server\Exception\OAuthServerException * @expectedExceptionCode 2 From 9a58bc15f668b19439e7fcbf33572117182408b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=CC=87smail=20BASKIN?= Date: Sat, 7 May 2016 17:43:43 +0300 Subject: [PATCH 002/191] Include redirect_uri check on authorization endpoint on implicit grant --- src/Grant/ImplicitGrant.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 976acefb..634a79cd 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -142,6 +142,11 @@ class ImplicitGrant extends AbstractAuthorizeGrant $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient(); } + } elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1 + || empty($client->getRedirectUri()) + ) { + $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); + throw OAuthServerException::invalidClient(); } $scopes = $this->validateScopes( From 655a4b27159f1d88ccd2cfb42687da65742b04e7 Mon Sep 17 00:00:00 2001 From: Luca Degasperi Date: Thu, 30 Jun 2016 16:49:47 +0200 Subject: [PATCH 003/191] Make ClientRepositoryInterface more flexible This small change will allow the use of the ```ClientRepositoryInterface``` for more use cases than simply validating clients when authorizing them. There might be some places where this change will affect the behavior. I also think the ```$mustValidateSecret``` is redundant since in an implementation a check could be done wether ```$clientSecret``` is null or not. --- src/Repositories/ClientRepositoryInterface.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Repositories/ClientRepositoryInterface.php b/src/Repositories/ClientRepositoryInterface.php index fc56c2f3..4653adaf 100644 --- a/src/Repositories/ClientRepositoryInterface.php +++ b/src/Repositories/ClientRepositoryInterface.php @@ -17,12 +17,12 @@ interface ClientRepositoryInterface extends RepositoryInterface * Get a client. * * @param string $clientIdentifier The client's identifier - * @param string $grantType The grant type used + * @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 unless the client * is confidential * * @return \League\OAuth2\Server\Entities\ClientEntityInterface */ - public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true); + public function getClientEntity($clientIdentifier, $grantType = null, $clientSecret = null, $mustValidateSecret = false); } From ee8841fe66d6d30f95ce31311407f97d403e14db Mon Sep 17 00:00:00 2001 From: Pedro Cambra Date: Mon, 31 Oct 2016 09:57:44 +0900 Subject: [PATCH 004/191] Added Zend diactoros library dependency to the examples --- examples/composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/composer.json b/examples/composer.json index 3c6e550b..59d6d557 100644 --- a/examples/composer.json +++ b/examples/composer.json @@ -6,7 +6,8 @@ "league/event": "^2.1", "lcobucci/jwt": "^3.1", "paragonie/random_compat": "^1.1", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0", + "zendframework/zend-diactoros": "^1.0" }, "autoload": { "psr-4": { From 6426e597a3813aed641984cece81800109d25ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Kooman?= Date: Tue, 24 Jan 2017 11:36:34 +0100 Subject: [PATCH 005/191] Fix PKCE code verifier encoding to match specification The current implementation of PKCE does not follow the specification correctly regarding the encoding of the code verifier. This patch correctly encodes the hash of the code verifier according to Appenix A of RFC 7636. --- src/Grant/AuthCodeGrant.php | 2 +- tests/Grant/AuthCodeGrantTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index df89400e..0d05e7c8 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -144,7 +144,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant case 'S256': if ( hash_equals( - urlencode(base64_encode(hash('sha256', $codeVerifier))), + strtr(rtrim(base64_encode(hash('sha256', $codeVerifier)), '='), '+/', '-_'), $authCodePayload->code_challenge ) === false ) { diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 498fdb4e..d9f366e6 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -630,7 +630,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase 'user_id' => 123, 'scopes' => ['foo'], 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => urlencode(base64_encode(hash('sha256', 'foobar'))), + 'code_challenge' => strtr(rtrim(base64_encode(hash('sha256', 'foobar')), '='), '+/', '-_'), 'code_challenge_method' => 'S256', ] ) From 13c608b849a6d805f119bf046651363171d6fe51 Mon Sep 17 00:00:00 2001 From: Toby Griffiths Date: Wed, 1 Mar 2017 13:08:42 +0000 Subject: [PATCH 006/191] Corrected DateInterval from 1 min to 1 month --- examples/public/middleware_use.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/public/middleware_use.php b/examples/public/middleware_use.php index 68b4ebc1..21f6bc23 100644 --- a/examples/public/middleware_use.php +++ b/examples/public/middleware_use.php @@ -62,7 +62,7 @@ $app = new App([ // Enable the refresh token grant on the server with a token TTL of 1 month $server->enableGrantType( new RefreshTokenGrant($refreshTokenRepository), - new \DateInterval('PT1M') + new \DateInterval('P1M') ); return $server; From d73b15ae320af678fb7e73a42bcf255e7248e729 Mon Sep 17 00:00:00 2001 From: Stanimir Stoyanov Date: Mon, 20 Mar 2017 14:52:35 +0200 Subject: [PATCH 007/191] Getter and setter for the payload and ability to pass options to json_encode --- src/Exception/OAuthServerException.php | 46 ++++++++++++++++++++------ 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 6ffa0fb1..f0b5fef5 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -33,6 +33,11 @@ class OAuthServerException extends \Exception */ private $redirectUri; + /** + * @var array + */ + private $payload; + /** * Throw a new exception. * @@ -50,6 +55,33 @@ class OAuthServerException extends \Exception $this->errorType = $errorType; $this->hint = $hint; $this->redirectUri = $redirectUri; + $this->payload = [ + 'error' => $errorType, + 'message' => $message, + ]; + if ($hint !== null) { + $this->payload['hint'] = $hint; + } + } + + /** + * Returns the current payload. + * + * @return array + */ + public function getPayload() + { + return $this->payload; + } + + /** + * Updates the current payload. + * + * @param array $payload + */ + public function setPayload(array $payload) + { + $this->payload = $payload; } /** @@ -205,21 +237,15 @@ class OAuthServerException extends \Exception * * @param ResponseInterface $response * @param bool $useFragment True if errors should be in the URI fragment instead of query string + * @param int $jsonOptions options passed to json_encode * * @return ResponseInterface */ - public function generateHttpResponse(ResponseInterface $response, $useFragment = false) + public function generateHttpResponse(ResponseInterface $response, $useFragment = false, $jsonOptions = 0) { $headers = $this->getHttpHeaders(); - $payload = [ - 'error' => $this->getErrorType(), - 'message' => $this->getMessage(), - ]; - - if ($this->hint !== null) { - $payload['hint'] = $this->hint; - } + $payload = $this->getPayload(); if ($this->redirectUri !== null) { if ($useFragment === true) { @@ -235,7 +261,7 @@ class OAuthServerException extends \Exception $response = $response->withHeader($header, $content); } - $response->getBody()->write(json_encode($payload)); + $response->getBody()->write(json_encode($payload, $jsonOptions)); return $response->withStatus($this->getHttpStatusCode()); } From 83228bdcd52e47c1824cfcc394b74bed97eb10a0 Mon Sep 17 00:00:00 2001 From: Dave Marshall Date: Mon, 27 Mar 2017 12:11:25 +0100 Subject: [PATCH 008/191] Change case for implict grant token_type --- src/Grant/ImplicitGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 62a48147..466f32ce 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -200,7 +200,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant $finalRedirectUri, [ 'access_token' => (string) $accessToken->convertToJWT($this->privateKey), - 'token_type' => 'bearer', + 'token_type' => 'Bearer', 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(), 'state' => $authorizationRequest->getState(), ], From 2482630221bef4347e37c02fe5119a18f9ceda99 Mon Sep 17 00:00:00 2001 From: Erick Torres Date: Fri, 16 Jun 2017 12:02:34 -0500 Subject: [PATCH 009/191] Fix codeVerifier hash verification. --- 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 df89400e..7e64b416 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -144,7 +144,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant case 'S256': if ( hash_equals( - urlencode(base64_encode(hash('sha256', $codeVerifier))), + rtrim(strtr(base64_encode(hash('sha256', $codeVerifier, true)), '+/', '-_'), '='), $authCodePayload->code_challenge ) === false ) { From 2167edf1d98ec22ef2e0ece600b82d200a834397 Mon Sep 17 00:00:00 2001 From: Erick Torres Date: Fri, 16 Jun 2017 12:02:48 -0500 Subject: [PATCH 010/191] Validate codeVerifier and codeChallenge correctly. --- src/Grant/AuthCodeGrant.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 7e64b416..5cfafdc8 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -134,6 +134,17 @@ class AuthCodeGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidRequest('code_verifier'); } + // Validate code_verifier according to RFC-7636 + // @see: https://tools.ietf.org/html/rfc7636#section-4.1 + $isValidCodeVerifier = (bool) preg_match('#[A-Za-z0-9\-|\.|\_|\~]{43,128}#', $codeVerifier); + + if ($isValidCodeVerifier === false) { + throw OAuthServerException::invalidRequest( + 'code_verifier', + 'Code Verifier must follow the specifications of RFC-7636.' + ); + } + switch ($authCodePayload->code_challenge_method) { case 'plain': if (hash_equals($codeVerifier, $authCodePayload->code_challenge) === false) { @@ -272,6 +283,17 @@ class AuthCodeGrant extends AbstractAuthorizeGrant ); } + // Validate code_challenge according to RFC-7636 + // @see: https://tools.ietf.org/html/rfc7636#section-4.2 + $isValidCodeChallenge = (bool) preg_match('#[A-Za-z0-9\-|\.|\_|\~]{43}#', $codeChallenge); + + if ($isValidCodeChallenge === false) { + throw OAuthServerException::invalidRequest( + 'code_challenged', + 'Code challenge must follow the specifications of RFC-7636.' + ); + } + $authorizationRequest->setCodeChallenge($codeChallenge); $authorizationRequest->setCodeChallengeMethod($codeChallengeMethod); } From 880e3b45901076d8b9ae0004cc5e36dff6625e2a Mon Sep 17 00:00:00 2001 From: Erick Torres Date: Fri, 16 Jun 2017 12:03:04 -0500 Subject: [PATCH 011/191] Fix invalid code_challenge_method key. --- 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 5cfafdc8..4b04997f 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -341,7 +341,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant 'user_id' => $authCode->getUserIdentifier(), 'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'), 'code_challenge' => $authorizationRequest->getCodeChallenge(), - 'code_challenge_method ' => $authorizationRequest->getCodeChallengeMethod(), + 'code_challenge_method' => $authorizationRequest->getCodeChallengeMethod(), ] ) ), From 11ad87b5f53a88dcf4427d50a836857cc1871570 Mon Sep 17 00:00:00 2001 From: Erick Torres Date: Fri, 16 Jun 2017 12:03:14 -0500 Subject: [PATCH 012/191] Update tests / Add missing. --- tests/Grant/AuthCodeGrantTest.php | 172 ++++++++++++++++++++++++++++-- 1 file changed, 165 insertions(+), 7 deletions(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 498fdb4e..3fbe52fb 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -32,9 +32,21 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase */ protected $cryptStub; + /** + * @var string Valid generated Code verifier. + */ + protected $codeVerifier; + + /** + * @var string Valid generated code challenge using a proper code verifier. + */ + protected $codeChallenge; + public function setUp() { $this->cryptStub = new CryptTraitStub; + $this->codeVerifier = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '='); + $this->codeChallenge = rtrim(strtr(base64_encode(hash('sha256',$this->codeVerifier, true)), '+/', '-_'), '='); } public function testGetIdentifier() @@ -164,7 +176,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase 'response_type' => 'code', 'client_id' => 'foo', 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => 'FOOBAR', + 'code_challenge' => $this->codeChallenge, ] ); @@ -548,7 +560,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase 'grant_type' => 'authorization_code', 'client_id' => 'foo', 'redirect_uri' => 'http://foo/bar', - 'code_verifier' => 'foobar', + 'code_verifier' => $this->codeVerifier, 'code' => $this->cryptStub->doEncrypt( json_encode( [ @@ -558,7 +570,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase 'user_id' => 123, 'scopes' => ['foo'], 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => 'foobar', + 'code_challenge' => $this->codeVerifier, 'code_challenge_method' => 'plain', ] ) @@ -620,7 +632,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase 'grant_type' => 'authorization_code', 'client_id' => 'foo', 'redirect_uri' => 'http://foo/bar', - 'code_verifier' => 'foobar', + 'code_verifier' => $this->codeVerifier, 'code' => $this->cryptStub->doEncrypt( json_encode( [ @@ -630,7 +642,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase 'user_id' => 123, 'scopes' => ['foo'], 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => urlencode(base64_encode(hash('sha256', 'foobar'))), + 'code_challenge' => $this->codeChallenge, 'code_challenge_method' => 'S256', ] ) @@ -1069,7 +1081,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase 'grant_type' => 'authorization_code', 'client_id' => 'foo', 'redirect_uri' => 'http://foo/bar', - 'code_verifier' => 'nope', + 'code_verifier' => $this->codeVerifier, 'code' => $this->cryptStub->doEncrypt( json_encode( [ @@ -1164,7 +1176,153 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); } catch (OAuthServerException $e) { - $this->assertEquals($e->getHint(), 'Failed to verify `code_verifier`.'); + $this->assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.'); + } + } + + public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInvalidChars() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $client->setRedirectUri('http://foo/bar'); + $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->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $request = new ServerRequest( + [], + [], + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code_verifier' => 'dqX7C-RbqjHYtytmhGTigKdZCXfxq-+xbsk9_GxUcaE', // Malformed code. Contains `+`. + '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', + 'code_challenge' => $this->codeChallenge, + 'code_challenge_method' => 'S256', + ] + ) + ), + ] + ); + + try { + /* @var StubResponseType $response */ + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } catch (OAuthServerException $e) { + $this->assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.'); + } + } + + public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInvalidLength() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $client->setRedirectUri('http://foo/bar'); + $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->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $request = new ServerRequest( + [], + [], + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code_verifier' => 'dqX7C-RbqjHY', // Malformed code. Invalid length. + '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', + 'code_challenge' => 'R7T1y1HPNFvs1WDCrx4lfoBS6KD2c71pr8OHvULjvv8', + 'code_challenge_method' => 'S256', + ] + ) + ), + ] + ); + + try { + /* @var StubResponseType $response */ + $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } catch (OAuthServerException $e) { + $this->assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.'); } } From 4710743b8708fdf9c60d259f2bc76c34cd52157d Mon Sep 17 00:00:00 2001 From: Erick Torres Date: Fri, 16 Jun 2017 17:09:13 -0500 Subject: [PATCH 013/191] Add "dist: trusty" into travis setting file --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 13962424..f5bcbbd2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: php +dist: trusty sudo: false cache: From 170ce2fd2d238a6c0cba46dbb4d27f28089f35b8 Mon Sep 17 00:00:00 2001 From: Diogo Oliveira de Melo Date: Fri, 30 Jun 2017 15:42:23 -0300 Subject: [PATCH 014/191] Replaces array_key_exists by isset, which is faster, on ImplicitGrant. --- src/Grant/ImplicitGrant.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 62a48147..2ec9aed7 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -95,8 +95,8 @@ class ImplicitGrant extends AbstractAuthorizeGrant public function canRespondToAuthorizationRequest(ServerRequestInterface $request) { return ( - array_key_exists('response_type', $request->getQueryParams()) - && $request->getQueryParams()['response_type'] === 'token' + isset($request->getQueryParams()['response_type']) + && 'token' === $request->getQueryParams()['response_type'] && isset($request->getQueryParams()['client_id']) ); } From 6bdd108145d16af2f0493d29c45eef988088bd87 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Fri, 16 Jun 2017 16:51:16 +0100 Subject: [PATCH 015/191] Escape scope parameter to reduce pontential XSS vector --- src/Exception/OAuthServerException.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 6ffa0fb1..6cd82bb3 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -105,7 +105,10 @@ class OAuthServerException extends \Exception public static function invalidScope($scope, $redirectUri = null) { $errorMessage = 'The requested scope is invalid, unknown, or malformed'; - $hint = sprintf('Check the `%s` scope', $scope); + $hint = sprintf( + 'Check the `%s` scope', + htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false) + ); return new static($errorMessage, 5, 'invalid_scope', 400, $hint, $redirectUri); } From 57d199b88962c4f462719a55eb72cdb373efc7aa Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Fri, 16 Jun 2017 16:52:36 +0100 Subject: [PATCH 016/191] Stricter validation of code challenge value to match RFC 7636 requirements --- src/Grant/AuthCodeGrant.php | 7 ++ tests/Grant/AuthCodeGrantTest.php | 113 +++++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 1 deletion(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index df89400e..119c1f96 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -264,6 +264,13 @@ class AuthCodeGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidRequest('code_challenge'); } + if (preg_match("/^[A-Za-z0-9-._~]{43,128}$/", $codeChallenge) !== 1) { + throw OAuthServerException::invalidRequest( + 'code_challenge', + 'The code_challenge must be between 43 and 128 characters' + ); + } + $codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain'); if (in_array($codeChallengeMethod, ['plain', 'S256']) === false) { throw OAuthServerException::invalidRequest( diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 498fdb4e..63c9042a 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -164,13 +164,124 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase 'response_type' => 'code', 'client_id' => 'foo', 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => 'FOOBAR', + 'code_challenge' => str_repeat('A', 43), ] ); $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); } + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooShort() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new AuthCodeGrant( + $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), + $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), + new \DateInterval('PT10M') + ); + $grant->enableCodeExchangeProof(); + $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), + ] + ); + + $grant->validateAuthorizationRequest($request); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLong() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new AuthCodeGrant( + $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), + $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), + new \DateInterval('PT10M') + ); + $grant->enableCodeExchangeProof(); + $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), + ] + ); + + $grant->validateAuthorizationRequest($request); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testValidateAuthorizationRequestCodeChallengeInvalidCharacters() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new AuthCodeGrant( + $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), + $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), + new \DateInterval('PT10M') + ); + $grant->enableCodeExchangeProof(); + $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) . '!', + ] + ); + + $grant->validateAuthorizationRequest($request); + } + /** * @expectedException \League\OAuth2\Server\Exception\OAuthServerException * @expectedExceptionCode 3 From 2f8de3d2302beb490abb9475cf426148801c25c4 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Fri, 16 Jun 2017 16:58:49 +0100 Subject: [PATCH 017/191] Ensure the server is the exclusive owner of the key --- src/CryptKey.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/CryptKey.php b/src/CryptKey.php index aedeafb0..8133c607 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -44,6 +44,23 @@ class CryptKey throw new \LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath)); } + // Verify the permissions of the key + $keyPathPerms = decoct(fileperms($keyPath) & 0777); + if ($keyPathPerms !== '600') { + // Attempt to correct the permissions + if (chmod($keyPath, 0600) === false) { + // @codeCoverageIgnoreStart + throw new \LogicException( + sprintf( + 'Key file "%s" permissions are not correct, should be 600 instead of %s, unable to automatically resolve the issue', + $keyPath, + $keyPathPerms + ) + ); + // @codeCoverageIgnoreEnd + } + } + $this->keyPath = $keyPath; $this->passPhrase = $passPhrase; } From 63530443fed426621d93abe19584aefcbe22e33d Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Fri, 16 Jun 2017 16:59:29 +0100 Subject: [PATCH 018/191] Better error checking when saving a temporary key to ensure file was written successfully and the server is the exclusive mode --- src/CryptKey.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/CryptKey.php b/src/CryptKey.php index 8133c607..f3051d04 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -74,7 +74,8 @@ class CryptKey */ private function saveKeyToFile($key) { - $keyPath = sys_get_temp_dir() . '/' . sha1($key) . '.key'; + $tmpDir = sys_get_temp_dir(); + $keyPath = $tmpDir . '/' . sha1($key) . '.key'; if (!file_exists($keyPath) && !touch($keyPath)) { // @codeCoverageIgnoreStart @@ -82,7 +83,17 @@ class CryptKey // @codeCoverageIgnoreEnd } - file_put_contents($keyPath, $key); + if (file_put_contents($keyPath, $key) === false) { + // @codeCoverageIgnoreStart + throw new \RuntimeException('Unable to write key file to temporary directory "%s"', $tmpDir); + // @codeCoverageIgnoreEnd + } + + if (chmod($keyPath, 0600) === false) { + // @codeCoverageIgnoreStart + throw new \RuntimeException('The key file "%s" file mode could not be changed with chmod to 600', $keyPath); + // @codeCoverageIgnoreEnd + } return 'file://' . $keyPath; } From 4a717104fa57287182f73b57a7788b32b629bfb9 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 15:30:21 +0100 Subject: [PATCH 019/191] Shuffle the contents of the authorization code payload --- src/Grant/AuthCodeGrant.php | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 119c1f96..5adb2a69 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -311,6 +311,26 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $authorizationRequest->getScopes() ); + $payload = [ + 'client_id' => $authCode->getClient()->getIdentifier(), + 'redirect_uri' => $authCode->getRedirectUri(), + 'auth_code_id' => $authCode->getIdentifier(), + 'scopes' => $authCode->getScopes(), + 'user_id' => $authCode->getUserIdentifier(), + 'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'), + 'code_challenge' => $authorizationRequest->getCodeChallenge(), + 'code_challenge_method ' => $authorizationRequest->getCodeChallengeMethod(), + '_padding' => base64_encode(random_bytes(mt_rand(8, 256))) + ]; + + // Shuffle the payload so that the structure is no longer know and obvious + $keys = array_keys($payload); + shuffle($keys); + $shuffledPayload = []; + foreach ($keys as $key) { + $shuffledPayload[$key] = $payload[$key]; + } + $response = new RedirectResponse(); $response->setRedirectUri( $this->makeRedirectUri( @@ -318,16 +338,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant [ 'code' => $this->encrypt( json_encode( - [ - 'client_id' => $authCode->getClient()->getIdentifier(), - 'redirect_uri' => $authCode->getRedirectUri(), - 'auth_code_id' => $authCode->getIdentifier(), - 'scopes' => $authCode->getScopes(), - 'user_id' => $authCode->getUserIdentifier(), - 'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'), - 'code_challenge' => $authorizationRequest->getCodeChallenge(), - 'code_challenge_method ' => $authorizationRequest->getCodeChallengeMethod(), - ] + $shuffledPayload ) ), 'state' => $authorizationRequest->getState(), From 1af4012df459cf8382b9d184af59161fbe62f192 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 15:57:40 +0100 Subject: [PATCH 020/191] New property on AuthorizationServer to receive an encryption key which is used for future encryption/decryption instead of keybased encryption/decryption --- composer.json | 3 ++- src/AuthorizationServer.php | 26 +++++++++++++++++++ src/CryptTrait.php | 25 ++++++++++++++++++ src/Grant/GrantTypeInterface.php | 7 +++++ tests/AuthorizationServerTest.php | 6 +++++ .../AuthorizationServerMiddlewareTest.php | 2 ++ 6 files changed, 68 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 64df3ccb..7ce6374e 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,8 @@ "league/event": "^2.1", "lcobucci/jwt": "^3.1", "paragonie/random_compat": "^1.1 || ^2.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0", + "defuse/php-encryption": "^2.1" }, "require-dev": { "phpunit/phpunit": "^4.8 || ^5.0", diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 0517124a..4ba9b8d1 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -26,6 +26,8 @@ class AuthorizationServer implements EmitterAwareInterface { use EmitterAwareTrait; + const ENCRYPTION_KEY_ERROR = 'You must set the encryption key going forward to improve the security of this library - see this page for more information https://xxxx/xxxx'; + /** * @var GrantTypeInterface[] */ @@ -66,6 +68,11 @@ class AuthorizationServer implements EmitterAwareInterface */ private $scopeRepository; + /** + * @var string + */ + private $encryptionKey; + /** * New server instance. * @@ -101,6 +108,16 @@ class AuthorizationServer implements EmitterAwareInterface $this->responseType = $responseType; } + /** + * Set the encryption key + * + * @param string $key + */ + public function setEncryptionKey($key) + { + $this->encryptionKey = $key; + } + /** * Enable a grant type on the server. * @@ -120,6 +137,11 @@ class AuthorizationServer implements EmitterAwareInterface $grantType->setPublicKey($this->publicKey); $grantType->setEmitter($this->getEmitter()); + if ($this->encryptionKey === null) { + error_log(self::ENCRYPTION_KEY_ERROR); + } + $grantType->setEncryptionKey($this->encryptionKey); + $this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType; $this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] = $accessTokenTTL; } @@ -135,6 +157,10 @@ class AuthorizationServer implements EmitterAwareInterface */ public function validateAuthorizationRequest(ServerRequestInterface $request) { + if ($this->encryptionKey === null) { + error_log(self::ENCRYPTION_KEY_ERROR); + } + foreach ($this->enabledGrantTypes as $grantType) { if ($grantType->canRespondToAuthorizationRequest($request)) { return $grantType->validateAuthorizationRequest($request); diff --git a/src/CryptTrait.php b/src/CryptTrait.php index d417e115..8e6808d0 100644 --- a/src/CryptTrait.php +++ b/src/CryptTrait.php @@ -11,6 +11,8 @@ namespace League\OAuth2\Server; +use Defuse\Crypto\Crypto; + trait CryptTrait { /** @@ -23,6 +25,11 @@ trait CryptTrait */ protected $publicKey; + /** + * @var string + */ + protected $encryptionKey; + /** * Set path to private key. * @@ -54,6 +61,10 @@ trait CryptTrait */ protected function encrypt($unencryptedData) { + if ($this->encryptionKey !== null) { + return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey); + } + $privateKey = openssl_pkey_get_private($this->privateKey->getKeyPath(), $this->privateKey->getPassPhrase()); $privateKeyDetails = @openssl_pkey_get_details($privateKey); if ($privateKeyDetails === null) { @@ -91,6 +102,10 @@ trait CryptTrait */ protected function decrypt($encryptedData) { + if ($this->encryptionKey !== null) { + return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey); + } + $publicKey = openssl_pkey_get_public($this->publicKey->getKeyPath()); $publicKeyDetails = @openssl_pkey_get_details($publicKey); if ($publicKeyDetails === null) { @@ -118,4 +133,14 @@ trait CryptTrait return $output; } + + /** + * Set the encryption key + * + * @param string $key + */ + public function setEncryptionKey($key = null) + { + $this->encryptionKey = $key; + } } diff --git a/src/Grant/GrantTypeInterface.php b/src/Grant/GrantTypeInterface.php index c01a571d..34028ccb 100644 --- a/src/Grant/GrantTypeInterface.php +++ b/src/Grant/GrantTypeInterface.php @@ -132,4 +132,11 @@ interface GrantTypeInterface extends EmitterAwareInterface * @param CryptKey $publicKey */ public function setPublicKey(CryptKey $publicKey); + + /** + * Set the encryption key + * + * @param string|null $key + */ + public function setEncryptionKey($key = null); } diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 909da159..ead431b3 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -36,6 +36,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase 'file://' . __DIR__ . '/Stubs/public.key', new StubResponseType() ); + $server->setEncryptionKey(base64_encode(random_bytes(36))); $server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M')); @@ -66,6 +67,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase 'file://' . __DIR__ . '/Stubs/public.key', new StubResponseType() ); + $server->setEncryptionKey(base64_encode(random_bytes(36))); $server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M')); @@ -87,6 +89,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase 'file://' . __DIR__ . '/Stubs/private.key', 'file://' . __DIR__ . '/Stubs/public.key' ); + $server->setEncryptionKey(base64_encode(random_bytes(36))); $abstractGrantReflection = new \ReflectionClass($server); $method = $abstractGrantReflection->getMethod('getResponseType'); @@ -106,6 +109,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase 'file://' . __DIR__ . '/Stubs/private.key', 'file://' . __DIR__ . '/Stubs/public.key' ); + $server->setEncryptionKey(base64_encode(random_bytes(36))); $authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(); $authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity()); @@ -152,6 +156,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase 'file://' . __DIR__ . '/Stubs/private.key', 'file://' . __DIR__ . '/Stubs/public.key' ); + $server->setEncryptionKey(base64_encode(random_bytes(36))); $server->enableGrantType($grant); $request = new ServerRequest( @@ -184,6 +189,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase 'file://' . __DIR__ . '/Stubs/private.key', 'file://' . __DIR__ . '/Stubs/public.key' ); + $server->setEncryptionKey(base64_encode(random_bytes(36))); $request = new ServerRequest( [], diff --git a/tests/Middleware/AuthorizationServerMiddlewareTest.php b/tests/Middleware/AuthorizationServerMiddlewareTest.php index affc2a3b..f4c417f0 100644 --- a/tests/Middleware/AuthorizationServerMiddlewareTest.php +++ b/tests/Middleware/AuthorizationServerMiddlewareTest.php @@ -36,6 +36,7 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase 'file://' . __DIR__ . '/../Stubs/public.key', new StubResponseType() ); + $server->setEncryptionKey(base64_encode(random_bytes(36))); $server->enableGrantType(new ClientCredentialsGrant()); @@ -69,6 +70,7 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase 'file://' . __DIR__ . '/../Stubs/public.key', new StubResponseType() ); + $server->setEncryptionKey(base64_encode(random_bytes(36))); $server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M')); From 76c1349181138532747ad704b4907b558dfecad4 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 16:29:23 +0100 Subject: [PATCH 021/191] Updated random_compat version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7ce6374e..5360a945 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "ext-openssl": "*", "league/event": "^2.1", "lcobucci/jwt": "^3.1", - "paragonie/random_compat": "^1.1 || ^2.0", + "paragonie/random_compat": "^2.0", "psr/http-message": "^1.0", "defuse/php-encryption": "^2.1" }, From dd5eee150d2e30e792bf982cde83700929a0e72d Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 16:29:50 +0100 Subject: [PATCH 022/191] Ensure response type also has access to the encryption key --- src/AuthorizationServer.php | 1 + src/ResponseTypes/ResponseTypeInterface.php | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 4ba9b8d1..8c100775 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -226,6 +226,7 @@ class AuthorizationServer implements EmitterAwareInterface } $this->responseType->setPrivateKey($this->privateKey); + $this->responseType->setEncryptionKey($this->encryptionKey); return $this->responseType; } diff --git a/src/ResponseTypes/ResponseTypeInterface.php b/src/ResponseTypes/ResponseTypeInterface.php index 9f358a53..8ac20b8c 100644 --- a/src/ResponseTypes/ResponseTypeInterface.php +++ b/src/ResponseTypes/ResponseTypeInterface.php @@ -33,4 +33,11 @@ interface ResponseTypeInterface * @return ResponseInterface */ public function generateHttpResponse(ResponseInterface $response); + + /** + * Set the encryption key + * + * @param string|null $key + */ + public function setEncryptionKey($key = null); } From 1954120c3df2ddd7256a2dafc1e2b34d3d40ddab Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 16:30:29 +0100 Subject: [PATCH 023/191] Use catch all exception --- src/Grant/RefreshTokenGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php index 17448a92..53dfdf7d 100644 --- a/src/Grant/RefreshTokenGrant.php +++ b/src/Grant/RefreshTokenGrant.php @@ -102,7 +102,7 @@ class RefreshTokenGrant extends AbstractGrant // Validate refresh token try { $refreshToken = $this->decrypt($encryptedRefreshToken); - } catch (\LogicException $e) { + } catch (\Exception $e) { throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token'); } From 107cfc3678d3ed9d44b88e2654b37d101ed6b964 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 16:30:36 +0100 Subject: [PATCH 024/191] Updated examples --- examples/composer.json | 5 +- examples/composer.lock | 178 ++++++++++++++++++++----- examples/public/auth_code.php | 1 + examples/public/client_credentials.php | 1 + examples/public/implicit.php | 1 + examples/public/middleware_use.php | 1 + examples/public/password.php | 1 + examples/public/refresh_token.php | 7 +- 8 files changed, 158 insertions(+), 37 deletions(-) diff --git a/examples/composer.json b/examples/composer.json index 3c6e550b..79ab47cd 100644 --- a/examples/composer.json +++ b/examples/composer.json @@ -5,8 +5,9 @@ "require-dev": { "league/event": "^2.1", "lcobucci/jwt": "^3.1", - "paragonie/random_compat": "^1.1", - "psr/http-message": "^1.0" + "paragonie/random_compat": "^2.0", + "psr/http-message": "^1.0", + "defuse/php-encryption": "^2.1" }, "autoload": { "psr-4": { diff --git a/examples/composer.lock b/examples/composer.lock index 9c6c83cb..7210f31e 100644 --- a/examples/composer.lock +++ b/examples/composer.lock @@ -4,23 +4,25 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "48bcb7a3514d7c7f271c554ba1440124", - "content-hash": "e41be75973527cb9d63f27ad14ac8624", + "content-hash": "9813ed7c3b6dcf107f44df9392935b8f", "packages": [ { "name": "container-interop/container-interop", - "version": "1.1.0", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/container-interop/container-interop.git", - "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e" + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e", - "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e", + "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8", + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8", "shasum": "" }, + "require": { + "psr/container": "^1.0" + }, "type": "library", "autoload": { "psr-4": { @@ -32,7 +34,8 @@ "MIT" ], "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", - "time": "2014-12-30 15:22:37" + "homepage": "https://github.com/container-interop/container-interop", + "time": "2017-02-14T19:40:03+00:00" }, { "name": "nikic/fast-route", @@ -75,7 +78,7 @@ "router", "routing" ], - "time": "2015-06-18 19:15:47" + "time": "2015-06-18T19:15:47+00:00" }, { "name": "pimple/pimple", @@ -121,20 +124,69 @@ "container", "dependency injection" ], - "time": "2015-09-11 15:10:35" + "time": "2015-09-11T15:10:35+00:00" }, { - "name": "psr/http-message", - "version": "1.0", + "name": "psr/container", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298" + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", - "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2017-02-14T16:28:37+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", "shasum": "" }, "require": { @@ -162,6 +214,7 @@ } ], "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", "keywords": [ "http", "http-message", @@ -170,7 +223,7 @@ "request", "response" ], - "time": "2015-05-04 20:22:00" + "time": "2016-08-06T14:39:51+00:00" }, { "name": "slim/slim", @@ -236,22 +289,85 @@ "micro", "router" ], - "time": "2015-12-07 14:11:09" + "time": "2015-12-07T14:11:09+00:00" } ], "packages-dev": [ { - "name": "lcobucci/jwt", - "version": "3.1.1", + "name": "defuse/php-encryption", + "version": "v2.1.0", "source": { "type": "git", - "url": "https://github.com/lcobucci/jwt.git", - "reference": "afea8e682e911a21574fd8519321b32522fa25b5" + "url": "https://github.com/defuse/php-encryption.git", + "reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/jwt/zipball/afea8e682e911a21574fd8519321b32522fa25b5", - "reference": "afea8e682e911a21574fd8519321b32522fa25b5", + "url": "https://api.github.com/repos/defuse/php-encryption/zipball/5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689", + "reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "paragonie/random_compat": "~2.0", + "php": ">=5.4.0" + }, + "require-dev": { + "nikic/php-parser": "^2.0|^3.0", + "phpunit/phpunit": "^4|^5" + }, + "bin": [ + "bin/generate-defuse-key" + ], + "type": "library", + "autoload": { + "psr-4": { + "Defuse\\Crypto\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Hornby", + "email": "taylor@defuse.ca", + "homepage": "https://defuse.ca/" + }, + { + "name": "Scott Arciszewski", + "email": "info@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "Secure PHP Encryption Library", + "keywords": [ + "aes", + "authenticated encryption", + "cipher", + "crypto", + "cryptography", + "encrypt", + "encryption", + "openssl", + "security", + "symmetric key cryptography" + ], + "time": "2017-05-18T21:28:48+00:00" + }, + { + "name": "lcobucci/jwt", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/jwt.git", + "reference": "ddce703826f9c5229781933b1a39069e38e6a0f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/ddce703826f9c5229781933b1a39069e38e6a0f3", + "reference": "ddce703826f9c5229781933b1a39069e38e6a0f3", "shasum": "" }, "require": { @@ -259,7 +375,7 @@ "php": ">=5.5" }, "require-dev": { - "mdanter/ecc": "~0.3", + "mdanter/ecc": "~0.3.1", "mikey179/vfsstream": "~1.5", "phpmd/phpmd": "~2.2", "phpunit/php-invoker": "~1.1", @@ -296,7 +412,7 @@ "JWS", "jwt" ], - "time": "2016-03-24 22:46:13" + "time": "2016-10-31T20:09:32+00:00" }, { "name": "league/event", @@ -346,20 +462,20 @@ "event", "listener" ], - "time": "2015-05-21 12:24:47" + "time": "2015-05-21T12:24:47+00:00" }, { "name": "paragonie/random_compat", - "version": "v1.4.1", + "version": "v2.0.10", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "c7e26a21ba357863de030f0b9e701c7d04593774" + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/c7e26a21ba357863de030f0b9e701c7d04593774", - "reference": "c7e26a21ba357863de030f0b9e701c7d04593774", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d", + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d", "shasum": "" }, "require": { @@ -394,7 +510,7 @@ "pseudorandom", "random" ], - "time": "2016-03-18 20:34:03" + "time": "2017-03-13T16:27:32+00:00" } ], "aliases": [], diff --git a/examples/public/auth_code.php b/examples/public/auth_code.php index e014f55a..51f1838c 100644 --- a/examples/public/auth_code.php +++ b/examples/public/auth_code.php @@ -46,6 +46,7 @@ $app = new App([ $privateKeyPath, $publicKeyPath ); + $server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'); // Enable the authentication code grant on the server with a token TTL of 1 hour $server->enableGrantType( diff --git a/examples/public/client_credentials.php b/examples/public/client_credentials.php index c982f275..fd7f5ee9 100644 --- a/examples/public/client_credentials.php +++ b/examples/public/client_credentials.php @@ -42,6 +42,7 @@ $app = new App([ $privateKey, $publicKey ); + $server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'); // Enable the client credentials grant on the server $server->enableGrantType( diff --git a/examples/public/implicit.php b/examples/public/implicit.php index 2a82097f..b496ce19 100644 --- a/examples/public/implicit.php +++ b/examples/public/implicit.php @@ -42,6 +42,7 @@ $app = new App([ $privateKeyPath, $publicKeyPath ); + $server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'); // Enable the implicit grant on the server with a token TTL of 1 hour $server->enableGrantType(new ImplicitGrant(new \DateInterval('PT1H'))); diff --git a/examples/public/middleware_use.php b/examples/public/middleware_use.php index 21f6bc23..32b17aff 100644 --- a/examples/public/middleware_use.php +++ b/examples/public/middleware_use.php @@ -48,6 +48,7 @@ $app = new App([ $privateKeyPath, $publicKeyPath ); + $server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'); // Enable the authentication code grant on the server with a token TTL of 1 hour $server->enableGrantType( diff --git a/examples/public/password.php b/examples/public/password.php index 02a85a56..b62acc95 100644 --- a/examples/public/password.php +++ b/examples/public/password.php @@ -26,6 +26,7 @@ $app = new App([ 'file://' . __DIR__ . '/../private.key', // path to private key 'file://' . __DIR__ . '/../public.key' // path to public key ); + $server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'); $grant = new PasswordGrant( new UserRepository(), // instance of UserRepositoryInterface diff --git a/examples/public/refresh_token.php b/examples/public/refresh_token.php index 0649c117..b4efd45b 100644 --- a/examples/public/refresh_token.php +++ b/examples/public/refresh_token.php @@ -43,6 +43,7 @@ $app = new App([ $privateKeyPath, $publicKeyPath ); + $server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'); // Enable the refresh token grant on the server $grant = new RefreshTokenGrant($refreshTokenRepository); @@ -66,10 +67,8 @@ $app->post('/access_token', function (ServerRequestInterface $request, ResponseI } catch (OAuthServerException $exception) { return $exception->generateHttpResponse($response); } catch (\Exception $exception) { - $body = new Stream('php://temp', 'r+'); - $body->write($exception->getMessage()); - - return $response->withStatus(500)->withBody($body); + $response->getBody()->write($exception->getMessage()); + return $response->withStatus(500); } }); From e123fe82d0f70191276b88acf377d4fbe19b5cc3 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 16:33:56 +0100 Subject: [PATCH 025/191] Ignore error_log messages in code coverage --- src/AuthorizationServer.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 8c100775..bbe9c4b8 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -138,7 +138,9 @@ class AuthorizationServer implements EmitterAwareInterface $grantType->setEmitter($this->getEmitter()); if ($this->encryptionKey === null) { + // @codeCoverageIgnoreStart error_log(self::ENCRYPTION_KEY_ERROR); + // @codeCoverageIgnoreEnd } $grantType->setEncryptionKey($this->encryptionKey); @@ -158,7 +160,9 @@ class AuthorizationServer implements EmitterAwareInterface public function validateAuthorizationRequest(ServerRequestInterface $request) { if ($this->encryptionKey === null) { + // @codeCoverageIgnoreStart error_log(self::ENCRYPTION_KEY_ERROR); + // @codeCoverageIgnoreEnd } foreach ($this->enabledGrantTypes as $grantType) { From 0706d66c76e1ff77fe004dcde13c1a3120b71b36 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 16:37:53 +0100 Subject: [PATCH 026/191] =?UTF-8?q?Don=E2=80=99t=20pad=20and=20shuffle=20t?= =?UTF-8?q?he=20payload=20if=20an=20encryption=20key=20has=20been=20set?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Grant/AuthCodeGrant.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 5adb2a69..a8787a54 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -320,15 +320,20 @@ class AuthCodeGrant extends AbstractAuthorizeGrant 'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'), 'code_challenge' => $authorizationRequest->getCodeChallenge(), 'code_challenge_method ' => $authorizationRequest->getCodeChallengeMethod(), - '_padding' => base64_encode(random_bytes(mt_rand(8, 256))) ]; - // Shuffle the payload so that the structure is no longer know and obvious - $keys = array_keys($payload); - shuffle($keys); - $shuffledPayload = []; - foreach ($keys as $key) { - $shuffledPayload[$key] = $payload[$key]; + if ($this->encryptionKey === null) { + // Add padding to vary the length of the payload + $payload['_padding'] = base64_encode(random_bytes(mt_rand(8, 256))); + // Shuffle the payload so that the structure is no longer know and obvious + $keys = array_keys($payload); + shuffle($keys); + $shuffledPayload = []; + foreach ($keys as $key) { + $shuffledPayload[$key] = $payload[$key]; + } + } else { + $shuffledPayload = $payload; } $response = new RedirectResponse(); From 765a01021bdd7abaa35d4f1347e6e883f51d82c4 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 16:37:59 +0100 Subject: [PATCH 027/191] Updated error message --- src/AuthorizationServer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index bbe9c4b8..a298944f 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -26,7 +26,7 @@ class AuthorizationServer implements EmitterAwareInterface { use EmitterAwareTrait; - const ENCRYPTION_KEY_ERROR = 'You must set the encryption key going forward to improve the security of this library - see this page for more information https://xxxx/xxxx'; + const ENCRYPTION_KEY_ERROR = 'You must set the encryption key going forward to improve the security of this library - see this page for more information https://oauth2.thephpleague.com/v5-security-improvements/'; /** * @var GrantTypeInterface[] From 09c167ac43548af6320546a23a51ac50349c0155 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 17:17:55 +0100 Subject: [PATCH 028/191] Updated changelog and readme --- CHANGELOG.md | 9 +++++++++ README.md | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ae31e9d..9391de30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 5.1.4 (released 2017-07-01) + +* Fixed multiple security vulnerabilities as a result of a security audit paid for by the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source). All users of this library are encouraged to update as soon as possible to this version or version 6.0 or greater. + * It is recommended on each `AuthorizationServer` instance you set the `setEncryptionKey()`. This will result in stronger encryption being used. If this method is not set messages will be sent to the defined error handling routines (using `error_log`). Please see the examples and documentation for examples. +* TravisCI now tests PHP 7.1 (Issue #671) +* Fix middleware example fatal error (Issue #682) +* Fix typo in the first README sentence (Issue #690) +* Corrected DateInterval from 1 min to 1 month (Issue #709) + ## 5.1.3 (released 2016-10-12) * Fixed WWW-Authenticate header (Issue #669) diff --git a/README.md b/README.md index 8da0cf44..d86bb3f7 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ This package is released under the MIT License. See the bundled [LICENSE](https: This code is principally developed and maintained by [Alex Bilbie](https://twitter.com/alexbilbie). -Special thanks to [all of these awesome contributors](https://github.com/thephpleague/oauth2-server/contributors) +Special thanks to [all of these awesome contributors](https://github.com/thephpleague/oauth2-server/contributors). + +Additional thanks go to the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source) for funding a security audit of this library. The initial code was developed as part of the [Linkey](http://linkey.blogs.lincoln.ac.uk) project which was funded by [JISC](http://jisc.ac.uk) under the Access and Identity Management programme. From aee1779432498dfc3db4677476b21ff44b524c65 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 16:19:23 +0000 Subject: [PATCH 029/191] Apply fixes from StyleCI --- examples/public/middleware_use.php | 3 ++- examples/public/refresh_token.php | 2 +- src/Grant/AuthCodeGrant.php | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/public/middleware_use.php b/examples/public/middleware_use.php index 32b17aff..f6a7fcf9 100644 --- a/examples/public/middleware_use.php +++ b/examples/public/middleware_use.php @@ -8,11 +8,11 @@ */ use League\OAuth2\Server\AuthorizationServer; -use League\OAuth2\Server\ResourceServer; use League\OAuth2\Server\Grant\AuthCodeGrant; use League\OAuth2\Server\Grant\RefreshTokenGrant; use League\OAuth2\Server\Middleware\AuthorizationServerMiddleware; use League\OAuth2\Server\Middleware\ResourceServerMiddleware; +use League\OAuth2\Server\ResourceServer; use OAuth2ServerExamples\Repositories\AccessTokenRepository; use OAuth2ServerExamples\Repositories\AuthCodeRepository; use OAuth2ServerExamples\Repositories\ClientRepository; @@ -75,6 +75,7 @@ $app = new App([ new AccessTokenRepository(), $publicKeyPath ); + return $server; }, ]); diff --git a/examples/public/refresh_token.php b/examples/public/refresh_token.php index b4efd45b..25c32100 100644 --- a/examples/public/refresh_token.php +++ b/examples/public/refresh_token.php @@ -17,7 +17,6 @@ use OAuth2ServerExamples\Repositories\ScopeRepository; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Slim\App; -use Zend\Diactoros\Stream; include __DIR__ . '/../vendor/autoload.php'; @@ -68,6 +67,7 @@ $app->post('/access_token', function (ServerRequestInterface $request, ResponseI return $exception->generateHttpResponse($response); } catch (\Exception $exception) { $response->getBody()->write($exception->getMessage()); + return $response->withStatus(500); } }); diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index a8787a54..2a05355e 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -264,7 +264,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidRequest('code_challenge'); } - if (preg_match("/^[A-Za-z0-9-._~]{43,128}$/", $codeChallenge) !== 1) { + if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeChallenge) !== 1) { throw OAuthServerException::invalidRequest( 'code_challenge', 'The code_challenge must be between 43 and 128 characters' From 06424fdbe2fff2475976b2f7a46a6b4a4f2cdde4 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 17:24:11 +0100 Subject: [PATCH 030/191] Use Trusty for TravisCI --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 13962424..9536e362 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,8 @@ language: php sudo: false +dist: trusty + cache: directories: - vendor From cc2c3a704446700653235b42116a4540e460e8b3 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 18:07:01 +0100 Subject: [PATCH 031/191] Removed unnecessary stuff from composer.json --- composer.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/composer.json b/composer.json index 5360a945..51d467b5 100644 --- a/composer.json +++ b/composer.json @@ -61,11 +61,6 @@ "LeagueTests\\": "tests/" } }, - "extra": { - "branch-alias": { - "dev-V5-WIP": "5.0-dev" - } - }, "suggest": { "indigophp/hash-compat": "Polyfill for hash_equals function for PHP 5.5" } From 7953f27b38ee349686d615024760a993a40a1511 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 18:07:09 +0100 Subject: [PATCH 032/191] Stop testing HHVM --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9536e362..c0ce917b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,6 @@ language: php sudo: false -dist: trusty - cache: directories: - vendor @@ -14,7 +12,6 @@ php: - 5.6 - 7.0 - 7.1 - - hhvm install: - travis_retry composer install --no-interaction --prefer-source From 0f73bf0054604d3620e121b70d81c97f873b66ac Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 18:07:42 +0100 Subject: [PATCH 033/191] Encryption key just uses Defuse\Crypto now, no key based crypto --- src/CryptTrait.php | 94 +++------------------------------------------- 1 file changed, 6 insertions(+), 88 deletions(-) diff --git a/src/CryptTrait.php b/src/CryptTrait.php index 8e6808d0..805969b0 100644 --- a/src/CryptTrait.php +++ b/src/CryptTrait.php @@ -1,11 +1,9 @@ * @copyright Copyright (c) Alex Bilbie * @license http://mit-license.org/ - * * @link https://github.com/thephpleague/oauth2-server */ @@ -15,80 +13,26 @@ use Defuse\Crypto\Crypto; trait CryptTrait { - /** - * @var CryptKey - */ - protected $privateKey; - - /** - * @var CryptKey - */ - protected $publicKey; - /** * @var string */ protected $encryptionKey; - /** - * Set path to private key. - * - * @param CryptKey $privateKey - */ - public function setPrivateKey(CryptKey $privateKey) - { - $this->privateKey = $privateKey; - } - - /** - * Set path to public key. - * - * @param CryptKey $publicKey - */ - public function setPublicKey(CryptKey $publicKey) - { - $this->publicKey = $publicKey; - } - /** * Encrypt data with a private key. * * @param string $unencryptedData * * @throws \LogicException - * * @return string */ protected function encrypt($unencryptedData) { - if ($this->encryptionKey !== null) { + try { return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey); + } catch (\Exception $e) { + throw new \LogicException($e->getMessage()); } - - $privateKey = openssl_pkey_get_private($this->privateKey->getKeyPath(), $this->privateKey->getPassPhrase()); - $privateKeyDetails = @openssl_pkey_get_details($privateKey); - if ($privateKeyDetails === null) { - throw new \LogicException( - sprintf('Could not get details of private key: %s', $this->privateKey->getKeyPath()) - ); - } - - $chunkSize = ceil($privateKeyDetails['bits'] / 8) - 11; - $output = ''; - - while ($unencryptedData) { - $chunk = substr($unencryptedData, 0, $chunkSize); - $unencryptedData = substr($unencryptedData, $chunkSize); - if (openssl_private_encrypt($chunk, $encrypted, $privateKey) === false) { - // @codeCoverageIgnoreStart - throw new \LogicException('Failed to encrypt data'); - // @codeCoverageIgnoreEnd - } - $output .= $encrypted; - } - openssl_pkey_free($privateKey); - - return base64_encode($output); } /** @@ -97,41 +41,15 @@ trait CryptTrait * @param string $encryptedData * * @throws \LogicException - * * @return string */ protected function decrypt($encryptedData) { - if ($this->encryptionKey !== null) { + try { return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey); + } catch (\Exception $e) { + throw new \LogicException($e->getMessage()); } - - $publicKey = openssl_pkey_get_public($this->publicKey->getKeyPath()); - $publicKeyDetails = @openssl_pkey_get_details($publicKey); - if ($publicKeyDetails === null) { - throw new \LogicException( - sprintf('Could not get details of public key: %s', $this->publicKey->getKeyPath()) - ); - } - - $chunkSize = ceil($publicKeyDetails['bits'] / 8); - $output = ''; - - $encryptedData = base64_decode($encryptedData); - - while ($encryptedData) { - $chunk = substr($encryptedData, 0, $chunkSize); - $encryptedData = substr($encryptedData, $chunkSize); - if (openssl_public_decrypt($chunk, $decrypted, $publicKey/*, OPENSSL_PKCS1_OAEP_PADDING*/) === false) { - // @codeCoverageIgnoreStart - throw new \LogicException('Failed to decrypt data'); - // @codeCoverageIgnoreEnd - } - $output .= $decrypted; - } - openssl_pkey_free($publicKey); - - return $output; } /** From 850793ab88e0baa6c063cd662ebec41b1bb617a5 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 18:08:49 +0100 Subject: [PATCH 034/191] Added missing methods --- .../BearerTokenValidator.php | 16 ++++++++++++++++ src/Grant/AbstractGrant.php | 16 ++++++++++++++++ src/Grant/GrantTypeInterface.php | 7 ------- src/ResponseTypes/AbstractResponseType.php | 17 +++++++++++++++++ 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/AuthorizationValidators/BearerTokenValidator.php b/src/AuthorizationValidators/BearerTokenValidator.php index 2bab4cb5..1547f6bf 100644 --- a/src/AuthorizationValidators/BearerTokenValidator.php +++ b/src/AuthorizationValidators/BearerTokenValidator.php @@ -12,6 +12,7 @@ namespace League\OAuth2\Server\AuthorizationValidators; use Lcobucci\JWT\Parser; use Lcobucci\JWT\Signer\Rsa\Sha256; use Lcobucci\JWT\ValidationData; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\CryptTrait; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; @@ -26,6 +27,11 @@ class BearerTokenValidator implements AuthorizationValidatorInterface */ private $accessTokenRepository; + /** + * @var \League\OAuth2\Server\CryptKey + */ + protected $publicKey; + /** * @param AccessTokenRepositoryInterface $accessTokenRepository */ @@ -34,6 +40,16 @@ class BearerTokenValidator implements AuthorizationValidatorInterface $this->accessTokenRepository = $accessTokenRepository; } + /** + * Set the private key + * + * @param \League\OAuth2\Server\CryptKey $key + */ + public function setPublicKey(CryptKey $key) + { + $this->publicKey = $key; + } + /** * {@inheritdoc} */ diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index d916e3f1..3ac98cf4 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -11,6 +11,7 @@ namespace League\OAuth2\Server\Grant; use League\Event\EmitterAwareTrait; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\CryptTrait; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\AuthCodeEntityInterface; @@ -75,6 +76,11 @@ abstract class AbstractGrant implements GrantTypeInterface */ protected $refreshTokenTTL; + /** + * @var \League\OAuth2\Server\CryptKey + */ + protected $privateKey; + /** * @param ClientRepositoryInterface $clientRepository */ @@ -131,6 +137,16 @@ abstract class AbstractGrant implements GrantTypeInterface $this->refreshTokenTTL = $refreshTokenTTL; } + /** + * Set the private key + * + * @param \League\OAuth2\Server\CryptKey $key + */ + public function setPrivateKey(CryptKey $key) + { + $this->privateKey = $key; + } + /** * Validate the client. * diff --git a/src/Grant/GrantTypeInterface.php b/src/Grant/GrantTypeInterface.php index 34028ccb..7aa98242 100644 --- a/src/Grant/GrantTypeInterface.php +++ b/src/Grant/GrantTypeInterface.php @@ -126,13 +126,6 @@ interface GrantTypeInterface extends EmitterAwareInterface */ public function setPrivateKey(CryptKey $privateKey); - /** - * Set the path to the public key. - * - * @param CryptKey $publicKey - */ - public function setPublicKey(CryptKey $publicKey); - /** * Set the encryption key * diff --git a/src/ResponseTypes/AbstractResponseType.php b/src/ResponseTypes/AbstractResponseType.php index 6e164392..0c256f17 100644 --- a/src/ResponseTypes/AbstractResponseType.php +++ b/src/ResponseTypes/AbstractResponseType.php @@ -11,6 +11,7 @@ namespace League\OAuth2\Server\ResponseTypes; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\CryptTrait; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; @@ -29,6 +30,11 @@ abstract class AbstractResponseType implements ResponseTypeInterface */ protected $refreshToken; + /** + * @var CryptKey + */ + protected $privateKey; + /** * {@inheritdoc} */ @@ -44,4 +50,15 @@ abstract class AbstractResponseType implements ResponseTypeInterface { $this->refreshToken = $refreshToken; } + + /** + * Set the private key + * + * @param \League\OAuth2\Server\CryptKey $key + */ + public function setPrivateKey(CryptKey $key) + { + $this->privateKey = $key; + } + } From 72349ef22f94b940a406c0e506fda3beb6f0289b Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 18:10:33 +0100 Subject: [PATCH 035/191] Encryption key is now always required so remove redundent code --- src/AuthorizationServer.php | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index a298944f..0df6b1f9 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -26,8 +26,6 @@ class AuthorizationServer implements EmitterAwareInterface { use EmitterAwareTrait; - const ENCRYPTION_KEY_ERROR = 'You must set the encryption key going forward to improve the security of this library - see this page for more information https://oauth2.thephpleague.com/v5-security-improvements/'; - /** * @var GrantTypeInterface[] */ @@ -108,16 +106,6 @@ class AuthorizationServer implements EmitterAwareInterface $this->responseType = $responseType; } - /** - * Set the encryption key - * - * @param string $key - */ - public function setEncryptionKey($key) - { - $this->encryptionKey = $key; - } - /** * Enable a grant type on the server. * @@ -136,12 +124,6 @@ class AuthorizationServer implements EmitterAwareInterface $grantType->setPrivateKey($this->privateKey); $grantType->setPublicKey($this->publicKey); $grantType->setEmitter($this->getEmitter()); - - if ($this->encryptionKey === null) { - // @codeCoverageIgnoreStart - error_log(self::ENCRYPTION_KEY_ERROR); - // @codeCoverageIgnoreEnd - } $grantType->setEncryptionKey($this->encryptionKey); $this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType; @@ -159,12 +141,6 @@ class AuthorizationServer implements EmitterAwareInterface */ public function validateAuthorizationRequest(ServerRequestInterface $request) { - if ($this->encryptionKey === null) { - // @codeCoverageIgnoreStart - error_log(self::ENCRYPTION_KEY_ERROR); - // @codeCoverageIgnoreEnd - } - foreach ($this->enabledGrantTypes as $grantType) { if ($grantType->canRespondToAuthorizationRequest($request)) { return $grantType->validateAuthorizationRequest($request); From 76c2b6f88cccaa07f9eceaab42e0306dd839cacb Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 18:11:10 +0100 Subject: [PATCH 036/191] AuthorizationServer no longer needs to know about the public key --- src/AuthorizationServer.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 0df6b1f9..ece5458b 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -3,7 +3,6 @@ * @author Alex Bilbie * @copyright Copyright (c) Alex Bilbie * @license http://mit-license.org/ - * * @link https://github.com/thephpleague/oauth2-server */ @@ -78,7 +77,7 @@ class AuthorizationServer implements EmitterAwareInterface * @param AccessTokenRepositoryInterface $accessTokenRepository * @param ScopeRepositoryInterface $scopeRepository * @param CryptKey|string $privateKey - * @param CryptKey|string $publicKey + * @param string $encryptionKey * @param null|ResponseTypeInterface $responseType */ public function __construct( @@ -86,7 +85,7 @@ class AuthorizationServer implements EmitterAwareInterface AccessTokenRepositoryInterface $accessTokenRepository, ScopeRepositoryInterface $scopeRepository, $privateKey, - $publicKey, + $encryptionKey, ResponseTypeInterface $responseType = null ) { $this->clientRepository = $clientRepository; @@ -98,11 +97,7 @@ class AuthorizationServer implements EmitterAwareInterface } $this->privateKey = $privateKey; - if ($publicKey instanceof CryptKey === false) { - $publicKey = new CryptKey($publicKey); - } - $this->publicKey = $publicKey; - + $this->encryptionKey = $encryptionKey; $this->responseType = $responseType; } From aac467e6165247741829f700a15985f67acad5a7 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 18:11:19 +0100 Subject: [PATCH 037/191] Fixed broken tests --- tests/AuthorizationServerTest.php | 13 +--- tests/CryptTraitTest.php | 28 +------- tests/Grant/AbstractGrantTest.php | 2 - tests/Grant/AuthCodeGrantTest.php | 65 ++++++------------- tests/Grant/ImplicitGrantTest.php | 5 -- tests/Grant/RefreshTokenGrantTest.php | 19 +++--- .../AuthorizationServerMiddlewareTest.php | 12 ++-- .../ResponseTypes/BearerResponseTypeTest.php | 19 ++---- tests/Stubs/CryptTraitStub.php | 8 ++- 9 files changed, 51 insertions(+), 120 deletions(-) diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index ead431b3..7b921ff9 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -33,10 +33,9 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), 'file://' . __DIR__ . '/Stubs/private.key', - 'file://' . __DIR__ . '/Stubs/public.key', + base64_encode(random_bytes(36)), new StubResponseType() ); - $server->setEncryptionKey(base64_encode(random_bytes(36))); $server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M')); @@ -64,10 +63,9 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase $accessTokenRepositoryMock, $scopeRepositoryMock, 'file://' . __DIR__ . '/Stubs/private.key', - 'file://' . __DIR__ . '/Stubs/public.key', + base64_encode(random_bytes(36)), new StubResponseType() ); - $server->setEncryptionKey(base64_encode(random_bytes(36))); $server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M')); @@ -89,7 +87,6 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase 'file://' . __DIR__ . '/Stubs/private.key', 'file://' . __DIR__ . '/Stubs/public.key' ); - $server->setEncryptionKey(base64_encode(random_bytes(36))); $abstractGrantReflection = new \ReflectionClass($server); $method = $abstractGrantReflection->getMethod('getResponseType'); @@ -109,7 +106,6 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase 'file://' . __DIR__ . '/Stubs/private.key', 'file://' . __DIR__ . '/Stubs/public.key' ); - $server->setEncryptionKey(base64_encode(random_bytes(36))); $authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(); $authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity()); @@ -120,9 +116,6 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase new \DateInterval('PT10M') ); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/Stubs/public.key')); - $server->enableGrantType($grant); $authRequest = new AuthorizationRequest(); @@ -156,7 +149,6 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase 'file://' . __DIR__ . '/Stubs/private.key', 'file://' . __DIR__ . '/Stubs/public.key' ); - $server->setEncryptionKey(base64_encode(random_bytes(36))); $server->enableGrantType($grant); $request = new ServerRequest( @@ -189,7 +181,6 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase 'file://' . __DIR__ . '/Stubs/private.key', 'file://' . __DIR__ . '/Stubs/public.key' ); - $server->setEncryptionKey(base64_encode(random_bytes(36))); $request = new ServerRequest( [], diff --git a/tests/CryptTraitTest.php b/tests/CryptTraitTest.php index d0ada49e..8c7d2642 100644 --- a/tests/CryptTraitTest.php +++ b/tests/CryptTraitTest.php @@ -8,7 +8,7 @@ use LeagueTests\Stubs\CryptTraitStub; class CryptTraitTest extends \PHPUnit_Framework_TestCase { /** - * CryptTrait stub + * @var \LeagueTests\Stubs\CryptTraitStub */ protected $cryptStub; @@ -26,30 +26,4 @@ class CryptTraitTest extends \PHPUnit_Framework_TestCase $this->assertNotEquals($payload, $encrypted); $this->assertEquals($payload, $plainText); } - - /** - * @expectedException \LogicException - */ - public function testBadPrivateKey() - { - $this->cryptStub->setPrivateKey(new CryptKey(__DIR__ . '/Stubs/public.key')); - $this->cryptStub->doEncrypt(''); - } - - /** - * @expectedException \LogicException - */ - public function testBadPublicKey() - { - $this->cryptStub->setPublicKey(new CryptKey(__DIR__ . '/Stubs/private.key')); - $this->cryptStub->doDecrypt(''); - } - - /** - * @expectedException \LogicException - */ - public function testNonExistentKey() - { - new CryptKey('foo/bar'); - } } diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 3ef3f133..542c78dc 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -27,8 +27,6 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase { /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $grantMock->setPrivateKey(new CryptKey(__DIR__ . '/../Stubs/private.key')); - $grantMock->setPublicKey(new CryptKey(__DIR__ . '/../Stubs/public.key')); $grantMock->setEmitter(new Emitter()); } diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 63c9042a..3bccba0a 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -510,9 +510,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); } @@ -537,9 +535,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->completeAuthorizationRequest($authRequest); } @@ -574,8 +570,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -643,8 +638,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -715,8 +709,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -773,7 +766,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase new \DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -820,7 +813,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase new \DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -873,8 +866,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -919,8 +911,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -983,8 +974,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1044,8 +1034,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1105,8 +1094,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1164,8 +1152,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1237,8 +1224,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1310,8 +1296,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1370,9 +1355,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); } @@ -1398,9 +1381,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); } @@ -1427,9 +1408,6 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase new \DateInterval('PT10M') ); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); } @@ -1464,8 +1442,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1536,8 +1513,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1608,8 +1584,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index 0fc06370..3bfe4b84 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -283,7 +283,6 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); @@ -307,7 +306,6 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->completeAuthorizationRequest($authRequest); @@ -329,7 +327,6 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); @@ -354,7 +351,6 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->completeAuthorizationRequest($authRequest); @@ -379,7 +375,6 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->completeAuthorizationRequest($authRequest); diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index 90a63276..47d7ad17 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -21,7 +21,7 @@ use Zend\Diactoros\ServerRequest; class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase { /** - * CryptTrait stub + * @var CryptTraitStub */ protected $cryptStub; @@ -65,7 +65,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $oldRefreshToken = $this->cryptStub->doEncrypt( @@ -121,7 +121,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $oldRefreshToken = $this->cryptStub->doEncrypt( @@ -180,7 +180,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $oldRefreshToken = $this->cryptStub->doEncrypt( @@ -227,7 +227,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = new ServerRequest(); @@ -259,7 +259,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $oldRefreshToken = 'foobar'; @@ -291,14 +291,13 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); - $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $oldRefreshToken = $this->cryptStub->doEncrypt( @@ -344,7 +343,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $oldRefreshToken = $this->cryptStub->doEncrypt( @@ -391,7 +390,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $oldRefreshToken = $this->cryptStub->doEncrypt( diff --git a/tests/Middleware/AuthorizationServerMiddlewareTest.php b/tests/Middleware/AuthorizationServerMiddlewareTest.php index f4c417f0..74dffbf7 100644 --- a/tests/Middleware/AuthorizationServerMiddlewareTest.php +++ b/tests/Middleware/AuthorizationServerMiddlewareTest.php @@ -33,10 +33,9 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase $accessRepositoryMock, $scopeRepositoryMock, 'file://' . __DIR__ . '/../Stubs/private.key', - 'file://' . __DIR__ . '/../Stubs/public.key', + base64_encode(random_bytes(36)), new StubResponseType() ); - $server->setEncryptionKey(base64_encode(random_bytes(36))); $server->enableGrantType(new ClientCredentialsGrant()); @@ -67,10 +66,9 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), 'file://' . __DIR__ . '/../Stubs/private.key', - 'file://' . __DIR__ . '/../Stubs/public.key', + base64_encode(random_bytes(36)), new StubResponseType() ); - $server->setEncryptionKey(base64_encode(random_bytes(36))); $server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M')); @@ -99,7 +97,8 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase $response = $exception->generateHttpResponse(new Response()); $this->assertEquals(302, $response->getStatusCode()); - $this->assertEquals('http://foo/bar?error=invalid_scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope', $response->getHeader('location')[0]); + $this->assertEquals('http://foo/bar?error=invalid_scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope', + $response->getHeader('location')[0]); } public function testOAuthErrorResponseRedirectUriFragment() @@ -108,6 +107,7 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase $response = $exception->generateHttpResponse(new Response(), true); $this->assertEquals(302, $response->getStatusCode()); - $this->assertEquals('http://foo/bar#error=invalid_scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope', $response->getHeader('location')[0]); + $this->assertEquals('http://foo/bar#error=invalid_scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope', + $response->getHeader('location')[0]); } } diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index ca660ab7..7f710d92 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -23,7 +23,7 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $responseType = new BearerTokenResponse($accessTokenRepositoryMock); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -67,7 +67,7 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $responseType = new BearerTokenResponseWithParams($accessTokenRepositoryMock); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -115,7 +115,7 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $responseType = new BearerTokenResponse($accessTokenRepositoryMock); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -141,7 +141,6 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false); $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $request = new ServerRequest(); @@ -162,7 +161,7 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $responseType = new BearerTokenResponse($accessTokenRepositoryMock); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -185,7 +184,6 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $json = json_decode((string) $response->getBody()); $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $request = new ServerRequest(); @@ -205,7 +203,7 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase { $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -231,7 +229,6 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(true); $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $request = new ServerRequest(); @@ -253,12 +250,11 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $responseType = new BearerTokenResponse($accessTokenRepositoryMock); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $request = new ServerRequest(); @@ -280,12 +276,11 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $responseType = new BearerTokenResponse($accessTokenRepositoryMock); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $request = new ServerRequest(); diff --git a/tests/Stubs/CryptTraitStub.php b/tests/Stubs/CryptTraitStub.php index 2414c199..a481a849 100644 --- a/tests/Stubs/CryptTraitStub.php +++ b/tests/Stubs/CryptTraitStub.php @@ -11,8 +11,12 @@ class CryptTraitStub public function __construct() { - $this->setPrivateKey(new CryptKey('file://' . __DIR__ . '/private.key')); - $this->setPublicKey(new CryptKey('file://' . __DIR__ . '/public.key')); + $this->setEncryptionKey(base64_encode(random_bytes(36))); + } + + public function getKey() + { + return $this->encryptionKey; } public function doEncrypt($unencryptedData) From 523434902cc43fb43b281fb5b7c05c76f3a1834c Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 18:15:41 +0100 Subject: [PATCH 038/191] Removed dead code --- src/AuthorizationServer.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index ece5458b..46a9b27a 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -117,7 +117,6 @@ class AuthorizationServer implements EmitterAwareInterface $grantType->setClientRepository($this->clientRepository); $grantType->setScopeRepository($this->scopeRepository); $grantType->setPrivateKey($this->privateKey); - $grantType->setPublicKey($this->publicKey); $grantType->setEmitter($this->getEmitter()); $grantType->setEncryptionKey($this->encryptionKey); From e1ef13306773e87d0ab6e358d93def3786c0d729 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 18:22:44 +0100 Subject: [PATCH 039/191] Dropped PHP 5.5 compatability --- .travis.yml | 2 -- README.md | 2 -- composer.json | 8 ++------ 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index c0ce917b..1f085674 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,6 @@ cache: - vendor php: - - 5.5.9 - - 5.5 - 5.6 - 7.0 - 7.1 diff --git a/README.md b/README.md index d86bb3f7..62c865e8 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,9 @@ This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](ht The following versions of PHP are supported: -* PHP 5.5 (>=5.5.9) * PHP 5.6 * PHP 7.0 * PHP 7.1 -* HHVM The `openssl` extension is also required. diff --git a/composer.json b/composer.json index 51d467b5..d6740aa4 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "homepage": "https://oauth2.thephpleague.com/", "license": "MIT", "require": { - "php": ">=5.5.9", + "php": ">=5.6.0", "ext-openssl": "*", "league/event": "^2.1", "lcobucci/jwt": "^3.1", @@ -14,8 +14,7 @@ }, "require-dev": { "phpunit/phpunit": "^4.8 || ^5.0", - "zendframework/zend-diactoros": "^1.0", - "indigophp/hash-compat": "^1.1" + "zendframework/zend-diactoros": "^1.0" }, "repositories": [ { @@ -60,8 +59,5 @@ "psr-4": { "LeagueTests\\": "tests/" } - }, - "suggest": { - "indigophp/hash-compat": "Polyfill for hash_equals function for PHP 5.5" } } From f5c3ba0b244c427d265749b076a99077d99a4631 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 18:22:51 +0100 Subject: [PATCH 040/191] Removed dead code --- src/Grant/AuthCodeGrant.php | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 2a05355e..594bc7ab 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -322,20 +322,6 @@ class AuthCodeGrant extends AbstractAuthorizeGrant 'code_challenge_method ' => $authorizationRequest->getCodeChallengeMethod(), ]; - if ($this->encryptionKey === null) { - // Add padding to vary the length of the payload - $payload['_padding'] = base64_encode(random_bytes(mt_rand(8, 256))); - // Shuffle the payload so that the structure is no longer know and obvious - $keys = array_keys($payload); - shuffle($keys); - $shuffledPayload = []; - foreach ($keys as $key) { - $shuffledPayload[$key] = $payload[$key]; - } - } else { - $shuffledPayload = $payload; - } - $response = new RedirectResponse(); $response->setRedirectUri( $this->makeRedirectUri( @@ -343,7 +329,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant [ 'code' => $this->encrypt( json_encode( - $shuffledPayload + $payload ) ), 'state' => $authorizationRequest->getState(), From 417a64ad43b52b7bb7a627d50e84adffe2481a4e Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 18:33:03 +0100 Subject: [PATCH 041/191] Added security notice --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 62c865e8..08ca06b5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # PHP OAuth 2.0 Server +### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning: +### Security Notice + +### Please upgrade to version `>=5.1.14` (backwards compatible) or `6.x` (one tiny breaking change) to fix some potential security vulnerabilities +### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning: + [![Latest Version](http://img.shields.io/packagist/v/league/oauth2-server.svg?style=flat-square)](https://github.com/thephpleague/oauth2-server/releases) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) [![Build Status](https://img.shields.io/travis/thephpleague/oauth2-server/master.svg?style=flat-square)](https://travis-ci.org/thephpleague/oauth2-server) From 00c645545accf9941c4a401037f143523c5dd3b8 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 18:33:17 +0100 Subject: [PATCH 042/191] Updated changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9391de30..ca978bc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 6.0.0 (released 2017-07-01) + +* Breaking change: The `AuthorizationServer` constructor now expects an encryption key string instead of a public key +* Remove support for HHVM +* Remove support for PHP 5.5 + ## 5.1.4 (released 2017-07-01) * Fixed multiple security vulnerabilities as a result of a security audit paid for by the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source). All users of this library are encouraged to update as soon as possible to this version or version 6.0 or greater. From 0a6a4deca65b049e1d1739a0ba3f46c98c22209e Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 18:38:35 +0100 Subject: [PATCH 043/191] 5.1.4 not 5.1.14 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 08ca06b5..e63865d8 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning: ### Security Notice -### Please upgrade to version `>=5.1.14` (backwards compatible) or `6.x` (one tiny breaking change) to fix some potential security vulnerabilities +### Please upgrade to version `>=5.1.4` (backwards compatible) or `6.x` (one tiny breaking change) to fix some potential security vulnerabilities ### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning: [![Latest Version](http://img.shields.io/packagist/v/league/oauth2-server.svg?style=flat-square)](https://github.com/thephpleague/oauth2-server/releases) From 2824f7d27e7bf3f1a711aad24db96425f3278906 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 1 Jul 2017 18:46:48 +0100 Subject: [PATCH 044/191] Fixed examples --- examples/public/auth_code.php | 4 +--- examples/public/client_credentials.php | 4 +--- examples/public/implicit.php | 3 +-- examples/public/middleware_use.php | 4 +--- examples/public/password.php | 3 +-- examples/public/refresh_token.php | 4 +--- 6 files changed, 6 insertions(+), 16 deletions(-) diff --git a/examples/public/auth_code.php b/examples/public/auth_code.php index 51f1838c..3c4ca68d 100644 --- a/examples/public/auth_code.php +++ b/examples/public/auth_code.php @@ -36,7 +36,6 @@ $app = new App([ $refreshTokenRepository = new RefreshTokenRepository(); $privateKeyPath = 'file://' . __DIR__ . '/../private.key'; - $publicKeyPath = 'file://' . __DIR__ . '/../public.key'; // Setup the authorization server $server = new AuthorizationServer( @@ -44,9 +43,8 @@ $app = new App([ $accessTokenRepository, $scopeRepository, $privateKeyPath, - $publicKeyPath + 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' ); - $server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'); // Enable the authentication code grant on the server with a token TTL of 1 hour $server->enableGrantType( diff --git a/examples/public/client_credentials.php b/examples/public/client_credentials.php index fd7f5ee9..433fbdce 100644 --- a/examples/public/client_credentials.php +++ b/examples/public/client_credentials.php @@ -32,7 +32,6 @@ $app = new App([ // Path to public and private keys $privateKey = 'file://' . __DIR__ . '/../private.key'; //$privateKey = new CryptKey('file://path/to/private.key', 'passphrase'); // if private key has a pass phrase - $publicKey = 'file://' . __DIR__ . '/../public.key'; // Setup the authorization server $server = new AuthorizationServer( @@ -40,9 +39,8 @@ $app = new App([ $accessTokenRepository, $scopeRepository, $privateKey, - $publicKey + 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' ); - $server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'); // Enable the client credentials grant on the server $server->enableGrantType( diff --git a/examples/public/implicit.php b/examples/public/implicit.php index b496ce19..73de09ec 100644 --- a/examples/public/implicit.php +++ b/examples/public/implicit.php @@ -32,7 +32,6 @@ $app = new App([ $accessTokenRepository = new AccessTokenRepository(); $privateKeyPath = 'file://' . __DIR__ . '/../private.key'; - $publicKeyPath = 'file://' . __DIR__ . '/../public.key'; // Setup the authorization server $server = new AuthorizationServer( @@ -40,7 +39,7 @@ $app = new App([ $accessTokenRepository, $scopeRepository, $privateKeyPath, - $publicKeyPath + 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' ); $server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'); diff --git a/examples/public/middleware_use.php b/examples/public/middleware_use.php index f6a7fcf9..121b9155 100644 --- a/examples/public/middleware_use.php +++ b/examples/public/middleware_use.php @@ -38,7 +38,6 @@ $app = new App([ $refreshTokenRepository = new RefreshTokenRepository(); $privateKeyPath = 'file://' . __DIR__ . '/../private.key'; - $publicKeyPath = 'file://' . __DIR__ . '/../public.key'; // Setup the authorization server $server = new AuthorizationServer( @@ -46,9 +45,8 @@ $app = new App([ $accessTokenRepository, $scopeRepository, $privateKeyPath, - $publicKeyPath + 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' ); - $server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'); // Enable the authentication code grant on the server with a token TTL of 1 hour $server->enableGrantType( diff --git a/examples/public/password.php b/examples/public/password.php index b62acc95..6857e988 100644 --- a/examples/public/password.php +++ b/examples/public/password.php @@ -24,9 +24,8 @@ $app = new App([ new AccessTokenRepository(), // instance of AccessTokenRepositoryInterface new ScopeRepository(), // instance of ScopeRepositoryInterface 'file://' . __DIR__ . '/../private.key', // path to private key - 'file://' . __DIR__ . '/../public.key' // path to public key + 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' // encryption key ); - $server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'); $grant = new PasswordGrant( new UserRepository(), // instance of UserRepositoryInterface diff --git a/examples/public/refresh_token.php b/examples/public/refresh_token.php index 25c32100..39be0826 100644 --- a/examples/public/refresh_token.php +++ b/examples/public/refresh_token.php @@ -32,7 +32,6 @@ $app = new App([ $refreshTokenRepository = new RefreshTokenRepository(); $privateKeyPath = 'file://' . __DIR__ . '/../private.key'; - $publicKeyPath = 'file://' . __DIR__ . '/../public.key'; // Setup the authorization server $server = new AuthorizationServer( @@ -40,9 +39,8 @@ $app = new App([ $accessTokenRepository, $scopeRepository, $privateKeyPath, - $publicKeyPath + 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' ); - $server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'); // Enable the refresh token grant on the server $grant = new RefreshTokenGrant($refreshTokenRepository); From 315d079033df8f14e18860d9a56ff8f9653eeb22 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sun, 2 Jul 2017 18:44:55 +0100 Subject: [PATCH 045/191] Added link to security release information page --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e63865d8..e3d88169 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning: ### Security Notice -### Please upgrade to version `>=5.1.4` (backwards compatible) or `6.x` (one tiny breaking change) to fix some potential security vulnerabilities +### Please upgrade to version `>=5.1.4` (backwards compatible) or `6.x` (one tiny breaking change) to fix some potential security vulnerabilities - [visit this page for more information](https://oauth2.thephpleague.com/v5-security-improvements/) ### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning: [![Latest Version](http://img.shields.io/packagist/v/league/oauth2-server.svg?style=flat-square)](https://github.com/thephpleague/oauth2-server/releases) From 88bf8b236781439cf52877ab59c1adb6166a8b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Parmentier?= Date: Mon, 3 Jul 2017 20:28:28 +0200 Subject: [PATCH 046/191] Fix missing sprintf --- src/CryptKey.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CryptKey.php b/src/CryptKey.php index f3051d04..c707b223 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -79,19 +79,19 @@ class CryptKey if (!file_exists($keyPath) && !touch($keyPath)) { // @codeCoverageIgnoreStart - throw new \RuntimeException('"%s" key file could not be created', $keyPath); + throw new \RuntimeException(sprintf('"%s" key file could not be created', $keyPath)); // @codeCoverageIgnoreEnd } if (file_put_contents($keyPath, $key) === false) { // @codeCoverageIgnoreStart - throw new \RuntimeException('Unable to write key file to temporary directory "%s"', $tmpDir); + throw new \RuntimeException(sprintf('Unable to write key file to temporary directory "%s"', $tmpDir)); // @codeCoverageIgnoreEnd } if (chmod($keyPath, 0600) === false) { // @codeCoverageIgnoreStart - throw new \RuntimeException('The key file "%s" file mode could not be changed with chmod to 600', $keyPath); + throw new \RuntimeException(sprintf('The key file "%s" file mode could not be changed with chmod to 600', $keyPath)); // @codeCoverageIgnoreEnd } From e2f9b73df3d3a54283363212db0f0c5f0a583734 Mon Sep 17 00:00:00 2001 From: Erick Torres Date: Fri, 7 Jul 2017 12:19:11 -0500 Subject: [PATCH 047/191] Fix broken tests --- tests/Grant/AuthCodeGrantTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 3ef6fb91..48fde6cc 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -1308,8 +1308,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1381,8 +1380,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], From 88ccb6ff138b84d550edaeefa9870c7b37f4998d Mon Sep 17 00:00:00 2001 From: Erick Torres Date: Fri, 7 Jul 2017 12:35:42 -0500 Subject: [PATCH 048/191] Fix codeVerifier check. Keep code style. --- src/Grant/AuthCodeGrant.php | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 453fabf4..9c132c4c 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -136,9 +136,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant // Validate code_verifier according to RFC-7636 // @see: https://tools.ietf.org/html/rfc7636#section-4.1 - $isValidCodeVerifier = (bool) preg_match('#[A-Za-z0-9\-|\.|\_|\~]{43,128}#', $codeVerifier); - - if ($isValidCodeVerifier === false) { + if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeVerifier) !== 1) { throw OAuthServerException::invalidRequest( 'code_verifier', 'Code Verifier must follow the specifications of RFC-7636.' @@ -275,13 +273,6 @@ class AuthCodeGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidRequest('code_challenge'); } - if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeChallenge) !== 1) { - throw OAuthServerException::invalidRequest( - 'code_challenge', - 'The code_challenge must be between 43 and 128 characters' - ); - } - $codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain'); if (in_array($codeChallengeMethod, ['plain', 'S256']) === false) { throw OAuthServerException::invalidRequest( @@ -292,9 +283,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant // Validate code_challenge according to RFC-7636 // @see: https://tools.ietf.org/html/rfc7636#section-4.2 - $isValidCodeChallenge = (bool) preg_match('#[A-Za-z0-9\-|\.|\_|\~]{43}#', $codeChallenge); - - if ($isValidCodeChallenge === false) { + if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeChallenge) !== 1) { throw OAuthServerException::invalidRequest( 'code_challenged', 'Code challenge must follow the specifications of RFC-7636.' From 80fc8e654b6ab6ba66000ddd7b95f8d7203c2443 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Wed, 19 Jul 2017 07:57:47 +0100 Subject: [PATCH 049/191] Trigger E_USER_NOTICE instead of throwing an exception if key cannot be chmod to 600 --- src/CryptKey.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CryptKey.php b/src/CryptKey.php index c707b223..557d6dc7 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -50,12 +50,13 @@ class CryptKey // Attempt to correct the permissions if (chmod($keyPath, 0600) === false) { // @codeCoverageIgnoreStart - throw new \LogicException( + trigger_error( sprintf( 'Key file "%s" permissions are not correct, should be 600 instead of %s, unable to automatically resolve the issue', $keyPath, $keyPathPerms - ) + ), + E_USER_NOTICE ); // @codeCoverageIgnoreEnd } From a1b8d87b473e9b956b80e7e20b31ae20dc670d30 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Wed, 19 Jul 2017 07:58:56 +0100 Subject: [PATCH 050/191] Updated changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca978bc7..2b532e9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 6.0.1 (released 2017-07-19) + +To address feedback from the security release the following change has been made: + +* If an RSA key cannot be chmod'ed to 600 then it will now throw a E_USER_NOTICE instead of an exception. + ## 6.0.0 (released 2017-07-01) * Breaking change: The `AuthorizationServer` constructor now expects an encryption key string instead of a public key From ecc07abb3367e34c3090aa67212421dee9bac905 Mon Sep 17 00:00:00 2001 From: Benjamin Dieleman Date: Thu, 27 Jul 2017 17:27:36 +0200 Subject: [PATCH 051/191] Updated PHPDoc about the unicity violation exception throwing UniqueTokenIdentifierConstraintViolationException can be thrown when persisting tokens --- src/Repositories/AccessTokenRepositoryInterface.php | 3 +++ src/Repositories/AuthCodeRepositoryInterface.php | 3 +++ src/Repositories/RefreshTokenRepositoryInterface.php | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/Repositories/AccessTokenRepositoryInterface.php b/src/Repositories/AccessTokenRepositoryInterface.php index 04e98bc6..72ddf1f4 100644 --- a/src/Repositories/AccessTokenRepositoryInterface.php +++ b/src/Repositories/AccessTokenRepositoryInterface.php @@ -12,6 +12,7 @@ namespace League\OAuth2\Server\Repositories; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; +use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException; /** * Access token interface. @@ -33,6 +34,8 @@ interface AccessTokenRepositoryInterface extends RepositoryInterface * Persists a new access token to permanent storage. * * @param AccessTokenEntityInterface $accessTokenEntity + * + * @throws UniqueTokenIdentifierConstraintViolationException */ public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity); diff --git a/src/Repositories/AuthCodeRepositoryInterface.php b/src/Repositories/AuthCodeRepositoryInterface.php index 4d23439a..2dc285b8 100644 --- a/src/Repositories/AuthCodeRepositoryInterface.php +++ b/src/Repositories/AuthCodeRepositoryInterface.php @@ -10,6 +10,7 @@ namespace League\OAuth2\Server\Repositories; use League\OAuth2\Server\Entities\AuthCodeEntityInterface; +use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException; /** * Auth code storage interface. @@ -27,6 +28,8 @@ interface AuthCodeRepositoryInterface extends RepositoryInterface * Persists a new auth code to permanent storage. * * @param AuthCodeEntityInterface $authCodeEntity + * + * @throws UniqueTokenIdentifierConstraintViolationException */ public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity); diff --git a/src/Repositories/RefreshTokenRepositoryInterface.php b/src/Repositories/RefreshTokenRepositoryInterface.php index d7e686cc..0c0697bf 100644 --- a/src/Repositories/RefreshTokenRepositoryInterface.php +++ b/src/Repositories/RefreshTokenRepositoryInterface.php @@ -10,6 +10,7 @@ namespace League\OAuth2\Server\Repositories; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; +use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException; /** * Refresh token interface. @@ -27,6 +28,8 @@ interface RefreshTokenRepositoryInterface extends RepositoryInterface * Create a new refresh token_name. * * @param RefreshTokenEntityInterface $refreshTokenEntity + * + * @throws UniqueTokenIdentifierConstraintViolationException */ public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity); From 0f1ddaaacf2018002f2f7840555e47982c200ac2 Mon Sep 17 00:00:00 2001 From: Mathieu Alorent Date: Sat, 29 Jul 2017 17:41:44 +0200 Subject: [PATCH 052/191] Fix #772 - PR should be based on master branch --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0391ae5e..af439e0a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,7 @@ Thanks for contributing to this project. -**Please submit your pull request against the `develop` branch only.** +**Please submit your pull request against the `master` branch only.** Please ensure that you run `phpunit` from the project root after you've made any changes. From 79038ced785e3fc733408b6ba8a88be7d0c30b59 Mon Sep 17 00:00:00 2001 From: Hugo Hamon Date: Wed, 2 Aug 2017 17:55:11 +0200 Subject: [PATCH 053/191] [BC Break] Fixes invalid code challenge method payload key name I guess this change might be a BC break for existing and active authorization tokens when they're validated by the server. The good thing is that an authorization token has a very short expiration time and is used once to request an access token. --- src/Grant/AuthCodeGrant.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 594bc7ab..a138366f 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -312,14 +312,14 @@ class AuthCodeGrant extends AbstractAuthorizeGrant ); $payload = [ - 'client_id' => $authCode->getClient()->getIdentifier(), - 'redirect_uri' => $authCode->getRedirectUri(), - 'auth_code_id' => $authCode->getIdentifier(), - 'scopes' => $authCode->getScopes(), - 'user_id' => $authCode->getUserIdentifier(), - 'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'), - 'code_challenge' => $authorizationRequest->getCodeChallenge(), - 'code_challenge_method ' => $authorizationRequest->getCodeChallengeMethod(), + 'client_id' => $authCode->getClient()->getIdentifier(), + 'redirect_uri' => $authCode->getRedirectUri(), + 'auth_code_id' => $authCode->getIdentifier(), + 'scopes' => $authCode->getScopes(), + 'user_id' => $authCode->getUserIdentifier(), + 'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'), + 'code_challenge' => $authorizationRequest->getCodeChallenge(), + 'code_challenge_method' => $authorizationRequest->getCodeChallengeMethod(), ]; $response = new RedirectResponse(); From 2aca909d203e8a925da8c3a3f16a803683cecf04 Mon Sep 17 00:00:00 2001 From: Yannick de Lange Date: Tue, 1 Aug 2017 14:59:21 +0200 Subject: [PATCH 054/191] Removed chmod from CryptKey and add toggle to disable checking --- src/CryptKey.php | 27 +++++++++++---------------- tests/AuthorizationServerTest.php | 7 +++++++ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/CryptKey.php b/src/CryptKey.php index 557d6dc7..2ede9e33 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -29,8 +29,9 @@ class CryptKey /** * @param string $keyPath * @param null|string $passPhrase + * @param bool $keyPermissionsCheck */ - public function __construct($keyPath, $passPhrase = null) + public function __construct($keyPath, $passPhrase = null, $keyPermissionsCheck = true) { if (preg_match(self::RSA_KEY_PATTERN, $keyPath)) { $keyPath = $this->saveKeyToFile($keyPath); @@ -44,21 +45,15 @@ class CryptKey throw new \LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath)); } - // Verify the permissions of the key - $keyPathPerms = decoct(fileperms($keyPath) & 0777); - if ($keyPathPerms !== '600') { - // Attempt to correct the permissions - if (chmod($keyPath, 0600) === false) { - // @codeCoverageIgnoreStart - trigger_error( - sprintf( - 'Key file "%s" permissions are not correct, should be 600 instead of %s, unable to automatically resolve the issue', - $keyPath, - $keyPathPerms - ), - E_USER_NOTICE - ); - // @codeCoverageIgnoreEnd + if ($keyPermissionsCheck === true) { + // Verify the permissions of the key + $keyPathPerms = decoct(fileperms($keyPath) & 0777); + if (in_array($keyPathPerms, ['600', '660'], true) === false) { + trigger_error(sprintf( + 'Key file "%s" permissions are not correct, should be 600 or 660 instead of %s', + $keyPath, + $keyPathPerms + ), E_USER_NOTICE); } } diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 7b921ff9..91ca9e4b 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -26,6 +26,13 @@ use Zend\Diactoros\ServerRequestFactory; class AuthorizationServerTest extends \PHPUnit_Framework_TestCase { + public function setUp() + { + // Make sure the keys have the correct permissions. + chmod(__DIR__ . '/Stubs/private.key', 0600); + chmod(__DIR__ . '/Stubs/public.key', 0600); + } + public function testRespondToRequestInvalidGrantType() { $server = new AuthorizationServer( From c86c7dde70cecc459cb3592f8a862389e3d841d0 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Thu, 3 Aug 2017 16:07:11 +0100 Subject: [PATCH 055/191] Fix #759 --- 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 6cd82bb3..45e03c07 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -152,7 +152,7 @@ class OAuthServerException extends \Exception */ public static function invalidRefreshToken($hint = null) { - return new static('The refresh token is invalid.', 8, 'invalid_request', 400, $hint); + return new static('The refresh token is invalid.', 8, 'invalid_request', 401, $hint); } /** From 925776958fc3f5278e74363663c20147af32b668 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Thu, 3 Aug 2017 16:09:23 +0100 Subject: [PATCH 056/191] Updated changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b532e9c..958a941e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 6.0.2 (released 2017-08-03) + +* An invalid refresh token that can't be decrypted now returns a HTTP 401 error instead of HTTP 400 (Issue #759) +* Removed chmod from CryptKey and add toggle to disable checking (Issue #776) +* Fixes invalid code challenge method payload key name (Issue #777) + ## 6.0.1 (released 2017-07-19) To address feedback from the security release the following change has been made: From 5b223a9501f882bb0ebadfee9fd92778ffe8b5ae Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Wed, 11 Oct 2017 10:33:10 +0100 Subject: [PATCH 057/191] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index e3d88169..0cf7b95b 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,10 @@ 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. + + Sponsor + + ## 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). From e7ee483d11afc5658e3b2e51f1bff24406431848 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Fri, 13 Oct 2017 23:02:29 +0100 Subject: [PATCH 058/191] Changed function comment to reflect we are setting the public, instead of private key --- src/AuthorizationValidators/BearerTokenValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AuthorizationValidators/BearerTokenValidator.php b/src/AuthorizationValidators/BearerTokenValidator.php index 1547f6bf..6f299ce4 100644 --- a/src/AuthorizationValidators/BearerTokenValidator.php +++ b/src/AuthorizationValidators/BearerTokenValidator.php @@ -41,7 +41,7 @@ class BearerTokenValidator implements AuthorizationValidatorInterface } /** - * Set the private key + * Set the public key * * @param \League\OAuth2\Server\CryptKey $key */ From c70451abd50aee741e668590f5c6fed641f8f534 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 18 Oct 2017 22:08:11 +0100 Subject: [PATCH 059/191] Add an exception for a missing scope --- src/Exception/OAuthServerException.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 45e03c07..2258185f 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -195,6 +195,21 @@ class OAuthServerException extends \Exception ); } + /** + * Missing scope error + * + * @param null|string $redirectUri A HTTP URI to redirect the user back to + * + * @return static + */ + public static function missingScope($redirectUri = null) + { + $errorMessage = 'No scope was specified for this request'; + $hint = 'Set a default scope on the server if no scopes are passed in the request'; + + return new static($errorMessage, 11, 'missing_scope', 400, $hint, $redirectUri); + } + /** * @return string */ From c996b665285dffde76ac9452b1e7bc3e2dd55f22 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 18 Oct 2017 22:08:41 +0100 Subject: [PATCH 060/191] Add means to set default scopes for grants --- src/Grant/AbstractGrant.php | 30 +++++++++++++++++++++------- src/Grant/AuthCodeGrant.php | 2 +- src/Grant/ClientCredentialsGrant.php | 6 +++--- src/Grant/GrantTypeInterface.php | 7 +++++++ src/Grant/ImplicitGrant.php | 6 +++--- src/Grant/PasswordGrant.php | 6 +++--- src/Grant/RefreshTokenGrant.php | 4 ++-- 7 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 3ac98cf4..b52b14cb 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -56,6 +56,11 @@ abstract class AbstractGrant implements GrantTypeInterface */ protected $scopeRepository; + /** + * @var string + */ + protected $defaultScope = ''; + /** * @var AuthCodeRepositoryInterface */ @@ -105,6 +110,14 @@ abstract class AbstractGrant implements GrantTypeInterface $this->scopeRepository = $scopeRepository; } + /** + * @param string $scope + */ + public function setDefaultScope($scope) + { + $this->defaultScope = $scope; + } + /** * @param RefreshTokenRepositoryInterface $refreshTokenRepository */ @@ -211,10 +224,8 @@ abstract class AbstractGrant implements GrantTypeInterface * * @return ScopeEntityInterface[] */ - public function validateScopes( - $scopes, - $redirectUri = null - ) { + public function validateScopes($scopes, $redirectUri = null) + { $scopesList = array_filter( explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), function ($scope) { @@ -222,7 +233,8 @@ abstract class AbstractGrant implements GrantTypeInterface } ); - $scopes = []; + $validScopes = []; + foreach ($scopesList as $scopeItem) { $scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem); @@ -230,10 +242,14 @@ abstract class AbstractGrant implements GrantTypeInterface throw OAuthServerException::invalidScope($scopeItem, $redirectUri); } - $scopes[] = $scope; + $validScopes[] = $scope; } - return $scopes; + if (empty($validScopes)) { + throw OAuthServerException::missingScope($redirectUri); + } + + return $validScopes; } /** diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index a138366f..a8528bb5 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -243,7 +243,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant } $scopes = $this->validateScopes( - $this->getQueryStringParameter('scope', $request), + $this->getQueryStringParameter('scope', $request, $this->defaultScope), is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] : $client->getRedirectUri() diff --git a/src/Grant/ClientCredentialsGrant.php b/src/Grant/ClientCredentialsGrant.php index b5b968d4..ed157aaf 100644 --- a/src/Grant/ClientCredentialsGrant.php +++ b/src/Grant/ClientCredentialsGrant.php @@ -29,13 +29,13 @@ class ClientCredentialsGrant extends AbstractGrant ) { // Validate request $client = $this->validateClient($request); - $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); + $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); // Finalize the requested scopes - $scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client); + $finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client); // Issue and persist access token - $accessToken = $this->issueAccessToken($accessTokenTTL, $client, null, $scopes); + $accessToken = $this->issueAccessToken($accessTokenTTL, $client, null, $finalizedScopes); // Inject access token into response type $responseType->setAccessToken($accessToken); diff --git a/src/Grant/GrantTypeInterface.php b/src/Grant/GrantTypeInterface.php index 7aa98242..0e721435 100644 --- a/src/Grant/GrantTypeInterface.php +++ b/src/Grant/GrantTypeInterface.php @@ -119,6 +119,13 @@ interface GrantTypeInterface extends EmitterAwareInterface */ public function setScopeRepository(ScopeRepositoryInterface $scopeRepository); + /** + * Set the default scope. + * + * @param string $scope + */ + public function setDefaultScope($scope); + /** * Set the path to the private key. * diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 466f32ce..9f000eb0 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -145,14 +145,14 @@ class ImplicitGrant extends AbstractAuthorizeGrant } $scopes = $this->validateScopes( - $this->getQueryStringParameter('scope', $request), + $this->getQueryStringParameter('scope', $request, $this->defaultScope), is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] : $client->getRedirectUri() ); // Finalize the requested scopes - $scopes = $this->scopeRepository->finalizeScopes( + $finalizedScopes = $this->scopeRepository->finalizeScopes( $scopes, $this->getIdentifier(), $client @@ -165,7 +165,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant $authorizationRequest->setClient($client); $authorizationRequest->setRedirectUri($redirectUri); $authorizationRequest->setState($stateParameter); - $authorizationRequest->setScopes($scopes); + $authorizationRequest->setScopes($finalizedScopes); return $authorizationRequest; } diff --git a/src/Grant/PasswordGrant.php b/src/Grant/PasswordGrant.php index 31755613..cfd7e9fe 100644 --- a/src/Grant/PasswordGrant.php +++ b/src/Grant/PasswordGrant.php @@ -49,14 +49,14 @@ class PasswordGrant extends AbstractGrant ) { // Validate request $client = $this->validateClient($request); - $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); + $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); $user = $this->validateUser($request, $client); // Finalize the requested scopes - $scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier()); + $finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier()); // Issue and persist new tokens - $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $scopes); + $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes); $refreshToken = $this->issueRefreshToken($accessToken); // Inject tokens into response diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php index 53dfdf7d..4fda5974 100644 --- a/src/Grant/RefreshTokenGrant.php +++ b/src/Grant/RefreshTokenGrant.php @@ -44,11 +44,11 @@ class RefreshTokenGrant extends AbstractGrant // Validate request $client = $this->validateClient($request); $oldRefreshToken = $this->validateOldRefreshToken($request, $client->getIdentifier()); - $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); + $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); // If no new scopes are requested then give the access token the original session scopes if (count($scopes) === 0) { - $scopes = array_map(function ($scopeId) use ($client) { + $scopes = array_map(function ($scopeId) { $scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId); if ($scope instanceof ScopeEntityInterface === false) { From 5a28fb8af4503407de10c5b32f90c2a46ed87145 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 18 Oct 2017 22:09:53 +0100 Subject: [PATCH 061/191] Set a default scope for the authorization server --- src/AuthorizationServer.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 46a9b27a..6da9c524 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -70,6 +70,11 @@ class AuthorizationServer implements EmitterAwareInterface */ private $encryptionKey; + /** + * @var string + */ + private $defaultScope; + /** * New server instance. * @@ -78,6 +83,7 @@ class AuthorizationServer implements EmitterAwareInterface * @param ScopeRepositoryInterface $scopeRepository * @param CryptKey|string $privateKey * @param string $encryptionKey + * @param null|string $defaultScope * @param null|ResponseTypeInterface $responseType */ public function __construct( @@ -86,6 +92,7 @@ class AuthorizationServer implements EmitterAwareInterface ScopeRepositoryInterface $scopeRepository, $privateKey, $encryptionKey, + $defaultScope = null, ResponseTypeInterface $responseType = null ) { $this->clientRepository = $clientRepository; @@ -97,6 +104,8 @@ class AuthorizationServer implements EmitterAwareInterface } $this->privateKey = $privateKey; + $this->defaultScope = $defaultScope; + $this->encryptionKey = $encryptionKey; $this->responseType = $responseType; } @@ -116,6 +125,7 @@ class AuthorizationServer implements EmitterAwareInterface $grantType->setAccessTokenRepository($this->accessTokenRepository); $grantType->setClientRepository($this->clientRepository); $grantType->setScopeRepository($this->scopeRepository); + $grantType->setDefaultScope($this->defaultScope); $grantType->setPrivateKey($this->privateKey); $grantType->setEmitter($this->getEmitter()); $grantType->setEncryptionKey($this->encryptionKey); From 4d28eadf932d8fc4747d8b69dc65142048b8e1cd Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 18 Oct 2017 22:11:02 +0100 Subject: [PATCH 062/191] Update tests so they don't trigger missing or invalid scope exceptions --- tests/Grant/AuthCodeGrantTest.php | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 3bccba0a..025135c3 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -27,6 +27,9 @@ use Zend\Diactoros\ServerRequest; class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase { + + const DEFAULT_SCOPE = 'default'; + /** * @var CryptTraitStub */ @@ -77,15 +80,22 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase { $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( [], @@ -112,12 +122,18 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $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( [], @@ -144,6 +160,10 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $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(), @@ -151,6 +171,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase ); $grant->enableCodeExchangeProof(); $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $request = new ServerRequest( [], @@ -429,6 +451,10 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $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(), @@ -436,6 +462,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase ); $grant->enableCodeExchangeProof(); $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $request = new ServerRequest( [], @@ -466,6 +494,10 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $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(), @@ -473,6 +505,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase ); $grant->enableCodeExchangeProof(); $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $request = new ServerRequest( [], From 8c788e9fc8e5d6c152bce346e3ebeaae16568cb0 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 18 Oct 2017 22:11:13 +0100 Subject: [PATCH 063/191] Update tests so they don't trigger missing or invalid scope exceptions --- tests/AuthorizationServerTest.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 91ca9e4b..15376c22 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -17,6 +17,7 @@ use League\OAuth2\Server\ResponseTypes\BearerTokenResponse; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\AuthCodeEntity; use LeagueTests\Stubs\ClientEntity; +use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; use Psr\Http\Message\ResponseInterface; @@ -26,6 +27,9 @@ use Zend\Diactoros\ServerRequestFactory; class AuthorizationServerTest extends \PHPUnit_Framework_TestCase { + + const DEFAULT_SCOPE = 'basic '; + public function setUp() { // Make sure the keys have the correct permissions. @@ -41,6 +45,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), 'file://' . __DIR__ . '/Stubs/private.key', base64_encode(random_bytes(36)), + self::DEFAULT_SCOPE, new StubResponseType() ); @@ -59,7 +64,9 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepository->method('getClientEntity')->willReturn(new ClientEntity()); + $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -71,6 +78,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase $scopeRepositoryMock, 'file://' . __DIR__ . '/Stubs/private.key', base64_encode(random_bytes(36)), + self::DEFAULT_SCOPE, new StubResponseType() ); @@ -142,6 +150,10 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase $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(), @@ -152,9 +164,10 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase $server = new AuthorizationServer( $clientRepositoryMock, $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), - $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), + $scopeRepositoryMock, 'file://' . __DIR__ . '/Stubs/private.key', - 'file://' . __DIR__ . '/Stubs/public.key' + 'file://' . __DIR__ . '/Stubs/public.key', + self::DEFAULT_SCOPE ); $server->enableGrantType($grant); From 24f29b6382fbe8e77bc6e34faddae52d1e8d69b7 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Thu, 19 Oct 2017 22:37:19 +0100 Subject: [PATCH 064/191] Fix more tests to support default scope setting --- tests/Grant/ClientCredentialsGrantTest.php | 8 +++++++- tests/Grant/ImplicitGrantTest.php | 4 ++++ tests/Grant/PasswordGrantTest.php | 6 ++++++ tests/Grant/RefreshTokenGrantTest.php | 7 +++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index a1665831..8ad7e2b7 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -9,11 +9,14 @@ use League\OAuth2\Server\Repositories\ClientRepositoryInterface; use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; +use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use Zend\Diactoros\ServerRequest; class ClientCredentialsGrantTest extends \PHPUnit_Framework_TestCase { + const DEFAULT_SCOPE = 'basic'; + public function testGetIdentifier() { $grant = new ClientCredentialsGrant(); @@ -30,13 +33,16 @@ class ClientCredentialsGrantTest extends \PHPUnit_Framework_TestCase $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $grant = new ClientCredentialsGrant(); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $serverRequest = new ServerRequest(); $serverRequest = $serverRequest->withParsedBody( @@ -51,4 +57,4 @@ class ClientCredentialsGrantTest extends \PHPUnit_Framework_TestCase $this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface); } -} +} \ No newline at end of file diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index 3bfe4b84..18cb04b8 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -22,6 +22,8 @@ use Zend\Diactoros\ServerRequest; class ImplicitGrantTest extends \PHPUnit_Framework_TestCase { + const DEFAULT_SCOPE = 'basic'; + /** * CryptTrait stub */ @@ -96,6 +98,7 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $request = new ServerRequest( [], @@ -130,6 +133,7 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $request = new ServerRequest( [], diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index b380bfb2..ae4b311d 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -13,12 +13,15 @@ use League\OAuth2\Server\Repositories\UserRepositoryInterface; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\RefreshTokenEntity; +use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; use Zend\Diactoros\ServerRequest; class PasswordGrantTest extends \PHPUnit_Framework_TestCase { + const DEFAULT_SCOPE = 'basic'; + public function testGetIdentifier() { $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); @@ -46,13 +49,16 @@ class PasswordGrantTest extends \PHPUnit_Framework_TestCase $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); + $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $serverRequest = new ServerRequest(); $serverRequest = $serverRequest->withParsedBody( diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index 47d7ad17..514d4c62 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -20,6 +20,8 @@ use Zend\Diactoros\ServerRequest; class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase { + const DEFAULT_SCOPE = 'basic'; + /** * @var CryptTraitStub */ @@ -61,12 +63,17 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase ->expects($this->once()) ->method('persistNewRefreshToken')->willReturnSelf(); + $oldRefreshTokenCheckResult = ['scopes' => $scopeEntity->getIdentifier()]; + + $refreshTokenRepositoryMock->method('validateOldRefreshToken')->willReturn($oldRefreshTokenCheckResult); + $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $oldRefreshToken = $this->cryptStub->doEncrypt( json_encode( From 203be5ca20de4a0a3520513b7507d7e2dc5fc0f8 Mon Sep 17 00:00:00 2001 From: Diogo Oliveira de Melo Date: Fri, 20 Oct 2017 09:23:36 -0200 Subject: [PATCH 065/191] Revert comparison order, as suggested by @Sephster --- src/Grant/ImplicitGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 2ec9aed7..693880b4 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -96,7 +96,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant { return ( isset($request->getQueryParams()['response_type']) - && 'token' === $request->getQueryParams()['response_type'] + && $request->getQueryParams()['response_type'] === 'token' && isset($request->getQueryParams()['client_id']) ); } From 23c7138d48f0d760d3faeb2ef4eb2c9186ee293d Mon Sep 17 00:00:00 2001 From: Brian Retterer Date: Mon, 23 Oct 2017 15:26:10 +0000 Subject: [PATCH 066/191] Apply fixes from StyleCI --- src/AuthorizationServer.php | 1 + src/CryptTrait.php | 4 ++++ src/ResponseTypes/AbstractResponseType.php | 1 - tests/AuthorizationServerTest.php | 1 - tests/CryptTraitTest.php | 1 - tests/Grant/AbstractGrantTest.php | 1 - tests/Grant/AuthCodeGrantTest.php | 1 - tests/Stubs/CryptTraitStub.php | 1 - 8 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 46a9b27a..35e745d3 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -3,6 +3,7 @@ * @author Alex Bilbie * @copyright Copyright (c) Alex Bilbie * @license http://mit-license.org/ + * * @link https://github.com/thephpleague/oauth2-server */ diff --git a/src/CryptTrait.php b/src/CryptTrait.php index 805969b0..125a757e 100644 --- a/src/CryptTrait.php +++ b/src/CryptTrait.php @@ -1,9 +1,11 @@ * @copyright Copyright (c) Alex Bilbie * @license http://mit-license.org/ + * * @link https://github.com/thephpleague/oauth2-server */ @@ -24,6 +26,7 @@ trait CryptTrait * @param string $unencryptedData * * @throws \LogicException + * * @return string */ protected function encrypt($unencryptedData) @@ -41,6 +44,7 @@ trait CryptTrait * @param string $encryptedData * * @throws \LogicException + * * @return string */ protected function decrypt($encryptedData) diff --git a/src/ResponseTypes/AbstractResponseType.php b/src/ResponseTypes/AbstractResponseType.php index 0c256f17..d013bab0 100644 --- a/src/ResponseTypes/AbstractResponseType.php +++ b/src/ResponseTypes/AbstractResponseType.php @@ -60,5 +60,4 @@ abstract class AbstractResponseType implements ResponseTypeInterface { $this->privateKey = $key; } - } diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 91ca9e4b..4571c5e1 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -3,7 +3,6 @@ namespace LeagueTests; use League\OAuth2\Server\AuthorizationServer; -use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Grant\AuthCodeGrant; use League\OAuth2\Server\Grant\ClientCredentialsGrant; diff --git a/tests/CryptTraitTest.php b/tests/CryptTraitTest.php index 8c7d2642..3d60ec9d 100644 --- a/tests/CryptTraitTest.php +++ b/tests/CryptTraitTest.php @@ -2,7 +2,6 @@ namespace LeagueTests\Utils; -use League\OAuth2\Server\CryptKey; use LeagueTests\Stubs\CryptTraitStub; class CryptTraitTest extends \PHPUnit_Framework_TestCase diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 542c78dc..4cd490e3 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -3,7 +3,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; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 3bccba0a..d63aec29 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -2,7 +2,6 @@ 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; diff --git a/tests/Stubs/CryptTraitStub.php b/tests/Stubs/CryptTraitStub.php index a481a849..3fe02199 100644 --- a/tests/Stubs/CryptTraitStub.php +++ b/tests/Stubs/CryptTraitStub.php @@ -2,7 +2,6 @@ namespace LeagueTests\Stubs; -use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\CryptTrait; class CryptTraitStub From 63c2c21ee66a37fe4f42f35676294a99983d1419 Mon Sep 17 00:00:00 2001 From: Brian Retterer Date: Mon, 23 Oct 2017 11:26:21 -0400 Subject: [PATCH 067/191] Update readme file to bring in Andy, Brian, and Simon --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0cf7b95b..4f8b0906 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,8 @@ This package is released under the MIT License. See the bundled [LICENSE](https: ## Credits -This code is principally developed and maintained by [Alex Bilbie](https://twitter.com/alexbilbie). +This code is principally developed and maintained by [Andy Millington](https://twitter.com/Sephster), [Brian +Retterer](https://twitter.com/bretterer), and [Simon Hamp](https://twitter.com/simonhamp). Special thanks to [all of these awesome contributors](https://github.com/thephpleague/oauth2-server/contributors). From 825017f27ecd20f101bcf864769222e24a92f886 Mon Sep 17 00:00:00 2001 From: Luca Santarella Date: Wed, 25 Oct 2017 18:30:17 -0400 Subject: [PATCH 068/191] Ability to specify query delimiter, such as `?` instead of the hard-coded `#` --- src/Grant/ImplicitGrant.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 2f7ea51f..4ed2de63 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -27,11 +27,18 @@ class ImplicitGrant extends AbstractAuthorizeGrant private $accessTokenTTL; /** - * @param \DateInterval $accessTokenTTL + * @var string */ - public function __construct(\DateInterval $accessTokenTTL) + private $queryDelimiter; + + /** + * @param \DateInterval $accessTokenTTL + * @param string $queryDelimiter + */ + public function __construct(\DateInterval $accessTokenTTL, $queryDelimiter = '#') { $this->accessTokenTTL = $accessTokenTTL; + $this->queryDelimiter = $queryDelimiter; } /** @@ -204,7 +211,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(), 'state' => $authorizationRequest->getState(), ], - '#' + $this->queryDelimiter ) ); From 606f69e6cd8f1746f2ef5c9d539b303d569d27ef Mon Sep 17 00:00:00 2001 From: Luca Santarella Date: Wed, 25 Oct 2017 18:33:26 -0400 Subject: [PATCH 069/191] Fixed indentation in comment to match code style --- src/Grant/ImplicitGrant.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 4ed2de63..ed2f4b5d 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -32,9 +32,9 @@ class ImplicitGrant extends AbstractAuthorizeGrant private $queryDelimiter; /** - * @param \DateInterval $accessTokenTTL - * @param string $queryDelimiter - */ + * @param \DateInterval $accessTokenTTL + * @param string $queryDelimiter + */ public function __construct(\DateInterval $accessTokenTTL, $queryDelimiter = '#') { $this->accessTokenTTL = $accessTokenTTL; From a4fc05c31e8463ca984743749de2366131b9ae5b Mon Sep 17 00:00:00 2001 From: Luca Santarella Date: Wed, 25 Oct 2017 18:33:26 -0400 Subject: [PATCH 070/191] Fixed indentation in comment to match code style --- src/Grant/ImplicitGrant.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 4ed2de63..4a16314f 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -31,10 +31,10 @@ class ImplicitGrant extends AbstractAuthorizeGrant */ private $queryDelimiter; - /** - * @param \DateInterval $accessTokenTTL - * @param string $queryDelimiter - */ + /** + * @param \DateInterval $accessTokenTTL + * @param string $queryDelimiter + */ public function __construct(\DateInterval $accessTokenTTL, $queryDelimiter = '#') { $this->accessTokenTTL = $accessTokenTTL; From 4d77aee4a9eef4c7523a24cf7512463f5cea23f7 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sat, 28 Oct 2017 18:29:55 +0700 Subject: [PATCH 071/191] =?UTF-8?q?Added=20a=20reference=20to=20myself?= =?UTF-8?q?=C2=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4f8b0906..93e3341e 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,8 @@ This package is released under the MIT License. See the bundled [LICENSE](https: This code is principally developed and maintained by [Andy Millington](https://twitter.com/Sephster), [Brian Retterer](https://twitter.com/bretterer), and [Simon Hamp](https://twitter.com/simonhamp). +Between 2012 and 2017 this library was developed and maintained by [Alex Bilbie](https://alexbilbie.com/). + Special thanks to [all of these awesome contributors](https://github.com/thephpleague/oauth2-server/contributors). Additional thanks go to the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source) for funding a security audit of this library. From 4563685375f12de71a4fd938a00adabf55ad3c40 Mon Sep 17 00:00:00 2001 From: Ron Arts Date: Mon, 30 Oct 2017 16:21:17 +0100 Subject: [PATCH 072/191] Also accept an RSA key with crlf --- .gitattributes | 4 +++- src/CryptKey.php | 2 +- tests/CryptKeyTest.php | 6 ++++++ tests/Stubs/public.key.crlf | 6 ++++++ 4 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 tests/Stubs/public.key.crlf diff --git a/.gitattributes b/.gitattributes index d85a794e..bea6eefb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,4 +10,6 @@ /phpunit.xml.dist export-ignore /CHANGELOG.md export-ignore /CONTRIBUTING.md export-ignore -/README.md export-ignore \ No newline at end of file +/README.md export-ignore + ++*.crlf eol=crlf diff --git a/src/CryptKey.php b/src/CryptKey.php index 2ede9e33..0e06f7ab 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -14,7 +14,7 @@ namespace League\OAuth2\Server; class CryptKey { const RSA_KEY_PATTERN = - '/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----\n)(.|\n)+(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)$/'; + '/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----)(.|\n|\r)+(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)$/'; /** * @var string diff --git a/tests/CryptKeyTest.php b/tests/CryptKeyTest.php index c7f7f4a0..08bf27e8 100644 --- a/tests/CryptKeyTest.php +++ b/tests/CryptKeyTest.php @@ -21,6 +21,12 @@ class CryptKeyTest extends \PHPUnit_Framework_TestCase $this->assertEquals('file://' . $keyFile, $key->getKeyPath()); $this->assertEquals('secret', $key->getPassPhrase()); + + $keyFile = __DIR__ . '/Stubs/public.key.crlf'; + $key = new CryptKey($keyFile, 'secret'); + + $this->assertEquals('file://' . $keyFile, $key->getKeyPath()); + $this->assertEquals('secret', $key->getPassPhrase()); } public function testKeyFileCreation() diff --git a/tests/Stubs/public.key.crlf b/tests/Stubs/public.key.crlf new file mode 100644 index 00000000..25010108 --- /dev/null +++ b/tests/Stubs/public.key.crlf @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOBcFjGUlo3BJ9zjwQLgAHn6Oy +5Si0uB7MublTiPob8rWTiCE4weAFqzPoAB07vB0t0f8c1R8rmwHMD5ljWPBgJ8Fe +wtwAUzprOBcau6DWukd/TKxXWeVLAl/NZxijI+jR5QDBYLNBtj1G4LBVHMmINd3r +yCycbf9ac3rcC8zhrQIDAQAB +-----END PUBLIC KEY----- From 90fec631040e572f0d4c559351da3efc702b1081 Mon Sep 17 00:00:00 2001 From: Ron Arts Date: Mon, 30 Oct 2017 16:41:10 +0100 Subject: [PATCH 073/191] Setup the public.key.crlf with the proper permissions --- tests/AuthorizationServerTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 91ca9e4b..c937e84a 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -31,6 +31,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase // Make sure the keys have the correct permissions. chmod(__DIR__ . '/Stubs/private.key', 0600); chmod(__DIR__ . '/Stubs/public.key', 0600); + chmod(__DIR__ . '/Stubs/public.key.crlf', 0600); } public function testRespondToRequestInvalidGrantType() From f9143b5163deb146def3400014a70a59312a69f6 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 30 Oct 2017 23:26:11 +0000 Subject: [PATCH 074/191] Fix the refresh token grant test --- tests/Grant/RefreshTokenGrantTest.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index 514d4c62..dbaf75d1 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -49,6 +49,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); + $scopeEntity->setIdentifier(self::DEFAULT_SCOPE); $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -63,10 +64,6 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase ->expects($this->once()) ->method('persistNewRefreshToken')->willReturnSelf(); - $oldRefreshTokenCheckResult = ['scopes' => $scopeEntity->getIdentifier()]; - - $refreshTokenRepositoryMock->method('validateOldRefreshToken')->willReturn($oldRefreshTokenCheckResult); - $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); @@ -81,7 +78,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], + 'scopes' => [self::DEFAULT_SCOPE], 'user_id' => 123, 'expire_time' => time() + 3600, ] From 1161ceda0d2c31f5b0162558149d63a934168cc2 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 30 Oct 2017 23:26:49 +0000 Subject: [PATCH 075/191] Fix the authorization server middleware test --- tests/Middleware/AuthorizationServerMiddlewareTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/Middleware/AuthorizationServerMiddlewareTest.php b/tests/Middleware/AuthorizationServerMiddlewareTest.php index 74dffbf7..b41774de 100644 --- a/tests/Middleware/AuthorizationServerMiddlewareTest.php +++ b/tests/Middleware/AuthorizationServerMiddlewareTest.php @@ -11,18 +11,24 @@ use League\OAuth2\Server\Repositories\ClientRepositoryInterface; use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; +use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use Zend\Diactoros\Response; use Zend\Diactoros\ServerRequestFactory; class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase { + + const DEFAULT_SCOPE = 'basic'; + public function testValidResponse() { $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepository->method('getClientEntity')->willReturn(new ClientEntity()); + $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $accessRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -34,6 +40,7 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase $scopeRepositoryMock, 'file://' . __DIR__ . '/../Stubs/private.key', base64_encode(random_bytes(36)), + self::DEFAULT_SCOPE, new StubResponseType() ); From a49f6ff80d2ed95914a04be237c844c817a974a8 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 30 Oct 2017 23:36:19 +0000 Subject: [PATCH 076/191] Remove setting default scope in the constructor --- src/AuthorizationServer.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 6da9c524..805d1e73 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -73,7 +73,7 @@ class AuthorizationServer implements EmitterAwareInterface /** * @var string */ - private $defaultScope; + private $defaultScope = null; /** * New server instance. @@ -83,7 +83,6 @@ class AuthorizationServer implements EmitterAwareInterface * @param ScopeRepositoryInterface $scopeRepository * @param CryptKey|string $privateKey * @param string $encryptionKey - * @param null|string $defaultScope * @param null|ResponseTypeInterface $responseType */ public function __construct( @@ -92,7 +91,6 @@ class AuthorizationServer implements EmitterAwareInterface ScopeRepositoryInterface $scopeRepository, $privateKey, $encryptionKey, - $defaultScope = null, ResponseTypeInterface $responseType = null ) { $this->clientRepository = $clientRepository; @@ -103,9 +101,6 @@ class AuthorizationServer implements EmitterAwareInterface $privateKey = new CryptKey($privateKey); } $this->privateKey = $privateKey; - - $this->defaultScope = $defaultScope; - $this->encryptionKey = $encryptionKey; $this->responseType = $responseType; } From 3828f87b192885438febcf09c570dc56df630585 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 30 Oct 2017 23:48:02 +0000 Subject: [PATCH 077/191] Fix tests as no longer set the default scope in the constructor Use new setDefaultScope() method instead. Also changed default scope to be a blank string instead of null --- src/AuthorizationServer.php | 12 +++++++++++- tests/AuthorizationServerTest.php | 8 ++++---- .../Middleware/AuthorizationServerMiddlewareTest.php | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 805d1e73..1122749b 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -73,7 +73,7 @@ class AuthorizationServer implements EmitterAwareInterface /** * @var string */ - private $defaultScope = null; + private $defaultScope = ''; /** * New server instance. @@ -209,4 +209,14 @@ class AuthorizationServer implements EmitterAwareInterface return $this->responseType; } + + /** + * Set the default scope for the authorization server. + * + * @param string $defaultScope + */ + public function setDefaultScope($defaultScope) + { + $this->defaultScope = $defaultScope; + } } diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 15376c22..594a7ca6 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -45,7 +45,6 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), 'file://' . __DIR__ . '/Stubs/private.key', base64_encode(random_bytes(36)), - self::DEFAULT_SCOPE, new StubResponseType() ); @@ -78,10 +77,10 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase $scopeRepositoryMock, 'file://' . __DIR__ . '/Stubs/private.key', base64_encode(random_bytes(36)), - self::DEFAULT_SCOPE, new StubResponseType() ); + $server->setDefaultScope(self::DEFAULT_SCOPE); $server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M')); $_POST['grant_type'] = 'client_credentials'; @@ -166,9 +165,10 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), $scopeRepositoryMock, 'file://' . __DIR__ . '/Stubs/private.key', - 'file://' . __DIR__ . '/Stubs/public.key', - self::DEFAULT_SCOPE + 'file://' . __DIR__ . '/Stubs/public.key' ); + + $server->setDefaultScope(self::DEFAULT_SCOPE); $server->enableGrantType($grant); $request = new ServerRequest( diff --git a/tests/Middleware/AuthorizationServerMiddlewareTest.php b/tests/Middleware/AuthorizationServerMiddlewareTest.php index b41774de..1556405a 100644 --- a/tests/Middleware/AuthorizationServerMiddlewareTest.php +++ b/tests/Middleware/AuthorizationServerMiddlewareTest.php @@ -40,10 +40,10 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase $scopeRepositoryMock, 'file://' . __DIR__ . '/../Stubs/private.key', base64_encode(random_bytes(36)), - self::DEFAULT_SCOPE, new StubResponseType() ); + $server->setDefaultScope(self::DEFAULT_SCOPE); $server->enableGrantType(new ClientCredentialsGrant()); $_POST['grant_type'] = 'client_credentials'; From f79d3f27cf85c093de3dce41880c4d1116b81d7d Mon Sep 17 00:00:00 2001 From: Ron Arts Date: Tue, 31 Oct 2017 10:14:46 +0100 Subject: [PATCH 078/191] Incorporate https://github.com/thephpleague/oauth2-server/pull/731. Thanks. Now can handle cr/lf, cr, and lf endings. And on php5 large keys as well. --- src/CryptKey.php | 2 +- tests/AuthorizationServerTest.php | 2 +- tests/CryptKeyTest.php | 2 +- tests/Stubs/private.key.crlf | 27 +++++++++++++++++++++++++++ tests/Stubs/public.key.crlf | 6 ------ 5 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 tests/Stubs/private.key.crlf delete mode 100644 tests/Stubs/public.key.crlf diff --git a/src/CryptKey.php b/src/CryptKey.php index 0e06f7ab..9ec9202f 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -14,7 +14,7 @@ namespace League\OAuth2\Server; class CryptKey { const RSA_KEY_PATTERN = - '/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----)(.|\n|\r)+(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)$/'; + '/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----).+(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)$/s'; /** * @var string diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index c937e84a..dfbb51d4 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -31,7 +31,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase // Make sure the keys have the correct permissions. chmod(__DIR__ . '/Stubs/private.key', 0600); chmod(__DIR__ . '/Stubs/public.key', 0600); - chmod(__DIR__ . '/Stubs/public.key.crlf', 0600); + chmod(__DIR__ . '/Stubs/private.key.crlf', 0600); } public function testRespondToRequestInvalidGrantType() diff --git a/tests/CryptKeyTest.php b/tests/CryptKeyTest.php index 08bf27e8..ce3c8081 100644 --- a/tests/CryptKeyTest.php +++ b/tests/CryptKeyTest.php @@ -22,7 +22,7 @@ class CryptKeyTest extends \PHPUnit_Framework_TestCase $this->assertEquals('file://' . $keyFile, $key->getKeyPath()); $this->assertEquals('secret', $key->getPassPhrase()); - $keyFile = __DIR__ . '/Stubs/public.key.crlf'; + $keyFile = __DIR__ . '/Stubs/private.key.crlf'; $key = new CryptKey($keyFile, 'secret'); $this->assertEquals('file://' . $keyFile, $key->getKeyPath()); diff --git a/tests/Stubs/private.key.crlf b/tests/Stubs/private.key.crlf new file mode 100644 index 00000000..5e7e5a01 --- /dev/null +++ b/tests/Stubs/private.key.crlf @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAtHYxRBYATiiyDFs3pEhFg6Ei/UiQEmolTaQyQK810xHY23+X +4elLl6HP1J09mefmJ3ZdIgjIOS6rfK1BQnZIvI+IkoC7+qpD92y9f48iL0tCYKsn +i1LFFjP0bESTGDe7XANifQPkp9GvKgJbu7h1/ac8x4CBSU0ZjtEvinQRsdYil6OM +MXLWGozbBy13X8G+Ganv2i1aPZ2B25GyrH6lVIEwztGrSYxUrFVL+8dHhONf6PYX +19gjdzxkXCYQy2AGMc1FevZmnpIqDNQwX7CUUXQ4TDJmiP0aBEni094gUhnRFUr9 +dmGpLQcCb2i0WMh2K+swFk3EutDAJ+73LKoZ3QIDAQABAoIBADo8Tge3xd9zGIoO +QbV9MRmaPW1ZJk0a/fDBRQpEwGzdvIqQ8VWQ8Lj9GdF18LQi9s3TT5i1FtAFNIfm +bUHiY/SdqSgF7SOmIIrPB5QLf6+dbM0/TmKSklFo8L6jnohZK9g0q2rGf9p8Ozem +TS4WB9WUS3PiD1a1T8Mb1Gisri0h7rvI4TIkrcx6lUUCgphCZd2TWUhmE3YmybOg +4h855W685g/ydzjwB+5Y6CS3V6a78Z5Gb4df3l0XfqCWh/xzuNs7nIpRv8CE0vRE +vq9j/cVyKkzMjiagteJaisTCBkDmtAi9dEVL8uaSDoTJq1g+VOGuJxHUm31Pavqr +3RwvXS0CgYEA74jUqmzxAwr/uBWquIkfMg+hsKjJe3gsSAJIAPzcA9OkzZd9w/1R +P8C92N2UaDbCW7ZEl7ZzS+IO6nA4OcR98j77/nBk6cYykyVRkSaj01epz3bRApxc +R18e49MBftSMnI5R7lIJO/UAIRfd0rntX4jkdVAdn9s/VOvG8w4KQXcCgYEAwN3W +b3azSNYlj4CW8+t6qS/3JQ/qpPgVuqkqP9dQXC9O6VlV03pJIwFk2Ldjd7/eXT+0 +hFVB3O71iECfet/1UgustlgFp5I4ZrPmYF/J1nGpx1KIE8P4d0qC8lODtdnsGAcU ++/vBjXinX7pWgM8e6LAJzqNUq/xal/wNY325dEsCgYB7J0+n+/ECToJhhApNbHq0 +g2LvcCh/Ka8iqsGYeGkqMoOWDKBlxvUiIRe6y1nFJvpQquqjUfP/fM+Ma3wM/2B9 +zzJChEjuBK/2BYblaQdr3rN47i7R99BeBaLdIZywN9m/mFC5hkYnJHUXjqzG7j8E +El7bjgBdMx1hrQOR7ZMKSwKBgQC2SXXBiBlPwEdj6I/EH06h1hnrR63pGim/cN/j +0ye62WPmHW+HH888bLbaNgqnRgtvayS85rAHlzst+pZBVqfRUgN9nJhLl2IDgAlA +EYj9TBTBtXmz5MdUSHKXguO73yrMUvU8bOi1Q9I+IipcOGboWmoKikke/LbLa4lj +/ZJpHQKBgQCuDanU+AJKgUQkkC2gHwT8quxPoRcFFErHp3iaDAwd5XsZJG9FHQUP +RkPE+JkSaj65byFLhCPHUayfk4Y4udHEy4cXiv2SxZNK8q1HwuFEvb7uFprj0hNs +14qJunONVt/jzswdwO5kGVbpGlHl7U0JABnTJP71fW/rE5SH4zYxqg== +-----END RSA PRIVATE KEY----- diff --git a/tests/Stubs/public.key.crlf b/tests/Stubs/public.key.crlf deleted file mode 100644 index 25010108..00000000 --- a/tests/Stubs/public.key.crlf +++ /dev/null @@ -1,6 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOBcFjGUlo3BJ9zjwQLgAHn6Oy -5Si0uB7MublTiPob8rWTiCE4weAFqzPoAB07vB0t0f8c1R8rmwHMD5ljWPBgJ8Fe -wtwAUzprOBcau6DWukd/TKxXWeVLAl/NZxijI+jR5QDBYLNBtj1G4LBVHMmINd3r -yCycbf9ac3rcC8zhrQIDAQAB ------END PUBLIC KEY----- From d0619385b8852529e3a078513a192f7f021d39b3 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Tue, 31 Oct 2017 21:00:14 +0000 Subject: [PATCH 079/191] Add a basic test to ensure we throw an exception when no scope is given --- tests/Grant/AbstractGrantTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 542c78dc..11139bfb 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -459,6 +459,21 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase $grantMock->validateScopes('basic '); } + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 11 + */ + public function testValidateScopesMissingScope() + { + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn(null); + + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setScopeRepository($scopeRepositoryMock); + + $grantMock->validateScopes(''); + } + public function testGenerateUniqueIdentifier() { $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); From b2fe909a71a2790d3f81404df431b30315ed5c88 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Tue, 31 Oct 2017 22:58:07 +0000 Subject: [PATCH 080/191] Removed the missing scope exception as should be using invalid_scope --- src/Exception/OAuthServerException.php | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 2258185f..45e03c07 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -195,21 +195,6 @@ class OAuthServerException extends \Exception ); } - /** - * Missing scope error - * - * @param null|string $redirectUri A HTTP URI to redirect the user back to - * - * @return static - */ - public static function missingScope($redirectUri = null) - { - $errorMessage = 'No scope was specified for this request'; - $hint = 'Set a default scope on the server if no scopes are passed in the request'; - - return new static($errorMessage, 11, 'missing_scope', 400, $hint, $redirectUri); - } - /** * @return string */ From 4806eda45ae605bc5d58edd110c6a179576fa258 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Tue, 31 Oct 2017 22:59:01 +0000 Subject: [PATCH 081/191] Change to throw invalid scope instead of missing scope exception --- src/Grant/AbstractGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index b52b14cb..bbb9efc0 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -246,7 +246,7 @@ abstract class AbstractGrant implements GrantTypeInterface } if (empty($validScopes)) { - throw OAuthServerException::missingScope($redirectUri); + throw OAuthServerException::invalidScope($redirectUri); } return $validScopes; From bd2cdaf5da5ffbe1a2417bbb8126f8cfe03721de Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Tue, 31 Oct 2017 23:01:19 +0000 Subject: [PATCH 082/191] Change missing scope test to check for invalid_scope exception --- tests/Grant/AbstractGrantTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 11139bfb..83009716 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -461,7 +461,7 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase /** * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 11 + * @expectedExceptionCode 5 */ public function testValidateScopesMissingScope() { From ab760a805c36484bf8d522ae1ce7fda35c0ead99 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 6 Nov 2017 21:19:07 +0000 Subject: [PATCH 083/191] Remove default scope from abstract grant This should be added to the AbstractAuthorizeGrant instead as it is only used for an authorization request --- src/Grant/AbstractGrant.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index bbb9efc0..297bce2b 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -56,11 +56,6 @@ abstract class AbstractGrant implements GrantTypeInterface */ protected $scopeRepository; - /** - * @var string - */ - protected $defaultScope = ''; - /** * @var AuthCodeRepositoryInterface */ @@ -110,14 +105,6 @@ abstract class AbstractGrant implements GrantTypeInterface $this->scopeRepository = $scopeRepository; } - /** - * @param string $scope - */ - public function setDefaultScope($scope) - { - $this->defaultScope = $scope; - } - /** * @param RefreshTokenRepositoryInterface $refreshTokenRepository */ From 42ea0de9fb3ad310f6b47958269beb4c9bca6c07 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 6 Nov 2017 21:19:38 +0000 Subject: [PATCH 084/191] Add default scope to the AbstractAuthorizeGrant --- src/Grant/AbstractAuthorizeGrant.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Grant/AbstractAuthorizeGrant.php b/src/Grant/AbstractAuthorizeGrant.php index 7f05100c..6a8d7c07 100644 --- a/src/Grant/AbstractAuthorizeGrant.php +++ b/src/Grant/AbstractAuthorizeGrant.php @@ -13,6 +13,11 @@ namespace League\OAuth2\Server\Grant; abstract class AbstractAuthorizeGrant extends AbstractGrant { + /** + * @var string + */ + protected $defaultScope = ''; + /** * @param string $uri * @param array $params @@ -26,4 +31,12 @@ abstract class AbstractAuthorizeGrant extends AbstractGrant return $uri . http_build_query($params); } + + /** + * @param string $scope + */ + public function setDefaultScope($scope) + { + $this->defaultScope = $scope; + } } From 9cd86a9154e07a8a50cd5318f76de3f287b74929 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 6 Nov 2017 21:21:14 +0000 Subject: [PATCH 085/191] Remove default scope for the ClientCredentialsGrant --- src/Grant/ClientCredentialsGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/ClientCredentialsGrant.php b/src/Grant/ClientCredentialsGrant.php index ed157aaf..2dce0e54 100644 --- a/src/Grant/ClientCredentialsGrant.php +++ b/src/Grant/ClientCredentialsGrant.php @@ -29,7 +29,7 @@ class ClientCredentialsGrant extends AbstractGrant ) { // Validate request $client = $this->validateClient($request); - $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); + $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); // Finalize the requested scopes $finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client); From 82b81c7f6ff5e3edf829991493bc5c7b23a8fee4 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 6 Nov 2017 21:22:09 +0000 Subject: [PATCH 086/191] Remove setDefaultScope function from the grant interface --- src/Grant/GrantTypeInterface.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Grant/GrantTypeInterface.php b/src/Grant/GrantTypeInterface.php index 0e721435..7aa98242 100644 --- a/src/Grant/GrantTypeInterface.php +++ b/src/Grant/GrantTypeInterface.php @@ -119,13 +119,6 @@ interface GrantTypeInterface extends EmitterAwareInterface */ public function setScopeRepository(ScopeRepositoryInterface $scopeRepository); - /** - * Set the default scope. - * - * @param string $scope - */ - public function setDefaultScope($scope); - /** * Set the path to the private key. * From 093c7755fa7cd69198d60257caeba891a17103c0 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 6 Nov 2017 21:23:14 +0000 Subject: [PATCH 087/191] Remove default scope from the Password Grant --- src/Grant/PasswordGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/PasswordGrant.php b/src/Grant/PasswordGrant.php index cfd7e9fe..0a0c80a7 100644 --- a/src/Grant/PasswordGrant.php +++ b/src/Grant/PasswordGrant.php @@ -49,7 +49,7 @@ class PasswordGrant extends AbstractGrant ) { // Validate request $client = $this->validateClient($request); - $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); + $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); $user = $this->validateUser($request, $client); // Finalize the requested scopes From cc6eb63dd83c4b13b2dcb7b522e5f495cc20e3f3 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 6 Nov 2017 21:23:52 +0000 Subject: [PATCH 088/191] Remove default scope from the Refresh Token Grant --- src/Grant/RefreshTokenGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php index 4fda5974..d03b4edb 100644 --- a/src/Grant/RefreshTokenGrant.php +++ b/src/Grant/RefreshTokenGrant.php @@ -44,7 +44,7 @@ class RefreshTokenGrant extends AbstractGrant // Validate request $client = $this->validateClient($request); $oldRefreshToken = $this->validateOldRefreshToken($request, $client->getIdentifier()); - $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); + $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); // If no new scopes are requested then give the access token the original session scopes if (count($scopes) === 0) { From 0f080638641ad322a2e079170a8812f9dc21571a Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 6 Nov 2017 22:33:28 +0000 Subject: [PATCH 089/191] Fixed use of default scope so it is only for authorization requests --- src/AuthorizationServer.php | 6 ++- src/Grant/AbstractAuthorizeGrant.php | 15 ++++++++ src/Grant/AbstractGrant.php | 4 -- src/Grant/AuthCodeGrant.php | 12 ++++-- src/Grant/ImplicitGrant.php | 12 ++++-- tests/Grant/AbstractGrantTest.php | 15 -------- tests/Grant/AuthCodeGrantTest.php | 35 +++++++++++++++-- tests/Grant/ClientCredentialsGrantTest.php | 6 --- tests/Grant/ImplicitGrantTest.php | 38 +++++++++++++++++++ tests/Grant/PasswordGrantTest.php | 6 --- tests/Grant/RefreshTokenGrantTest.php | 6 +-- .../AuthorizationServerMiddlewareTest.php | 7 ---- 12 files changed, 109 insertions(+), 53 deletions(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 1122749b..f8674004 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -11,6 +11,7 @@ namespace League\OAuth2\Server; use League\Event\EmitterAwareInterface; use League\Event\EmitterAwareTrait; use League\OAuth2\Server\Exception\OAuthServerException; +use League\OAuth2\Server\Grant\AbstractAuthorizeGrant; use League\OAuth2\Server\Grant\GrantTypeInterface; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; use League\OAuth2\Server\Repositories\ClientRepositoryInterface; @@ -120,11 +121,14 @@ class AuthorizationServer implements EmitterAwareInterface $grantType->setAccessTokenRepository($this->accessTokenRepository); $grantType->setClientRepository($this->clientRepository); $grantType->setScopeRepository($this->scopeRepository); - $grantType->setDefaultScope($this->defaultScope); $grantType->setPrivateKey($this->privateKey); $grantType->setEmitter($this->getEmitter()); $grantType->setEncryptionKey($this->encryptionKey); + if ($grantType instanceof AbstractAuthorizeGrant) { + $grantType->setDefaultScope($this->defaultScope); + } + $this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType; $this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] = $accessTokenTTL; } diff --git a/src/Grant/AbstractAuthorizeGrant.php b/src/Grant/AbstractAuthorizeGrant.php index 6a8d7c07..0362fa22 100644 --- a/src/Grant/AbstractAuthorizeGrant.php +++ b/src/Grant/AbstractAuthorizeGrant.php @@ -11,6 +11,8 @@ namespace League\OAuth2\Server\Grant; +use League\OAuth2\Server\Exception\OAuthServerException; + abstract class AbstractAuthorizeGrant extends AbstractGrant { /** @@ -39,4 +41,17 @@ abstract class AbstractAuthorizeGrant extends AbstractGrant { $this->defaultScope = $scope; } + + /** + * @param ScopeEntityInterface[] $requestedScopes + * @param string $redirectUri + * + * @throws OAuthServerException + */ + protected function checkScopesRequested($requestedScopes, $redirectUri = null) + { + if (empty($requestedScopes)) { + throw OAuthServerException::invalidScope($redirectUri); + } + } } diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 297bce2b..b4f121da 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -232,10 +232,6 @@ abstract class AbstractGrant implements GrantTypeInterface $validScopes[] = $scope; } - if (empty($validScopes)) { - throw OAuthServerException::invalidScope($redirectUri); - } - return $validScopes; } diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index a8528bb5..e26b94aa 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -242,13 +242,19 @@ class AuthCodeGrant extends AbstractAuthorizeGrant } } + $redirectUri = is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] : $client->getRedirectUri(); + $scopes = $this->validateScopes( $this->getQueryStringParameter('scope', $request, $this->defaultScope), - is_array($client->getRedirectUri()) - ? $client->getRedirectUri()[0] - : $client->getRedirectUri() + $redirectUri ); + try { + $this->checkScopesRequested($scopes, $redirectUri); + } catch (OAuthServerException $ex) { + throw $ex; + } + $stateParameter = $this->getQueryStringParameter('state', $request); $authorizationRequest = new AuthorizationRequest(); diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 9f000eb0..5e102f32 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -144,13 +144,19 @@ class ImplicitGrant extends AbstractAuthorizeGrant } } + $redirectUri = is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] : $client->getRedirectUri(); + $scopes = $this->validateScopes( $this->getQueryStringParameter('scope', $request, $this->defaultScope), - is_array($client->getRedirectUri()) - ? $client->getRedirectUri()[0] - : $client->getRedirectUri() + $redirectUri ); + try { + $this->checkScopesRequested($scopes, $redirectUri); + } catch (OAuthServerException $ex) { + throw $ex; + } + // Finalize the requested scopes $finalizedScopes = $this->scopeRepository->finalizeScopes( $scopes, diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 83009716..542c78dc 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -459,21 +459,6 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase $grantMock->validateScopes('basic '); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 5 - */ - public function testValidateScopesMissingScope() - { - $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); - $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn(null); - - $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $grantMock->setScopeRepository($scopeRepositoryMock); - - $grantMock->validateScopes(''); - } - public function testGenerateUniqueIdentifier() { $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 025135c3..0e85144f 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -1656,16 +1656,45 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase } /** - * @expectedException \LogicException + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException ++ * @expectedExceptionCode 5 */ - public function testCompleteAuthorizationRequestNoUser() + public function testValidateAuthorizationRequestFailsWithoutScope() { + $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->completeAuthorizationRequest(new AuthorizationRequest()); + $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + [], + [], + [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + ] + ); + + $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); } } diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index 8ad7e2b7..299cf020 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -9,14 +9,11 @@ use League\OAuth2\Server\Repositories\ClientRepositoryInterface; use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; -use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use Zend\Diactoros\ServerRequest; class ClientCredentialsGrantTest extends \PHPUnit_Framework_TestCase { - const DEFAULT_SCOPE = 'basic'; - public function testGetIdentifier() { $grant = new ClientCredentialsGrant(); @@ -33,16 +30,13 @@ class ClientCredentialsGrantTest extends \PHPUnit_Framework_TestCase $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); - $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); - $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $grant = new ClientCredentialsGrant(); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $grant->setDefaultScope(self::DEFAULT_SCOPE); $serverRequest = new ServerRequest(); $serverRequest = $serverRequest->withParsedBody( diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index 18cb04b8..5009c605 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -411,4 +411,42 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->completeAuthorizationRequest(new AuthorizationRequest()); } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 5 + */ + public function testValidateAuthorizationRequestFailsWithoutScope() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + $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); + + $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + ] + ); + + $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); + } } diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index ae4b311d..b380bfb2 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -13,15 +13,12 @@ use League\OAuth2\Server\Repositories\UserRepositoryInterface; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\RefreshTokenEntity; -use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; use Zend\Diactoros\ServerRequest; class PasswordGrantTest extends \PHPUnit_Framework_TestCase { - const DEFAULT_SCOPE = 'basic'; - public function testGetIdentifier() { $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); @@ -49,16 +46,13 @@ class PasswordGrantTest extends \PHPUnit_Framework_TestCase $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); - $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); - $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $grant->setDefaultScope(self::DEFAULT_SCOPE); $serverRequest = new ServerRequest(); $serverRequest = $serverRequest->withParsedBody( diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index dbaf75d1..47d7ad17 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -20,8 +20,6 @@ use Zend\Diactoros\ServerRequest; class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase { - const DEFAULT_SCOPE = 'basic'; - /** * @var CryptTraitStub */ @@ -49,7 +47,6 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); - $scopeEntity->setIdentifier(self::DEFAULT_SCOPE); $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -70,7 +67,6 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setDefaultScope(self::DEFAULT_SCOPE); $oldRefreshToken = $this->cryptStub->doEncrypt( json_encode( @@ -78,7 +74,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', 'access_token_id' => 'abcdef', - 'scopes' => [self::DEFAULT_SCOPE], + 'scopes' => ['foo'], 'user_id' => 123, 'expire_time' => time() + 3600, ] diff --git a/tests/Middleware/AuthorizationServerMiddlewareTest.php b/tests/Middleware/AuthorizationServerMiddlewareTest.php index 1556405a..74dffbf7 100644 --- a/tests/Middleware/AuthorizationServerMiddlewareTest.php +++ b/tests/Middleware/AuthorizationServerMiddlewareTest.php @@ -11,24 +11,18 @@ use League\OAuth2\Server\Repositories\ClientRepositoryInterface; use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; -use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use Zend\Diactoros\Response; use Zend\Diactoros\ServerRequestFactory; class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase { - - const DEFAULT_SCOPE = 'basic'; - public function testValidResponse() { $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepository->method('getClientEntity')->willReturn(new ClientEntity()); - $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); - $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $accessRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -43,7 +37,6 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase new StubResponseType() ); - $server->setDefaultScope(self::DEFAULT_SCOPE); $server->enableGrantType(new ClientCredentialsGrant()); $_POST['grant_type'] = 'client_credentials'; From 13be5578253e0d549c6a76b4c2481441825e4358 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 6 Nov 2017 22:51:11 +0000 Subject: [PATCH 090/191] Re-add the complete testCompleteAuthorizationRequestNoUser() --- tests/Grant/AuthCodeGrantTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 0e85144f..395d1ace 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -1655,6 +1655,20 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface); } + /** + * @expectedException \LogicException + */ + public function testCompleteAuthorizationRequestNoUser() + { + $grant = new AuthCodeGrant( + $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), + $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), + new \DateInterval('PT10M') + ); + + $grant->completeAuthorizationRequest(new AuthorizationRequest()); + } + /** * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 5 From ce8248c10f56115fed3b8445a45fc09460e1c3c6 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 6 Nov 2017 22:56:54 +0000 Subject: [PATCH 091/191] Remove erroneous character --- 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 395d1ace..ebfd4878 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -1671,7 +1671,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase /** * @expectedException \League\OAuth2\Server\Exception\OAuthServerException -+ * @expectedExceptionCode 5 + * @expectedExceptionCode 5 */ public function testValidateAuthorizationRequestFailsWithoutScope() { From 04f3d39b459a6558d7027c6faf51bf7cc12d19cc Mon Sep 17 00:00:00 2001 From: Gabriel Caruso Date: Wed, 8 Nov 2017 16:07:07 -0200 Subject: [PATCH 092/191] Use PHPUnit\Framework\TestCase instead of PHPUnit_Framework_TestCase --- tests/AuthorizationServerTest.php | 3 ++- tests/CryptKeyTest.php | 3 ++- tests/CryptTraitTest.php | 3 ++- tests/Grant/AbstractGrantTest.php | 3 ++- tests/Grant/AuthCodeGrantTest.php | 3 ++- tests/Grant/ClientCredentialsGrantTest.php | 3 ++- tests/Grant/ImplicitGrantTest.php | 3 ++- tests/Grant/PasswordGrantTest.php | 3 ++- tests/Grant/RefreshTokenGrantTest.php | 3 ++- tests/Middleware/AuthorizationServerMiddlewareTest.php | 3 ++- tests/Middleware/ResourceServerMiddlewareTest.php | 3 ++- tests/ResourceServerTest.php | 3 ++- tests/ResponseTypes/BearerResponseTypeTest.php | 3 ++- 13 files changed, 26 insertions(+), 13 deletions(-) diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 4571c5e1..6fc59715 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -19,11 +19,12 @@ use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; use Psr\Http\Message\ResponseInterface; +use PHPUnit\Framework\TestCase; use Zend\Diactoros\Response; use Zend\Diactoros\ServerRequest; use Zend\Diactoros\ServerRequestFactory; -class AuthorizationServerTest extends \PHPUnit_Framework_TestCase +class AuthorizationServerTest extends TestCase { public function setUp() { diff --git a/tests/CryptKeyTest.php b/tests/CryptKeyTest.php index c7f7f4a0..f4fd0659 100644 --- a/tests/CryptKeyTest.php +++ b/tests/CryptKeyTest.php @@ -3,8 +3,9 @@ namespace LeagueTests\Utils; use League\OAuth2\Server\CryptKey; +use PHPUnit\Framework\TestCase; -class CryptKeyTest extends \PHPUnit_Framework_TestCase +class CryptKeyTest extends TestCase { /** * @expectedException \LogicException diff --git a/tests/CryptTraitTest.php b/tests/CryptTraitTest.php index 3d60ec9d..26427e59 100644 --- a/tests/CryptTraitTest.php +++ b/tests/CryptTraitTest.php @@ -3,8 +3,9 @@ namespace LeagueTests\Utils; use LeagueTests\Stubs\CryptTraitStub; +use PHPUnit\Framework\TestCase; -class CryptTraitTest extends \PHPUnit_Framework_TestCase +class CryptTraitTest extends TestCase { /** * @var \LeagueTests\Stubs\CryptTraitStub diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 4cd490e3..d0d9b3c8 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -18,9 +18,10 @@ use LeagueTests\Stubs\AuthCodeEntity; use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\ScopeEntity; +use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; -class AbstractGrantTest extends \PHPUnit_Framework_TestCase +class AbstractGrantTest extends TestCase { public function testGetSet() { diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index d63aec29..213b4a03 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -22,9 +22,10 @@ use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; +use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; -class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase +class AuthCodeGrantTest extends TestCase { /** * @var CryptTraitStub diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index a1665831..96d8d578 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -10,9 +10,10 @@ use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\StubResponseType; +use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; -class ClientCredentialsGrantTest extends \PHPUnit_Framework_TestCase +class ClientCredentialsGrantTest extends TestCase { public function testGetIdentifier() { diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index 3bfe4b84..f962024a 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -18,9 +18,10 @@ use LeagueTests\Stubs\CryptTraitStub; use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; +use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; -class ImplicitGrantTest extends \PHPUnit_Framework_TestCase +class ImplicitGrantTest extends TestCase { /** * CryptTrait stub diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index b380bfb2..0c1be581 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -15,9 +15,10 @@ use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; +use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; -class PasswordGrantTest extends \PHPUnit_Framework_TestCase +class PasswordGrantTest extends TestCase { public function testGetIdentifier() { diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index 47d7ad17..f7f803f7 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -16,9 +16,10 @@ use LeagueTests\Stubs\CryptTraitStub; use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; +use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; -class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase +class RefreshTokenGrantTest extends TestCase { /** * @var CryptTraitStub diff --git a/tests/Middleware/AuthorizationServerMiddlewareTest.php b/tests/Middleware/AuthorizationServerMiddlewareTest.php index 74dffbf7..bc860770 100644 --- a/tests/Middleware/AuthorizationServerMiddlewareTest.php +++ b/tests/Middleware/AuthorizationServerMiddlewareTest.php @@ -12,10 +12,11 @@ use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\StubResponseType; +use PHPUnit\Framework\TestCase; use Zend\Diactoros\Response; use Zend\Diactoros\ServerRequestFactory; -class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase +class AuthorizationServerMiddlewareTest extends TestCase { public function testValidResponse() { diff --git a/tests/Middleware/ResourceServerMiddlewareTest.php b/tests/Middleware/ResourceServerMiddlewareTest.php index 549c8003..2269c45a 100644 --- a/tests/Middleware/ResourceServerMiddlewareTest.php +++ b/tests/Middleware/ResourceServerMiddlewareTest.php @@ -8,10 +8,11 @@ use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; use League\OAuth2\Server\ResourceServer; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; +use PHPUnit\Framework\TestCase; use Zend\Diactoros\Response; use Zend\Diactoros\ServerRequest; -class ResourceServerMiddlewareTest extends \PHPUnit_Framework_TestCase +class ResourceServerMiddlewareTest extends TestCase { public function testValidResponse() { diff --git a/tests/ResourceServerTest.php b/tests/ResourceServerTest.php index 8a3353cc..3120cad2 100644 --- a/tests/ResourceServerTest.php +++ b/tests/ResourceServerTest.php @@ -6,9 +6,10 @@ namespace LeagueTests; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; use League\OAuth2\Server\ResourceServer; +use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequestFactory; -class ResourceServerTest extends \PHPUnit_Framework_TestCase +class ResourceServerTest extends TestCase { public function testValidateAuthenticatedRequest() { diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 7f710d92..daad734e 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -11,11 +11,12 @@ use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\ScopeEntity; +use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Zend\Diactoros\Response; use Zend\Diactoros\ServerRequest; -class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase +class BearerResponseTypeTest extends TestCase { public function testGenerateHttpResponse() { From 3871aee48caf1519e5f0b0417ef98f1f0b935e5a Mon Sep 17 00:00:00 2001 From: Gabriel Caruso Date: Wed, 8 Nov 2017 16:20:31 -0200 Subject: [PATCH 093/191] Bump PHPUnit version for compatibility --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d6740aa4..d8d11125 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ "defuse/php-encryption": "^2.1" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.0", + "phpunit/phpunit": "^4.8.38 || ^5.7.21", "zendframework/zend-diactoros": "^1.0" }, "repositories": [ From c895885700e0648e723136412a40cf2a8bb0f8b2 Mon Sep 17 00:00:00 2001 From: Sephster Date: Mon, 13 Nov 2017 22:19:44 +0000 Subject: [PATCH 094/191] Modify grants so only auth requests use default scopes --- src/Grant/AbstractAuthorizeGrant.php | 21 ------------------- src/Grant/AbstractGrant.php | 26 +++++++++++++++++------ src/Grant/AuthCodeGrant.php | 12 +++-------- src/Grant/ClientCredentialsGrant.php | 2 +- src/Grant/GrantTypeInterface.php | 7 +++++++ src/Grant/ImplicitGrant.php | 12 +++-------- src/Grant/PasswordGrant.php | 2 +- src/Grant/RefreshTokenGrant.php | 31 +++++++++------------------- 8 files changed, 45 insertions(+), 68 deletions(-) diff --git a/src/Grant/AbstractAuthorizeGrant.php b/src/Grant/AbstractAuthorizeGrant.php index 0362fa22..0ef8a8b0 100644 --- a/src/Grant/AbstractAuthorizeGrant.php +++ b/src/Grant/AbstractAuthorizeGrant.php @@ -33,25 +33,4 @@ abstract class AbstractAuthorizeGrant extends AbstractGrant return $uri . http_build_query($params); } - - /** - * @param string $scope - */ - public function setDefaultScope($scope) - { - $this->defaultScope = $scope; - } - - /** - * @param ScopeEntityInterface[] $requestedScopes - * @param string $redirectUri - * - * @throws OAuthServerException - */ - protected function checkScopesRequested($requestedScopes, $redirectUri = null) - { - if (empty($requestedScopes)) { - throw OAuthServerException::invalidScope($redirectUri); - } - } } diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index b4f121da..419631a4 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -81,6 +81,11 @@ abstract class AbstractGrant implements GrantTypeInterface */ protected $privateKey; + /** + * @string + */ + protected $defaultScope; + /** * @param ClientRepositoryInterface $clientRepository */ @@ -147,6 +152,14 @@ abstract class AbstractGrant implements GrantTypeInterface $this->privateKey = $key; } + /** + * @param string $scope + */ + public function setDefaultScope($scope) + { + $this->defaultScope = $scope; + } + /** * Validate the client. * @@ -213,12 +226,9 @@ abstract class AbstractGrant implements GrantTypeInterface */ public function validateScopes($scopes, $redirectUri = null) { - $scopesList = array_filter( - explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), - function ($scope) { - return !empty($scope); - } - ); + $scopesList = array_filter(explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), function ($scope) { + return !empty($scope); + }); $validScopes = []; @@ -232,6 +242,10 @@ abstract class AbstractGrant implements GrantTypeInterface $validScopes[] = $scope; } + if (empty($validScopes)) { + throw OAuthServerException::invalidScope($redirectUri); + } + return $validScopes; } diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index e26b94aa..a8528bb5 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -242,19 +242,13 @@ class AuthCodeGrant extends AbstractAuthorizeGrant } } - $redirectUri = is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] : $client->getRedirectUri(); - $scopes = $this->validateScopes( $this->getQueryStringParameter('scope', $request, $this->defaultScope), - $redirectUri + is_array($client->getRedirectUri()) + ? $client->getRedirectUri()[0] + : $client->getRedirectUri() ); - try { - $this->checkScopesRequested($scopes, $redirectUri); - } catch (OAuthServerException $ex) { - throw $ex; - } - $stateParameter = $this->getQueryStringParameter('state', $request); $authorizationRequest = new AuthorizationRequest(); diff --git a/src/Grant/ClientCredentialsGrant.php b/src/Grant/ClientCredentialsGrant.php index 2dce0e54..ed157aaf 100644 --- a/src/Grant/ClientCredentialsGrant.php +++ b/src/Grant/ClientCredentialsGrant.php @@ -29,7 +29,7 @@ class ClientCredentialsGrant extends AbstractGrant ) { // Validate request $client = $this->validateClient($request); - $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); + $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); // Finalize the requested scopes $finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client); diff --git a/src/Grant/GrantTypeInterface.php b/src/Grant/GrantTypeInterface.php index 7aa98242..0e721435 100644 --- a/src/Grant/GrantTypeInterface.php +++ b/src/Grant/GrantTypeInterface.php @@ -119,6 +119,13 @@ interface GrantTypeInterface extends EmitterAwareInterface */ public function setScopeRepository(ScopeRepositoryInterface $scopeRepository); + /** + * Set the default scope. + * + * @param string $scope + */ + public function setDefaultScope($scope); + /** * Set the path to the private key. * diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 5e102f32..9f000eb0 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -144,19 +144,13 @@ class ImplicitGrant extends AbstractAuthorizeGrant } } - $redirectUri = is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] : $client->getRedirectUri(); - $scopes = $this->validateScopes( $this->getQueryStringParameter('scope', $request, $this->defaultScope), - $redirectUri + is_array($client->getRedirectUri()) + ? $client->getRedirectUri()[0] + : $client->getRedirectUri() ); - try { - $this->checkScopesRequested($scopes, $redirectUri); - } catch (OAuthServerException $ex) { - throw $ex; - } - // Finalize the requested scopes $finalizedScopes = $this->scopeRepository->finalizeScopes( $scopes, diff --git a/src/Grant/PasswordGrant.php b/src/Grant/PasswordGrant.php index 0a0c80a7..cfd7e9fe 100644 --- a/src/Grant/PasswordGrant.php +++ b/src/Grant/PasswordGrant.php @@ -49,7 +49,7 @@ class PasswordGrant extends AbstractGrant ) { // Validate request $client = $this->validateClient($request); - $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); + $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); $user = $this->validateUser($request, $client); // Finalize the requested scopes diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php index d03b4edb..66a3b266 100644 --- a/src/Grant/RefreshTokenGrant.php +++ b/src/Grant/RefreshTokenGrant.php @@ -44,28 +44,17 @@ class RefreshTokenGrant extends AbstractGrant // Validate request $client = $this->validateClient($request); $oldRefreshToken = $this->validateOldRefreshToken($request, $client->getIdentifier()); - $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); + $scopes = $this->validateScopes($this->getRequestParameter( + 'scope', + $request, + implode(self::SCOPE_DELIMITER_STRING, $oldRefreshToken['scopes'])) + ); - // If no new scopes are requested then give the access token the original session scopes - if (count($scopes) === 0) { - $scopes = array_map(function ($scopeId) { - $scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId); - - if ($scope instanceof ScopeEntityInterface === false) { - // @codeCoverageIgnoreStart - throw OAuthServerException::invalidScope($scopeId); - // @codeCoverageIgnoreEnd - } - - return $scope; - }, $oldRefreshToken['scopes']); - } else { - // The OAuth spec says that a refreshed access token can have the original scopes or fewer so ensure - // the request doesn't include any new scopes - foreach ($scopes as $scope) { - if (in_array($scope->getIdentifier(), $oldRefreshToken['scopes']) === false) { - throw OAuthServerException::invalidScope($scope->getIdentifier()); - } + // The OAuth spec says that a refreshed access token can have the original scopes or fewer so ensure + // the request doesn't include any new scopes + foreach ($scopes as $scope) { + if (in_array($scope->getIdentifier(), $oldRefreshToken['scopes']) === false) { + throw OAuthServerException::invalidScope($scope->getIdentifier()); } } From 512d4898e230f6edce995e8c00504e7e96ef144b Mon Sep 17 00:00:00 2001 From: Sephster Date: Mon, 13 Nov 2017 22:20:16 +0000 Subject: [PATCH 095/191] Revert previous change --- src/AuthorizationServer.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index f8674004..42313bff 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -121,14 +121,11 @@ class AuthorizationServer implements EmitterAwareInterface $grantType->setAccessTokenRepository($this->accessTokenRepository); $grantType->setClientRepository($this->clientRepository); $grantType->setScopeRepository($this->scopeRepository); + $grantType->setDefaultScope($this->defaultScope); $grantType->setPrivateKey($this->privateKey); $grantType->setEmitter($this->getEmitter()); $grantType->setEncryptionKey($this->encryptionKey); - if ($grantType instanceof AbstractAuthorizeGrant) { - $grantType->setDefaultScope($this->defaultScope); - } - $this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType; $this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] = $accessTokenTTL; } From 65789e0f397a26ab5851c5840c8fc3dc42810191 Mon Sep 17 00:00:00 2001 From: Sephster Date: Mon, 13 Nov 2017 22:20:42 +0000 Subject: [PATCH 096/191] Fix tests to support default scopes for authorization requests --- tests/Grant/ClientCredentialsGrantTest.php | 6 +++++ tests/Grant/PasswordGrantTest.php | 6 +++++ tests/Grant/RefreshTokenGrantTest.php | 24 ++++++++----------- .../AuthorizationServerMiddlewareTest.php | 6 +++++ 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index 299cf020..8ad7e2b7 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -9,11 +9,14 @@ use League\OAuth2\Server\Repositories\ClientRepositoryInterface; use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; +use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use Zend\Diactoros\ServerRequest; class ClientCredentialsGrantTest extends \PHPUnit_Framework_TestCase { + const DEFAULT_SCOPE = 'basic'; + public function testGetIdentifier() { $grant = new ClientCredentialsGrant(); @@ -30,13 +33,16 @@ class ClientCredentialsGrantTest extends \PHPUnit_Framework_TestCase $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $grant = new ClientCredentialsGrant(); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $serverRequest = new ServerRequest(); $serverRequest = $serverRequest->withParsedBody( diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index b380bfb2..ae4b311d 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -13,12 +13,15 @@ use League\OAuth2\Server\Repositories\UserRepositoryInterface; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\RefreshTokenEntity; +use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; use Zend\Diactoros\ServerRequest; class PasswordGrantTest extends \PHPUnit_Framework_TestCase { + const DEFAULT_SCOPE = 'basic'; + public function testGetIdentifier() { $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); @@ -46,13 +49,16 @@ class PasswordGrantTest extends \PHPUnit_Framework_TestCase $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); + $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $serverRequest = new ServerRequest(); $serverRequest = $serverRequest->withParsedBody( diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index 47d7ad17..eb6e18fb 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -45,21 +45,18 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); + $scopeEntity->setIdentifier('foo'); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); - $accessTokenRepositoryMock - ->expects($this->once()) - ->method('persistNewAccessToken')->willReturnSelf(); + $accessTokenRepositoryMock->expects($this->once())->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); - $refreshTokenRepositoryMock - ->expects($this->once()) - ->method('persistNewRefreshToken')->willReturnSelf(); + $refreshTokenRepositoryMock->expects($this->once())->method('persistNewRefreshToken')->willReturnSelf(); $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); @@ -82,13 +79,12 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase ); $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, - ] - ); + $serverRequest = $serverRequest->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + 'scopes' => ['foo'], + ]); $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); diff --git a/tests/Middleware/AuthorizationServerMiddlewareTest.php b/tests/Middleware/AuthorizationServerMiddlewareTest.php index 74dffbf7..c44681e1 100644 --- a/tests/Middleware/AuthorizationServerMiddlewareTest.php +++ b/tests/Middleware/AuthorizationServerMiddlewareTest.php @@ -11,18 +11,23 @@ use League\OAuth2\Server\Repositories\ClientRepositoryInterface; use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; +use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use Zend\Diactoros\Response; use Zend\Diactoros\ServerRequestFactory; class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase { + const DEFAULT_SCOPE = 'basic'; + public function testValidResponse() { $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepository->method('getClientEntity')->willReturn(new ClientEntity()); + $scopeEntity = new ScopeEntity; $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $accessRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -37,6 +42,7 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase new StubResponseType() ); + $server->setDefaultScope(self::DEFAULT_SCOPE); $server->enableGrantType(new ClientCredentialsGrant()); $_POST['grant_type'] = 'client_credentials'; From eb645063c7c4ef43d3bfd1df514a8b6bdd7b5e35 Mon Sep 17 00:00:00 2001 From: Sephster Date: Mon, 13 Nov 2017 22:25:31 +0000 Subject: [PATCH 097/191] Reverted the abstract authorise grant to its previous state --- src/Grant/AbstractAuthorizeGrant.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Grant/AbstractAuthorizeGrant.php b/src/Grant/AbstractAuthorizeGrant.php index 0ef8a8b0..7f05100c 100644 --- a/src/Grant/AbstractAuthorizeGrant.php +++ b/src/Grant/AbstractAuthorizeGrant.php @@ -11,15 +11,8 @@ namespace League\OAuth2\Server\Grant; -use League\OAuth2\Server\Exception\OAuthServerException; - abstract class AbstractAuthorizeGrant extends AbstractGrant { - /** - * @var string - */ - protected $defaultScope = ''; - /** * @param string $uri * @param array $params From c6bf2e1df099d4c76fdf98f210cc8a24590fabaf Mon Sep 17 00:00:00 2001 From: Sephster Date: Mon, 13 Nov 2017 22:31:50 +0000 Subject: [PATCH 098/191] Remove unnecessary white spaces --- tests/AuthorizationServerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 594a7ca6..4fe21724 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -28,7 +28,7 @@ use Zend\Diactoros\ServerRequestFactory; class AuthorizationServerTest extends \PHPUnit_Framework_TestCase { - const DEFAULT_SCOPE = 'basic '; + const DEFAULT_SCOPE = 'basic'; public function setUp() { From a5c5929dc9eb5e6fc5c5b1676459464f39bfe6bd Mon Sep 17 00:00:00 2001 From: Sephster Date: Mon, 13 Nov 2017 22:34:12 +0000 Subject: [PATCH 099/191] Change default scope to be basic --- 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 ebfd4878..179d5feb 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -28,7 +28,7 @@ use Zend\Diactoros\ServerRequest; class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase { - const DEFAULT_SCOPE = 'default'; + const DEFAULT_SCOPE = 'basic'; /** * @var CryptTraitStub From 1e3a84fc851efbe7a9d95d4096c62ba5b0578d99 Mon Sep 17 00:00:00 2001 From: Sephster Date: Mon, 13 Nov 2017 23:00:27 +0000 Subject: [PATCH 100/191] Add a test to ensure response requests fail without a scope specified --- tests/Grant/ClientCredentialsGrantTest.php | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index 8ad7e2b7..848d8ea7 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -57,4 +57,42 @@ class ClientCredentialsGrantTest extends \PHPUnit_Framework_TestCase $this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface); } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 5 + */ + public function testRespondToRequestFailsWithoutScope() + { + $client = new ClientEntity(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $scope = new ScopeEntity(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); + + $grant = new ClientCredentialsGrant(); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + + $this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface); + } } \ No newline at end of file From 1bcee9aabac6dc7c784ae4008e08826e700ec4a6 Mon Sep 17 00:00:00 2001 From: Sephster Date: Mon, 13 Nov 2017 23:16:30 +0000 Subject: [PATCH 101/191] Add a test for a missing scope for the password grant --- tests/Grant/AuthCodeGrantTest.php | 2 - tests/Grant/ClientCredentialsGrantTest.php | 2 - tests/Grant/ImplicitGrantTest.php | 2 - tests/Grant/PasswordGrantTest.php | 46 ++++++++++++++++++++++ 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 179d5feb..4a1c6d4a 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -1708,7 +1708,5 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase 'redirect_uri' => 'http://foo/bar', ] ); - - $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); } } diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index 848d8ea7..8559490d 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -92,7 +92,5 @@ class ClientCredentialsGrantTest extends \PHPUnit_Framework_TestCase $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); - - $this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface); } } \ No newline at end of file diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index 5009c605..6e175ea6 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -446,7 +446,5 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase 'redirect_uri' => 'http://foo/bar', ] ); - - $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); } } diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index ae4b311d..8c4337ba 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -173,4 +173,50 @@ class PasswordGrantTest extends \PHPUnit_Framework_TestCase $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 5 + */ + public function testRespondToRequestFailsWithoutScope() + { + $client = new ClientEntity(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + $userEntity = new UserEntity(); + $userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); + + $scope = new ScopeEntity(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); + + $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'username' => 'foo', + 'password' => 'bar', + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + } } From 6e6baf5b7551f5180f0e71ac3a0a60e9193abcd4 Mon Sep 17 00:00:00 2001 From: Sephster Date: Mon, 13 Nov 2017 23:57:24 +0000 Subject: [PATCH 102/191] Remove abstract authorize grant use --- src/AuthorizationServer.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 40386653..c375af47 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -12,7 +12,6 @@ namespace League\OAuth2\Server; use League\Event\EmitterAwareInterface; use League\Event\EmitterAwareTrait; use League\OAuth2\Server\Exception\OAuthServerException; -use League\OAuth2\Server\Grant\AbstractAuthorizeGrant; use League\OAuth2\Server\Grant\GrantTypeInterface; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; use League\OAuth2\Server\Repositories\ClientRepositoryInterface; From dc9c1a10234bab396592f62f41131d80006819fc Mon Sep 17 00:00:00 2001 From: Sephster Date: Mon, 13 Nov 2017 23:59:55 +0000 Subject: [PATCH 103/191] Remove blank line to keep code consistent --- tests/Grant/AuthCodeGrantTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 6b06fe37..0e24d120 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -27,7 +27,6 @@ use Zend\Diactoros\ServerRequest; class AuthCodeGrantTest extends TestCase { - const DEFAULT_SCOPE = 'basic'; /** From b50c7622db08b34959407dc59aa7cc2e789d7379 Mon Sep 17 00:00:00 2001 From: Sephster Date: Tue, 14 Nov 2017 00:12:04 +0000 Subject: [PATCH 104/191] Add in validation for authorization requests. Fixes thephpleague/oauth2-server#677 --- tests/Grant/AuthCodeGrantTest.php | 2 ++ tests/Grant/ImplicitGrantTest.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 0e24d120..6b60f2b0 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -1707,5 +1707,7 @@ class AuthCodeGrantTest extends TestCase 'redirect_uri' => 'http://foo/bar', ] ); + + $grant->validateAuthorizationRequest($request); } } diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index db62d09c..dff2c341 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -447,5 +447,7 @@ class ImplicitGrantTest extends TestCase 'redirect_uri' => 'http://foo/bar', ] ); + + $grant->validateAuthorizationRequest($request); } } From 92739360093fef51bf642b214c32901e29bf8da2 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 18 Nov 2017 18:46:03 +0000 Subject: [PATCH 105/191] Fix bug where not specifying the bad scope --- src/Grant/AbstractGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 419631a4..d8548eca 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -243,7 +243,7 @@ abstract class AbstractGrant implements GrantTypeInterface } if (empty($validScopes)) { - throw OAuthServerException::invalidScope($redirectUri); + throw OAuthServerException::invalidScope('', $redirectUri); } return $validScopes; From 2765481b9f0384fa1da68938369e3dcd84975d6a Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 18 Nov 2017 18:47:38 +0000 Subject: [PATCH 106/191] Handle no scope hint --- src/Exception/OAuthServerException.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 45e03c07..8d101c4c 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -105,10 +105,15 @@ class OAuthServerException extends \Exception public static function invalidScope($scope, $redirectUri = null) { $errorMessage = 'The requested scope is invalid, unknown, or malformed'; - $hint = sprintf( - 'Check the `%s` scope', - htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false) - ); + + if (empty($scope)) { + $hint = 'Specify a scope in the request or set a default scope'; + } else { + $hint = sprintf( + 'Check the `%s` scope', + htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false) + ); + } return new static($errorMessage, 5, 'invalid_scope', 400, $hint, $redirectUri); } From f88961eddd0efee189088b39c1252f934e231cda Mon Sep 17 00:00:00 2001 From: Iman Date: Thu, 23 Nov 2017 21:26:39 +0330 Subject: [PATCH 107/191] flatten code --- src/AuthorizationServer.php | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index c375af47..69c16954 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -178,17 +178,19 @@ class AuthorizationServer implements EmitterAwareInterface public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response) { foreach ($this->enabledGrantTypes as $grantType) { - if ($grantType->canRespondToAccessTokenRequest($request)) { - $tokenResponse = $grantType->respondToAccessTokenRequest( - $request, - $this->getResponseType(), - $this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] - ); - - if ($tokenResponse instanceof ResponseTypeInterface) { - return $tokenResponse->generateHttpResponse($response); - } + if (!$grantType->canRespondToAccessTokenRequest($request)) { + continue; } + $tokenResponse = $grantType->respondToAccessTokenRequest( + $request, + $this->getResponseType(), + $this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] + ); + + if ($tokenResponse instanceof ResponseTypeInterface) { + return $tokenResponse->generateHttpResponse($response); + } + } throw OAuthServerException::unsupportedGrantType(); From d1d68242eaa2e9c09a5455b27c1b28187d3174fc Mon Sep 17 00:00:00 2001 From: Gabriel Caruso Date: Thu, 30 Nov 2017 23:52:50 -0200 Subject: [PATCH 108/191] Test against PHP 7.2 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 1f085674..a9023fa6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ php: - 5.6 - 7.0 - 7.1 + - 7.2 install: - travis_retry composer install --no-interaction --prefer-source From 9ceafe5dd36c14cc325180beb242ee78125b2bf5 Mon Sep 17 00:00:00 2001 From: Gabriel Caruso Date: Wed, 6 Dec 2017 18:24:42 -0200 Subject: [PATCH 109/191] Refactoring tests --- tests/AuthorizationServerTest.php | 9 +++-- tests/Grant/AbstractGrantTest.php | 11 +++--- tests/Grant/AuthCodeGrantTest.php | 38 +++++++++---------- tests/Grant/ClientCredentialsGrantTest.php | 4 +- tests/Grant/ImplicitGrantTest.php | 8 ++-- tests/Grant/PasswordGrantTest.php | 4 +- tests/Grant/RefreshTokenGrantTest.php | 8 ++-- .../ResponseTypes/BearerResponseTypeTest.php | 24 ++++++------ 8 files changed, 54 insertions(+), 52 deletions(-) diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index a08b6b7a..068b914d 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -106,7 +106,7 @@ class AuthorizationServerTest extends TestCase $method = $abstractGrantReflection->getMethod('getResponseType'); $method->setAccessible(true); - $this->assertTrue($method->invoke($server) instanceof BearerTokenResponse); + $this->assertInstanceOf(BearerTokenResponse::class, $method->invoke($server)); } public function testCompleteAuthorizationRequest() @@ -138,8 +138,9 @@ class AuthorizationServerTest extends TestCase $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); - $this->assertTrue( - $server->completeAuthorizationRequest($authRequest, new Response) instanceof ResponseInterface + $this->assertInstanceOf( + ResponseInterface::class, + $server->completeAuthorizationRequest($authRequest, new Response) ); } @@ -186,7 +187,7 @@ class AuthorizationServerTest extends TestCase ] ); - $this->assertTrue($server->validateAuthorizationRequest($request) instanceof AuthorizationRequest); + $this->assertInstanceOf(AuthorizationRequest::class, $server->validateAuthorizationRequest($request)); } public function testValidateAuthorizationRequestWithMissingRedirectUri() diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index d0d9b3c8..6266df0a 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -342,7 +342,7 @@ class AbstractGrantTest extends TestCase $accessToken = new AccessTokenEntity(); /** @var RefreshTokenEntityInterface $refreshToken */ $refreshToken = $issueRefreshTokenMethod->invoke($grantMock, $accessToken); - $this->assertTrue($refreshToken instanceof RefreshTokenEntityInterface); + $this->assertInstanceOf(RefreshTokenEntityInterface::class, $refreshToken); $this->assertEquals($accessToken, $refreshToken->getAccessToken()); } @@ -367,7 +367,7 @@ class AbstractGrantTest extends TestCase 123, [new ScopeEntity()] ); - $this->assertTrue($accessToken instanceof AccessTokenEntityInterface); + $this->assertInstanceOf(AccessTokenEntityInterface::class, $accessToken); } public function testIssueAuthCode() @@ -383,7 +383,8 @@ class AbstractGrantTest extends TestCase $issueAuthCodeMethod = $abstractGrantReflection->getMethod('issueAuthCode'); $issueAuthCodeMethod->setAccessible(true); - $this->assertTrue( + $this->assertInstanceOf( + AuthCodeEntityInterface::class, $issueAuthCodeMethod->invoke( $grantMock, new \DateInterval('PT1H'), @@ -391,7 +392,7 @@ class AbstractGrantTest extends TestCase 123, 'http://foo/bar', [new ScopeEntity()] - ) instanceof AuthCodeEntityInterface + ) ); } @@ -467,7 +468,7 @@ class AbstractGrantTest extends TestCase $method = $abstractGrantReflection->getMethod('generateUniqueIdentifier'); $method->setAccessible(true); - $this->assertTrue(is_string($method->invoke($grantMock))); + $this->assertInternalType('string', $method->invoke($grantMock)); } public function testCanRespondToAuthorizationRequest() diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 6b60f2b0..9ce035c0 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -111,7 +111,7 @@ class AuthCodeGrantTest extends TestCase ] ); - $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); + $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); } public function testValidateAuthorizationRequestRedirectUriArray() @@ -149,7 +149,7 @@ class AuthCodeGrantTest extends TestCase ] ); - $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); + $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); } public function testValidateAuthorizationRequestCodeChallenge() @@ -189,7 +189,7 @@ class AuthCodeGrantTest extends TestCase ] ); - $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); + $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); } /** @@ -545,7 +545,7 @@ class AuthCodeGrantTest extends TestCase ); $grant->setEncryptionKey($this->cryptStub->getKey()); - $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); + $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } /** @@ -636,8 +636,8 @@ class AuthCodeGrantTest extends TestCase /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); - $this->assertTrue($response->getAccessToken() instanceof AccessTokenEntityInterface); - $this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface); + $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); + $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } public function testRespondToAccessTokenRequestCodeChallengePlain() @@ -707,8 +707,8 @@ class AuthCodeGrantTest extends TestCase /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); - $this->assertTrue($response->getAccessToken() instanceof AccessTokenEntityInterface); - $this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface); + $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); + $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } public function testRespondToAccessTokenRequestCodeChallengeS256() @@ -778,8 +778,8 @@ class AuthCodeGrantTest extends TestCase /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); - $this->assertTrue($response->getAccessToken() instanceof AccessTokenEntityInterface); - $this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface); + $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); + $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } /** @@ -1390,7 +1390,7 @@ class AuthCodeGrantTest extends TestCase ); $grant->setEncryptionKey($this->cryptStub->getKey()); - $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); + $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } /** @@ -1416,7 +1416,7 @@ class AuthCodeGrantTest extends TestCase ); $grant->setEncryptionKey($this->cryptStub->getKey()); - $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); + $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } /** @@ -1441,7 +1441,7 @@ class AuthCodeGrantTest extends TestCase new \DateInterval('PT10M') ); - $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); + $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } public function testRefreshTokenRepositoryUniqueConstraintCheck() @@ -1508,8 +1508,8 @@ class AuthCodeGrantTest extends TestCase /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); - $this->assertTrue($response->getAccessToken() instanceof AccessTokenEntityInterface); - $this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface); + $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); + $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } /** @@ -1579,8 +1579,8 @@ class AuthCodeGrantTest extends TestCase /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); - $this->assertTrue($response->getAccessToken() instanceof AccessTokenEntityInterface); - $this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface); + $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); + $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } /** @@ -1650,8 +1650,8 @@ class AuthCodeGrantTest extends TestCase /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); - $this->assertTrue($response->getAccessToken() instanceof AccessTokenEntityInterface); - $this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface); + $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); + $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } /** diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index cfcdfba5..e24f892a 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -56,7 +56,7 @@ class ClientCredentialsGrantTest extends TestCase $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); - $this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface); + $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); } /** @@ -94,4 +94,4 @@ class ClientCredentialsGrantTest extends TestCase $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); } -} \ No newline at end of file +} diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index dff2c341..8358ae02 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -116,7 +116,7 @@ class ImplicitGrantTest extends TestCase ] ); - $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); + $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); } public function testValidateAuthorizationRequestRedirectUriArray() @@ -151,7 +151,7 @@ class ImplicitGrantTest extends TestCase ] ); - $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); + $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); } /** @@ -290,7 +290,7 @@ class ImplicitGrantTest extends TestCase $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); + $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } /** @@ -334,7 +334,7 @@ class ImplicitGrantTest extends TestCase $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); + $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } /** diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index 469044af..55290ce3 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -74,8 +74,8 @@ class PasswordGrantTest extends TestCase $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); - $this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface); - $this->assertTrue($responseType->getRefreshToken() instanceof RefreshTokenEntityInterface); + $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); + $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); } /** diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index d7b3a8fd..89598115 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -90,8 +90,8 @@ class RefreshTokenGrantTest extends TestCase $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); - $this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface); - $this->assertTrue($responseType->getRefreshToken() instanceof RefreshTokenEntityInterface); + $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); + $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); } public function testRespondToReducedScopes() @@ -147,8 +147,8 @@ class RefreshTokenGrantTest extends TestCase $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); - $this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface); - $this->assertTrue($responseType->getRefreshToken() instanceof RefreshTokenEntityInterface); + $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); + $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); } /** diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index daad734e..56ae9e3e 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -48,7 +48,7 @@ class BearerResponseTypeTest extends TestCase $response = $responseType->generateHttpResponse(new Response()); - $this->assertTrue($response instanceof ResponseInterface); + $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('no-cache', $response->getHeader('pragma')[0]); $this->assertEquals('no-store', $response->getHeader('cache-control')[0]); @@ -56,10 +56,10 @@ class BearerResponseTypeTest extends TestCase $response->getBody()->rewind(); $json = json_decode($response->getBody()->getContents()); - $this->assertEquals('Bearer', $json->token_type); - $this->assertTrue(isset($json->expires_in)); - $this->assertTrue(isset($json->access_token)); - $this->assertTrue(isset($json->refresh_token)); + $this->assertAttributeEquals('Bearer', 'token_type', $json); + $this->assertObjectHasAttribute('expires_in', $json); + $this->assertObjectHasAttribute('access_token', $json); + $this->assertObjectHasAttribute('refresh_token', $json); } public function testGenerateHttpResponseWithExtraParams() @@ -92,7 +92,7 @@ class BearerResponseTypeTest extends TestCase $response = $responseType->generateHttpResponse(new Response()); - $this->assertTrue($response instanceof ResponseInterface); + $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('no-cache', $response->getHeader('pragma')[0]); $this->assertEquals('no-store', $response->getHeader('cache-control')[0]); @@ -100,13 +100,13 @@ class BearerResponseTypeTest extends TestCase $response->getBody()->rewind(); $json = json_decode($response->getBody()->getContents()); - $this->assertEquals('Bearer', $json->token_type); - $this->assertTrue(isset($json->expires_in)); - $this->assertTrue(isset($json->access_token)); - $this->assertTrue(isset($json->refresh_token)); + $this->assertAttributeEquals('Bearer', 'token_type', $json); + $this->assertObjectHasAttribute('expires_in', $json); + $this->assertObjectHasAttribute('access_token', $json); + $this->assertObjectHasAttribute('refresh_token', $json); - $this->assertTrue(isset($json->foo)); - $this->assertEquals('bar', $json->foo); + $this->assertObjectHasAttribute('foo', $json); + $this->assertAttributeEquals('bar', 'foo', $json); } public function testDetermineAccessTokenInHeaderValidToken() From f5a1feb67da1dc29d46c576f7537c7c5bd0e81a9 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 13 Dec 2017 21:05:36 +0000 Subject: [PATCH 110/191] Added PHP 7.2 to the supported versions --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 93e3341e..c4859453 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ The following versions of PHP are supported: * PHP 5.6 * PHP 7.0 * PHP 7.1 +* PHP 7.2 The `openssl` extension is also required. From 1c36b70dab96b37cf5970d6c12e041e5dfc6d799 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 23 Dec 2017 02:06:18 +0000 Subject: [PATCH 111/191] Fixed ordering so we only hash after base64 encoding --- src/Grant/AuthCodeGrant.php | 2 +- tests/Grant/AuthCodeGrantTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index c39679b6..6f2b6ff8 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -144,7 +144,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant case 'S256': if ( hash_equals( - strtr(rtrim(base64_encode(hash('sha256', $codeVerifier)), '='), '+/', '-_'), + hash('sha256', strtr(rtrim(base64_encode($codeVerifier), '='), '+/', '-_')), $authCodePayload->code_challenge ) === false ) { diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 80a95c31..0146561a 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -767,7 +767,7 @@ class AuthCodeGrantTest extends TestCase 'user_id' => 123, 'scopes' => ['foo'], 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => strtr(rtrim(base64_encode(hash('sha256', 'foobar')), '='), '+/', '-_'), + 'code_challenge' => hash('sha256', strtr(rtrim(base64_encode('foobar'), '='), '+/', '-_')), 'code_challenge_method' => 'S256', ] ) From 276d5b655bc9ba7d9b5b6e5547f61ff645ec3cba Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 23 Dec 2017 13:20:52 +0000 Subject: [PATCH 112/191] Update README.md Updating readme to refer to the latest 5.1.* branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c4859453..7f421104 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning: ### Security Notice -### Please upgrade to version `>=5.1.4` (backwards compatible) or `6.x` (one tiny breaking change) to fix some potential security vulnerabilities - [visit this page for more information](https://oauth2.thephpleague.com/v5-security-improvements/) +### Please upgrade to version `>=5.1.6` (backwards compatible) or `6.x` (one tiny breaking change) to fix some potential security vulnerabilities - [visit this page for more information](https://oauth2.thephpleague.com/v5-security-improvements/) ### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning: [![Latest Version](http://img.shields.io/packagist/v/league/oauth2-server.svg?style=flat-square)](https://github.com/thephpleague/oauth2-server/releases) From a0cabb573c7cd5ee01803daec992d6ee3677c4ae Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 23 Dec 2017 23:33:42 +0000 Subject: [PATCH 113/191] Update AbstractGrant.php Temporarily removing check on empty scopes as causing issues for Passport users --- src/Grant/AbstractGrant.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index d8548eca..25378955 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -242,10 +242,6 @@ abstract class AbstractGrant implements GrantTypeInterface $validScopes[] = $scope; } - if (empty($validScopes)) { - throw OAuthServerException::invalidScope('', $redirectUri); - } - return $validScopes; } From dcae4af6cec57075587642bce20727fdddbb2c1a Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 24 Dec 2017 00:06:18 +0000 Subject: [PATCH 114/191] Remove missing scope test Temporarily removing missing scope test for the AuthGrant --- tests/Grant/AuthCodeGrantTest.php | 43 ------------------------------- 1 file changed, 43 deletions(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 0146561a..6d0e801e 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -1667,47 +1667,4 @@ class AuthCodeGrantTest extends TestCase $grant->completeAuthorizationRequest(new AuthorizationRequest()); } - - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 5 - */ - public function testValidateAuthorizationRequestFailsWithoutScope() - { - $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); - - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - [], - [], - [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', - ] - ); - - $grant->validateAuthorizationRequest($request); - } } From 41bba7f58c48ee3daf0053cd8cc8d6d33cba4071 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 24 Dec 2017 00:07:22 +0000 Subject: [PATCH 115/191] Removing missing scope test Temporarily removing missing scope test as have reverted this functionality for version 6.1.1 --- tests/Grant/ImplicitGrantTest.php | 38 ------------------------------- 1 file changed, 38 deletions(-) diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index 8358ae02..0080548f 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -412,42 +412,4 @@ class ImplicitGrantTest extends TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->completeAuthorizationRequest(new AuthorizationRequest()); } - - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 5 - */ - public function testValidateAuthorizationRequestFailsWithoutScope() - { - $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); - $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); - - $grant = new ImplicitGrant(new \DateInterval('PT10M')); - $grant->setClientRepository($clientRepositoryMock); - $grant->setScopeRepository($scopeRepositoryMock); - - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - $headers = [], - $cookies = [], - $queryParams = [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', - ] - ); - - $grant->validateAuthorizationRequest($request); - } } From 57ca83a8baadd5b5a6c1d82da7d261089e55585c Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 24 Dec 2017 00:18:20 +0000 Subject: [PATCH 116/191] Removing missing scope tests as temporarily reverted this functionality --- tests/Grant/ClientCredentialsGrantTest.php | 36 ----------------- tests/Grant/PasswordGrantTest.php | 46 ---------------------- 2 files changed, 82 deletions(-) diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index e24f892a..6c7b5a36 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -58,40 +58,4 @@ class ClientCredentialsGrantTest extends TestCase $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); } - - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 5 - */ - public function testRespondToRequestFailsWithoutScope() - { - $client = new ClientEntity(); - $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $clientRepositoryMock->method('getClientEntity')->willReturn($client); - - $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); - $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); - - $scope = new ScopeEntity(); - $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); - $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); - $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); - - $grant = new ClientCredentialsGrant(); - $grant->setClientRepository($clientRepositoryMock); - $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setScopeRepository($scopeRepositoryMock); - - $serverRequest = new ServerRequest(); - $serverRequest = $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 55290ce3..2ee700f8 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -174,50 +174,4 @@ class PasswordGrantTest extends TestCase $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); } - - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 5 - */ - public function testRespondToRequestFailsWithoutScope() - { - $client = new ClientEntity(); - $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $clientRepositoryMock->method('getClientEntity')->willReturn($client); - - $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); - $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); - - $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); - $userEntity = new UserEntity(); - $userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity); - - $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); - $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); - $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); - - $scope = new ScopeEntity(); - $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); - $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); - $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); - - $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); - $grant->setClientRepository($clientRepositoryMock); - $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setScopeRepository($scopeRepositoryMock); - - $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'username' => 'foo', - 'password' => 'bar', - ] - ); - - $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); - } } From 5b79b40df9c9a057602c17f6dace2bc88d201b85 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Fri, 29 Dec 2017 12:25:39 +0000 Subject: [PATCH 117/191] Fixed count placement to make code more efficient as per scrutinizer feedback --- examples/public/api.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/public/api.php b/examples/public/api.php index 3032ffed..2f896434 100644 --- a/examples/public/api.php +++ b/examples/public/api.php @@ -49,16 +49,18 @@ $app->get( ], ]; + $totalUsers = count($users); + // If the access token doesn't have the `basic` scope hide users' names if (in_array('basic', $request->getAttribute('oauth_scopes')) === false) { - for ($i = 0; $i < count($users); $i++) { + for ($i = 0; $i < $totalUsers; $i++) { unset($users[$i]['name']); } } // If the access token doesn't have the `email` scope hide users' email addresses if (in_array('email', $request->getAttribute('oauth_scopes')) === false) { - for ($i = 0; $i < count($users); $i++) { + for ($i = 0; $i < $totalUsers; $i++) { unset($users[$i]['email']); } } From ff29721ca94bfb9cd0f0b8ab2c3a05eb831e52b6 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Fri, 29 Dec 2017 12:29:47 +0000 Subject: [PATCH 118/191] Removing call to setEncryptionKey as no such function on the authorization server --- examples/public/implicit.php | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/public/implicit.php b/examples/public/implicit.php index 73de09ec..73e46e46 100644 --- a/examples/public/implicit.php +++ b/examples/public/implicit.php @@ -41,7 +41,6 @@ $app = new App([ $privateKeyPath, 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' ); - $server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'); // Enable the implicit grant on the server with a token TTL of 1 hour $server->enableGrantType(new ImplicitGrant(new \DateInterval('PT1H'))); From 01d21b25332846a93e959d2e7194d37bbea03b58 Mon Sep 17 00:00:00 2001 From: Erick Torres Date: Fri, 29 Dec 2017 12:32:12 -0500 Subject: [PATCH 119/191] Update statement to generate codeChallenge in AuthCodeGrantTest --- 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 a858871a..c72073ec 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -48,7 +48,7 @@ class AuthCodeGrantTest extends TestCase { $this->cryptStub = new CryptTraitStub; $this->codeVerifier = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '='); - $this->codeChallenge = rtrim(strtr(base64_encode(hash('sha256',$this->codeVerifier, true)), '+/', '-_'), '='); + $this->codeChallenge = hash('sha256', strtr(rtrim(base64_encode($this->codeVerifier), '='), '+/', '-_')); } public function testGetIdentifier() From 2ec8d148b062e7a04be88df2348fc94c9d91a8c7 Mon Sep 17 00:00:00 2001 From: Ron Arts Date: Wed, 3 Jan 2018 09:41:39 +0100 Subject: [PATCH 120/191] fix .gitattributes --- .gitattributes | 1 - tests/Stubs/.gitattributes | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 tests/Stubs/.gitattributes diff --git a/.gitattributes b/.gitattributes index bea6eefb..24a9c15e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,4 +12,3 @@ /CONTRIBUTING.md export-ignore /README.md export-ignore -+*.crlf eol=crlf diff --git a/tests/Stubs/.gitattributes b/tests/Stubs/.gitattributes new file mode 100644 index 00000000..ea9fa3f5 --- /dev/null +++ b/tests/Stubs/.gitattributes @@ -0,0 +1 @@ +private.key.crlf text eol=crlf From 91d9c11fb4b4874796e9367f52d5217a33f1178a Mon Sep 17 00:00:00 2001 From: Ron Arts Date: Wed, 3 Jan 2018 10:18:32 +0100 Subject: [PATCH 121/191] Fixed tests, allow whitespace at the end of a key --- src/CryptKey.php | 2 +- tests/CryptKeyTest.php | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/CryptKey.php b/src/CryptKey.php index 9ec9202f..935461cc 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -14,7 +14,7 @@ namespace League\OAuth2\Server; class CryptKey { const RSA_KEY_PATTERN = - '/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----).+(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)$/s'; + '/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----).+(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)\s*$/s'; /** * @var string diff --git a/tests/CryptKeyTest.php b/tests/CryptKeyTest.php index ce3c8081..2a0b71ab 100644 --- a/tests/CryptKeyTest.php +++ b/tests/CryptKeyTest.php @@ -21,12 +21,6 @@ class CryptKeyTest extends \PHPUnit_Framework_TestCase $this->assertEquals('file://' . $keyFile, $key->getKeyPath()); $this->assertEquals('secret', $key->getPassPhrase()); - - $keyFile = __DIR__ . '/Stubs/private.key.crlf'; - $key = new CryptKey($keyFile, 'secret'); - - $this->assertEquals('file://' . $keyFile, $key->getKeyPath()); - $this->assertEquals('secret', $key->getPassPhrase()); } public function testKeyFileCreation() @@ -38,5 +32,13 @@ class CryptKeyTest extends \PHPUnit_Framework_TestCase 'file://' . sys_get_temp_dir() . '/' . sha1($keyContent) . '.key', $key->getKeyPath() ); + + $keyContent = file_get_contents(__DIR__ . '/Stubs/private.key.crlf'); + $key = new CryptKey($keyContent); + + $this->assertEquals( + 'file://' . sys_get_temp_dir() . '/' . sha1($keyContent) . '.key', + $key->getKeyPath() + ); } } From ef8a74152764012ba0c228b82b87207cdee103a0 Mon Sep 17 00:00:00 2001 From: Ron Arts Date: Thu, 4 Jan 2018 12:17:31 +0100 Subject: [PATCH 122/191] In public/private keys, force the header to be on its own line, allow missing \n after the footer --- src/CryptKey.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CryptKey.php b/src/CryptKey.php index 935461cc..0d5f5cf6 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -14,7 +14,7 @@ namespace League\OAuth2\Server; class CryptKey { const RSA_KEY_PATTERN = - '/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----).+(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)\s*$/s'; + '/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----)\R.*(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)\R?$/s'; /** * @var string From 292272d1281d58abef60c9ad8507cb770103a9c8 Mon Sep 17 00:00:00 2001 From: SunMar Date: Mon, 20 Nov 2017 07:42:09 +0100 Subject: [PATCH 123/191] Allow CryptTrait to accept a \Defuse\Crypto\Key as encryption key #812 --- src/AuthorizationServer.php | 4 +-- src/CryptTrait.php | 17 ++++++++++--- src/Grant/GrantTypeInterface.php | 2 +- src/ResponseTypes/ResponseTypeInterface.php | 2 +- tests/CryptTraitTest.php | 27 +++++++++++++-------- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 69c16954..84a0e93a 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -67,7 +67,7 @@ class AuthorizationServer implements EmitterAwareInterface private $scopeRepository; /** - * @var string + * @var string|\Defuse\Crypto\Key */ private $encryptionKey; @@ -83,7 +83,7 @@ class AuthorizationServer implements EmitterAwareInterface * @param AccessTokenRepositoryInterface $accessTokenRepository * @param ScopeRepositoryInterface $scopeRepository * @param CryptKey|string $privateKey - * @param string $encryptionKey + * @param string|\Defuse\Crypto\Key $encryptionKey * @param null|ResponseTypeInterface $responseType */ public function __construct( diff --git a/src/CryptTrait.php b/src/CryptTrait.php index 125a757e..c8713ff3 100644 --- a/src/CryptTrait.php +++ b/src/CryptTrait.php @@ -12,11 +12,12 @@ namespace League\OAuth2\Server; use Defuse\Crypto\Crypto; +use Defuse\Crypto\Key; trait CryptTrait { /** - * @var string + * @var string|Key */ protected $encryptionKey; @@ -32,7 +33,11 @@ trait CryptTrait protected function encrypt($unencryptedData) { try { - return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey); + if($this->encryptionKey instanceof Key) { + return Crypto::encrypt($unencryptedData, $this->encryptionKey); + } else { + return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey); + } } catch (\Exception $e) { throw new \LogicException($e->getMessage()); } @@ -50,7 +55,11 @@ trait CryptTrait protected function decrypt($encryptedData) { try { - return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey); + if($this->encryptionKey instanceof Key) { + return Crypto::decrypt($encryptedData, $this->encryptionKey); + } else { + return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey); + } } catch (\Exception $e) { throw new \LogicException($e->getMessage()); } @@ -59,7 +68,7 @@ trait CryptTrait /** * Set the encryption key * - * @param string $key + * @param string|Key $key */ public function setEncryptionKey($key = null) { diff --git a/src/Grant/GrantTypeInterface.php b/src/Grant/GrantTypeInterface.php index 0e721435..56f1ee99 100644 --- a/src/Grant/GrantTypeInterface.php +++ b/src/Grant/GrantTypeInterface.php @@ -136,7 +136,7 @@ interface GrantTypeInterface extends EmitterAwareInterface /** * Set the encryption key * - * @param string|null $key + * @param string|\Defuse\Crypto\Key|null $key */ public function setEncryptionKey($key = null); } diff --git a/src/ResponseTypes/ResponseTypeInterface.php b/src/ResponseTypes/ResponseTypeInterface.php index 8ac20b8c..f76eaa6f 100644 --- a/src/ResponseTypes/ResponseTypeInterface.php +++ b/src/ResponseTypes/ResponseTypeInterface.php @@ -37,7 +37,7 @@ interface ResponseTypeInterface /** * Set the encryption key * - * @param string|null $key + * @param string|\Defuse\Crypto\Key|null $key */ public function setEncryptionKey($key = null); } diff --git a/tests/CryptTraitTest.php b/tests/CryptTraitTest.php index 26427e59..e0954508 100644 --- a/tests/CryptTraitTest.php +++ b/tests/CryptTraitTest.php @@ -2,26 +2,33 @@ namespace LeagueTests\Utils; +use Defuse\Crypto\Key; use LeagueTests\Stubs\CryptTraitStub; use PHPUnit\Framework\TestCase; class CryptTraitTest extends TestCase { - /** - * @var \LeagueTests\Stubs\CryptTraitStub - */ - protected $cryptStub; - - public function setUp() + public function testEncryptDecryptWithPassword() { - $this->cryptStub = new CryptTraitStub; + $cryptStub = new CryptTraitStub(); + $cryptStub->setEncryptionKey(base64_encode(random_bytes(36))); + + return $this->encryptDecrypt($cryptStub); } - public function testEncryptDecrypt() + public function testEncryptDecryptWithKey() { + $cryptStub = new CryptTraitStub(); + $cryptStub->setEncryptionKey(Key::createNewRandomKey()); + + return $this->encryptDecrypt($cryptStub); + } + + protected function encryptDecrypt(CryptTraitStub $cryptStub) { + $payload = 'alex loves whisky'; - $encrypted = $this->cryptStub->doEncrypt($payload); - $plainText = $this->cryptStub->doDecrypt($encrypted); + $encrypted = $cryptStub->doEncrypt($payload); + $plainText = $cryptStub->doDecrypt($encrypted); $this->assertNotEquals($payload, $encrypted); $this->assertEquals($payload, $plainText); From a3289c6ecb3c0b0df2b448c482613fe0a4a2d3f9 Mon Sep 17 00:00:00 2001 From: knewzen <772608204@qq.com> Date: Fri, 5 Jan 2018 01:08:14 +0800 Subject: [PATCH 124/191] remove codesponsor --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 7f421104..28047d6b 100644 --- a/README.md +++ b/README.md @@ -62,9 +62,7 @@ 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. - - Sponsor - + ## Commercial Support From cf9acb32b8c9a6537a90e79bd8dfc0f00a70d2da Mon Sep 17 00:00:00 2001 From: Simon Hobbs Date: Sat, 13 Jan 2018 15:29:42 +1100 Subject: [PATCH 125/191] Allow some more secure options without tsk-tsk. --- src/CryptKey.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CryptKey.php b/src/CryptKey.php index 2ede9e33..151b57fc 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -48,7 +48,7 @@ class CryptKey if ($keyPermissionsCheck === true) { // Verify the permissions of the key $keyPathPerms = decoct(fileperms($keyPath) & 0777); - if (in_array($keyPathPerms, ['600', '660'], true) === false) { + if (in_array($keyPathPerms, ['400', '440', '600', '660'], true) === false) { trigger_error(sprintf( 'Key file "%s" permissions are not correct, should be 600 or 660 instead of %s', $keyPath, From d22f222e65c03623d8a2c829f5b853ca3e52f0f9 Mon Sep 17 00:00:00 2001 From: liverbool Date: Sat, 13 Jan 2018 11:52:31 +0700 Subject: [PATCH 126/191] BUGFIX: Wrong redirect uri. This's bugfix when redirect on error. --- src/Grant/AuthCodeGrant.php | 4 ++-- src/Grant/ImplicitGrant.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 6f2b6ff8..6a342228 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -249,9 +249,9 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $scopes = $this->validateScopes( $this->getQueryStringParameter('scope', $request, $this->defaultScope), - is_array($client->getRedirectUri()) + $redirectUri ?: (is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] - : $client->getRedirectUri() + : $client->getRedirectUri()) ); $stateParameter = $this->getQueryStringParameter('state', $request); diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index f3c9e694..a10ac642 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -158,9 +158,9 @@ class ImplicitGrant extends AbstractAuthorizeGrant $scopes = $this->validateScopes( $this->getQueryStringParameter('scope', $request, $this->defaultScope), - is_array($client->getRedirectUri()) + $redirectUri ?: (is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] - : $client->getRedirectUri() + : $client->getRedirectUri()) ); // Finalize the requested scopes From 1b692e22982d0972905d393e19a9e0855cc8eb81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20G=C3=B3mez?= Date: Thu, 18 Jan 2018 05:31:44 +0100 Subject: [PATCH 127/191] Fix S256 code challenge method According to [RFC7636#section-4.3](https://tools.ietf.org/html/rfc7636#section-4.3): If the "code_challenge_method" from Section 4.3 was "S256", the received "code_verifier" is hashed by SHA-256, base64url-encoded, and then compared to the "code_challenge", i.e.: BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) == code_challenge So, the hash must be done before the base64_encode. The tests are modified to use example data from the [RFC7636#appendix-B](https://tools.ietf.org/html/rfc7636#appendix-B). --- src/Grant/AuthCodeGrant.php | 2 +- tests/Grant/AuthCodeGrantTest.php | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 6f2b6ff8..18720f45 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -144,7 +144,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant case 'S256': if ( hash_equals( - hash('sha256', strtr(rtrim(base64_encode($codeVerifier), '='), '+/', '-_')), + strtr(rtrim(base64_encode(hash('sha256', $codeVerifier, true)), '='), '+/', '-_'), $authCodePayload->code_challenge ) === false ) { diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 6d0e801e..97206a76 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -744,6 +744,10 @@ class AuthCodeGrantTest extends TestCase $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); + // [RFC 7636] Appendix B. Example for the S256 code_challenge_method + $codeVerifier = 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'; + $codeChallenge = 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM'; + $request = new ServerRequest( [], [], @@ -757,7 +761,7 @@ class AuthCodeGrantTest extends TestCase 'grant_type' => 'authorization_code', 'client_id' => 'foo', 'redirect_uri' => 'http://foo/bar', - 'code_verifier' => 'foobar', + 'code_verifier' => $codeVerifier, 'code' => $this->cryptStub->doEncrypt( json_encode( [ @@ -767,7 +771,7 @@ class AuthCodeGrantTest extends TestCase 'user_id' => 123, 'scopes' => ['foo'], 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => hash('sha256', strtr(rtrim(base64_encode('foobar'), '='), '+/', '-_')), + 'code_challenge' => $codeChallenge, 'code_challenge_method' => 'S256', ] ) From d2641b560d6012657efa653ed7275eb04964ac56 Mon Sep 17 00:00:00 2001 From: Karim PINCHON Date: Mon, 29 Jan 2018 11:05:10 +0100 Subject: [PATCH 128/191] Do not create key file if it already exists and it is the same --- src/CryptKey.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/CryptKey.php b/src/CryptKey.php index 0d5f5cf6..524bd358 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -73,7 +73,11 @@ class CryptKey $tmpDir = sys_get_temp_dir(); $keyPath = $tmpDir . '/' . sha1($key) . '.key'; - if (!file_exists($keyPath) && !touch($keyPath)) { + if (file_exists($keyPath)) { + return 'file://' . $keyPath; + } + + if (!touch($keyPath)) { // @codeCoverageIgnoreStart throw new \RuntimeException(sprintf('"%s" key file could not be created', $keyPath)); // @codeCoverageIgnoreEnd From b3cd73cac7c788aa50d8a924ccfa0adbc9f1d5eb Mon Sep 17 00:00:00 2001 From: liverbool Date: Fri, 9 Feb 2018 05:54:05 +0700 Subject: [PATCH 129/191] code cleaner cc. Co-Authored-By: Andrew Millington --- src/Grant/AuthCodeGrant.php | 12 ++++++------ src/Grant/ImplicitGrant.php | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 6a342228..7f83d3fe 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -240,18 +240,18 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient(); } - } elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1 - || empty($client->getRedirectUri()) - ) { + } elseif (empty($client->getRedirectUri())) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient(); + } else { + $redirectUri = is_array($client->getRedirectUri()) + ? $client->getRedirectUri()[0] + : $client->getRedirectUri(); } $scopes = $this->validateScopes( $this->getQueryStringParameter('scope', $request, $this->defaultScope), - $redirectUri ?: (is_array($client->getRedirectUri()) - ? $client->getRedirectUri()[0] - : $client->getRedirectUri()) + $redirectUri ); $stateParameter = $this->getQueryStringParameter('state', $request); diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index a10ac642..9dd80ce1 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -149,18 +149,18 @@ class ImplicitGrant extends AbstractAuthorizeGrant $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient(); } - } elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1 - || empty($client->getRedirectUri()) - ) { + } elseif (empty($client->getRedirectUri())) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient(); + } else { + $redirectUri = is_array($client->getRedirectUri()) + ? $client->getRedirectUri()[0] + : $client->getRedirectUri(); } $scopes = $this->validateScopes( $this->getQueryStringParameter('scope', $request, $this->defaultScope), - $redirectUri ?: (is_array($client->getRedirectUri()) - ? $client->getRedirectUri()[0] - : $client->getRedirectUri()) + $redirectUri ); // Finalize the requested scopes From 5fb9fc929ac3d04ddd4141b0151a23483ab74141 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 11 Feb 2018 20:10:01 +0000 Subject: [PATCH 130/191] Reinstate check on client redirect URI to fail if multiple redirect URIs have been listed for the client and one has not been specified in the auth request --- src/Grant/AuthCodeGrant.php | 3 ++- src/Grant/ImplicitGrant.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 7f83d3fe..ba77c99a 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -240,7 +240,8 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient(); } - } elseif (empty($client->getRedirectUri())) { + } elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1 + || empty($client->getRedirectUri())) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient(); } else { diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 9dd80ce1..5a6fccb1 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -149,7 +149,8 @@ class ImplicitGrant extends AbstractAuthorizeGrant $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient(); } - } elseif (empty($client->getRedirectUri())) { + } elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1 + || empty($client->getRedirectUri())) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient(); } else { From eca385ab08e7f80252e0dd97144bd65da633fc03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= Date: Sun, 11 Feb 2018 21:51:47 +0100 Subject: [PATCH 131/191] Static analysis with PHPStan --- .travis.yml | 2 +- composer.json | 5 +++-- src/AuthorizationServer.php | 7 +++++-- src/Entities/AccessTokenEntityInterface.php | 3 ++- src/Entities/RefreshTokenEntityInterface.php | 2 +- src/Entities/TokenInterface.php | 6 +++--- src/Entities/Traits/AccessTokenTrait.php | 3 ++- src/Entities/Traits/TokenEntityTrait.php | 6 +++--- src/Exception/OAuthServerException.php | 2 +- src/Grant/AbstractGrant.php | 2 +- src/RequestTypes/AuthorizationRequest.php | 6 +++--- src/ResourceServer.php | 4 +++- 12 files changed, 28 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index a9023fa6..187d4f6f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ cache: - vendor php: - - 5.6 - 7.0 - 7.1 - 7.2 @@ -17,6 +16,7 @@ install: script: - vendor/bin/phpunit + - vendor/bin/phpstan analyse -l 6 src branches: only: diff --git a/composer.json b/composer.json index d8d11125..86f82c41 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "homepage": "https://oauth2.thephpleague.com/", "license": "MIT", "require": { - "php": ">=5.6.0", + "php": ">=7.0.0", "ext-openssl": "*", "league/event": "^2.1", "lcobucci/jwt": "^3.1", @@ -14,7 +14,8 @@ }, "require-dev": { "phpunit/phpunit": "^4.8.38 || ^5.7.21", - "zendframework/zend-diactoros": "^1.0" + "zendframework/zend-diactoros": "^1.0", + "phpstan/phpstan": "^0.9.2" }, "repositories": [ { diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 69c16954..876b0083 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -17,6 +17,7 @@ use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; use League\OAuth2\Server\Repositories\ClientRepositoryInterface; use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; use League\OAuth2\Server\RequestTypes\AuthorizationRequest; +use League\OAuth2\Server\ResponseTypes\AbstractResponseType; use League\OAuth2\Server\ResponseTypes\BearerTokenResponse; use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface; use Psr\Http\Message\ResponseInterface; @@ -190,7 +191,7 @@ class AuthorizationServer implements EmitterAwareInterface if ($tokenResponse instanceof ResponseTypeInterface) { return $tokenResponse->generateHttpResponse($response); } - + } throw OAuthServerException::unsupportedGrantType(); @@ -207,7 +208,9 @@ class AuthorizationServer implements EmitterAwareInterface $this->responseType = new BearerTokenResponse(); } - $this->responseType->setPrivateKey($this->privateKey); + if ($this->responseType instanceof AbstractResponseType === true) { + $this->responseType->setPrivateKey($this->privateKey); + } $this->responseType->setEncryptionKey($this->encryptionKey); return $this->responseType; diff --git a/src/Entities/AccessTokenEntityInterface.php b/src/Entities/AccessTokenEntityInterface.php index c297e267..4da7600e 100644 --- a/src/Entities/AccessTokenEntityInterface.php +++ b/src/Entities/AccessTokenEntityInterface.php @@ -9,6 +9,7 @@ namespace League\OAuth2\Server\Entities; +use Lcobucci\JWT\Token; use League\OAuth2\Server\CryptKey; interface AccessTokenEntityInterface extends TokenInterface @@ -18,7 +19,7 @@ interface AccessTokenEntityInterface extends TokenInterface * * @param CryptKey $privateKey * - * @return string + * @return Token */ public function convertToJWT(CryptKey $privateKey); } diff --git a/src/Entities/RefreshTokenEntityInterface.php b/src/Entities/RefreshTokenEntityInterface.php index 05e86e00..e4f10400 100644 --- a/src/Entities/RefreshTokenEntityInterface.php +++ b/src/Entities/RefreshTokenEntityInterface.php @@ -21,7 +21,7 @@ interface RefreshTokenEntityInterface /** * Set the token's identifier. * - * @param $identifier + * @param mixed $identifier */ public function setIdentifier($identifier); diff --git a/src/Entities/TokenInterface.php b/src/Entities/TokenInterface.php index c842b09a..378adbdc 100644 --- a/src/Entities/TokenInterface.php +++ b/src/Entities/TokenInterface.php @@ -21,7 +21,7 @@ interface TokenInterface /** * Set the token's identifier. * - * @param $identifier + * @param mixed $identifier */ public function setIdentifier($identifier); @@ -42,14 +42,14 @@ interface TokenInterface /** * Set the identifier of the user associated with the token. * - * @param string|int $identifier The identifier of the user + * @param string|int|null $identifier The identifier of the user */ public function setUserIdentifier($identifier); /** * Get the token user's identifier. * - * @return string|int + * @return string|int|null */ public function getUserIdentifier(); diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php index 741d6c19..81fc1bfd 100644 --- a/src/Entities/Traits/AccessTokenTrait.php +++ b/src/Entities/Traits/AccessTokenTrait.php @@ -12,6 +12,7 @@ namespace League\OAuth2\Server\Entities\Traits; use Lcobucci\JWT\Builder; use Lcobucci\JWT\Signer\Key; use Lcobucci\JWT\Signer\Rsa\Sha256; +use Lcobucci\JWT\Token; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; @@ -23,7 +24,7 @@ trait AccessTokenTrait * * @param CryptKey $privateKey * - * @return string + * @return Token */ public function convertToJWT(CryptKey $privateKey) { diff --git a/src/Entities/Traits/TokenEntityTrait.php b/src/Entities/Traits/TokenEntityTrait.php index 0b5608cd..c6653cce 100644 --- a/src/Entities/Traits/TokenEntityTrait.php +++ b/src/Entities/Traits/TokenEntityTrait.php @@ -25,7 +25,7 @@ trait TokenEntityTrait protected $expiryDateTime; /** - * @var string|int + * @var string|int|null */ protected $userIdentifier; @@ -77,7 +77,7 @@ trait TokenEntityTrait /** * Set the identifier of the user associated with the token. * - * @param string|int $identifier The identifier of the user + * @param string|int|null $identifier The identifier of the user */ public function setUserIdentifier($identifier) { @@ -87,7 +87,7 @@ trait TokenEntityTrait /** * Get the token user's identifier. * - * @return string|int + * @return string|int|null */ public function getUserIdentifier() { diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 8d101c4c..756dfd3b 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -131,7 +131,7 @@ class OAuthServerException extends \Exception /** * Server error. * - * @param $hint + * @param string $hint * * @return static * diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 25378955..e806ba09 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -341,7 +341,7 @@ abstract class AbstractGrant implements GrantTypeInterface * * @param \DateInterval $accessTokenTTL * @param ClientEntityInterface $client - * @param string $userIdentifier + * @param string|null $userIdentifier * @param ScopeEntityInterface[] $scopes * * @throws OAuthServerException diff --git a/src/RequestTypes/AuthorizationRequest.php b/src/RequestTypes/AuthorizationRequest.php index 41bfb509..ce5a0034 100644 --- a/src/RequestTypes/AuthorizationRequest.php +++ b/src/RequestTypes/AuthorizationRequest.php @@ -53,7 +53,7 @@ class AuthorizationRequest /** * The redirect URI used in the request * - * @var string + * @var string|null */ protected $redirectUri; @@ -159,7 +159,7 @@ class AuthorizationRequest } /** - * @return string + * @return string|null */ public function getRedirectUri() { @@ -167,7 +167,7 @@ class AuthorizationRequest } /** - * @param string $redirectUri + * @param string|null $redirectUri */ public function setRedirectUri($redirectUri) { diff --git a/src/ResourceServer.php b/src/ResourceServer.php index 5e9c13f3..e1f98d6d 100644 --- a/src/ResourceServer.php +++ b/src/ResourceServer.php @@ -63,7 +63,9 @@ class ResourceServer $this->authorizationValidator = new BearerTokenValidator($this->accessTokenRepository); } - $this->authorizationValidator->setPublicKey($this->publicKey); + if ($this->authorizationValidator instanceof BearerTokenValidator === true) { + $this->authorizationValidator->setPublicKey($this->publicKey); + } return $this->authorizationValidator; } From 06a23a1dd0a46a49bb7078f4864a5f8edb677291 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 11 Feb 2018 22:12:55 +0000 Subject: [PATCH 132/191] Update CryptKey.php Change the error message to reflect that the server will also accept 440 and 400 as a valid file permission --- src/CryptKey.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CryptKey.php b/src/CryptKey.php index 151b57fc..b3136077 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -50,7 +50,7 @@ class CryptKey $keyPathPerms = decoct(fileperms($keyPath) & 0777); if (in_array($keyPathPerms, ['400', '440', '600', '660'], true) === false) { trigger_error(sprintf( - 'Key file "%s" permissions are not correct, should be 600 or 660 instead of %s', + 'Key file "%s" permissions are not correct, recommend changing to 600 or 660 instead of %s', $keyPath, $keyPathPerms ), E_USER_NOTICE); From 1f87c7a7be7f202d4957908cbfd157b63770b04c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= Date: Sun, 11 Feb 2018 23:14:34 +0100 Subject: [PATCH 133/191] Update PHPUnit, run static analysis on tests --- .travis.yml | 2 +- composer.json | 5 ++-- phpstan.neon | 9 +++++++ src/Exception/OAuthServerException.php | 5 +++- src/ResponseTypes/RedirectResponse.php | 4 +++- tests/AuthorizationServerTest.php | 8 +++---- .../ResponseTypes/BearerResponseTypeTest.php | 24 +++++-------------- tests/{ => Utils}/CryptKeyTest.php | 6 ++--- tests/{ => Utils}/CryptTraitTest.php | 0 9 files changed, 33 insertions(+), 30 deletions(-) create mode 100644 phpstan.neon rename tests/{ => Utils}/CryptKeyTest.php (82%) rename tests/{ => Utils}/CryptTraitTest.php (100%) diff --git a/.travis.yml b/.travis.yml index 187d4f6f..21f3e926 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ install: script: - vendor/bin/phpunit - - vendor/bin/phpstan analyse -l 6 src + - vendor/bin/phpstan analyse -l 6 -c phpstan.neon src tests branches: only: diff --git a/composer.json b/composer.json index 86f82c41..814d7054 100644 --- a/composer.json +++ b/composer.json @@ -13,9 +13,10 @@ "defuse/php-encryption": "^2.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.38 || ^5.7.21", + "phpunit/phpunit": "^6.3 || ^7.0", "zendframework/zend-diactoros": "^1.0", - "phpstan/phpstan": "^0.9.2" + "phpstan/phpstan": "^0.9.2", + "phpstan/phpstan-phpunit": "^0.9.4" }, "repositories": [ { diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 00000000..a4800dd5 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,9 @@ +includes: + - vendor/phpstan/phpstan-phpunit/extension.neon + - vendor/phpstan/phpstan-phpunit/rules.neon + - vendor/phpstan/phpstan-phpunit/strictRules.neon +parameters: + ignoreErrors: + - '#Class Zend\\Diactoros\\ServerRequest constructor invoked with \d+ parameters, 0-6 required#' + - '#Parameter \#2 \$key of method Lcobucci\\JWT\\Builder::sign\(\) expects string, Lcobucci\\JWT\\Signer\\Key given#' + reportUnmatchedIgnoredErrors: false diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 756dfd3b..e50942c0 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -236,10 +236,13 @@ class OAuthServerException extends \Exception $this->redirectUri .= (strstr($this->redirectUri, '?') === false) ? '?' : '&'; } - return $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload)); + /** @var ResponseInterface $response */ + $response = $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload)); + return $response; } foreach ($headers as $header => $content) { + /** @var ResponseInterface $response */ $response = $response->withHeader($header, $content); } diff --git a/src/ResponseTypes/RedirectResponse.php b/src/ResponseTypes/RedirectResponse.php index e4639148..f40f087b 100644 --- a/src/ResponseTypes/RedirectResponse.php +++ b/src/ResponseTypes/RedirectResponse.php @@ -35,6 +35,8 @@ class RedirectResponse extends AbstractResponseType */ public function generateHttpResponse(ResponseInterface $response) { - return $response->withStatus(302)->withHeader('Location', $this->redirectUri); + /** @var ResponseInterface $response */ + $response = $response->withStatus(302)->withHeader('Location', $this->redirectUri); + return $response; } } diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 8b409a00..07783841 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -198,16 +198,16 @@ class AuthorizationServerTest extends TestCase $clientRepositoryMock->method('getClientEntity')->willReturn($client); $grant = new AuthCodeGrant( - $this->getMock(AuthCodeRepositoryInterface::class), - $this->getMock(RefreshTokenRepositoryInterface::class), + $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), + $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $server = new AuthorizationServer( $clientRepositoryMock, - $this->getMock(AccessTokenRepositoryInterface::class), - $this->getMock(ScopeRepositoryInterface::class), + $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), + $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), 'file://' . __DIR__ . '/Stubs/private.key', 'file://' . __DIR__ . '/Stubs/public.key' ); diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 56ae9e3e..31245b07 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -20,9 +20,7 @@ class BearerResponseTypeTest extends TestCase { public function testGenerateHttpResponse() { - $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - - $responseType = new BearerTokenResponse($accessTokenRepositoryMock); + $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $responseType->setEncryptionKey(base64_encode(random_bytes(36))); @@ -64,9 +62,7 @@ class BearerResponseTypeTest extends TestCase public function testGenerateHttpResponseWithExtraParams() { - $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - - $responseType = new BearerTokenResponseWithParams($accessTokenRepositoryMock); + $responseType = new BearerTokenResponseWithParams(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $responseType->setEncryptionKey(base64_encode(random_bytes(36))); @@ -111,10 +107,7 @@ class BearerResponseTypeTest extends TestCase public function testDetermineAccessTokenInHeaderValidToken() { - $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false); - - $responseType = new BearerTokenResponse($accessTokenRepositoryMock); + $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $responseType->setEncryptionKey(base64_encode(random_bytes(36))); @@ -158,9 +151,8 @@ class BearerResponseTypeTest extends TestCase public function testDetermineAccessTokenInHeaderInvalidJWT() { $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false); - $responseType = new BearerTokenResponse($accessTokenRepositoryMock); + $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $responseType->setEncryptionKey(base64_encode(random_bytes(36))); @@ -247,9 +239,7 @@ class BearerResponseTypeTest extends TestCase public function testDetermineAccessTokenInHeaderInvalidToken() { - $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - - $responseType = new BearerTokenResponse($accessTokenRepositoryMock); + $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $responseType->setEncryptionKey(base64_encode(random_bytes(36))); @@ -273,9 +263,7 @@ class BearerResponseTypeTest extends TestCase public function testDetermineMissingBearerInHeader() { - $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - - $responseType = new BearerTokenResponse($accessTokenRepositoryMock); + $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $responseType->setEncryptionKey(base64_encode(random_bytes(36))); diff --git a/tests/CryptKeyTest.php b/tests/Utils/CryptKeyTest.php similarity index 82% rename from tests/CryptKeyTest.php rename to tests/Utils/CryptKeyTest.php index 70bbc8d7..9f3f337c 100644 --- a/tests/CryptKeyTest.php +++ b/tests/Utils/CryptKeyTest.php @@ -17,7 +17,7 @@ class CryptKeyTest extends TestCase public function testKeyCreation() { - $keyFile = __DIR__ . '/Stubs/public.key'; + $keyFile = __DIR__ . '/../Stubs/public.key'; $key = new CryptKey($keyFile, 'secret'); $this->assertEquals('file://' . $keyFile, $key->getKeyPath()); @@ -26,7 +26,7 @@ class CryptKeyTest extends TestCase public function testKeyFileCreation() { - $keyContent = file_get_contents(__DIR__ . '/Stubs/public.key'); + $keyContent = file_get_contents(__DIR__ . '/../Stubs/public.key'); $key = new CryptKey($keyContent); $this->assertEquals( @@ -34,7 +34,7 @@ class CryptKeyTest extends TestCase $key->getKeyPath() ); - $keyContent = file_get_contents(__DIR__ . '/Stubs/private.key.crlf'); + $keyContent = file_get_contents(__DIR__ . '/../Stubs/private.key.crlf'); $key = new CryptKey($keyContent); $this->assertEquals( diff --git a/tests/CryptTraitTest.php b/tests/Utils/CryptTraitTest.php similarity index 100% rename from tests/CryptTraitTest.php rename to tests/Utils/CryptTraitTest.php From 80a949601f7d981328c5cb7a4d98d3e03fb52aa1 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Fri, 9 Feb 2018 16:12:31 +0200 Subject: [PATCH 134/191] Fixed docblock opener It's important for tools relying on docblock types to perform static analysis (think phan, phpstan, psalm, etc) --- src/Entities/Traits/EntityTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entities/Traits/EntityTrait.php b/src/Entities/Traits/EntityTrait.php index 20c86591..05452923 100644 --- a/src/Entities/Traits/EntityTrait.php +++ b/src/Entities/Traits/EntityTrait.php @@ -11,7 +11,7 @@ namespace League\OAuth2\Server\Entities\Traits; trait EntityTrait { - /* + /** * @var string */ protected $identifier; From e0b65a283109f038b7f951a81d6f65986d8c0a7c Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 17 Feb 2018 11:46:47 +0000 Subject: [PATCH 135/191] Set default mustValidateSecret to true --- src/Repositories/ClientRepositoryInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Repositories/ClientRepositoryInterface.php b/src/Repositories/ClientRepositoryInterface.php index a2eb3309..ba0610d5 100644 --- a/src/Repositories/ClientRepositoryInterface.php +++ b/src/Repositories/ClientRepositoryInterface.php @@ -27,5 +27,5 @@ interface ClientRepositoryInterface extends RepositoryInterface * * @return ClientEntityInterface */ - public function getClientEntity($clientIdentifier, $grantType = null, $clientSecret = null, $mustValidateSecret = false); + public function getClientEntity($clientIdentifier, $grantType = null, $clientSecret = null, $mustValidateSecret = true); } From 7a5c511807931830b8cb8ae2a0d2139a47d90fbb Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 17 Feb 2018 16:15:48 +0000 Subject: [PATCH 136/191] Remove temp variables and @var comments --- src/Exception/OAuthServerException.php | 5 +---- src/ResponseTypes/RedirectResponse.php | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index e50942c0..756dfd3b 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -236,13 +236,10 @@ class OAuthServerException extends \Exception $this->redirectUri .= (strstr($this->redirectUri, '?') === false) ? '?' : '&'; } - /** @var ResponseInterface $response */ - $response = $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload)); - return $response; + return $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload)); } foreach ($headers as $header => $content) { - /** @var ResponseInterface $response */ $response = $response->withHeader($header, $content); } diff --git a/src/ResponseTypes/RedirectResponse.php b/src/ResponseTypes/RedirectResponse.php index f40f087b..e4639148 100644 --- a/src/ResponseTypes/RedirectResponse.php +++ b/src/ResponseTypes/RedirectResponse.php @@ -35,8 +35,6 @@ class RedirectResponse extends AbstractResponseType */ public function generateHttpResponse(ResponseInterface $response) { - /** @var ResponseInterface $response */ - $response = $response->withStatus(302)->withHeader('Location', $this->redirectUri); - return $response; + return $response->withStatus(302)->withHeader('Location', $this->redirectUri); } } From 8614aea887f9ae6ace5d4e67aa69f7d988bad0c4 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 17 Feb 2018 16:27:41 +0000 Subject: [PATCH 137/191] Revert "Remove temp variables and @var comments" This reverts commit 7a5c511807931830b8cb8ae2a0d2139a47d90fbb. --- src/Exception/OAuthServerException.php | 5 ++++- src/ResponseTypes/RedirectResponse.php | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 756dfd3b..e50942c0 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -236,10 +236,13 @@ class OAuthServerException extends \Exception $this->redirectUri .= (strstr($this->redirectUri, '?') === false) ? '?' : '&'; } - return $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload)); + /** @var ResponseInterface $response */ + $response = $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload)); + return $response; } foreach ($headers as $header => $content) { + /** @var ResponseInterface $response */ $response = $response->withHeader($header, $content); } diff --git a/src/ResponseTypes/RedirectResponse.php b/src/ResponseTypes/RedirectResponse.php index e4639148..f40f087b 100644 --- a/src/ResponseTypes/RedirectResponse.php +++ b/src/ResponseTypes/RedirectResponse.php @@ -35,6 +35,8 @@ class RedirectResponse extends AbstractResponseType */ public function generateHttpResponse(ResponseInterface $response) { - return $response->withStatus(302)->withHeader('Location', $this->redirectUri); + /** @var ResponseInterface $response */ + $response = $response->withStatus(302)->withHeader('Location', $this->redirectUri); + return $response; } } From cd5233392e2c29915e843707c14411d021f83931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= Date: Mon, 12 Feb 2018 10:19:16 +0100 Subject: [PATCH 138/191] Updated dependencies, more strict static analysis --- .travis.yml | 8 ++++++-- composer.json | 9 +++++---- phpstan.neon | 6 +----- src/Exception/OAuthServerException.php | 5 +---- src/Grant/AbstractGrant.php | 2 +- src/Grant/AuthCodeGrant.php | 8 ++++---- src/Grant/ImplicitGrant.php | 4 ++-- src/Grant/RefreshTokenGrant.php | 3 +-- src/ResponseTypes/RedirectResponse.php | 4 +--- 9 files changed, 22 insertions(+), 27 deletions(-) diff --git a/.travis.yml b/.travis.yml index 21f3e926..85474626 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,11 @@ sudo: false cache: directories: - - vendor + - vendor + +env: + - DEPENDENCIES="" + - DEPENDENCIES="--prefer-lowest --prefer-stable" php: - 7.0 @@ -12,7 +16,7 @@ php: - 7.2 install: - - travis_retry composer install --no-interaction --prefer-source + - composer update --no-interaction --prefer-dist $DEPENDENCIES script: - vendor/bin/phpunit diff --git a/composer.json b/composer.json index 814d7054..8e7fd7e6 100644 --- a/composer.json +++ b/composer.json @@ -7,16 +7,17 @@ "php": ">=7.0.0", "ext-openssl": "*", "league/event": "^2.1", - "lcobucci/jwt": "^3.1", + "lcobucci/jwt": "^3.2.2", "paragonie/random_compat": "^2.0", - "psr/http-message": "^1.0", + "psr/http-message": "^1.0.1", "defuse/php-encryption": "^2.1" }, "require-dev": { "phpunit/phpunit": "^6.3 || ^7.0", - "zendframework/zend-diactoros": "^1.0", + "zendframework/zend-diactoros": "^1.3.2", "phpstan/phpstan": "^0.9.2", - "phpstan/phpstan-phpunit": "^0.9.4" + "phpstan/phpstan-phpunit": "^0.9.4", + "phpstan/phpstan-strict-rules": "^0.9.0" }, "repositories": [ { diff --git a/phpstan.neon b/phpstan.neon index a4800dd5..88c21d40 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,8 +2,4 @@ includes: - vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-phpunit/rules.neon - vendor/phpstan/phpstan-phpunit/strictRules.neon -parameters: - ignoreErrors: - - '#Class Zend\\Diactoros\\ServerRequest constructor invoked with \d+ parameters, 0-6 required#' - - '#Parameter \#2 \$key of method Lcobucci\\JWT\\Builder::sign\(\) expects string, Lcobucci\\JWT\\Signer\\Key given#' - reportUnmatchedIgnoredErrors: false + - vendor/phpstan/phpstan-strict-rules/rules.neon diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 281eae0e..97fc142e 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -262,13 +262,10 @@ class OAuthServerException extends \Exception $this->redirectUri .= (strstr($this->redirectUri, '?') === false) ? '?' : '&'; } - /** @var ResponseInterface $response */ - $response = $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload)); - return $response; + return $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload)); } foreach ($headers as $header => $content) { - /** @var ResponseInterface $response */ $response = $response->withHeader($header, $content); } diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index e806ba09..304ba99b 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -204,7 +204,7 @@ abstract class AbstractGrant implements GrantTypeInterface throw OAuthServerException::invalidClient(); } elseif ( is_array($client->getRedirectUri()) - && in_array($redirectUri, $client->getRedirectUri()) === false + && in_array($redirectUri, $client->getRedirectUri(), true) === false ) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient(); diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index d7900581..dc880365 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -235,13 +235,13 @@ class AuthCodeGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidClient(); } elseif ( is_array($client->getRedirectUri()) - && in_array($redirectUri, $client->getRedirectUri()) === false + && in_array($redirectUri, $client->getRedirectUri(), true) === false ) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient(); } - } elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1 - || empty($client->getRedirectUri())) { + } elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1 + || empty($client->getRedirectUri())) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient(); } else { @@ -278,7 +278,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant } $codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain'); - if (in_array($codeChallengeMethod, ['plain', 'S256']) === false) { + if (in_array($codeChallengeMethod, ['plain', 'S256'], true) === false) { throw OAuthServerException::invalidRequest( 'code_challenge_method', 'Code challenge method must be `plain` or `S256`' diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 5a6fccb1..dfb96743 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -144,12 +144,12 @@ class ImplicitGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidClient(); } elseif ( is_array($client->getRedirectUri()) - && in_array($redirectUri, $client->getRedirectUri()) === false + && in_array($redirectUri, $client->getRedirectUri(), true) === false ) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient(); } - } elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1 + } 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(); diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php index 66a3b266..f8e022b4 100644 --- a/src/Grant/RefreshTokenGrant.php +++ b/src/Grant/RefreshTokenGrant.php @@ -11,7 +11,6 @@ namespace League\OAuth2\Server\Grant; -use League\OAuth2\Server\Entities\ScopeEntityInterface; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface; use League\OAuth2\Server\RequestEvent; @@ -53,7 +52,7 @@ class RefreshTokenGrant extends AbstractGrant // The OAuth spec says that a refreshed access token can have the original scopes or fewer so ensure // the request doesn't include any new scopes foreach ($scopes as $scope) { - if (in_array($scope->getIdentifier(), $oldRefreshToken['scopes']) === false) { + if (in_array($scope->getIdentifier(), $oldRefreshToken['scopes'], true) === false) { throw OAuthServerException::invalidScope($scope->getIdentifier()); } } diff --git a/src/ResponseTypes/RedirectResponse.php b/src/ResponseTypes/RedirectResponse.php index f40f087b..e4639148 100644 --- a/src/ResponseTypes/RedirectResponse.php +++ b/src/ResponseTypes/RedirectResponse.php @@ -35,8 +35,6 @@ class RedirectResponse extends AbstractResponseType */ public function generateHttpResponse(ResponseInterface $response) { - /** @var ResponseInterface $response */ - $response = $response->withStatus(302)->withHeader('Location', $this->redirectUri); - return $response; + return $response->withStatus(302)->withHeader('Location', $this->redirectUri); } } From 25c3c216a0447791e95a3ea304dc3a06fce789c3 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 17 Feb 2018 19:31:59 +0000 Subject: [PATCH 139/191] Apply fixes from StyleCI --- src/AuthorizationServer.php | 1 - src/Exception/OAuthServerException.php | 4 ++-- src/Grant/ImplicitGrant.php | 2 +- tests/AuthorizationServerTest.php | 3 +-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 876b0083..885776ec 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -191,7 +191,6 @@ class AuthorizationServer implements EmitterAwareInterface if ($tokenResponse instanceof ResponseTypeInterface) { return $tokenResponse->generateHttpResponse($response); } - } throw OAuthServerException::unsupportedGrantType(); diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 97fc142e..b67bcf03 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -34,7 +34,7 @@ class OAuthServerException extends \Exception private $redirectUri; /** - * @var array + * @var array */ private $payload; @@ -245,7 +245,7 @@ class OAuthServerException extends \Exception * * @param ResponseInterface $response * @param bool $useFragment True if errors should be in the URI fragment instead of query string - * @param int $jsonOptions options passed to json_encode + * @param int $jsonOptions options passed to json_encode * * @return ResponseInterface */ diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index dfb96743..19e3e684 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -33,7 +33,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant /** * @param \DateInterval $accessTokenTTL - * @param string $queryDelimiter + * @param string $queryDelimiter */ public function __construct(\DateInterval $accessTokenTTL, $queryDelimiter = '#') { diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 07783841..b003c23f 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -19,15 +19,14 @@ use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; -use Psr\Http\Message\ResponseInterface; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\ResponseInterface; use Zend\Diactoros\Response; use Zend\Diactoros\ServerRequest; use Zend\Diactoros\ServerRequestFactory; class AuthorizationServerTest extends TestCase { - const DEFAULT_SCOPE = 'basic'; public function setUp() From b78c012796212b5830f6ca6a2ac41de69c82df75 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 18 Feb 2018 13:51:34 +0000 Subject: [PATCH 140/191] Change code challenge and verifier to be constants in test --- tests/Grant/AuthCodeGrantTest.php | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 40e4ef99..6a319234 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -34,25 +34,13 @@ class AuthCodeGrantTest extends TestCase */ protected $cryptStub; - /** - * @var string Valid generated Code verifier. - */ - protected $codeVerifier; + const CODE_VERIFIER = 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'; - /** - * @var string Valid generated code challenge using a proper code verifier. - */ - protected $codeChallenge; + const CODE_CHALLENGE = 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM'; public function setUp() { $this->cryptStub = new CryptTraitStub; - - // [RFC 7636] Appendix B. Example for the S256 code_challenge_method - // $this->codeVerifier = 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'; - $this->codeVerifier = strtr(rtrim(base64_encode(random_bytes(32)), '='), '+/', '-_'); - // $this->codeChallenge = 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM'; - $this->codeChallenge = strtr(rtrim(base64_encode(hash('sha256', $this->codeVerifier, true)), '='), '+/', '-_'); } public function testGetIdentifier() @@ -201,7 +189,7 @@ class AuthCodeGrantTest extends TestCase 'response_type' => 'code', 'client_id' => 'foo', 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => $this->codeChallenge, + 'code_challenge' => self::CODE_CHALLENGE, ] ); @@ -702,7 +690,7 @@ class AuthCodeGrantTest extends TestCase 'grant_type' => 'authorization_code', 'client_id' => 'foo', 'redirect_uri' => 'http://foo/bar', - 'code_verifier' => $this->codeVerifier, + 'code_verifier' => self::CODE_VERIFIER, 'code' => $this->cryptStub->doEncrypt( json_encode( [ @@ -712,7 +700,7 @@ class AuthCodeGrantTest extends TestCase 'user_id' => 123, 'scopes' => ['foo'], 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => $this->codeVerifier, + 'code_challenge' => self::CODE_VERIFIER, 'code_challenge_method' => 'plain', ] ) @@ -773,7 +761,7 @@ class AuthCodeGrantTest extends TestCase 'grant_type' => 'authorization_code', 'client_id' => 'foo', 'redirect_uri' => 'http://foo/bar', - 'code_verifier' => $this->codeVerifier, + 'code_verifier' => self::CODE_VERIFIER, 'code' => $this->cryptStub->doEncrypt( json_encode( [ @@ -783,7 +771,7 @@ class AuthCodeGrantTest extends TestCase 'user_id' => 123, 'scopes' => ['foo'], 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => $this->codeChallenge, + 'code_challenge' => self::CODE_CHALLENGE, 'code_challenge_method' => 'S256', ] ) @@ -1216,7 +1204,7 @@ class AuthCodeGrantTest extends TestCase 'grant_type' => 'authorization_code', 'client_id' => 'foo', 'redirect_uri' => 'http://foo/bar', - 'code_verifier' => $this->codeVerifier, + 'code_verifier' => self::CODE_VERIFIER, 'code' => $this->cryptStub->doEncrypt( json_encode( [ @@ -1370,7 +1358,7 @@ class AuthCodeGrantTest extends TestCase 'user_id' => 123, 'scopes' => ['foo'], 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => $this->codeChallenge, + 'code_challenge' => self::CODE_CHALLENGE, 'code_challenge_method' => 'S256', ] ) From 6679418436027ac21feb34e91f889b7f0d498b8a Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 18 Feb 2018 14:20:37 +0000 Subject: [PATCH 141/191] Update readme and changelog --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++-- README.md | 43 ++++++++++++++++++++++++++++++------------- 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 958a941e..bbb98a82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # Changelog +## 7.0.0 (released 2018-02-17) + +* Drop support for PHP 5.6 +* Drop support for version 5.x and 6.x of the library +* Accept RSA key with CRLF line endings (PR #805) +* Fix S256 code challenege method (PR #842) +* Skip key file creation if the file already exists (PR #845) +* Set correct redirect URI when validating scopes (PR #840) +* Use PHPStan for static analysis of code (PR #848) +* Do not issue an error if key file permissions are 400 or 440 (PR #839) +* Add get and set methods for OAuth Server Exception payloads. Allow implementer to specify the JSON encode options (PR #719) +* ClientRepository interface will now accept null for the Grant type to improve extensibility options (PR #607) +* Update PHPUnit version and provide PHPStan coverage for tests (PR #849) +* Upgrade library dependencies and enforce stricter static analysis checks (PR #852) +* Fix PKCE implementation (PR #744) + +## 6.1.1 (released 2017-12-23) + +* Removed check on empty scopes + +## 6.1.0 (released 2017-12-23) + +* Changed the token type issued by the Implicit Grant to be Bearer instead of bearer. (PR #724) +* Replaced call to array_key_exists() with the faster isset() on the Implicit Grant. (PR #749) +* Allow specification of query delimiter character in the Password Grant (PR #801) +* Add Zend Diactoros library dependency to examples (PR #678) +* Can set default scope for the authorization endpoint. If no scope is passed during an authorization request, the default scope will be used if set. If not, the server will issue an invalid scope exception (PR #811) +* Added validation for redirect URIs on the authorization end point to ensure exactly one redirection URI has been passed (PR #573) + ## 6.0.2 (released 2017-08-03) * An invalid refresh token that can't be decrypted now returns a HTTP 401 error instead of HTTP 400 (Issue #759) @@ -9,7 +38,7 @@ ## 6.0.1 (released 2017-07-19) To address feedback from the security release the following change has been made: - + * If an RSA key cannot be chmod'ed to 600 then it will now throw a E_USER_NOTICE instead of an exception. ## 6.0.0 (released 2017-07-01) @@ -349,4 +378,4 @@ Version 5 is a complete code rewrite. ## 1.0.0 (released 2013-02-15) -* First major release \ No newline at end of file +* First major release diff --git a/README.md b/README.md index 28047d6b..f6b6d12f 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,12 @@ # PHP OAuth 2.0 Server -### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning: -### Security Notice - -### Please upgrade to version `>=5.1.6` (backwards compatible) or `6.x` (one tiny breaking change) to fix some potential security vulnerabilities - [visit this page for more information](https://oauth2.thephpleague.com/v5-security-improvements/) -### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning: - [![Latest Version](http://img.shields.io/packagist/v/league/oauth2-server.svg?style=flat-square)](https://github.com/thephpleague/oauth2-server/releases) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) [![Build Status](https://img.shields.io/travis/thephpleague/oauth2-server/master.svg?style=flat-square)](https://travis-ci.org/thephpleague/oauth2-server) [![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/thephpleague/oauth2-server.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth2-server/code-structure) [![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/oauth2-server.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth2-server) [![Total Downloads](https://img.shields.io/packagist/dt/league/oauth2-server.svg?style=flat-square)](https://packagist.org/packages/league/oauth2-server) +[![PHPStan](https://img.shields.io/badge/PHPStan-enabled-brightgreen.svg?style=flat-square)](https://github.com/phpstan/phpstan) `league/oauth2-server` is a standards compliant implementation of an [OAuth 2.0](https://tools.ietf.org/html/rfc6749) authorization server written in PHP which makes working with OAuth 2.0 trivial. You can easily configure an OAuth 2.0 server to protect your API with access tokens, or allow clients to request new access tokens and refresh them. @@ -36,25 +31,48 @@ This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](ht The following versions of PHP are supported: -* PHP 5.6 * PHP 7.0 * PHP 7.1 * PHP 7.2 The `openssl` extension is also required. +## Installation + +``` +composer require league/oauth2-server +``` + ## Documentation -The library documentation can be found at [https://oauth2.thephpleague.com](https://oauth2.thephpleague.com). +The library documentation can be found at [https://oauth2.thephpleague.com](https://oauth2.thephpleague.com). You can contribute to the documentation in the [gh-pages branch](https://github.com/thephpleague/oauth2-server/tree/gh-pages/). +## Testing + +The library uses [PHPUnit](https://phpunit.de/) for unit tests and [PHPStan](https://github.com/phpstan/phpstan) for static analysis of the code. + +``` +vendor/bin/phpunit +vendor/bin/phpstan analyse -l 6 -c phpstan.neon src tests +``` + +## Continous Integration + +We use [Travis CI](https://travis-ci.org/), [Scrutinizer](https://scrutinizer-ci.com/), and [StyleCI](https://styleci.io/) for continuous integration. Check out [our](https://github.com/thephpleague/oauth2-server/blob/master/.travis.yml) [configuration](https://github.com/thephpleague/oauth2-server/blob/master/.scrutinizer.yml) [files](https://github.com/thephpleague/oauth2-server/blob/master/.styleci.yml) if you'd like to know more. + +## Community Integrations + +* [Laravel Passport](https://github.com/laravel/passport) +* [OAuth 2 Server for CakePHP 3](https://github.com/uafrica/oauth-server) + ## Changelog [See the project releases page](https://github.com/thephpleague/oauth2-server/releases) ## Contributing -Please see [CONTRIBUTING.md](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) and [CONDUCT.md](https://github.com/thephpleague/oauth2-server/blob/master/CONDUCT.md) for details. +Contributions are always welcome. Please see [CONTRIBUTING.md](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) and [CONDUCT.md](https://github.com/thephpleague/oauth2-server/blob/master/CONDUCT.md) for details. ## Support @@ -62,8 +80,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). @@ -78,11 +94,12 @@ 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), [Brian -Retterer](https://twitter.com/bretterer), and [Simon Hamp](https://twitter.com/simonhamp). +This code is principally developed and maintained by [Andy Millington](https://twitter.com/Sephster) and [Simon Hamp](https://twitter.com/simonhamp). Between 2012 and 2017 this library was developed and maintained by [Alex Bilbie](https://alexbilbie.com/). +PHP OAuth 2.0 Server is one of many packages provided by The PHP League. To find out more, please visit [our website](https://thephpleague.com). + Special thanks to [all of these awesome contributors](https://github.com/thephpleague/oauth2-server/contributors). Additional thanks go to the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source) for funding a security audit of this library. From 70396bec67f7dcf298fec2ea5ada830199e3fb5d Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 18 Feb 2018 15:28:28 +0000 Subject: [PATCH 142/191] Chang Changelog format --- CHANGELOG.md | 584 +++++++++++++++++++++++++++------------------------ README.md | 2 +- 2 files changed, 313 insertions(+), 273 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbb98a82..a0067a5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,381 +1,421 @@ # Changelog +All notable changes to this project will be documented in this file. -## 7.0.0 (released 2018-02-17) +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -* Drop support for PHP 5.6 -* Drop support for version 5.x and 6.x of the library -* Accept RSA key with CRLF line endings (PR #805) -* Fix S256 code challenege method (PR #842) -* Skip key file creation if the file already exists (PR #845) -* Set correct redirect URI when validating scopes (PR #840) -* Use PHPStan for static analysis of code (PR #848) -* Do not issue an error if key file permissions are 400 or 440 (PR #839) -* Add get and set methods for OAuth Server Exception payloads. Allow implementer to specify the JSON encode options (PR #719) -* ClientRepository interface will now accept null for the Grant type to improve extensibility options (PR #607) -* Update PHPUnit version and provide PHPStan coverage for tests (PR #849) -* Upgrade library dependencies and enforce stricter static analysis checks (PR #852) -* Fix PKCE implementation (PR #744) +## [Unreleased] -## 6.1.1 (released 2017-12-23) +## [7.0.0] - released 2018-02-17 -* Removed check on empty scopes +### Added +- Use PHPStan for static analysis of code (PR #848) +- Enforce stricter static analysis checks and upgrade library dependencies (PR #852) +- Provide PHPStan coverage for tests and update PHPUnit (PR #849) +- Get and set methods for OAuth Server Exception payloads. Allow implementer to specify the JSON encode options (PR #719) -## 6.1.0 (released 2017-12-23) +### Changed +- ClientRepository interface will now accept null for the Grant type to improve extensibility options (PR #607) +- Do not issue an error if key file permissions are 400 or 440 (PR #839) +- Skip key file creation if the file already exists (PR #845) +- Change changelog format and update readme -* Changed the token type issued by the Implicit Grant to be Bearer instead of bearer. (PR #724) -* Replaced call to array_key_exists() with the faster isset() on the Implicit Grant. (PR #749) -* Allow specification of query delimiter character in the Password Grant (PR #801) -* Add Zend Diactoros library dependency to examples (PR #678) -* Can set default scope for the authorization endpoint. If no scope is passed during an authorization request, the default scope will be used if set. If not, the server will issue an invalid scope exception (PR #811) -* Added validation for redirect URIs on the authorization end point to ensure exactly one redirection URI has been passed (PR #573) +### Removed +- Support for PHP 5.6 +- Support for version 5.x and 6.x of the library -## 6.0.2 (released 2017-08-03) +### Fixed +- PKCE implementation (PR #744) +- Set correct redirect URI when validating scopes (PR #840) +- S256 code challenege method (PR #842) +- Accept RSA key with CRLF line endings (PR #805) -* An invalid refresh token that can't be decrypted now returns a HTTP 401 error instead of HTTP 400 (Issue #759) -* Removed chmod from CryptKey and add toggle to disable checking (Issue #776) -* Fixes invalid code challenge method payload key name (Issue #777) +## [6.1.1] - 2017-12-23 -## 6.0.1 (released 2017-07-19) +- Removed check on empty scopes + +## [6.1.0] - 2017-12-23 + +- Changed the token type issued by the Implicit Grant to be Bearer instead of bearer. (PR #724) +- Replaced call to array_key_exists() with the faster isset() on the Implicit Grant. (PR #749) +- Allow specification of query delimiter character in the Password Grant (PR #801) +- Add Zend Diactoros library dependency to examples (PR #678) +- Can set default scope for the authorization endpoint. If no scope is passed during an authorization request, the default scope will be used if set. If not, the server will issue an invalid scope exception (PR #811) +- Added validation for redirect URIs on the authorization end point to ensure exactly one redirection URI has been passed (PR #573) + +## [6.0.2] - 2017-08-03 + +- An invalid refresh token that can't be decrypted now returns a HTTP 401 error instead of HTTP 400 (Issue #759) +- Removed chmod from CryptKey and add toggle to disable checking (Issue #776) +- Fixes invalid code challenge method payload key name (Issue #777) + +## [6.0.1] - 2017-07-19 To address feedback from the security release the following change has been made: -* If an RSA key cannot be chmod'ed to 600 then it will now throw a E_USER_NOTICE instead of an exception. +- If an RSA key cannot be chmod'ed to 600 then it will now throw a E_USER_NOTICE instead of an exception. -## 6.0.0 (released 2017-07-01) +## [6.0.0] - 2017-07-01 -* Breaking change: The `AuthorizationServer` constructor now expects an encryption key string instead of a public key -* Remove support for HHVM -* Remove support for PHP 5.5 +- Breaking change: The `AuthorizationServer` constructor now expects an encryption key string instead of a public key +- Remove support for HHVM +- Remove support for PHP 5.5 -## 5.1.4 (released 2017-07-01) +## [5.1.4] - 2017-07-01 -* Fixed multiple security vulnerabilities as a result of a security audit paid for by the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source). All users of this library are encouraged to update as soon as possible to this version or version 6.0 or greater. - * It is recommended on each `AuthorizationServer` instance you set the `setEncryptionKey()`. This will result in stronger encryption being used. If this method is not set messages will be sent to the defined error handling routines (using `error_log`). Please see the examples and documentation for examples. -* TravisCI now tests PHP 7.1 (Issue #671) -* Fix middleware example fatal error (Issue #682) -* Fix typo in the first README sentence (Issue #690) -* Corrected DateInterval from 1 min to 1 month (Issue #709) +- Fixed multiple security vulnerabilities as a result of a security audit paid for by the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source). All users of this library are encouraged to update as soon as possible to this version or version 6.0 or greater. + - It is recommended on each `AuthorizationServer` instance you set the `setEncryptionKey()`. This will result in stronger encryption being used. If this method is not set messages will be sent to the defined error handling routines (using `error_log`). Please see the examples and documentation for examples. +- TravisCI now tests PHP 7.1 (Issue #671) +- Fix middleware example fatal error (Issue #682) +- Fix typo in the first README sentence (Issue #690) +- Corrected DateInterval from 1 min to 1 month (Issue #709) -## 5.1.3 (released 2016-10-12) +## [5.1.3] - 2016-10-12 -* Fixed WWW-Authenticate header (Issue #669) -* Increase the recommended RSA key length from 1024 to 2048 bits (Issue #668) +- Fixed WWW-Authenticate header (Issue #669) +- Increase the recommended RSA key length from 1024 to 2048 bits (Issue #668) -## 5.1.2 (released 2016-09-19) +## [5.1.2] - 2016-09-19 -* Fixed `finalizeScopes` call (Issue #650) +- Fixed `finalizeScopes` call (Issue #650) -## 5.1.1 (released 2016-07-26) +## [5.1.1] - 2016-07-26 -* Improved test suite (Issue #614) -* Updated docblocks (Issue #616) -* Replace `array_shift` with `foreach` loop (Issue #621) -* Allow easy addition of custom fields to Bearer token response (Issue #624) -* Key file auto-generation from string (Issue #625) +- Improved test suite (Issue #614) +- Updated docblocks (Issue #616) +- Replace `array_shift` with `foreach` loop (Issue #621) +- Allow easy addition of custom fields to Bearer token response (Issue #624) +- Key file auto-generation from string (Issue #625) -## 5.1.0 (released 2016-06-28) +## [5.1.0] - 2016-06-28 -* Implemented RFC7636 (Issue #574) -* Unify middleware exception responses (Issue #578) -* Updated examples (Issue #589) -* Ensure state is in access denied redirect (Issue #597) -* Remove redundant `isExpired()` method from entity interfaces and traits (Issue #600) -* Added a check for unique access token constraint violation (Issue #601) -* Look at Authorization header directly for HTTP Basic auth checks (Issue #604) -* Added catch Runtime exception when parsing JWT string (Issue #605) -* Allow `paragonie/random_compat` 2.x (Issue #606) -* Added `indigophp/hash-compat` to Composer suggestions and `require-dev` for PHP 5.5 support +- Implemented RFC7636 (Issue #574) +- Unify middleware exception responses (Issue #578) +- Updated examples (Issue #589) +- Ensure state is in access denied redirect (Issue #597) +- Remove redundant `isExpired()` method from entity interfaces and traits (Issue #600) +- Added a check for unique access token constraint violation (Issue #601) +- Look at Authorization header directly for HTTP Basic auth checks (Issue #604) +- Added catch Runtime exception when parsing JWT string (Issue #605) +- Allow `paragonie/random_compat` 2.x (Issue #606) +- Added `indigophp/hash-compat` to Composer suggestions and `require-dev` for PHP 5.5 support -## 5.0.3 (released 2016-05-04) +## [5.0.3] - 2016-05-04 -* Fix hints in PasswordGrant (Issue #560) -* Add meaning of `Resource owner` to terminology.md (Issue #561) -* Use constant for event name instead of explicit string (Issue #563) -* Remove unused request property (Issue #564) -* Correct wrong phpdoc (Issue #569) -* Fixed typo in exception string (Issue #570) +- Fix hints in PasswordGrant (Issue #560) +- Add meaning of `Resource owner` to terminology.md (Issue #561) +- Use constant for event name instead of explicit string (Issue #563) +- Remove unused request property (Issue #564) +- Correct wrong phpdoc (Issue #569) +- Fixed typo in exception string (Issue #570) -## 5.0.2 (released 2016-04-18) +## [5.0.2] - 2016-04-18 -* `state` parameter is now correctly returned after implicit grant authorization -* Small code and docblock improvements +- `state` parameter is now correctly returned after implicit grant authorization +- Small code and docblock improvements -## 5.0.1 (released 2016-04-18) +## [5.0.1] - 2016-04-18 -* Fixes an issue (#550) whereby it was unclear whether or not to validate a client's secret during a request. +- Fixes an issue (#550) whereby it was unclear whether or not to validate a client's secret during a request. -## 5.0.0 (released 2016-04-17) +## [5.0.0] - 2016-04-17 Version 5 is a complete code rewrite. -* JWT support -* PSR-7 support -* Improved exception errors -* Replace all occurrences of the term "Storage" with "Repository" -* Simplify repositories -* Entities conform to interfaces and use traits -* Auth code grant updated - * Allow support for public clients - * Add support for #439 -* Client credentials grant updated -* Password grant updated - * Allow support for public clients -* Refresh token grant updated -* Implement Implicit grant -* Bearer token output type -* Remove MAC token output type -* Authorization server rewrite -* Resource server class moved to PSR-7 middleware -* Tests -* Much much better documentation +- Renamed Server class to AuthorizationServer +- Added ResourceServer class +- Run unit tests again PHP 5.5.9 as it's the minimum supported version +- Enable PHPUnit 5.0 support +- Improved examples and documentation +- Make it clearer that the implicit grant doesn't support refresh tokens +- Improved refresh token validation errors +- Fixed refresh token expiry date -Changes since RC2: +## [5.0.0-RC2] - 2016-04-10 -* Renamed Server class to AuthorizationServer -* Added ResourceServer class -* Run unit tests again PHP 5.5.9 as it's the minimum supported version -* Enable PHPUnit 5.0 support -* Improved examples and documentation -* Make it clearer that the implicit grant doesn't support refresh tokens -* Improved refresh token validation errors -* Fixed refresh token expiry date +- Allow multiple client redirect URIs (Issue #511) +- Remove unused mac token interface (Issue #503) +- Handle RSA key passphrase (Issue #502) +- Remove access token repository from response types (Issue #501) +- Remove unnecessary methods from entity interfaces (Issue #490) +- Ensure incoming JWT hasn't expired (Issue #509) +- Fix client identifier passed where user identifier is expected (Issue #498) +- Removed built-in entities; added traits to for quick re-use (Issue #504) +- Redirect uri is required only if the "redirect_uri" parameter was included in the authorization request (Issue #514) +- Removed templating for auth code and implicit grants (Issue #499) -## 5.0.0-RC2 (released 2016-04-10) - -Changes since RC1: - -* Allow multiple client redirect URIs (Issue #511) -* Remove unused mac token interface (Issue #503) -* Handle RSA key passphrase (Issue #502) -* Remove access token repository from response types (Issue #501) -* Remove unnecessary methods from entity interfaces (Issue #490) -* Ensure incoming JWT hasn't expired (Issue #509) -* Fix client identifier passed where user identifier is expected (Issue #498) -* Removed built-in entities; added traits to for quick re-use (Issue #504) -* Redirect uri is required only if the "redirect_uri" parameter was included in the authorization request (Issue #514) -* Removed templating for auth code and implicit grants (Issue #499) - -## 5.0.0-RC1 (release 2016-03-24) +## [5.0.0-RC1] - 2016-03-24 Version 5 is a complete code rewrite. -* JWT support -* PSR-7 support -* Improved exception errors -* Replace all occurrences of the term "Storage" with "Repository" -* Simplify repositories -* Entities conform to interfaces and use traits -* Auth code grant updated - * Allow support for public clients - * Add support for #439 -* Client credentials grant updated -* Password grant updated - * Allow support for public clients -* Refresh token grant updated -* Implement Implicit grant -* Bearer token output type -* Remove MAC token output type -* Authorization server rewrite -* Resource server class moved to PSR-7 middleware -* Tests -* Much much better documentation +- JWT support +- PSR-7 support +- Improved exception errors +- Replace all occurrences of the term "Storage" with "Repository" +- Simplify repositories +- Entities conform to interfaces and use traits +- Auth code grant updated + - Allow support for public clients + - Add support for #439 +- Client credentials grant updated +- Password grant updated + - Allow support for public clients +- Refresh token grant updated +- Implement Implicit grant +- Bearer token output type +- Remove MAC token output type +- Authorization server rewrite +- Resource server class moved to PSR-7 middleware +- Tests +- Much much better documentation -## 4.1.5 (released 2016-01-04) +## [4.1.5] - 2016-01-04 -* Enable Symfony 3.0 support (#412) +- Enable Symfony 3.0 support (#412) -## 4.1.4 (released 2015-11-13) +## [4.1.4] - 2015-11-13 -* Fix for determining access token in header (Issue #328) -* Refresh tokens are now returned for MAC responses (Issue #356) -* Added integration list to readme (Issue #341) -* Expose parameter passed to exceptions (Issue #345) -* Removed duplicate routing setup code (Issue #346) -* Docs fix (Issues #347, #360, #380) -* Examples fix (Issues #348, #358) -* Fix typo in docblock (Issue #352) -* Improved timeouts for MAC tokens (Issue #364) -* `hash_hmac()` should output raw binary data, not hexits (Issue #370) -* Improved regex for matching all Base64 characters (Issue #371) -* Fix incorrect signature parameter (Issue #372) -* AuthCodeGrant and RefreshTokenGrant don't require client_secret (Issue #377) -* Added priority argument to event listener (Issue #388) +- Fix for determining access token in header (Issue #328) +- Refresh tokens are now returned for MAC responses (Issue #356) +- Added integration list to readme (Issue #341) +- Expose parameter passed to exceptions (Issue #345) +- Removed duplicate routing setup code (Issue #346) +- Docs fix (Issues #347, #360, #380) +- Examples fix (Issues #348, #358) +- Fix typo in docblock (Issue #352) +- Improved timeouts for MAC tokens (Issue #364) +- `hash_hmac()` should output raw binary data, not hexits (Issue #370) +- Improved regex for matching all Base64 characters (Issue #371) +- Fix incorrect signature parameter (Issue #372) +- AuthCodeGrant and RefreshTokenGrant don't require client_secret (Issue #377) +- Added priority argument to event listener (Issue #388) -## 4.1.3 (released 2015-03-22) +## [4.1.3] - 2015-03-22 -* Docblock, namespace and inconsistency fixes (Issue #303) -* Docblock type fix (Issue #310) -* Example bug fix (Issue #300) -* Updated league/event to ~2.1 (Issue #311) -* Fixed missing session scope (Issue #319) -* Updated interface docs (Issue #323) -* `.travis.yml` updates +- Docblock, namespace and inconsistency fixes (Issue #303) +- Docblock type fix (Issue #310) +- Example bug fix (Issue #300) +- Updated league/event to ~2.1 (Issue #311) +- Fixed missing session scope (Issue #319) +- Updated interface docs (Issue #323) +- `.travis.yml` updates -## 4.1.2 (released 2015-01-01) +## [4.1.2] - 2015-01-01 -* Remove side-effects in hash_equals() implementation (Issue #290) +- Remove side-effects in hash_equals() implementation (Issue #290) -## 4.1.1 (released 2014-12-31) +## [4.1.1] - 2014-12-31 -* Changed `symfony/http-foundation` dependency version to `~2.4` so package can be installed in Laravel `4.1.*` +- Changed `symfony/http-foundation` dependency version to `~2.4` so package can be installed in Laravel `4.1.*` -## 4.1.0 (released 2014-12-27) +## [4.1.0] - 2014-12-27 -* Added MAC token support (Issue #158) -* Fixed example init code (Issue #280) -* Toggle refresh token rotation (Issue #286) -* Docblock fixes +- Added MAC token support (Issue #158) +- Fixed example init code (Issue #280) +- Toggle refresh token rotation (Issue #286) +- Docblock fixes -## 4.0.5 (released 2014-12-15) +## [4.0.5] - 2014-12-15 -* Prevent duplicate session in auth code grant (Issue #282) +- Prevent duplicate session in auth code grant (Issue #282) -## 4.0.4 (released 2014-12-03) +## [4.0.4] - 2014-12-03 -* Ensure refresh token hasn't expired (Issue #270) +- Ensure refresh token hasn't expired (Issue #270) -## 4.0.3 (released 2014-12-02) +## [4.0.3] - 2014-12-02 -* Fix bad type hintings (Issue #267) -* Do not forget to set the expire time (Issue #268) +- Fix bad type hintings (Issue #267) +- Do not forget to set the expire time (Issue #268) -## 4.0.2 (released 2014-11-21) +## [4.0.2] - 2014-11-21 -* Improved interfaces (Issue #255) -* Learnt how to spell delimiter and so `getScopeDelimiter()` and `setScopeDelimiter()` methods have been renamed -* Docblock improvements (Issue #254) +- Improved interfaces (Issue #255) +- Learnt how to spell delimiter and so `getScopeDelimiter()` and `setScopeDelimiter()` methods have been renamed +- Docblock improvements (Issue #254) -## 4.0.1 (released 2014-11-09) +## [4.0.1] - 2014-11-09 -* Alias the master branch in composer.json (Issue #243) -* Numerous PHP CodeSniffer fixes (Issue #244) -* .travis.yml update (Issue #245) -* The getAccessToken method should return an AccessTokenEntity object instead of a string in ResourceServer.php (#246) +- Alias the master branch in composer.json (Issue #243) +- Numerous PHP CodeSniffer fixes (Issue #244) +- .travis.yml update (Issue #245) +- The getAccessToken method should return an AccessTokenEntity object instead of a string in ResourceServer.php (#246) -## 4.0.0 (released 2014-11-08) +## [4.0.0] - 2014-11-08 -* Complete rewrite -* Check out the documentation - [http://oauth2.thephpleague.com](http://oauth2.thephpleague.com) +- Complete rewrite +- Check out the documentation - [http://oauth2.thephpleague.com](http://oauth2.thephpleague.com) -## 3.2.0 (released 2014-04-16) +## [3.2.0] - 2014-04-16 -* Added the ability to change the algorithm that is used to generate the token strings (Issue #151) +- Added the ability to change the algorithm that is used to generate the token strings (Issue #151) -## 3.1.2 (released 2014-02-26) +## [3.1.2] - 2014-02-26 -* Support Authorization being an environment variable. [See more](http://fortrabbit.com/docs/essentials/quirks-and-constraints#authorization-header) +- Support Authorization being an environment variable. [See more](http://fortrabbit.com/docs/essentials/quirks-and-constraints#authorization-header) -## 3.1.1 (released 2013-12-05) +## [3.1.1] - 2013-12-05 -* Normalize headers when `getallheaders()` is available (Issues #108 and #114) +- Normalize headers when `getallheaders()` is available (Issues #108 and #114) -## 3.1.0 (released 2013-12-05) +## [3.1.0] - 2013-12-05 -* No longer necessary to inject the authorisation server into a grant, the server will inject itself -* Added test for 1419ba8cdcf18dd034c8db9f7de86a2594b68605 +- No longer necessary to inject the authorisation server into a grant, the server will inject itself +- Added test for 1419ba8cdcf18dd034c8db9f7de86a2594b68605 -## 3.0.1 (released 2013-12-02) +## [3.0.1] - 2013-12-02 -* Forgot to tell TravisCI from testing PHP 5.3 +- Forgot to tell TravisCI from testing PHP 5.3 -## 3.0.0 (released 2013-12-02) +## [3.0.0] - 2013-12-02 -* Fixed spelling of Implicit grant class (Issue #84) -* Travis CI now tests for PHP 5.5 -* Fixes for checking headers for resource server (Issues #79 and #) -* The word "bearer" now has a capital "B" in JSON output to match OAuth 2.0 spec -* All grants no longer remove old sessions by default -* All grants now support custom access token TTL (Issue #92) -* All methods which didn't before return a value now return `$this` to support method chaining -* Removed the build in DB providers - these will be put in their own repos to remove baggage in the main repository -* Removed support for PHP 5.3 because this library now uses traits and will use other modern PHP features going forward -* Moved some grant related functions into a trait to reduce duplicate code +- Fixed spelling of Implicit grant class (Issue #84) +- Travis CI now tests for PHP 5.5 +- Fixes for checking headers for resource server (Issues #79 and #) +- The word "bearer" now has a capital "B" in JSON output to match OAuth 2.0 spec +- All grants no longer remove old sessions by default +- All grants now support custom access token TTL (Issue #92) +- All methods which didn't before return a value now return `$this` to support method chaining +- Removed the build in DB providers - these will be put in their own repos to remove baggage in the main repository +- Removed support for PHP 5.3 because this library now uses traits and will use other modern PHP features going forward +- Moved some grant related functions into a trait to reduce duplicate code -## 2.1.1 (released 2013-06-02) +## [2.1.1] - 2013-06-02 -* Added conditional `isValid()` flag to check for Authorization header only (thanks @alexmcroberts) -* Fixed semantic meaning of `requireScopeParam()` and `requireStateParam()` by changing their default value to true -* Updated some duff docblocks -* Corrected array key call in Resource.php (Issue #63) +- Added conditional `isValid()` flag to check for Authorization header only (thanks @alexmcroberts) +- Fixed semantic meaning of `requireScopeParam()` and `requireStateParam()` by changing their default value to true +- Updated some duff docblocks +- Corrected array key call in Resource.php (Issue #63) -## 2.1 (released 2013-05-10) +## [2.1.0] - 2013-05-10 -* Moved zetacomponents/database to "suggest" in composer.json. If you rely on this feature you now need to include " zetacomponents/database" into "require" key in your own composer.json. (Issue #51) -* New method in Refresh grant called `rotateRefreshTokens()`. Pass in `true` to issue a new refresh token each time an access token is refreshed. This parameter needs to be set to true in order to request reduced scopes with the new access token. (Issue #47) -* Rename `key` column in oauth_scopes table to `scope` as `key` is a reserved SQL word. (Issue #45) -* The `scope` parameter is no longer required by default as per the RFC. (Issue #43) -* You can now set multiple default scopes by passing an array into `setDefaultScope()`. (Issue #42) -* The password and client credentials grants now allow for multiple sessions per user. (Issue #32) -* Scopes associated to authorization codes are not held in their own table (Issue #44) -* Database schema updates. +- Moved zetacomponents/database to "suggest" in composer.json. If you rely on this feature you now need to include " zetacomponents/database" into "require" key in your own composer.json. (Issue #51) +- New method in Refresh grant called `rotateRefreshTokens()`. Pass in `true` to issue a new refresh token each time an access token is refreshed. This parameter needs to be set to true in order to request reduced scopes with the new access token. (Issue #47) +- Rename `key` column in oauth_scopes table to `scope` as `key` is a reserved SQL word. (Issue #45) +- The `scope` parameter is no longer required by default as per the RFC. (Issue #43) +- You can now set multiple default scopes by passing an array into `setDefaultScope()`. (Issue #42) +- The password and client credentials grants now allow for multiple sessions per user. (Issue #32) +- Scopes associated to authorization codes are not held in their own table (Issue #44) +- Database schema updates. -## 2.0.5 (released 2013-05-09) +## [2.0.5] - 2013-05-09 -* Fixed `oauth_session_token_scopes` table primary key -* Removed `DEFAULT ''` that has slipped into some tables -* Fixed docblock for `SessionInterface::associateRefreshToken()` +- Fixed `oauth_session_token_scopes` table primary key +- Removed `DEFAULT ''` that has slipped into some tables +- Fixed docblock for `SessionInterface::associateRefreshToken()` -## 2.0.4 (released 2013-05-09) +## [2.0.4] - 2013-05-09 -* Renamed primary key in oauth_client_endpoints table -* Adding missing column to oauth_session_authcodes -* SECURITY FIX: A refresh token should be bound to a client ID +- Renamed primary key in oauth_client_endpoints table +- Adding missing column to oauth_session_authcodes -## 2.0.3 (released 2013-05-08) +### Security +- A refresh token should be bound to a client ID -* Fixed a link to code in composer.json +## [2.0.3] - 2013-05-08 -## 2.0.2 (released 2013-05-08) +- Fixed a link to code in composer.json -* Updated README with wiki guides -* Removed `null` as default parameters in some methods in the storage interfaces -* Fixed license copyright +## [2.0.2] - 2013-05-08 -## 2.0.0 (released 2013-05-08) +- Updated README with wiki guides +- Removed `null` as default parameters in some methods in the storage interfaces +- Fixed license copyright + +## [2.0.0] - 2013-05-08 **If you're upgrading from v1.0.8 there are lots of breaking changes** -* Rewrote the session storage interface from scratch so methods are more obvious -* Included a PDO driver which implements the storage interfaces so the library is more "get up and go" -* Further normalised the database structure so all sessions no longer contain infomation related to authorization grant (which may or may not be enabled) -* A session can have multiple associated access tokens -* Individual grants can have custom expire times for access tokens -* Authorization codes now have a TTL of 10 minutes by default (can be manually set) -* Refresh tokens now have a TTL of one week by default (can be manually set) -* The client credentials grant will no longer gives out refresh tokens as per the specification +- Rewrote the session storage interface from scratch so methods are more obvious +- Included a PDO driver which implements the storage interfaces so the library is more "get up and go" +- Further normalised the database structure so all sessions no longer contain infomation related to authorization grant (which may or may not be enabled) +- A session can have multiple associated access tokens +- Individual grants can have custom expire times for access tokens +- Authorization codes now have a TTL of 10 minutes by default (can be manually set) +- Refresh tokens now have a TTL of one week by default (can be manually set) +- The client credentials grant will no longer gives out refresh tokens as per the specification -## 1.0.8 (released 2013-03-18) +## [1.0.8] - 2013-03-18 -* Fixed check for required state parameter -* Fixed check that user's credentials are correct in Password grant +- Fixed check for required state parameter +- Fixed check that user's credentials are correct in Password grant -## 1.0.7 (released 2013-03-04) +## [1.0.7] - 2013-03-04 -* Added method `requireStateParam()` -* Added method `requireScopeParam()` +- Added method `requireStateParam()` +- Added method `requireScopeParam()` -## 1.0.6 (released 2013-02-22) +## [1.0.6] - 2013-02-22 -* Added links to tutorials in the README -* Added missing `state` parameter request to the `checkAuthoriseParams()` method. +- Added links to tutorials in the README +- Added missing `state` parameter request to the `checkAuthoriseParams()` method. -## 1.0.5 (released 2013-02-21) +## [1.0.5] - 2013-02-21 -* Fixed the SQL example for SessionInterface::getScopes() +- Fixed the SQL example for SessionInterface::getScopes() -## 1.0.3 (released 2013-02-20) +## [1.0.3] - 2013-02-20 -* Changed all instances of the "authentication server" to "authorization server" +- Changed all instances of the "authentication server" to "authorization server" -## 1.0.2 (released 2013-02-20) +## [1.0.2] - 2013-02-20 -* Fixed MySQL create table order -* Fixed version number in composer.json +- Fixed MySQL create table order +- Fixed version number in composer.json -## 1.0.1 (released 2013-02-19) +## [1.0.1] - 2013-02-19 -* Updated AuthServer.php to use `self::getParam()` +- Updated AuthServer.php to use `self::getParam()` -## 1.0.0 (released 2013-02-15) +## 1.0.0 - 2013-02-15 -* First major release +- First major release + +[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/v7.0.0...HEAD +[7.0.0]: https://github.com/thephpleague/oauth2-server/compare/v6.1.1...v7.0.0 +[6.1.1]: https://github.com/thephpleague/oauth2-server/compare/v6.0.0...v6.1.1 +[6.1.0]: https://github.com/thephpleague/oauth2-server/compare/v6.0.0...v6.1.0 +[6.0.0]: https://github.com/thephpleague/oauth2-server/compare/v5.1.4...v6.0.0 +[5.1.4]: https://github.com/thephpleague/oauth2-server/compare/v5.1.3...v5.1.4 +[5.1.3]: https://github.com/thephpleague/oauth2-server/compare/v5.1.2...v5.1.3 +[5.1.2]: https://github.com/thephpleague/oauth2-server/compare/v5.1.1...v5.1.2 +[5.1.1]: https://github.com/thephpleague/oauth2-server/compare/v5.1.0...v5.1.1 +[5.1.0]: https://github.com/thephpleague/oauth2-server/compare/v5.0.2...v5.1.0 +[5.0.3]: https://github.com/thephpleague/oauth2-server/compare/v5.0.3...v5.0.2 +[5.0.2]: https://github.com/thephpleague/oauth2-server/compare/v5.0.1...v5.0.2 +[5.0.1]: https://github.com/thephpleague/oauth2-server/compare/v5.0.0...v5.0.1 +[5.0.0]: https://github.com/thephpleague/oauth2-server/compare/v5.0.0-RC2...v5.0.0 +[5.0.0-RC2]: https://github.com/thephpleague/oauth2-server/compare/v5.0.0-RC1...v5.0.0-RC2 +[5.0.0-RC1]: https://github.com/thephpleague/oauth2-server/compare/v4.1.5...v5.0.0-RC1 +[4.1.5]: https://github.com/thephpleague/oauth2-server/compare/v4.1.4...v4.1.5 +[4.1.4]: https://github.com/thephpleague/oauth2-server/compare/v4.1.3...v4.1.4 +[4.1.3]: https://github.com/thephpleague/oauth2-server/compare/v4.1.2...v4.1.3 +[4.1.2]: https://github.com/thephpleague/oauth2-server/compare/v4.1.1...v4.1.2 +[4.1.1]: https://github.com/thephpleague/oauth2-server/compare/v4.0.0...v4.1.1 +[4.1.0]: https://github.com/thephpleague/oauth2-server/compare/v4.0.5...v4.1.0 +[4.0.5]: https://github.com/thephpleague/oauth2-server/compare/v4.0.4...v4.0.5 +[4.0.4]: https://github.com/thephpleague/oauth2-server/compare/v4.0.3...v4.0.4 +[4.0.3]: https://github.com/thephpleague/oauth2-server/compare/v4.0.2...v4.0.3 +[4.0.2]: https://github.com/thephpleague/oauth2-server/compare/v4.0.1...v4.0.2 +[4.0.1]: https://github.com/thephpleague/oauth2-server/compare/v4.0.0...v4.0.1 +[4.0.0]: https://github.com/thephpleague/oauth2-server/compare/v3.2.0...v4.0.0 +[3.2.0]: https://github.com/thephpleague/oauth2-server/compare/v3.1.2...v3.2.0 +[3.1.2]: https://github.com/thephpleague/oauth2-server/compare/v3.1.1...v3.1.2 +[3.1.1]: https://github.com/thephpleague/oauth2-server/compare/v3.1.0...v3.1.1 +[3.1.0]: https://github.com/thephpleague/oauth2-server/compare/v3.0.1...v3.1.0 +[3.0.1]: https://github.com/thephpleague/oauth2-server/compare/v3.0.0...v3.0.1 +[3.0.0]: https://github.com/thephpleague/oauth2-server/compare/v2.1.1...v3.0.0 +[2.1.1]: https://github.com/thephpleague/oauth2-server/compare/v2.1.0...v2.1.1 +[2.1.0]: https://github.com/thephpleague/oauth2-server/compare/v2.0.5...v2.1.0 +[2.0.5]: https://github.com/thephpleague/oauth2-server/compare/v2.0.4...v2.0.5 +[2.0.4]: https://github.com/thephpleague/oauth2-server/compare/v2.0.3...v2.0.4 +[2.0.3]: https://github.com/thephpleague/oauth2-server/compare/v2.0.2...v2.0.3 +[2.0.2]: https://github.com/thephpleague/oauth2-server/compare/v2.0.0...v2.0.2 +[2.0.0]: https://github.com/thephpleague/oauth2-server/compare/v1.0.8...v2.0.0 +[1.0.8]: https://github.com/thephpleague/oauth2-server/compare/v1.0.7...v1.0.8 +[1.0.7]: https://github.com/thephpleague/oauth2-server/compare/v1.0.6...v1.0.7 +[1.0.6]: https://github.com/thephpleague/oauth2-server/compare/v1.0.5...v1.0.6 +[1.0.5]: https://github.com/thephpleague/oauth2-server/compare/v1.0.3...v1.0.5 +[1.0.3]: https://github.com/thephpleague/oauth2-server/compare/v1.0.2...v1.0.3 +[1.0.2]: https://github.com/thephpleague/oauth2-server/compare/v1.0.1...v1.0.2 +[1.0.1]: https://github.com/thephpleague/oauth2-server/compare/v1.0.0...v1.0.1 diff --git a/README.md b/README.md index f6b6d12f..7e9af882 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ `league/oauth2-server` is a standards compliant implementation of an [OAuth 2.0](https://tools.ietf.org/html/rfc6749) authorization server written in PHP which makes working with OAuth 2.0 trivial. You can easily configure an OAuth 2.0 server to protect your API with access tokens, or allow clients to request new access tokens and refresh them. -It supports out of the box the following grants: +Out of the box it supports the following grants: * Authorization code grant * Implicit grant From 9287f587fc55a41f345b869f28e07249591a24e0 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 18 Feb 2018 15:29:59 +0000 Subject: [PATCH 143/191] Update changelog link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e9af882..b5326742 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ We use [Travis CI](https://travis-ci.org/), [Scrutinizer](https://scrutinizer-ci ## Changelog -[See the project releases page](https://github.com/thephpleague/oauth2-server/releases) +See the [project changelog](https://github.com/thephpleague/oauth2-server/blob/master/CHANGELOG.md) ## Contributing From 028d91f670379b341c4e0f22106cb30d770dc15d Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 18 Feb 2018 15:33:41 +0000 Subject: [PATCH 144/191] Add code coverage for scrutinizer --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2684cac3..be2759a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,9 +20,13 @@ install: - composer update --no-interaction --prefer-dist $DEPENDENCIES script: - - vendor/bin/phpunit + - vendor/bin/phpunit --coverage-clover=coverage.clover - vendor/bin/phpstan analyse -l 6 -c phpstan.neon src tests +after_script: + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover + branches: only: - master From 49f66866f78cae75f104fcca57c3222d843acbd5 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 18 Feb 2018 15:38:28 +0000 Subject: [PATCH 145/191] Fix links for versions 6.0.1 - 6.0.2 --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0067a5b..078586c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -374,7 +374,9 @@ Version 5 is a complete code rewrite. [Unreleased]: https://github.com/thephpleague/oauth2-server/compare/v7.0.0...HEAD [7.0.0]: https://github.com/thephpleague/oauth2-server/compare/v6.1.1...v7.0.0 [6.1.1]: https://github.com/thephpleague/oauth2-server/compare/v6.0.0...v6.1.1 -[6.1.0]: https://github.com/thephpleague/oauth2-server/compare/v6.0.0...v6.1.0 +[6.1.0]: https://github.com/thephpleague/oauth2-server/compare/v6.0.2...v6.1.0 +[6.0.2]: https://github.com/thephpleague/oauth2-server/compare/v6.0.1...v6.0.2 +[6.0.1]: https://github.com/thephpleague/oauth2-server/compare/v6.0.0...v6.0.1 [6.0.0]: https://github.com/thephpleague/oauth2-server/compare/v5.1.4...v6.0.0 [5.1.4]: https://github.com/thephpleague/oauth2-server/compare/v5.1.3...v5.1.4 [5.1.3]: https://github.com/thephpleague/oauth2-server/compare/v5.1.2...v5.1.3 From 4f68d2a5f2c2012ab68f7294d02420752bc4542f Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 18 Feb 2018 15:51:41 +0000 Subject: [PATCH 146/191] Fix release tags in changelog --- CHANGELOG.md | 100 +++++++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 078586c0..08c40982 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -371,53 +371,53 @@ Version 5 is a complete code rewrite. - First major release -[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/v7.0.0...HEAD -[7.0.0]: https://github.com/thephpleague/oauth2-server/compare/v6.1.1...v7.0.0 -[6.1.1]: https://github.com/thephpleague/oauth2-server/compare/v6.0.0...v6.1.1 -[6.1.0]: https://github.com/thephpleague/oauth2-server/compare/v6.0.2...v6.1.0 -[6.0.2]: https://github.com/thephpleague/oauth2-server/compare/v6.0.1...v6.0.2 -[6.0.1]: https://github.com/thephpleague/oauth2-server/compare/v6.0.0...v6.0.1 -[6.0.0]: https://github.com/thephpleague/oauth2-server/compare/v5.1.4...v6.0.0 -[5.1.4]: https://github.com/thephpleague/oauth2-server/compare/v5.1.3...v5.1.4 -[5.1.3]: https://github.com/thephpleague/oauth2-server/compare/v5.1.2...v5.1.3 -[5.1.2]: https://github.com/thephpleague/oauth2-server/compare/v5.1.1...v5.1.2 -[5.1.1]: https://github.com/thephpleague/oauth2-server/compare/v5.1.0...v5.1.1 -[5.1.0]: https://github.com/thephpleague/oauth2-server/compare/v5.0.2...v5.1.0 -[5.0.3]: https://github.com/thephpleague/oauth2-server/compare/v5.0.3...v5.0.2 -[5.0.2]: https://github.com/thephpleague/oauth2-server/compare/v5.0.1...v5.0.2 -[5.0.1]: https://github.com/thephpleague/oauth2-server/compare/v5.0.0...v5.0.1 -[5.0.0]: https://github.com/thephpleague/oauth2-server/compare/v5.0.0-RC2...v5.0.0 -[5.0.0-RC2]: https://github.com/thephpleague/oauth2-server/compare/v5.0.0-RC1...v5.0.0-RC2 -[5.0.0-RC1]: https://github.com/thephpleague/oauth2-server/compare/v4.1.5...v5.0.0-RC1 -[4.1.5]: https://github.com/thephpleague/oauth2-server/compare/v4.1.4...v4.1.5 -[4.1.4]: https://github.com/thephpleague/oauth2-server/compare/v4.1.3...v4.1.4 -[4.1.3]: https://github.com/thephpleague/oauth2-server/compare/v4.1.2...v4.1.3 -[4.1.2]: https://github.com/thephpleague/oauth2-server/compare/v4.1.1...v4.1.2 -[4.1.1]: https://github.com/thephpleague/oauth2-server/compare/v4.0.0...v4.1.1 -[4.1.0]: https://github.com/thephpleague/oauth2-server/compare/v4.0.5...v4.1.0 -[4.0.5]: https://github.com/thephpleague/oauth2-server/compare/v4.0.4...v4.0.5 -[4.0.4]: https://github.com/thephpleague/oauth2-server/compare/v4.0.3...v4.0.4 -[4.0.3]: https://github.com/thephpleague/oauth2-server/compare/v4.0.2...v4.0.3 -[4.0.2]: https://github.com/thephpleague/oauth2-server/compare/v4.0.1...v4.0.2 -[4.0.1]: https://github.com/thephpleague/oauth2-server/compare/v4.0.0...v4.0.1 -[4.0.0]: https://github.com/thephpleague/oauth2-server/compare/v3.2.0...v4.0.0 -[3.2.0]: https://github.com/thephpleague/oauth2-server/compare/v3.1.2...v3.2.0 -[3.1.2]: https://github.com/thephpleague/oauth2-server/compare/v3.1.1...v3.1.2 -[3.1.1]: https://github.com/thephpleague/oauth2-server/compare/v3.1.0...v3.1.1 -[3.1.0]: https://github.com/thephpleague/oauth2-server/compare/v3.0.1...v3.1.0 -[3.0.1]: https://github.com/thephpleague/oauth2-server/compare/v3.0.0...v3.0.1 -[3.0.0]: https://github.com/thephpleague/oauth2-server/compare/v2.1.1...v3.0.0 -[2.1.1]: https://github.com/thephpleague/oauth2-server/compare/v2.1.0...v2.1.1 -[2.1.0]: https://github.com/thephpleague/oauth2-server/compare/v2.0.5...v2.1.0 -[2.0.5]: https://github.com/thephpleague/oauth2-server/compare/v2.0.4...v2.0.5 -[2.0.4]: https://github.com/thephpleague/oauth2-server/compare/v2.0.3...v2.0.4 -[2.0.3]: https://github.com/thephpleague/oauth2-server/compare/v2.0.2...v2.0.3 -[2.0.2]: https://github.com/thephpleague/oauth2-server/compare/v2.0.0...v2.0.2 -[2.0.0]: https://github.com/thephpleague/oauth2-server/compare/v1.0.8...v2.0.0 -[1.0.8]: https://github.com/thephpleague/oauth2-server/compare/v1.0.7...v1.0.8 -[1.0.7]: https://github.com/thephpleague/oauth2-server/compare/v1.0.6...v1.0.7 -[1.0.6]: https://github.com/thephpleague/oauth2-server/compare/v1.0.5...v1.0.6 -[1.0.5]: https://github.com/thephpleague/oauth2-server/compare/v1.0.3...v1.0.5 -[1.0.3]: https://github.com/thephpleague/oauth2-server/compare/v1.0.2...v1.0.3 -[1.0.2]: https://github.com/thephpleague/oauth2-server/compare/v1.0.1...v1.0.2 -[1.0.1]: https://github.com/thephpleague/oauth2-server/compare/v1.0.0...v1.0.1 +[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...HEAD +[7.0.0]: https://github.com/thephpleague/oauth2-server/compare/6.1.1...7.0.0 +[6.1.1]: https://github.com/thephpleague/oauth2-server/compare/6.0.0...6.1.1 +[6.1.0]: https://github.com/thephpleague/oauth2-server/compare/6.0.2...6.1.0 +[6.0.2]: https://github.com/thephpleague/oauth2-server/compare/6.0.1...6.0.2 +[6.0.1]: https://github.com/thephpleague/oauth2-server/compare/6.0.0...6.0.1 +[6.0.0]: https://github.com/thephpleague/oauth2-server/compare/5.1.4...6.0.0 +[5.1.4]: https://github.com/thephpleague/oauth2-server/compare/5.1.3...5.1.4 +[5.1.3]: https://github.com/thephpleague/oauth2-server/compare/5.1.2...5.1.3 +[5.1.2]: https://github.com/thephpleague/oauth2-server/compare/5.1.1...5.1.2 +[5.1.1]: https://github.com/thephpleague/oauth2-server/compare/5.1.0...5.1.1 +[5.1.0]: https://github.com/thephpleague/oauth2-server/compare/5.0.2...5.1.0 +[5.0.3]: https://github.com/thephpleague/oauth2-server/compare/5.0.3...5.0.2 +[5.0.2]: https://github.com/thephpleague/oauth2-server/compare/5.0.1...5.0.2 +[5.0.1]: https://github.com/thephpleague/oauth2-server/compare/5.0.0...5.0.1 +[5.0.0]: https://github.com/thephpleague/oauth2-server/compare/5.0.0-RC2...5.0.0 +[5.0.0-RC2]: https://github.com/thephpleague/oauth2-server/compare/5.0.0-RC1...5.0.0-RC2 +[5.0.0-RC1]: https://github.com/thephpleague/oauth2-server/compare/4.1.5...5.0.0-RC1 +[4.1.5]: https://github.com/thephpleague/oauth2-server/compare/4.1.4...4.1.5 +[4.1.4]: https://github.com/thephpleague/oauth2-server/compare/4.1.3...4.1.4 +[4.1.3]: https://github.com/thephpleague/oauth2-server/compare/4.1.2...4.1.3 +[4.1.2]: https://github.com/thephpleague/oauth2-server/compare/4.1.1...4.1.2 +[4.1.1]: https://github.com/thephpleague/oauth2-server/compare/4.0.0...4.1.1 +[4.1.0]: https://github.com/thephpleague/oauth2-server/compare/4.0.5...4.1.0 +[4.0.5]: https://github.com/thephpleague/oauth2-server/compare/4.0.4...4.0.5 +[4.0.4]: https://github.com/thephpleague/oauth2-server/compare/4.0.3...4.0.4 +[4.0.3]: https://github.com/thephpleague/oauth2-server/compare/4.0.2...4.0.3 +[4.0.2]: https://github.com/thephpleague/oauth2-server/compare/4.0.1...4.0.2 +[4.0.1]: https://github.com/thephpleague/oauth2-server/compare/4.0.0...4.0.1 +[4.0.0]: https://github.com/thephpleague/oauth2-server/compare/3.2.0...4.0.0 +[3.2.0]: https://github.com/thephpleague/oauth2-server/compare/3.1.2...3.2.0 +[3.1.2]: https://github.com/thephpleague/oauth2-server/compare/3.1.1...3.1.2 +[3.1.1]: https://github.com/thephpleague/oauth2-server/compare/3.1.0...3.1.1 +[3.1.0]: https://github.com/thephpleague/oauth2-server/compare/3.0.1...3.1.0 +[3.0.1]: https://github.com/thephpleague/oauth2-server/compare/3.0.0...3.0.1 +[3.0.0]: https://github.com/thephpleague/oauth2-server/compare/2.1.1...3.0.0 +[2.1.1]: https://github.com/thephpleague/oauth2-server/compare/2.1.0...2.1.1 +[2.1.0]: https://github.com/thephpleague/oauth2-server/compare/2.0.5...2.1.0 +[2.0.5]: https://github.com/thephpleague/oauth2-server/compare/2.0.4...2.0.5 +[2.0.4]: https://github.com/thephpleague/oauth2-server/compare/2.0.3...2.0.4 +[2.0.3]: https://github.com/thephpleague/oauth2-server/compare/2.0.2...2.0.3 +[2.0.2]: https://github.com/thephpleague/oauth2-server/compare/2.0.0...2.0.2 +[2.0.0]: https://github.com/thephpleague/oauth2-server/compare/1.0.8...2.0.0 +[1.0.8]: https://github.com/thephpleague/oauth2-server/compare/1.0.7...1.0.8 +[1.0.7]: https://github.com/thephpleague/oauth2-server/compare/1.0.6...1.0.7 +[1.0.6]: https://github.com/thephpleague/oauth2-server/compare/1.0.5...1.0.6 +[1.0.5]: https://github.com/thephpleague/oauth2-server/compare/1.0.3...1.0.5 +[1.0.3]: https://github.com/thephpleague/oauth2-server/compare/1.0.2...1.0.3 +[1.0.2]: https://github.com/thephpleague/oauth2-server/compare/1.0.1...1.0.2 +[1.0.1]: https://github.com/thephpleague/oauth2-server/compare/1.0.0...1.0.1 From 143afc9561ecb417d6560c1bd5331f5ee83d7f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= Date: Sun, 18 Feb 2018 21:17:32 +0100 Subject: [PATCH 147/191] PHPStan level 7 --- .travis.yml | 2 +- README.md | 2 +- phpstan.neon | 5 +++ src/Entities/AuthCodeEntityInterface.php | 4 +-- src/Entities/Traits/AuthCodeTrait.php | 4 +-- src/Grant/AbstractGrant.php | 2 +- src/RequestTypes/AuthorizationRequest.php | 6 ++-- tests/PHPStan/AbstractGrantExtension.php | 39 +++++++++++++++++++++++ 8 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 tests/PHPStan/AbstractGrantExtension.php diff --git a/.travis.yml b/.travis.yml index be2759a8..f900228a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ install: script: - vendor/bin/phpunit --coverage-clover=coverage.clover - - vendor/bin/phpstan analyse -l 6 -c phpstan.neon src tests + - vendor/bin/phpstan analyse -l 7 -c phpstan.neon src tests after_script: - wget https://scrutinizer-ci.com/ocular.phar diff --git a/README.md b/README.md index b5326742..83fba1fb 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ The library uses [PHPUnit](https://phpunit.de/) for unit tests and [PHPStan](htt ``` vendor/bin/phpunit -vendor/bin/phpstan analyse -l 6 -c phpstan.neon src tests +vendor/bin/phpstan analyse -l 7 -c phpstan.neon src tests ``` ## Continous Integration diff --git a/phpstan.neon b/phpstan.neon index 88c21d40..5cd9d80d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,3 +3,8 @@ includes: - vendor/phpstan/phpstan-phpunit/rules.neon - vendor/phpstan/phpstan-phpunit/strictRules.neon - vendor/phpstan/phpstan-strict-rules/rules.neon +services: + - + class: LeagueTests\PHPStan\AbstractGrantExtension + tags: + - phpstan.broker.dynamicMethodReturnTypeExtension diff --git a/src/Entities/AuthCodeEntityInterface.php b/src/Entities/AuthCodeEntityInterface.php index e71aa2c8..f1033af1 100644 --- a/src/Entities/AuthCodeEntityInterface.php +++ b/src/Entities/AuthCodeEntityInterface.php @@ -12,12 +12,12 @@ namespace League\OAuth2\Server\Entities; interface AuthCodeEntityInterface extends TokenInterface { /** - * @return string + * @return string|null */ public function getRedirectUri(); /** - * @param string $uri + * @param string|null $uri */ public function setRedirectUri($uri); } diff --git a/src/Entities/Traits/AuthCodeTrait.php b/src/Entities/Traits/AuthCodeTrait.php index 5bb9e306..7863443e 100644 --- a/src/Entities/Traits/AuthCodeTrait.php +++ b/src/Entities/Traits/AuthCodeTrait.php @@ -17,7 +17,7 @@ trait AuthCodeTrait protected $redirectUri; /** - * @return string + * @return string|null */ public function getRedirectUri() { @@ -25,7 +25,7 @@ trait AuthCodeTrait } /** - * @param string $uri + * @param string|null $uri */ public function setRedirectUri($uri) { diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 304ba99b..53b2853d 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -386,7 +386,7 @@ abstract class AbstractGrant implements GrantTypeInterface * @param \DateInterval $authCodeTTL * @param ClientEntityInterface $client * @param string $userIdentifier - * @param string $redirectUri + * @param string|null $redirectUri * @param ScopeEntityInterface[] $scopes * * @throws OAuthServerException diff --git a/src/RequestTypes/AuthorizationRequest.php b/src/RequestTypes/AuthorizationRequest.php index ce5a0034..150c920b 100644 --- a/src/RequestTypes/AuthorizationRequest.php +++ b/src/RequestTypes/AuthorizationRequest.php @@ -60,7 +60,7 @@ class AuthorizationRequest /** * The state parameter on the authorization request * - * @var string + * @var string|null */ protected $state; @@ -175,7 +175,7 @@ class AuthorizationRequest } /** - * @return string + * @return string|null */ public function getState() { @@ -183,7 +183,7 @@ class AuthorizationRequest } /** - * @param string $state + * @param string|null $state */ public function setState($state) { diff --git a/tests/PHPStan/AbstractGrantExtension.php b/tests/PHPStan/AbstractGrantExtension.php new file mode 100644 index 00000000..51d77776 --- /dev/null +++ b/tests/PHPStan/AbstractGrantExtension.php @@ -0,0 +1,39 @@ +getName(), [ + 'getRequestParameter', + 'getQueryStringParameter', + 'getCookieParameter', + ], true); + } + + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + { + return TypeCombinator::union(...[ + new StringType(), + isset($methodCall->args[2]) ? $scope->getType($methodCall->args[2]->value) : new NullType(), + ]); + } +} From 28e1418f64a114a066eda23c5846353f30399aaf Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 18 Feb 2018 20:29:37 +0000 Subject: [PATCH 148/191] Change to use correct release date for version 7 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08c40982..69c1f47e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] -## [7.0.0] - released 2018-02-17 +## [7.0.0] - released 2018-02-18 ### Added - Use PHPStan for static analysis of code (PR #848) From 6700b113a8604f03101bc7935841a80863334c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20B=C5=82aszczyk?= Date: Fri, 23 Feb 2018 17:48:51 +0100 Subject: [PATCH 149/191] Add new event types: access_token_issued and refresh_token_issued. --- src/Grant/AuthCodeGrant.php | 4 ++++ src/Grant/ClientCredentialsGrant.php | 4 ++++ src/Grant/PasswordGrant.php | 4 ++++ src/Grant/RefreshTokenGrant.php | 4 ++++ src/RequestEvent.php | 3 +++ 5 files changed, 19 insertions(+) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index d1669b2f..20d5041b 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -176,6 +176,10 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $authCodePayload->user_id, $scopes); $refreshToken = $this->issueRefreshToken($accessToken); + // Send events to emitter + $this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request)); + $this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request)); + // Inject tokens into response type $responseType->setAccessToken($accessToken); $responseType->setRefreshToken($refreshToken); diff --git a/src/Grant/ClientCredentialsGrant.php b/src/Grant/ClientCredentialsGrant.php index ed157aaf..026ce5e5 100644 --- a/src/Grant/ClientCredentialsGrant.php +++ b/src/Grant/ClientCredentialsGrant.php @@ -11,6 +11,7 @@ namespace League\OAuth2\Server\Grant; +use League\OAuth2\Server\RequestEvent; use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface; use Psr\Http\Message\ServerRequestInterface; @@ -37,6 +38,9 @@ class ClientCredentialsGrant extends AbstractGrant // Issue and persist access token $accessToken = $this->issueAccessToken($accessTokenTTL, $client, null, $finalizedScopes); + // Send event to emitter + $this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request)); + // Inject access token into response type $responseType->setAccessToken($accessToken); diff --git a/src/Grant/PasswordGrant.php b/src/Grant/PasswordGrant.php index cfd7e9fe..1d00998b 100644 --- a/src/Grant/PasswordGrant.php +++ b/src/Grant/PasswordGrant.php @@ -59,6 +59,10 @@ class PasswordGrant extends AbstractGrant $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes); $refreshToken = $this->issueRefreshToken($accessToken); + // Send events to emitter + $this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request)); + $this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request)); + // Inject tokens into response $responseType->setAccessToken($accessToken); $responseType->setRefreshToken($refreshToken); diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php index f8e022b4..519954be 100644 --- a/src/Grant/RefreshTokenGrant.php +++ b/src/Grant/RefreshTokenGrant.php @@ -65,6 +65,10 @@ class RefreshTokenGrant extends AbstractGrant $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $oldRefreshToken['user_id'], $scopes); $refreshToken = $this->issueRefreshToken($accessToken); + // Send events to emitter + $this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request)); + $this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request)); + // Inject tokens into response $responseType->setAccessToken($accessToken); $responseType->setRefreshToken($refreshToken); diff --git a/src/RequestEvent.php b/src/RequestEvent.php index 1558e11f..b1ca3f6b 100644 --- a/src/RequestEvent.php +++ b/src/RequestEvent.php @@ -18,6 +18,9 @@ class RequestEvent extends Event const USER_AUTHENTICATION_FAILED = 'user.authentication.failed'; const REFRESH_TOKEN_CLIENT_FAILED = 'refresh_token.client.failed'; + const REFRESH_TOKEN_ISSUED = 'refresh_token.issued'; + const ACCESS_TOKEN_ISSUED = 'access_token.issued'; + /** * @var ServerRequestInterface */ From 99e42f6f257a7d865a04898a7591fb7f20209917 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 26 Feb 2018 12:38:31 +0000 Subject: [PATCH 150/191] Remove paragonie/random_compat Removing paragonie/random_compat as no longer supporting PHP 5.x branches --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 8e7fd7e6..48a95701 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,6 @@ "ext-openssl": "*", "league/event": "^2.1", "lcobucci/jwt": "^3.2.2", - "paragonie/random_compat": "^2.0", "psr/http-message": "^1.0.1", "defuse/php-encryption": "^2.1" }, From e24964af07e5848d8908cc3df094907edbca9acd Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 26 Feb 2018 12:57:11 +0000 Subject: [PATCH 151/191] Update changelog Add removal of paragone/random_compat to changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69c1f47e..04e90401 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] +### Removed +- Remove paragone/random_compat from dependencies + ## [7.0.0] - released 2018-02-18 ### Added From 6723aadfe88c3d05e8eb72a772c34974be3ee906 Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Mon, 26 Feb 2018 15:56:28 +0000 Subject: [PATCH 152/191] Fix #837 Unifies how we fetch the client_id from the request and allows us to throw a more appropriate exception when the client_id parameter is missing. Improves the test method for this validation by checking the culpable method in this particular case. The test was missing this by calling the wrong method. --- src/Grant/AuthCodeGrant.php | 32 ++++++++++++++++++++++--------- tests/Grant/AuthCodeGrantTest.php | 2 +- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index d1669b2f..c7c7e8c9 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -196,6 +196,27 @@ class AuthCodeGrant extends AbstractAuthorizeGrant return 'authorization_code'; } + /** + * Fetch the client_id parameter from the query string. + * + * @return string + * @throws OAuthServerException + */ + protected function getClientIdFromRequest($request) + { + $clientId = $this->getQueryStringParameter( + 'client_id', + $request, + $this->getServerParameter('PHP_AUTH_USER', $request) + ); + + if (is_null($clientId)) { + throw OAuthServerException::invalidRequest('client_id'); + } + + return $clientId; + } + /** * {@inheritdoc} */ @@ -204,7 +225,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant return ( array_key_exists('response_type', $request->getQueryParams()) && $request->getQueryParams()['response_type'] === 'code' - && isset($request->getQueryParams()['client_id']) + && null !== $this->getClientIdFromRequest($request) ); } @@ -213,14 +234,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant */ public function validateAuthorizationRequest(ServerRequestInterface $request) { - $clientId = $this->getQueryStringParameter( - 'client_id', - $request, - $this->getServerParameter('PHP_AUTH_USER', $request) - ); - if (is_null($clientId)) { - throw OAuthServerException::invalidRequest('client_id'); - } + $clientId = $this->getClientIdFromRequest($request); $client = $this->clientRepository->getClientEntity( $clientId, diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 6a319234..e23bb06b 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -335,7 +335,7 @@ class AuthCodeGrantTest extends TestCase ] ); - $grant->validateAuthorizationRequest($request); + $grant->canRespondToAuthorizationRequest($request); } /** From 009c109716cf58cb0c35b24fbf4cd2f89a25e0ac Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Mon, 26 Feb 2018 16:04:48 +0000 Subject: [PATCH 153/191] TravisCI fix for PHPStan --- 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 c7c7e8c9..08c78c8a 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -199,7 +199,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant /** * Fetch the client_id parameter from the query string. * - * @return string + * @return string|null * @throws OAuthServerException */ protected function getClientIdFromRequest($request) From 62e06b7d3a5c7a1b468373ad8f982102f5233f98 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 26 Feb 2018 19:51:03 +0000 Subject: [PATCH 154/191] Removing Yoda condition Removed Yoda condition from code base --- 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 08c78c8a..424834cd 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -225,7 +225,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant return ( array_key_exists('response_type', $request->getQueryParams()) && $request->getQueryParams()['response_type'] === 'code' - && null !== $this->getClientIdFromRequest($request) + && $this->getClientIdFromRequest($request) !== null ); } From 2fdd6ce494ce780b92985b9d668c689e494e285b Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 26 Feb 2018 20:07:02 +0000 Subject: [PATCH 155/191] Add change for access and refresh token emitters --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04e90401..80a56b56 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] ### Removed +- Added event emitters for issued access and refresh tokens (PR #860) - Remove paragone/random_compat from dependencies ## [7.0.0] - released 2018-02-18 From e3266cb50a8da17e7b20ddeed115e489793e84b2 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 26 Feb 2018 20:08:02 +0000 Subject: [PATCH 156/191] Fix changelog categorisation --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80a56b56..0de8ea90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] -### Removed +### Added - Added event emitters for issued access and refresh tokens (PR #860) + +### Removed - Remove paragone/random_compat from dependencies ## [7.0.0] - released 2018-02-18 From c9b07f386cf1c9454bc024906952e76f9398b2c9 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 28 Feb 2018 20:01:01 +0000 Subject: [PATCH 157/191] Fix StyleCI issues and remove phpdoc order from StyleCI --- .styleci.yml | 1 - src/CryptTrait.php | 4 ++-- tests/Utils/CryptTraitTest.php | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.styleci.yml b/.styleci.yml index 6caf80c5..d3498157 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -29,7 +29,6 @@ enabled: - phpdoc_inline_tag - phpdoc_no_access - phpdoc_no_simplified_null_return - - phpdoc_order - phpdoc_property - phpdoc_scalar - phpdoc_separation diff --git a/src/CryptTrait.php b/src/CryptTrait.php index c8713ff3..be6f5a03 100644 --- a/src/CryptTrait.php +++ b/src/CryptTrait.php @@ -33,7 +33,7 @@ trait CryptTrait protected function encrypt($unencryptedData) { try { - if($this->encryptionKey instanceof Key) { + if ($this->encryptionKey instanceof Key) { return Crypto::encrypt($unencryptedData, $this->encryptionKey); } else { return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey); @@ -55,7 +55,7 @@ trait CryptTrait protected function decrypt($encryptedData) { try { - if($this->encryptionKey instanceof Key) { + if ($this->encryptionKey instanceof Key) { return Crypto::decrypt($encryptedData, $this->encryptionKey); } else { return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey); diff --git a/tests/Utils/CryptTraitTest.php b/tests/Utils/CryptTraitTest.php index e0954508..6b0d592b 100644 --- a/tests/Utils/CryptTraitTest.php +++ b/tests/Utils/CryptTraitTest.php @@ -24,8 +24,8 @@ class CryptTraitTest extends TestCase return $this->encryptDecrypt($cryptStub); } - protected function encryptDecrypt(CryptTraitStub $cryptStub) { - + protected function encryptDecrypt(CryptTraitStub $cryptStub) + { $payload = 'alex loves whisky'; $encrypted = $cryptStub->doEncrypt($payload); $plainText = $cryptStub->doDecrypt($encrypted); From a56acc8dd09bc91c66a6ea2b0610128ec764863c Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 28 Feb 2018 20:33:19 +0000 Subject: [PATCH 158/191] Minor code tidy up --- src/AuthorizationServer.php | 5 +++-- src/CryptTrait.php | 8 +++---- src/Grant/GrantTypeInterface.php | 3 ++- src/ResponseTypes/ResponseTypeInterface.php | 3 ++- tests/Utils/CryptTraitTest.php | 23 +++++++++++++-------- 5 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 51cf6905..f1e96146 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -9,6 +9,7 @@ namespace League\OAuth2\Server; +use Defuse\Crypto\Key; use League\Event\EmitterAwareInterface; use League\Event\EmitterAwareTrait; use League\OAuth2\Server\Exception\OAuthServerException; @@ -68,7 +69,7 @@ class AuthorizationServer implements EmitterAwareInterface private $scopeRepository; /** - * @var string|\Defuse\Crypto\Key + * @var string|Key */ private $encryptionKey; @@ -84,7 +85,7 @@ class AuthorizationServer implements EmitterAwareInterface * @param AccessTokenRepositoryInterface $accessTokenRepository * @param ScopeRepositoryInterface $scopeRepository * @param CryptKey|string $privateKey - * @param string|\Defuse\Crypto\Key $encryptionKey + * @param string|Key $encryptionKey * @param null|ResponseTypeInterface $responseType */ public function __construct( diff --git a/src/CryptTrait.php b/src/CryptTrait.php index be6f5a03..c9a6d7a6 100644 --- a/src/CryptTrait.php +++ b/src/CryptTrait.php @@ -35,9 +35,9 @@ trait CryptTrait try { if ($this->encryptionKey instanceof Key) { return Crypto::encrypt($unencryptedData, $this->encryptionKey); - } else { - return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey); } + + return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey); } catch (\Exception $e) { throw new \LogicException($e->getMessage()); } @@ -57,9 +57,9 @@ trait CryptTrait try { if ($this->encryptionKey instanceof Key) { return Crypto::decrypt($encryptedData, $this->encryptionKey); - } else { - return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey); } + + return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey); } catch (\Exception $e) { throw new \LogicException($e->getMessage()); } diff --git a/src/Grant/GrantTypeInterface.php b/src/Grant/GrantTypeInterface.php index 56f1ee99..2aee367f 100644 --- a/src/Grant/GrantTypeInterface.php +++ b/src/Grant/GrantTypeInterface.php @@ -11,6 +11,7 @@ namespace League\OAuth2\Server\Grant; +use Defuse\Crypto\Key; use League\Event\EmitterAwareInterface; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; @@ -136,7 +137,7 @@ interface GrantTypeInterface extends EmitterAwareInterface /** * Set the encryption key * - * @param string|\Defuse\Crypto\Key|null $key + * @param string|Key|null $key */ public function setEncryptionKey($key = null); } diff --git a/src/ResponseTypes/ResponseTypeInterface.php b/src/ResponseTypes/ResponseTypeInterface.php index f76eaa6f..5eddd607 100644 --- a/src/ResponseTypes/ResponseTypeInterface.php +++ b/src/ResponseTypes/ResponseTypeInterface.php @@ -11,6 +11,7 @@ namespace League\OAuth2\Server\ResponseTypes; +use Defuse\Crypto\Key; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; use Psr\Http\Message\ResponseInterface; @@ -37,7 +38,7 @@ interface ResponseTypeInterface /** * Set the encryption key * - * @param string|\Defuse\Crypto\Key|null $key + * @param string|Key|null $key */ public function setEncryptionKey($key = null); } diff --git a/tests/Utils/CryptTraitTest.php b/tests/Utils/CryptTraitTest.php index 6b0d592b..c517cec2 100644 --- a/tests/Utils/CryptTraitTest.php +++ b/tests/Utils/CryptTraitTest.php @@ -8,27 +8,32 @@ use PHPUnit\Framework\TestCase; class CryptTraitTest extends TestCase { + protected $cryptStub; + + protected function setUp() + { + $this->cryptStub = new CryptTraitStub(); + } + public function testEncryptDecryptWithPassword() { - $cryptStub = new CryptTraitStub(); - $cryptStub->setEncryptionKey(base64_encode(random_bytes(36))); + $this->cryptStub->setEncryptionKey(base64_encode(random_bytes(36))); - return $this->encryptDecrypt($cryptStub); + $this->encryptDecrypt(); } public function testEncryptDecryptWithKey() { - $cryptStub = new CryptTraitStub(); - $cryptStub->setEncryptionKey(Key::createNewRandomKey()); + $this->cryptStub->setEncryptionKey(Key::createNewRandomKey()); - return $this->encryptDecrypt($cryptStub); + $this->encryptDecrypt(); } - protected function encryptDecrypt(CryptTraitStub $cryptStub) + private function encryptDecrypt() { $payload = 'alex loves whisky'; - $encrypted = $cryptStub->doEncrypt($payload); - $plainText = $cryptStub->doDecrypt($encrypted); + $encrypted = $this->cryptStub->doEncrypt($payload); + $plainText = $this->cryptStub->doDecrypt($encrypted); $this->assertNotEquals($payload, $encrypted); $this->assertEquals($payload, $plainText); From bec0de16bb3da27536592004cf99822b02bfc480 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 28 Feb 2018 21:00:30 +0000 Subject: [PATCH 159/191] Update Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0de8ea90..045e54e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - Added event emitters for issued access and refresh tokens (PR #860) +- Can now use Defuse\Crypto\Key for encryption/decryption of keys which is faster than the Cryto class (PR #812) ### Removed - Remove paragone/random_compat from dependencies From 8f1bf887928c781882c8a00a479019ed55bf611b Mon Sep 17 00:00:00 2001 From: Steve Rhoades Date: Sat, 17 Mar 2018 09:30:14 -0700 Subject: [PATCH 160/191] Fix fatal error caused by ClientRepositoryInterface change --- 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 8d4b5219..eaa4e5c2 100644 --- a/examples/src/Repositories/ClientRepository.php +++ b/examples/src/Repositories/ClientRepository.php @@ -17,7 +17,7 @@ class ClientRepository implements ClientRepositoryInterface /** * {@inheritdoc} */ - public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true) + public function getClientEntity($clientIdentifier, $grantType = null, $clientSecret = null, $mustValidateSecret = true) { $clients = [ 'myawesomeapp' => [ From c8b44ff5c74dd1c6a3c4f8ddab6e84f7484beed4 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Fri, 20 Apr 2018 18:22:07 +0100 Subject: [PATCH 161/191] Revert fix for client ID exception --- src/Grant/AuthCodeGrant.php | 32 +++++++++---------------------- tests/Grant/AuthCodeGrantTest.php | 2 +- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index a3ab8a32..20d5041b 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -200,27 +200,6 @@ class AuthCodeGrant extends AbstractAuthorizeGrant return 'authorization_code'; } - /** - * Fetch the client_id parameter from the query string. - * - * @return string|null - * @throws OAuthServerException - */ - protected function getClientIdFromRequest($request) - { - $clientId = $this->getQueryStringParameter( - 'client_id', - $request, - $this->getServerParameter('PHP_AUTH_USER', $request) - ); - - if (is_null($clientId)) { - throw OAuthServerException::invalidRequest('client_id'); - } - - return $clientId; - } - /** * {@inheritdoc} */ @@ -229,7 +208,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant return ( array_key_exists('response_type', $request->getQueryParams()) && $request->getQueryParams()['response_type'] === 'code' - && $this->getClientIdFromRequest($request) !== null + && isset($request->getQueryParams()['client_id']) ); } @@ -238,7 +217,14 @@ class AuthCodeGrant extends AbstractAuthorizeGrant */ public function validateAuthorizationRequest(ServerRequestInterface $request) { - $clientId = $this->getClientIdFromRequest($request); + $clientId = $this->getQueryStringParameter( + 'client_id', + $request, + $this->getServerParameter('PHP_AUTH_USER', $request) + ); + if (is_null($clientId)) { + throw OAuthServerException::invalidRequest('client_id'); + } $client = $this->clientRepository->getClientEntity( $clientId, diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index e23bb06b..6a319234 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -335,7 +335,7 @@ class AuthCodeGrantTest extends TestCase ] ); - $grant->canRespondToAuthorizationRequest($request); + $grant->validateAuthorizationRequest($request); } /** From 9febc32e14b9ad868de5c30de7a03ed0368f3d55 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Fri, 20 Apr 2018 18:27:47 +0100 Subject: [PATCH 162/191] Add spacing around logical blocks --- src/Grant/AuthCodeGrant.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 20d5041b..2f33a97c 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -222,6 +222,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $request, $this->getServerParameter('PHP_AUTH_USER', $request) ); + if (is_null($clientId)) { throw OAuthServerException::invalidRequest('client_id'); } @@ -239,6 +240,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant } $redirectUri = $this->getQueryStringParameter('redirect_uri', $request); + if ($redirectUri !== null) { if ( is_string($client->getRedirectUri()) @@ -284,6 +286,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant } $codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain'); + if (in_array($codeChallengeMethod, ['plain', 'S256'], true) === false) { throw OAuthServerException::invalidRequest( 'code_challenge_method', From 6991777ff3ba4e4a0e6c1f5fe2f18277700dbcdd Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Fri, 20 Apr 2018 18:33:46 +0100 Subject: [PATCH 163/191] Fix blank line spacing issue --- 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 2f33a97c..daeb7849 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -286,7 +286,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant } $codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain'); - + if (in_array($codeChallengeMethod, ['plain', 'S256'], true) === false) { throw OAuthServerException::invalidRequest( 'code_challenge_method', From 8a619e5c1e76be858360c5e09fa9fb90a5b26f3f Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 21 Apr 2018 18:07:38 +0100 Subject: [PATCH 164/191] Change hint so it applies to both the auth and access token requests --- 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 b67bcf03..65fe861e 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -92,7 +92,7 @@ class OAuthServerException extends \Exception public static function unsupportedGrantType() { $errorMessage = 'The authorization grant type is not supported by the authorization server.'; - $hint = 'Check the `grant_type` parameter'; + $hint = 'Check that all required parameters have been provided'; return new static($errorMessage, 2, 'unsupported_grant_type', 400, $hint); } From 80bc291c51320196c8c448e23f09fe5895da2aa5 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 21 Apr 2018 21:29:21 +0100 Subject: [PATCH 165/191] Added null checks before calling set functions --- src/Entities/AuthCodeEntityInterface.php | 2 +- src/Grant/AbstractGrant.php | 5 ++++- src/Grant/AuthCodeGrant.php | 6 +++++- src/Grant/ImplicitGrant.php | 6 +++++- src/RequestTypes/AuthorizationRequest.php | 2 +- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Entities/AuthCodeEntityInterface.php b/src/Entities/AuthCodeEntityInterface.php index f1033af1..00e939c2 100644 --- a/src/Entities/AuthCodeEntityInterface.php +++ b/src/Entities/AuthCodeEntityInterface.php @@ -17,7 +17,7 @@ interface AuthCodeEntityInterface extends TokenInterface public function getRedirectUri(); /** - * @param string|null $uri + * @param string $uri */ public function setRedirectUri($uri); } diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 53b2853d..79a1ac47 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -407,7 +407,10 @@ abstract class AbstractGrant implements GrantTypeInterface $authCode->setExpiryDateTime((new \DateTime())->add($authCodeTTL)); $authCode->setClient($client); $authCode->setUserIdentifier($userIdentifier); - $authCode->setRedirectUri($redirectUri); + + if ($redirectUri !== null) { + $authCode->setRedirectUri($redirectUri); + } foreach ($scopes as $scope) { $authCode->addScope($scope); diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index d1669b2f..81152338 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -270,7 +270,11 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $authorizationRequest->setGrantTypeId($this->getIdentifier()); $authorizationRequest->setClient($client); $authorizationRequest->setRedirectUri($redirectUri); - $authorizationRequest->setState($stateParameter); + + if ($stateParameter !== null) { + $authorizationRequest->setState($stateParameter); + } + $authorizationRequest->setScopes($scopes); if ($this->enableCodeExchangeProof === true) { diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 19e3e684..80c34869 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -177,7 +177,11 @@ class ImplicitGrant extends AbstractAuthorizeGrant $authorizationRequest->setGrantTypeId($this->getIdentifier()); $authorizationRequest->setClient($client); $authorizationRequest->setRedirectUri($redirectUri); - $authorizationRequest->setState($stateParameter); + + if ($stateParameter !== null) { + $authorizationRequest->setState($stateParameter); + } + $authorizationRequest->setScopes($finalizedScopes); return $authorizationRequest; diff --git a/src/RequestTypes/AuthorizationRequest.php b/src/RequestTypes/AuthorizationRequest.php index 150c920b..5faa45d4 100644 --- a/src/RequestTypes/AuthorizationRequest.php +++ b/src/RequestTypes/AuthorizationRequest.php @@ -183,7 +183,7 @@ class AuthorizationRequest } /** - * @param string|null $state + * @param string $state */ public function setState($state) { From 27323b5c9afc382daf07baf18bbb3a2c02d639ea Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 21 Apr 2018 21:31:48 +0100 Subject: [PATCH 166/191] Fix spacing issue --- src/Grant/ImplicitGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 80c34869..b4157883 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -179,7 +179,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant $authorizationRequest->setRedirectUri($redirectUri); if ($stateParameter !== null) { - $authorizationRequest->setState($stateParameter); + $authorizationRequest->setState($stateParameter); } $authorizationRequest->setScopes($finalizedScopes); From 05f5d9003445b7d1dbdc2658df3debc64ea4ad70 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 21 Apr 2018 21:39:28 +0100 Subject: [PATCH 167/191] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 045e54e3..fcf87c3a 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] ### Added +- Upgrade PHPStan checks to level 7 (PR #893) - Added event emitters for issued access and refresh tokens (PR #860) - Can now use Defuse\Crypto\Key for encryption/decryption of keys which is faster than the Cryto class (PR #812) From c94ec122aaa53865c552960ff0c46ffb3953b89a Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 21 Apr 2018 21:41:48 +0100 Subject: [PATCH 168/191] Fix PR number for changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcf87c3a..9b6e7290 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] ### Added -- Upgrade PHPStan checks to level 7 (PR #893) +- Upgrade PHPStan checks to level 7 (PR #856) - Added event emitters for issued access and refresh tokens (PR #860) - Can now use Defuse\Crypto\Key for encryption/decryption of keys which is faster than the Cryto class (PR #812) From 242dd4dcfed16c0c1f1f2b1d71329c6f5b5a2231 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 21 Apr 2018 21:51:25 +0100 Subject: [PATCH 169/191] Fix docblock --- src/Entities/Traits/AuthCodeTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entities/Traits/AuthCodeTrait.php b/src/Entities/Traits/AuthCodeTrait.php index 7863443e..542d3678 100644 --- a/src/Entities/Traits/AuthCodeTrait.php +++ b/src/Entities/Traits/AuthCodeTrait.php @@ -25,7 +25,7 @@ trait AuthCodeTrait } /** - * @param string|null $uri + * @param string $uri */ public function setRedirectUri($uri) { From f9a91a79c236535e5f2ecc4c281c1cae13736152 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 21 Apr 2018 21:59:47 +0100 Subject: [PATCH 170/191] Put upgrade of PHPStan under changed category --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b6e7290..f5afa0ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] -### Added +### Changed - Upgrade PHPStan checks to level 7 (PR #856) + +### Added - Added event emitters for issued access and refresh tokens (PR #860) - Can now use Defuse\Crypto\Key for encryption/decryption of keys which is faster than the Cryto class (PR #812) From a5a51fad953b96983dd6541c88249c45b0d7d249 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 21 Apr 2018 22:05:08 +0100 Subject: [PATCH 171/191] Add PR 893 to the changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5afa0ec..2bf9502a 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 +- Changed hint for unsupportedGrantType exception so it no longer references the grant type parameter which isn't always expected (PR #893) - Upgrade PHPStan checks to level 7 (PR #856) ### Added From 38f26d522979f455c0d310211c8ce64659e80015 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 21 Apr 2018 22:07:30 +0100 Subject: [PATCH 172/191] Update our code of conduct --- CONDUCT.md | 75 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/CONDUCT.md b/CONDUCT.md index ea00abfc..45904feb 100644 --- a/CONDUCT.md +++ b/CONDUCT.md @@ -1,22 +1,73 @@ -# Contributor Code of Conduct +# Contributor Covenant Code of Conduct -As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. +## Our Pledge -We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, +religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery -* Personal attacks -* Trolling or insulting/derogatory comments +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment -* Publishing other's private information, such as physical or electronic addresses, without explicit permission -* Other unethical or unprofessional conduct. +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. +## Our Responsibilities -This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community in a direct capacity. Personal views, beliefs and values of individuals do not necessarily reflect those of the organisation or affiliated individuals and organisations. +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. -This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) \ No newline at end of file +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [INSERT EMAIL ADDRESS]. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org From aa5cc5eac7f2c92e3623e3d841174b66d3147b37 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 21 Apr 2018 22:22:53 +0100 Subject: [PATCH 173/191] Adding contact email address --- CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONDUCT.md b/CONDUCT.md index 45904feb..87ac4cc6 100644 --- a/CONDUCT.md +++ b/CONDUCT.md @@ -55,7 +55,7 @@ further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at [INSERT EMAIL ADDRESS]. All +reported by contacting the project team at andrew@noexceptions.io. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. From 8b40858509644edd49399444e9935c21fb36d9fd Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sat, 21 Apr 2018 22:25:51 +0100 Subject: [PATCH 174/191] Rename CONDUCT.md to CODE_OF_CONDUCT.md --- CONDUCT.md => CODE_OF_CONDUCT.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CONDUCT.md => CODE_OF_CONDUCT.md (100%) diff --git a/CONDUCT.md b/CODE_OF_CONDUCT.md similarity index 100% rename from CONDUCT.md rename to CODE_OF_CONDUCT.md From e38ea4ab343cbb6b407564ae660508b1f0cde037 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 22 Apr 2018 12:58:05 +0100 Subject: [PATCH 175/191] Update code of conduct link in the readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 83fba1fb..61d149a0 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ See the [project changelog](https://github.com/thephpleague/oauth2-server/blob/m ## Contributing -Contributions are always welcome. Please see [CONTRIBUTING.md](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) and [CONDUCT.md](https://github.com/thephpleague/oauth2-server/blob/master/CONDUCT.md) for details. +Contributions are always welcome. Please see [CONTRIBUTING.md](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](https://github.com/thephpleague/oauth2-server/blob/master/CODE_OF_CONDUCT.md) for details. ## Support From bd47b58f81aa2bf1d2b191e4c5b61685a26bb5b7 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 22 Apr 2018 15:16:23 +0100 Subject: [PATCH 176/191] Update changelog for version 7.1.0 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bf9502a..bece8e44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +## [7.1.0] - released 2018-04-22 + ### Changed - Changed hint for unsupportedGrantType exception so it no longer references the grant type parameter which isn't always expected (PR #893) - Upgrade PHPStan checks to level 7 (PR #856) @@ -383,6 +385,7 @@ Version 5 is a complete code rewrite. - First major release [Unreleased]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...HEAD +[7.1.0]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...7.1.0 [7.0.0]: https://github.com/thephpleague/oauth2-server/compare/6.1.1...7.0.0 [6.1.1]: https://github.com/thephpleague/oauth2-server/compare/6.0.0...6.1.1 [6.1.0]: https://github.com/thephpleague/oauth2-server/compare/6.0.2...6.1.0 From 35c6f28aefcd3dddf780c6b781057516cd1f101d Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Thu, 3 May 2018 17:06:27 +0100 Subject: [PATCH 177/191] Add drupal integration to the readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 61d149a0..30db07a6 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ We use [Travis CI](https://travis-ci.org/), [Scrutinizer](https://scrutinizer-ci ## Community Integrations +* [Drupal](https://www.drupal.org/project/simple_oauth) * [Laravel Passport](https://github.com/laravel/passport) * [OAuth 2 Server for CakePHP 3](https://github.com/uafrica/oauth-server) From 28276cb6884868de518e7c12feae7f0e28b57e6d Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Wed, 16 May 2018 13:36:29 +0100 Subject: [PATCH 178/191] Add PSR-7 to the requirements in the readme This fixes issue #640 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 30db07a6..e4d90f46 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ The following versions of PHP are supported: The `openssl` extension is also required. +All HTTP messages passed to the server should be [PSR-7 compliant](https://www.php-fig.org/psr/psr-7/). This ensures interoperability between other packages and frameworks. + ## Installation ``` From a3d4f583eda467012ac928056d2a84d05bbe520d Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Thu, 17 May 2018 13:06:03 +0100 Subject: [PATCH 179/191] Fix #745 --- src/Exception/OAuthServerException.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 65fe861e..24179ce5 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -294,14 +294,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') { - $authScheme = 'Basic'; - if (array_key_exists('HTTP_AUTHORIZATION', $_SERVER) !== false - && strpos($_SERVER['HTTP_AUTHORIZATION'], 'Bearer') === 0 - ) { - $authScheme = 'Bearer'; - } - $headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"'; + if ($this->errorType === 'invalid_client' && array_key_exists('HTTP_AUTHORIZATION', $_SERVER) !== false) { + $headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"'; } // @codeCoverageIgnoreEnd return $headers; From 8a25e0a01b1367c748d024019ddd34d48861171e Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Thu, 17 May 2018 13:12:32 +0100 Subject: [PATCH 180/191] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bece8e44..dcac3bfd 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] +### Fixed +- No longer set a WWW-Authenticate header for invalid clients if the client did not send an Authorization header in the original request + ## [7.1.0] - released 2018-04-22 ### Changed From 19d782d223710e604e3650666412b8b8be2cc2ba Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Thu, 17 May 2018 13:13:30 +0100 Subject: [PATCH 181/191] Fix alignment --- 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 24179ce5..9b798f76 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -295,7 +295,7 @@ class OAuthServerException extends \Exception // matching the authentication scheme used by the client. // @codeCoverageIgnoreStart if ($this->errorType === 'invalid_client' && array_key_exists('HTTP_AUTHORIZATION', $_SERVER) !== false) { - $headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"'; + $headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"'; } // @codeCoverageIgnoreEnd return $headers; From 3ea0cdc9365d427fe863c94939a2dba952c9f158 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Thu, 17 May 2018 13:19:32 +0100 Subject: [PATCH 182/191] Set authScheme --- src/Exception/OAuthServerException.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 9b798f76..fe615262 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -295,6 +295,8 @@ class OAuthServerException extends \Exception // matching the authentication scheme used by the client. // @codeCoverageIgnoreStart if ($this->errorType === 'invalid_client' && array_key_exists('HTTP_AUTHORIZATION', $_SERVER) !== false) { + $authScheme = strpos($_SERVER['HTTP_AUTHORIZATION'], 'Bearer') === 0 ? 'Bearer' : 'Basic'; + $headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"'; } // @codeCoverageIgnoreEnd From 0242d0c9968e55cb8737470f55882a13f7f8c895 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Thu, 17 May 2018 13:21:39 +0100 Subject: [PATCH 183/191] Remove spaces at end of line --- 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 fe615262..14297e75 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -295,7 +295,7 @@ class OAuthServerException extends \Exception // 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'; + $authScheme = strpos($_SERVER['HTTP_AUTHORIZATION'], 'Bearer') === 0 ? 'Bearer' : 'Basic'; $headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"'; } From 2e3ee60a2ac5ba0a7eb5673884f1e156438ce6a1 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Thu, 17 May 2018 13:27:30 +0100 Subject: [PATCH 184/191] Remove additional whitespace --- 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 14297e75..a62d961d 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -295,7 +295,7 @@ class OAuthServerException extends \Exception // 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'; + $authScheme = strpos($_SERVER['HTTP_AUTHORIZATION'], 'Bearer') === 0 ? 'Bearer' : 'Basic'; $headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"'; } From beec37d95ffd3890fc72ad3dce728c4d31641877 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 21 May 2018 14:58:56 +0100 Subject: [PATCH 185/191] Modify changelog for 7.1.1 release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcac3bfd..6b2cadef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +## [7.1.1] - released 2018-05-21 + ### Fixed - No longer set a WWW-Authenticate header for invalid clients if the client did not send an Authorization header in the original request From fc55621f2017c039ce03dbad815901c8723cf3cb Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 21 May 2018 15:00:06 +0100 Subject: [PATCH 186/191] Add link to 7.1.1 release in changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b2cadef..37474b66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -390,6 +390,7 @@ Version 5 is a complete code rewrite. - First major release [Unreleased]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...HEAD +[7.1.1]: https://github.com/thephpleague/oauth2-server/compare/7.1.0...7.1.1 [7.1.0]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...7.1.0 [7.0.0]: https://github.com/thephpleague/oauth2-server/compare/6.1.1...7.0.0 [6.1.1]: https://github.com/thephpleague/oauth2-server/compare/6.0.0...6.1.1 From 2e47fa7fcad3b207cfbefa0a0e26af00445f3906 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Mon, 21 May 2018 15:01:37 +0100 Subject: [PATCH 187/191] Add PR reference --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37474b66..b3f974a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [7.1.1] - released 2018-05-21 ### Fixed -- No longer set a WWW-Authenticate header for invalid clients if the client did not send an Authorization header in the original request +- No longer set a WWW-Authenticate header for invalid clients if the client did not send an Authorization header in the original request (PR #902) ## [7.1.0] - released 2018-04-22 From 9941a96feba4d4e8d793816194bc4ba1004ee620 Mon Sep 17 00:00:00 2001 From: Martin Dzibela Date: Tue, 22 May 2018 14:13:20 +0200 Subject: [PATCH 188/191] Fix uncaught exception produced by unsigned token --- src/AuthorizationValidators/BearerTokenValidator.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/AuthorizationValidators/BearerTokenValidator.php b/src/AuthorizationValidators/BearerTokenValidator.php index 6f299ce4..2efa3c8e 100644 --- a/src/AuthorizationValidators/BearerTokenValidator.php +++ b/src/AuthorizationValidators/BearerTokenValidator.php @@ -65,8 +65,12 @@ class BearerTokenValidator implements AuthorizationValidatorInterface try { // Attempt to parse and validate the JWT $token = (new Parser())->parse($jwt); - if ($token->verify(new Sha256(), $this->publicKey->getKeyPath()) === false) { - throw OAuthServerException::accessDenied('Access token could not be verified'); + try { + if ($token->verify(new Sha256(), $this->publicKey->getKeyPath()) === false) { + throw OAuthServerException::accessDenied('Access token could not be verified'); + } + } catch (\BadMethodCallException $exception) { + throw OAuthServerException::accessDenied('Access token is not signed'); } // Ensure access token hasn't expired From 02609c37ccdfb781970e57a801ee0b280c2d1780 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Tue, 22 May 2018 18:10:19 +0100 Subject: [PATCH 189/191] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3f974a7..d657b746 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] +### Fixed +- Catch and handle `BadMethodCallException` from the `verify()` method of the JWT token in the `validateAuthorization` method (PR #904) + ## [7.1.1] - released 2018-05-21 ### Fixed From ae4ab26aaf078c3933f2602687d1f19adc3ea682 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Thu, 24 May 2018 12:19:55 +0100 Subject: [PATCH 190/191] Add test for unsigned access token --- .../BearerTokenValidatorTest.php | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 tests/AuthorizationValidators/BearerTokenValidatorTest.php diff --git a/tests/AuthorizationValidators/BearerTokenValidatorTest.php b/tests/AuthorizationValidators/BearerTokenValidatorTest.php new file mode 100644 index 00000000..5690c9a9 --- /dev/null +++ b/tests/AuthorizationValidators/BearerTokenValidatorTest.php @@ -0,0 +1,41 @@ +getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + + $bearerTokenValidator = new BearerTokenValidator($accessTokenRepositoryMock); + $bearerTokenValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $unsignedJwt = (new Builder()) + ->setAudience('client-id') + ->setId('token-id', true) + ->setIssuedAt(time()) + ->setNotBefore(time()) + ->setExpiration(time()) + ->setSubject('user-id') + ->set('scopes', 'scope1 scope2 scope3 scope4') + ->getToken(); + + $request = new ServerRequest(); + $request = $request->withHeader('authorization', sprintf('Bearer %s', $unsignedJwt)); + + $bearerTokenValidator->validateAuthorization($request); + } +} From 72ead2e3ce39fbe0055b7b24c572e858193e52eb Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Thu, 24 May 2018 12:23:26 +0100 Subject: [PATCH 191/191] Fix unused use statement --- tests/AuthorizationValidators/BearerTokenValidatorTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/AuthorizationValidators/BearerTokenValidatorTest.php b/tests/AuthorizationValidators/BearerTokenValidatorTest.php index 5690c9a9..801846cb 100644 --- a/tests/AuthorizationValidators/BearerTokenValidatorTest.php +++ b/tests/AuthorizationValidators/BearerTokenValidatorTest.php @@ -3,7 +3,6 @@ namespace LeagueTests\AuthorizationValidators; use Lcobucci\JWT\Builder; -use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;