diff --git a/src/Entities/ClientEntityInterface.php b/src/Entities/ClientEntityInterface.php index f3838b11c..b0050ecea 100644 --- a/src/Entities/ClientEntityInterface.php +++ b/src/Entities/ClientEntityInterface.php @@ -38,4 +38,11 @@ public function getRedirectUri(): string|array; * Returns true if the client is confidential. */ public function isConfidential(): bool; + + /* + * Returns true if the client supports the given grant type. + * + * To be added in a future major release. + */ + // public function supportsGrantType(string $grantType): bool; } diff --git a/src/Entities/Traits/ClientTrait.php b/src/Entities/Traits/ClientTrait.php index b179cfac4..ada53fa5a 100644 --- a/src/Entities/Traits/ClientTrait.php +++ b/src/Entities/Traits/ClientTrait.php @@ -52,4 +52,12 @@ public function isConfidential(): bool { return $this->isConfidential; } + + /** + * Returns true if the client supports the given grant type. + */ + public function supportsGrantType(string $grantType): bool + { + return true; + } } diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 24a38d3fe..df8b58a3d 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -252,8 +252,17 @@ public static function slowDown(string $hint = '', ?Throwable $previous = null): } /** + * Unauthorized client error. + */ + public static function unauthorizedClient(?string $hint = null): static { - return $this->errorType; + return new static( + 'The authenticated client is not authorized to use this authorization grant type.', + 14, + 'unauthorized_client', + 400, + $hint + ); } /** diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 5ab81ff77..7c27e95c5 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -151,18 +151,18 @@ protected function validateClient(ServerRequestInterface $request): ClientEntity { [$clientId, $clientSecret] = $this->getClientCredentials($request); - if ($this->clientRepository->validateClient($clientId, $clientSecret, $this->getIdentifier()) === false) { - $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - - throw OAuthServerException::invalidClient($request); - } $client = $this->getClientEntityOrFail($clientId, $request); - // If a redirect URI is provided ensure it matches what is pre-registered - $redirectUri = $this->getRequestParameter('redirect_uri', $request); + if ($client->isConfidential()) { + if ($clientSecret === '') { + throw OAuthServerException::invalidRequest('client_secret'); + } - if ($redirectUri !== null) { - $this->validateRedirectUri($redirectUri, $client, $request); + if ($this->clientRepository->validateClient($clientId, $clientSecret, $this->getIdentifier()) === false) { + $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); + + throw OAuthServerException::invalidClient($request); + } } return $client; @@ -189,9 +189,22 @@ protected function getClientEntityOrFail(string $clientId, ServerRequestInterfac throw OAuthServerException::invalidClient($request); } + if ($this->supportsGrantType($client, $this->getIdentifier()) === false) { + throw OAuthServerException::unauthorizedClient(); + } + return $client; } + /** + * Returns true if the given client is authorized to use the given grant type. + */ + protected function supportsGrantType(ClientEntityInterface $client, string $grantType): bool + { + return method_exists($client, 'supportsGrantType') === false + || $client->supportsGrantType($grantType) === true; + } + /** * Gets the client credentials from the request from the request body or * the Http Basic Authorization header @@ -484,6 +497,10 @@ protected function issueAuthCode( */ protected function issueRefreshToken(AccessTokenEntityInterface $accessToken): ?RefreshTokenEntityInterface { + if ($this->supportsGrantType($accessToken->getClient(), 'refresh_token') === false) { + return null; + } + $refreshToken = $this->refreshTokenRepository->getNewRefreshToken(); if ($refreshToken === null) { diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 9fb5271c3..9075f1144 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -97,14 +97,7 @@ public function respondToAccessTokenRequest( ResponseTypeInterface $responseType, DateInterval $accessTokenTTL ): ResponseTypeInterface { - list($clientId) = $this->getClientCredentials($request); - - $client = $this->getClientEntityOrFail($clientId, $request); - - // Only validate the client if it is confidential - if ($client->isConfidential()) { - $this->validateClient($request); - } + $client = $this->validateClient($request); $encryptedAuthCode = $this->getRequestParameter('code', $request); diff --git a/src/Grant/ClientCredentialsGrant.php b/src/Grant/ClientCredentialsGrant.php index bee6abaa1..a24266c4c 100644 --- a/src/Grant/ClientCredentialsGrant.php +++ b/src/Grant/ClientCredentialsGrant.php @@ -34,9 +34,7 @@ public function respondToAccessTokenRequest( ResponseTypeInterface $responseType, DateInterval $accessTokenTTL ): ResponseTypeInterface { - list($clientId) = $this->getClientCredentials($request); - - $client = $this->getClientEntityOrFail($clientId, $request); + $client = $this->validateClient($request); if (!$client->isConfidential()) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); @@ -44,9 +42,6 @@ public function respondToAccessTokenRequest( throw OAuthServerException::invalidClient($request); } - // Validate request - $this->validateClient($request); - $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); // Finalize the requested scopes diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index adfb880be..5711af103 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -265,84 +265,6 @@ public function testValidateClientInvalidClientSecret(): void $validateClientMethod->invoke($grantMock, $serverRequest, true, true); } - public function testValidateClientInvalidRedirectUri(): void - { - $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); - $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $clientRepositoryMock->method('getClientEntity')->willReturn($client); - - /** @var AbstractGrant $grantMock */ - $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $grantMock->setClientRepository($clientRepositoryMock); - - $abstractGrantReflection = new ReflectionClass($grantMock); - - $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', - 'redirect_uri' => 'http://bar/foo', - ]); - - $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); - $validateClientMethod->setAccessible(true); - - $this->expectException(OAuthServerException::class); - - $validateClientMethod->invoke($grantMock, $serverRequest, true, true); - } - - public function testValidateClientInvalidRedirectUriArray(): void - { - $client = new ClientEntity(); - $client->setRedirectUri(['http://foo/bar']); - $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $clientRepositoryMock->method('getClientEntity')->willReturn($client); - - /** @var AbstractGrant $grantMock */ - $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $grantMock->setClientRepository($clientRepositoryMock); - - $abstractGrantReflection = new ReflectionClass($grantMock); - - $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', - 'redirect_uri' => 'http://bar/foo', - ]); - - $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); - $validateClientMethod->setAccessible(true); - - $this->expectException(OAuthServerException::class); - - $validateClientMethod->invoke($grantMock, $serverRequest, true, true); - } - - public function testValidateClientMalformedRedirectUri(): void - { - $client = new ClientEntity(); - $client->setRedirectUri('http://foo/bar'); - $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $clientRepositoryMock->method('getClientEntity')->willReturn($client); - - /** @var AbstractGrant $grantMock */ - $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $grantMock->setClientRepository($clientRepositoryMock); - - $abstractGrantReflection = new ReflectionClass($grantMock); - - $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', - 'redirect_uri' => ['not', 'a', 'string'], - ]); - - $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); - $validateClientMethod->setAccessible(true); - - $this->expectException(OAuthServerException::class); - - $validateClientMethod->invoke($grantMock, $serverRequest, true, true); - } - public function testValidateClientBadClient(): void { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -398,6 +320,7 @@ public function testIssueRefreshToken(): void $issueRefreshTokenMethod->setAccessible(true); $accessToken = new AccessTokenEntity(); + $accessToken->setClient(new ClientEntity()); /** @var RefreshTokenEntityInterface $refreshToken */ $refreshToken = $issueRefreshTokenMethod->invoke($grantMock, $accessToken); @@ -423,6 +346,34 @@ public function testIssueNullRefreshToken(): void $issueRefreshTokenMethod->setAccessible(true); $accessToken = new AccessTokenEntity(); + $accessToken->setClient(new ClientEntity()); + self::assertNull($issueRefreshTokenMethod->invoke($grantMock, $accessToken)); + } + + public function testIssueNullRefreshTokenUnauthorizedClient(): void + { + $client = $this->getMockBuilder(ClientEntity::class)->getMock(); + $client + ->expects(self::once()) + ->method('supportsGrantType') + ->with('refresh_token') + ->willReturn(false); + + $refreshTokenRepoMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepoMock->expects(self::never())->method('getNewRefreshToken'); + + /** @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(); + $accessToken->setClient($client); + self::assertNull($issueRefreshTokenMethod->invoke($grantMock, $accessToken)); } @@ -576,4 +527,30 @@ public function testCompleteAuthorizationRequest(): void $grantMock->completeAuthorizationRequest(new AuthorizationRequest()); } + + public function testUnauthorizedClient(): void + { + $client = $this->getMockBuilder(ClientEntity::class)->getMock(); + $client->method('supportsGrantType')->willReturn(false); + + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock + ->expects(self::once()) + ->method('getClientEntity') + ->with('foo') + ->willReturn($client); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setClientRepository($clientRepositoryMock); + + $abstractGrantReflection = new ReflectionClass($grantMock); + + $getClientEntityOrFailMethod = $abstractGrantReflection->getMethod('getClientEntityOrFail'); + $getClientEntityOrFailMethod->setAccessible(true); + + $this->expectException(OAuthServerException::class); + + $getClientEntityOrFailMethod->invoke($grantMock, 'foo', new ServerRequest()); + } } diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 390001721..393359cf0 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -616,7 +616,9 @@ public function testRespondToAccessTokenRequest(): void $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -647,6 +649,7 @@ public function testRespondToAccessTokenRequest(): void [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', + 'client_secret' => 'bar', 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( json_encode([ @@ -685,8 +688,11 @@ public function testRespondToAccessTokenRequestWithDefaultRedirectUri(): void $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -717,6 +723,7 @@ public function testRespondToAccessTokenRequestWithDefaultRedirectUri(): void [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', + 'client_secret' => 'bar', 'code' => $this->cryptStub->doEncrypt( json_encode([ 'auth_code_id' => uniqid(), @@ -743,13 +750,16 @@ public function testRespondToAccessTokenRequestUsingHttpBasicAuth(): void $client->setIdentifier('foo'); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn(new ScopeEntity()); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); @@ -806,6 +816,7 @@ public function testRespondToAccessTokenRequestForPublicClient(): void $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -813,7 +824,9 @@ public function testRespondToAccessTokenRequestForPublicClient(): void $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -871,14 +884,18 @@ public function testRespondToAccessTokenRequestNullRefreshToken(): void $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -948,7 +965,9 @@ public function testRespondToAccessTokenRequestCodeChallengePlain(): void $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -980,6 +999,7 @@ public function testRespondToAccessTokenRequestCodeChallengePlain(): void [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', + 'client_secret' => 'bar', 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, 'code' => $this->cryptStub->doEncrypt( @@ -1022,7 +1042,9 @@ public function testRespondToAccessTokenRequestCodeChallengeS256(): void $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -1054,6 +1076,7 @@ public function testRespondToAccessTokenRequestCodeChallengeS256(): void [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', + 'client_secret' => 'bar', 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, 'code' => $this->cryptStub->doEncrypt( @@ -1358,6 +1381,7 @@ public function testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), @@ -1447,6 +1471,7 @@ public function testRespondToAccessTokenRequestExpiredCode(): void $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), @@ -1535,6 +1560,7 @@ public function testRespondToAccessTokenRequestRevokedCode(): void [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', + 'client_secret' => 'bar', 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( json_encode([ @@ -1599,6 +1625,7 @@ public function testRespondToAccessTokenRequestClientMismatch(): void [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', + 'client_secret' => 'bar', 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( json_encode([ @@ -1662,6 +1689,7 @@ public function testRespondToAccessTokenRequestBadCode(): void [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', + 'client_secret' => 'bar', 'redirect_uri' => self::REDIRECT_URI, 'code' => 'badCode', ] @@ -1717,6 +1745,7 @@ public function testRespondToAccessTokenRequestNoEncryptionKey(): void [ 'grant_type' => 'authorization_code', 'client_id' => 'foo', + 'client_secret' => 'bar', 'redirect_uri' => self::REDIRECT_URI, 'code' => 'badCode', ] @@ -1737,7 +1766,6 @@ public function testRespondToAccessTokenRequestBadCodeVerifierPlain(): void $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); - $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -1812,7 +1840,6 @@ public function testRespondToAccessTokenRequestBadCodeVerifierS256(): void $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); - $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -1887,7 +1914,6 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); - $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -1962,7 +1988,6 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); - $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -2037,7 +2062,6 @@ public function testRespondToAccessTokenRequestMissingCodeVerifier(): void $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); - $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -2207,6 +2231,7 @@ public function testRefreshTokenRepositoryUniqueConstraintCheck(): void $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -2214,7 +2239,9 @@ public function testRefreshTokenRepositoryUniqueConstraintCheck(): void $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -2282,6 +2309,7 @@ public function testRefreshTokenRepositoryFailToPersist(): void $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -2289,7 +2317,9 @@ public function testRefreshTokenRepositoryFailToPersist(): void $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -2350,6 +2380,7 @@ public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop(): v $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -2357,7 +2388,9 @@ public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop(): v $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); diff --git a/tests/Grant/DeviceCodeGrantTest.php b/tests/Grant/DeviceCodeGrantTest.php index 4a9cebbd1..c02c1de08 100644 --- a/tests/Grant/DeviceCodeGrantTest.php +++ b/tests/Grant/DeviceCodeGrantTest.php @@ -349,6 +349,7 @@ public function testRespondToAccessTokenRequest(): void ->willReturn($deviceCodeEntity); $accessTokenEntity = new AccessTokenEntity(); + $accessTokenEntity->setClient($client); $accessTokenEntity->addScope($scope); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index 8c60a8c78..5c91c94f9 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -46,7 +46,9 @@ public function testRespondToRequest(): void $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); @@ -91,8 +93,11 @@ public function testRespondToRequestNullRefreshToken(): void $clientRepositoryMock->method('getClientEntity')->willReturn($client); $clientRepositoryMock->method('validateClient')->willReturn(true); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); @@ -167,9 +172,14 @@ public function testRespondToRequestMissingPassword(): void $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn(new ScopeEntity()); + $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); + $grant->setScopeRepository($scopeRepositoryMock); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index b2dbbadd2..1fe1eadab 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -61,7 +61,9 @@ public function testRespondToRequest(): void $scopeRepositoryMock->method('finalizeScopes')->willReturn([$scopeEntity]); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->expects(self::once())->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -125,8 +127,11 @@ public function testRespondToRequestNullRefreshToken(): void $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); $scopeRepositoryMock->method('finalizeScopes')->willReturn([$scopeEntity]); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->expects(self::once())->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -183,7 +188,9 @@ public function testRespondToReducedScopes(): void $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -569,10 +576,12 @@ public function testRespondToRequestFinalizeScopes(): void ->with($scopes, $grant->getIdentifier(), $client) ->willReturn($finalizedScopes); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); $accessTokenRepositoryMock ->method('getNewToken') ->with($client, $finalizedScopes) - ->willReturn(new AccessTokenEntity()); + ->willReturn($accessToken); $oldRefreshToken = json_encode( [ @@ -624,8 +633,11 @@ public function testRevokedRefreshToken(): void $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); $scopeRepositoryMock->method('finalizeScopes')->willReturn([$scopeEntity]); + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); $accessTokenRepositoryMock->expects(self::once())->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -772,7 +784,9 @@ public function testRespondToRequestWithIntUserId(): void $scopeRepositoryMock->method('finalizeScopes')->willReturn([$scopeEntity]); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenEntity = new AccessTokenEntity(); + $accessTokenEntity->setClient($client); + $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessTokenEntity); $accessTokenRepositoryMock->expects(self::once())->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();