From 655a4b27159f1d88ccd2cfb42687da65742b04e7 Mon Sep 17 00:00:00 2001 From: Luca Degasperi Date: Thu, 30 Jun 2016 16:49:47 +0200 Subject: [PATCH 01/50] 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 d73b15ae320af678fb7e73a42bcf255e7248e729 Mon Sep 17 00:00:00 2001 From: Stanimir Stoyanov Date: Mon, 20 Mar 2017 14:52:35 +0200 Subject: [PATCH 02/50] 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 2482630221bef4347e37c02fe5119a18f9ceda99 Mon Sep 17 00:00:00 2001 From: Erick Torres Date: Fri, 16 Jun 2017 12:02:34 -0500 Subject: [PATCH 03/50] 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 04/50] 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 05/50] 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 06/50] 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 07/50] 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 e2f9b73df3d3a54283363212db0f0c5f0a583734 Mon Sep 17 00:00:00 2001 From: Erick Torres Date: Fri, 7 Jul 2017 12:19:11 -0500 Subject: [PATCH 08/50] 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 09/50] 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 4563685375f12de71a4fd938a00adabf55ad3c40 Mon Sep 17 00:00:00 2001 From: Ron Arts Date: Mon, 30 Oct 2017 16:21:17 +0100 Subject: [PATCH 10/50] 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 11/50] 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 f79d3f27cf85c093de3dce41880c4d1116b81d7d Mon Sep 17 00:00:00 2001 From: Ron Arts Date: Tue, 31 Oct 2017 10:14:46 +0100 Subject: [PATCH 12/50] 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 5b79b40df9c9a057602c17f6dace2bc88d201b85 Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Fri, 29 Dec 2017 12:25:39 +0000 Subject: [PATCH 13/50] 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 14/50] 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 15/50] 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 16/50] 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 17/50] 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 18/50] 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 a3289c6ecb3c0b0df2b448c482613fe0a4a2d3f9 Mon Sep 17 00:00:00 2001 From: knewzen <772608204@qq.com> Date: Fri, 5 Jan 2018 01:08:14 +0800 Subject: [PATCH 19/50] 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 20/50] 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 21/50] 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 22/50] 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 23/50] 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 24/50] 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 25/50] 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 26/50] 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 27/50] 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 28/50] 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 29/50] 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 30/50] 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 31/50] 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 32/50] 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 33/50] 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 34/50] 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 35/50] 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 36/50] 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 37/50] 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 38/50] 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 39/50] 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 40/50] 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 41/50] 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 28e1418f64a114a066eda23c5846353f30399aaf Mon Sep 17 00:00:00 2001 From: Andrew Millington Date: Sun, 18 Feb 2018 20:29:37 +0000 Subject: [PATCH 42/50] 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 43/50] 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 44/50] 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 45/50] 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 46/50] 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 47/50] 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 48/50] 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 49/50] 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 50/50] 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