From b2840474fde964ca55772f84300fafc20e104ed0 Mon Sep 17 00:00:00 2001 From: filecage Date: Fri, 8 Mar 2019 18:16:16 +0100 Subject: [PATCH 01/12] AbstractGrant no longer tries to issue a refresh token if the Repository returned `null` --- src/Grant/AbstractGrant.php | 9 ++++++--- tests/Grant/AbstractGrantTest.php | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 49213331..45269ac7 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -472,16 +472,19 @@ abstract class AbstractGrant implements GrantTypeInterface * @throws OAuthServerException * @throws UniqueTokenIdentifierConstraintViolationException * - * @return RefreshTokenEntityInterface + * @return RefreshTokenEntityInterface|null */ protected function issueRefreshToken(AccessTokenEntityInterface $accessToken) { - $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS; - $refreshToken = $this->refreshTokenRepository->getNewRefreshToken(); + if ($refreshToken === null) { + return null; + } + $refreshToken->setExpiryDateTime((new DateTime())->add($this->refreshTokenTTL)); $refreshToken->setAccessToken($accessToken); + $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS; while ($maxGenerationAttempts-- > 0) { $refreshToken->setIdentifier($this->generateUniqueIdentifier()); try { diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 6266df0a..1ed4a935 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -18,6 +18,7 @@ use LeagueTests\Stubs\AuthCodeEntity; use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\ScopeEntity; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; @@ -346,6 +347,28 @@ class AbstractGrantTest extends TestCase $this->assertEquals($accessToken, $refreshToken->getAccessToken()); } + public function testIssueNullRefreshToken() + { + /** @var RefreshTokenRepositoryInterface|MockObject $refreshTokenRepoMock */ + $refreshTokenRepoMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepoMock + ->expects($this->once()) + ->method('getNewRefreshToken') + ->willReturn(null); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setRefreshTokenTTL(new \DateInterval('PT1M')); + $grantMock->setRefreshTokenRepository($refreshTokenRepoMock); + + $abstractGrantReflection = new \ReflectionClass($grantMock); + $issueRefreshTokenMethod = $abstractGrantReflection->getMethod('issueRefreshToken'); + $issueRefreshTokenMethod->setAccessible(true); + + $accessToken = new AccessTokenEntity(); + $this->assertNull($issueRefreshTokenMethod->invoke($grantMock, $accessToken)); + } + public function testIssueAccessToken() { $accessTokenRepoMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); From 2ea76ca4fdd6f41393d5185208afb4bddea31146 Mon Sep 17 00:00:00 2001 From: filecage Date: Fri, 8 Mar 2019 18:19:16 +0100 Subject: [PATCH 02/12] Adds handling for `null` issued refresh token to Grant implementations --- src/Grant/AuthCodeGrant.php | 16 +++---- src/Grant/PasswordGrant.php | 21 ++++---- src/Grant/RefreshTokenGrant.php | 16 +++---- tests/Grant/AuthCodeGrantTest.php | 69 +++++++++++++++++++++++++++ tests/Grant/PasswordGrantTest.php | 52 ++++++++++++++++++++ tests/Grant/RefreshTokenGrantTest.php | 62 ++++++++++++++++++++++++ 6 files changed, 211 insertions(+), 25 deletions(-) diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 235a6a96..d7dd6b8f 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -145,17 +145,17 @@ class AuthCodeGrant extends AbstractAuthorizeGrant } } - // Issue and persist access + refresh tokens + // Issue and persist new access token $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); + + // Issue and persist new refresh token if given + $refreshToken = $this->issueRefreshToken($accessToken); + if ($refreshToken !== null) { + $this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request)); + $responseType->setRefreshToken($refreshToken); + } // Revoke used auth code $this->authCodeRepository->revokeAuthCode($authCodePayload->auth_code_id); diff --git a/src/Grant/PasswordGrant.php b/src/Grant/PasswordGrant.php index 4b68ad44..6dc08958 100644 --- a/src/Grant/PasswordGrant.php +++ b/src/Grant/PasswordGrant.php @@ -35,7 +35,10 @@ class PasswordGrant extends AbstractGrant RefreshTokenRepositoryInterface $refreshTokenRepository ) { $this->setUserRepository($userRepository); - $this->setRefreshTokenRepository($refreshTokenRepository); + + if ($refreshTokenRepository !== null) { + $this->setRefreshTokenRepository($refreshTokenRepository); + } $this->refreshTokenTTL = new DateInterval('P1M'); } @@ -56,17 +59,17 @@ class PasswordGrant extends AbstractGrant // Finalize the requested scopes $finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier()); - // Issue and persist new tokens + // Issue and persist new access token $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); + + // Issue and persist new refresh token if given + $refreshToken = $this->issueRefreshToken($accessToken); + if ($refreshToken !== null) { + $this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request)); + $responseType->setRefreshToken($refreshToken); + } return $responseType; } diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php index 2181af88..24932441 100644 --- a/src/Grant/RefreshTokenGrant.php +++ b/src/Grant/RefreshTokenGrant.php @@ -63,17 +63,17 @@ class RefreshTokenGrant extends AbstractGrant $this->accessTokenRepository->revokeAccessToken($oldRefreshToken['access_token_id']); $this->refreshTokenRepository->revokeRefreshToken($oldRefreshToken['refresh_token_id']); - // Issue and persist new tokens + // Issue and persist new access token $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); + + // Issue and persist new refresh token if given + $refreshToken = $this->issueRefreshToken($accessToken); + if ($refreshToken !== null) { + $this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request)); + $responseType->setRefreshToken($refreshToken); + } return $responseType; } diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 6a319234..7b81f9fb 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -22,6 +22,7 @@ use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; @@ -644,6 +645,74 @@ class AuthCodeGrantTest extends TestCase $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } + public function testRespondToAccessTokenRequestNullRefreshToken() + { + $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(); + + /** @var RefreshTokenRepositoryInterface|MockObject $refreshTokenRepositoryMock */ + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(null); + + $grant = new AuthCodeGrant( + $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), + $refreshTokenRepositoryMock, + new \DateInterval('PT10M') + ); + + $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setEncryptionKey($this->cryptStub->getKey()); + + $request = new ServerRequest( + [], + [], + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code' => $this->cryptStub->doEncrypt( + json_encode( + [ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => 'http://foo/bar', + ] + ) + ), + ] + ); + + /** @var StubResponseType $response */ + $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + + $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); + $this->assertNull($response->getRefreshToken()); + } + public function testRespondToAccessTokenRequestCodeChallengePlain() { $client = new ClientEntity(); diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index 2ee700f8..e3f537ae 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -16,6 +16,8 @@ use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; +use OAuth2ServerExamples\Repositories\AccessTokenRepository; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; @@ -78,6 +80,56 @@ class PasswordGrantTest extends TestCase $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); } + public function testRespondToRequestNullRefreshToken () + { + /** @var ClientRepositoryInterface|MockObject $clientRepositoryMock */ + $client = new ClientEntity(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + /** @var AccessTokenRepositoryInterface|MockObject $accessTokenRepositoryMock */ + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + /** @var UserRepositoryInterface|MockObject $userRepositoryMock */ + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + $userEntity = new UserEntity(); + $userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity); + + /** @var ScopeRepositoryInterface|MockObject $scopeRepositoryMock */ + $scope = new ScopeEntity(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); + + /** @var RefreshTokenRepositoryInterface|MockObject $refreshTokenRepositoryMock */ + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(null); + + $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'username' => 'foo', + 'password' => 'bar', + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + + $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); + $this->assertNull($responseType->getRefreshToken()); + } + /** * @expectedException \League\OAuth2\Server\Exception\OAuthServerException */ diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index 89598115..fc7a2c26 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -16,6 +16,7 @@ use LeagueTests\Stubs\CryptTraitStub; use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; @@ -94,6 +95,67 @@ class RefreshTokenGrantTest extends TestCase $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); } + public function testRespondToRequestNullRefreshToken() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + + /** @var ClientRepositoryInterface|MockObject $clientRepositoryMock */ + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $scopeEntity = new ScopeEntity(); + $scopeEntity->setIdentifier('foo'); + + /** @var ScopeRepositoryInterface|MockObject $scopeRepositoryMock */ + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); + + /** @var AccessTokenRepositoryInterface|MockObject $accessTokenRepositoryMock */ + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->expects($this->once())->method('persistNewAccessToken')->willReturnSelf(); + + /** @var RefreshTokenRepositoryInterface|MockObject $refreshTokenRepositoryMock */ + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(null); + $refreshTokenRepositoryMock->expects($this->never())->method('persistNewRefreshToken'); + + $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $oldRefreshToken = $this->cryptStub->doEncrypt( + json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ) + ); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + 'scopes' => ['foo'], + ]); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + + $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); + $this->assertNull($responseType->getRefreshToken()); + } + public function testRespondToReducedScopes() { $client = new ClientEntity(); From 66d4ce6de80a570194e67cc13a42cef07a5c7641 Mon Sep 17 00:00:00 2001 From: filecage Date: Fri, 8 Mar 2019 18:21:55 +0100 Subject: [PATCH 03/12] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b75288d..c839d29f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Changed +- Refresh Tokens are now optional (#649) + ## [7.3.2] - released 2018-11-21 ### Fixed From aa5bbe5f06a1455ac5f4c2a6d515fb3a8ab4b3a0 Mon Sep 17 00:00:00 2001 From: filecage Date: Mon, 11 Mar 2019 23:26:35 +0100 Subject: [PATCH 04/12] boyscout: style CI tweaks --- src/Grant/AbstractGrant.php | 2 +- tests/Grant/PasswordGrantTest.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 45269ac7..90c75d51 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -478,7 +478,7 @@ abstract class AbstractGrant implements GrantTypeInterface { $refreshToken = $this->refreshTokenRepository->getNewRefreshToken(); if ($refreshToken === null) { - return null; + return; } $refreshToken->setExpiryDateTime((new DateTime())->add($this->refreshTokenTTL)); diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index e3f537ae..5f8b7e0c 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -16,7 +16,6 @@ use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; -use OAuth2ServerExamples\Repositories\AccessTokenRepository; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; @@ -80,7 +79,7 @@ class PasswordGrantTest extends TestCase $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); } - public function testRespondToRequestNullRefreshToken () + public function testRespondToRequestNullRefreshToken() { /** @var ClientRepositoryInterface|MockObject $clientRepositoryMock */ $client = new ClientEntity(); From ebf78132d759014df1c4f492bf65e9c16895e3c5 Mon Sep 17 00:00:00 2001 From: filecage Date: Mon, 11 Mar 2019 23:28:20 +0100 Subject: [PATCH 05/12] `refreshTokenRepository` parameter can not be null, condition is obsolete --- src/Grant/PasswordGrant.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Grant/PasswordGrant.php b/src/Grant/PasswordGrant.php index 6dc08958..124bb4f8 100644 --- a/src/Grant/PasswordGrant.php +++ b/src/Grant/PasswordGrant.php @@ -35,10 +35,7 @@ class PasswordGrant extends AbstractGrant RefreshTokenRepositoryInterface $refreshTokenRepository ) { $this->setUserRepository($userRepository); - - if ($refreshTokenRepository !== null) { - $this->setRefreshTokenRepository($refreshTokenRepository); - } + $this->setRefreshTokenRepository($refreshTokenRepository); $this->refreshTokenTTL = new DateInterval('P1M'); } From 64f0d89fadc80b409a7a106eb7f948d32d24f070 Mon Sep 17 00:00:00 2001 From: filecage Date: Mon, 11 Mar 2019 23:28:47 +0100 Subject: [PATCH 06/12] `getNewRefreshToken()` can also return `NULL` --- src/Repositories/RefreshTokenRepositoryInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Repositories/RefreshTokenRepositoryInterface.php b/src/Repositories/RefreshTokenRepositoryInterface.php index 0c0697bf..a769cf6d 100644 --- a/src/Repositories/RefreshTokenRepositoryInterface.php +++ b/src/Repositories/RefreshTokenRepositoryInterface.php @@ -20,7 +20,7 @@ interface RefreshTokenRepositoryInterface extends RepositoryInterface /** * Creates a new refresh token * - * @return RefreshTokenEntityInterface + * @return RefreshTokenEntityInterface|null */ public function getNewRefreshToken(); From 0742d5150cabc760a052473843d773e9fc472051 Mon Sep 17 00:00:00 2001 From: filecage Date: Wed, 13 Mar 2019 10:08:57 +0100 Subject: [PATCH 07/12] explicit is better than implicit :) --- src/Grant/AbstractGrant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 90c75d51..45269ac7 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -478,7 +478,7 @@ abstract class AbstractGrant implements GrantTypeInterface { $refreshToken = $this->refreshTokenRepository->getNewRefreshToken(); if ($refreshToken === null) { - return; + return null; } $refreshToken->setExpiryDateTime((new DateTime())->add($this->refreshTokenTTL)); From 6f6820f629c26b09e7c2cff41feb23d2505d027e Mon Sep 17 00:00:00 2001 From: filecage Date: Sat, 16 Mar 2019 13:12:34 +0100 Subject: [PATCH 08/12] removes @var hints the @var hints make PHP stan fail together with PHPUnit 6.3 --- tests/Grant/AbstractGrantTest.php | 1 - tests/Grant/AuthCodeGrantTest.php | 1 - tests/Grant/PasswordGrantTest.php | 5 ----- tests/Grant/RefreshTokenGrantTest.php | 4 ---- 4 files changed, 11 deletions(-) diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 1ed4a935..acb519bb 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -349,7 +349,6 @@ class AbstractGrantTest extends TestCase public function testIssueNullRefreshToken() { - /** @var RefreshTokenRepositoryInterface|MockObject $refreshTokenRepoMock */ $refreshTokenRepoMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepoMock ->expects($this->once()) diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 7b81f9fb..0abea0c9 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -662,7 +662,6 @@ class AuthCodeGrantTest extends TestCase $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); - /** @var RefreshTokenRepositoryInterface|MockObject $refreshTokenRepositoryMock */ $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(null); diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index 5f8b7e0c..5a9fad0b 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -81,28 +81,23 @@ class PasswordGrantTest extends TestCase public function testRespondToRequestNullRefreshToken() { - /** @var ClientRepositoryInterface|MockObject $clientRepositoryMock */ $client = new ClientEntity(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - /** @var AccessTokenRepositoryInterface|MockObject $accessTokenRepositoryMock */ $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); - /** @var UserRepositoryInterface|MockObject $userRepositoryMock */ $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); $userEntity = new UserEntity(); $userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity); - /** @var ScopeRepositoryInterface|MockObject $scopeRepositoryMock */ $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); - /** @var RefreshTokenRepositoryInterface|MockObject $refreshTokenRepositoryMock */ $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(null); diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index fc7a2c26..6743e270 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -100,23 +100,19 @@ class RefreshTokenGrantTest extends TestCase $client = new ClientEntity(); $client->setIdentifier('foo'); - /** @var ClientRepositoryInterface|MockObject $clientRepositoryMock */ $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); $scopeEntity = new ScopeEntity(); $scopeEntity->setIdentifier('foo'); - /** @var ScopeRepositoryInterface|MockObject $scopeRepositoryMock */ $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); - /** @var AccessTokenRepositoryInterface|MockObject $accessTokenRepositoryMock */ $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); $accessTokenRepositoryMock->expects($this->once())->method('persistNewAccessToken')->willReturnSelf(); - /** @var RefreshTokenRepositoryInterface|MockObject $refreshTokenRepositoryMock */ $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(null); $refreshTokenRepositoryMock->expects($this->never())->method('persistNewRefreshToken'); From 8cf39fd9cd44c1b6d9c8b98b605cbbf6718ae530 Mon Sep 17 00:00:00 2001 From: filecage Date: Sat, 16 Mar 2019 13:15:38 +0100 Subject: [PATCH 09/12] applies style CI diff --- tests/Grant/AbstractGrantTest.php | 1 - tests/Grant/AuthCodeGrantTest.php | 1 - tests/Grant/PasswordGrantTest.php | 1 - tests/Grant/RefreshTokenGrantTest.php | 1 - 4 files changed, 4 deletions(-) diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index acb519bb..75d3bcae 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -18,7 +18,6 @@ use LeagueTests\Stubs\AuthCodeEntity; use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\ScopeEntity; -use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 0abea0c9..04fb60c2 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -22,7 +22,6 @@ use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; -use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index 5a9fad0b..28183a98 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -16,7 +16,6 @@ use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; -use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index 6743e270..8f205361 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -16,7 +16,6 @@ use LeagueTests\Stubs\CryptTraitStub; use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; -use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; From 9bc7f6c8c540ae24961aee9909757de68c014036 Mon Sep 17 00:00:00 2001 From: filecage Date: Mon, 29 Apr 2019 19:13:26 +0200 Subject: [PATCH 10/12] removing simplified_null_return --- .styleci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.styleci.yml b/.styleci.yml index a77e823b..41f0dd0a 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -41,7 +41,6 @@ enabled: - print_to_echo - short_array_syntax - short_scalar_cast - - simplified_null_return - single_quote - spaces_cast - standardize_not_equal From 9236e842d9d47510653f5269b61a139bb292351b Mon Sep 17 00:00:00 2001 From: sephster Date: Sun, 5 May 2019 08:58:34 +0100 Subject: [PATCH 11/12] Clarify changelog message --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fc6905c..b1539ef8 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] ### Changed -- Refresh Tokens are now optional (#649) +- RefreshTokenRepository can now return null, allowing refresh tokens to be optional. (PR #649) ## [7.3.3] - released 2019-03-29 ### Added From 86869eafbb442ed9423850ffdd1bf366c9d613e1 Mon Sep 17 00:00:00 2001 From: sephster Date: Sun, 5 May 2019 09:03:13 +0100 Subject: [PATCH 12/12] Add whitespace around control blocks --- src/Grant/AbstractGrant.php | 2 ++ src/Grant/AuthCodeGrant.php | 1 + src/Grant/PasswordGrant.php | 1 + src/Grant/RefreshTokenGrant.php | 1 + 4 files changed, 5 insertions(+) diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 45269ac7..6d7ff18d 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -477,6 +477,7 @@ abstract class AbstractGrant implements GrantTypeInterface protected function issueRefreshToken(AccessTokenEntityInterface $accessToken) { $refreshToken = $this->refreshTokenRepository->getNewRefreshToken(); + if ($refreshToken === null) { return null; } @@ -485,6 +486,7 @@ abstract class AbstractGrant implements GrantTypeInterface $refreshToken->setAccessToken($accessToken); $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS; + while ($maxGenerationAttempts-- > 0) { $refreshToken->setIdentifier($this->generateUniqueIdentifier()); try { diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index d7dd6b8f..350fdde5 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -152,6 +152,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant // Issue and persist new refresh token if given $refreshToken = $this->issueRefreshToken($accessToken); + if ($refreshToken !== null) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request)); $responseType->setRefreshToken($refreshToken); diff --git a/src/Grant/PasswordGrant.php b/src/Grant/PasswordGrant.php index 124bb4f8..cb53ccd8 100644 --- a/src/Grant/PasswordGrant.php +++ b/src/Grant/PasswordGrant.php @@ -63,6 +63,7 @@ class PasswordGrant extends AbstractGrant // Issue and persist new refresh token if given $refreshToken = $this->issueRefreshToken($accessToken); + if ($refreshToken !== null) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request)); $responseType->setRefreshToken($refreshToken); diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php index 24932441..d0794075 100644 --- a/src/Grant/RefreshTokenGrant.php +++ b/src/Grant/RefreshTokenGrant.php @@ -70,6 +70,7 @@ class RefreshTokenGrant extends AbstractGrant // Issue and persist new refresh token if given $refreshToken = $this->issueRefreshToken($accessToken); + if ($refreshToken !== null) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request)); $responseType->setRefreshToken($refreshToken);