From d02437dd7397df13f0aba782e6dbb2cd0e0e867e Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Sun, 21 Feb 2016 18:13:39 +0000 Subject: [PATCH] Improved testing --- src/Exception/OAuthServerException.php | 8 + src/Grant/AbstractGrant.php | 11 +- src/Grant/AuthCodeGrant.php | 23 +- tests/Grant/AuthCodeGrantTest.php | 502 ++++++++++++++++++++++++- 4 files changed, 521 insertions(+), 23 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 951f8a82..af9379d9 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -286,4 +286,12 @@ class OAuthServerException extends \Exception { return $this->httpStatusCode; } + + /** + * @return null|string + */ + public function getHint() + { + return $this->hint; + } } diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 3823047d..407ae4db 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -16,6 +16,7 @@ use League\Event\Event; use League\OAuth2\Server\Entities\AccessTokenEntity; use League\OAuth2\Server\Entities\AuthCodeEntity; use League\OAuth2\Server\Entities\Interfaces\ClientEntityInterface; +use League\OAuth2\Server\Entities\Interfaces\ScopeEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntity; use League\OAuth2\Server\Entities\ScopeEntity; use League\OAuth2\Server\Exception\OAuthServerException; @@ -344,6 +345,11 @@ abstract class AbstractGrant implements GrantTypeInterface $accessToken->setUserIdentifier($userIdentifier); foreach ($scopes as $scope) { + if (is_string($scope)) { + $s = new ScopeEntity(); + $s->setIdentifier($scope); + $scope = $s; + } $accessToken->addScope($scope); } @@ -435,8 +441,7 @@ abstract class AbstractGrant implements GrantTypeInterface */ public function canRespondToRequest(ServerRequestInterface $request) { - return - isset($request->getParsedBody()['grant_type']) - && $request->getParsedBody()['grant_type'] === $this->getIdentifier(); + return isset($request->getParsedBody()['grant_type']) + && $request->getParsedBody()['grant_type'] === $this->getIdentifier(); } } diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 27dbb1c0..05afc7fd 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -61,7 +61,7 @@ class AuthCodeGrant extends AbstractGrant $this->authCodeTTL = $authCodeTTL; $this->refreshTokenTTL = new \DateInterval('P1M'); - $this->pathToLoginTemplate = __DIR__ . '/../ResponseTypes/DefaultTemplates/login_user'; + $this->pathToLoginTemplate = __DIR__ . '/../ResponseTypes/DefaultTemplates/login_user'; if ($pathToLoginTemplate !== null) { $this->pathToLoginTemplate = (substr($pathToLoginTemplate, -4) === '.php') ? substr($pathToLoginTemplate, 0, -4) @@ -108,6 +108,11 @@ class AuthCodeGrant extends AbstractGrant throw OAuthServerException::invalidClient(); } + $redirectUriParameter = $this->getQueryStringParameter('redirect_uri', $request, $client->getRedirectUri()); + if ($redirectUriParameter !== $client->getRedirectUri()) { + throw OAuthServerException::invalidClient(); + } + $scopes = $this->validateScopes($request, $client, $client->getRedirectUri()); $queryString = http_build_query($request->getQueryParams()); $postbackUri = new Uri( @@ -224,6 +229,7 @@ class AuthCodeGrant extends AbstractGrant json_encode( [ 'client_id' => $authCode->getClient()->getIdentifier(), + 'redirect_uri' => $authCode->getRedirectUri(), 'auth_code_id' => $authCode->getIdentifier(), 'scopes' => $authCode->getScopes(), 'user_id' => $authCode->getUserIdentifier(), @@ -258,7 +264,7 @@ class AuthCodeGrant extends AbstractGrant DateInterval $accessTokenTTL ) { // The redirect URI is required in this request - $redirectUri = $this->getQueryStringParameter('redirect_uri', $request, null); + $redirectUri = $this->getRequestParameter('redirect_uri', $request, null); if (is_null($redirectUri)) { throw OAuthServerException::invalidRequest('redirect_uri'); } @@ -285,6 +291,10 @@ class AuthCodeGrant extends AbstractGrant if ($authCodePayload->client_id !== $client->getIdentifier()) { throw OAuthServerException::invalidRequest('code', 'Authorization code was not issued to this client'); } + + if ($authCodePayload->redirect_uri !== $redirectUri) { + throw OAuthServerException::invalidRequest('redirect_uri', 'Invalid redirect URI'); + } } catch (\LogicException $e) { throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code'); } @@ -341,13 +351,8 @@ class AuthCodeGrant extends AbstractGrant && $request->getQueryParams()['response_type'] === 'code' ) { return $this->respondToAuthorizationRequest($request); - } elseif ( - array_key_exists('grant_type', $request->getParsedBody()) - && $request->getParsedBody()['grant_type'] === 'authorization_code' - ) { - return $this->respondToAccessTokenRequest($request, $responseType, $accessTokenTTL); - } else { - throw OAuthServerException::serverError('respondToRequest() should not have been called'); } + + return $this->respondToAccessTokenRequest($request, $responseType, $accessTokenTTL); } } diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index faf18318..d5764843 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -3,10 +3,14 @@ namespace LeagueTests\Grant; use League\OAuth2\Server\Entities\ClientEntity; +use League\OAuth2\Server\Entities\Interfaces\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\Interfaces\RefreshTokenEntityInterface; +use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Grant\AuthCodeGrant; use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface; use League\OAuth2\Server\Repositories\ClientRepositoryInterface; use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface; +use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; use League\OAuth2\Server\Repositories\UserRepositoryInterface; use League\OAuth2\Server\Utils\KeyCrypt; use LeagueTests\Stubs\StubResponseType; @@ -24,8 +28,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $this->getMock(RefreshTokenRepositoryInterface::class), $this->getMock(UserRepositoryInterface::class), new \DateInterval('PT10M'), - '', - '' + 'foo/bar.php', + 'foo/bar.php' ); $this->assertEquals('authorization_code', $grant->getIdentifier()); @@ -234,10 +238,6 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $this->assertTrue($response instanceof ResponseInterface); } - /** - * @expectedException \League\OAuth2\Server\Exception\OAuthServerException - * @expectedExceptionCode 4 - */ public function testRespondToAuthorizationRequestBadClient() { $client = null; @@ -252,9 +252,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $this->getMock(AuthCodeRepositoryInterface::class), $this->getMock(RefreshTokenRepositoryInterface::class), $userRepositoryMock, - new \DateInterval('PT10M'), - '', - '' + new \DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); $grant->setPathToPublicKey('file://' . __DIR__ . '/../Utils/public.key'); @@ -288,9 +286,69 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase ] ); - $response = $grant->respondToRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + try { + /** @var StubResponseType $response */ + $grant->respondToRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } catch (OAuthServerException $e) { + $this->assertEquals($e->getMessage(), 'Client authentication failed'); + } + } - $this->assertTrue($response instanceof ResponseInterface); + public function testRespondToAuthorizationRequestBadRedirectUri() + { + $client = new ClientEntity(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + $userEntity = new UserEntity(); + $userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + $userRepositoryMock, + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setPathToPublicKey('file://' . __DIR__ . '/../Utils/public.key'); + $grant->setPathToPrivateKey('file://' . __DIR__ . '/../Utils/private.key'); + + $request = new ServerRequest( + [ + 'HTTP_HOST' => 'auth-server.tld', + 'REQUEST_URI' => '/authorize', + ], + [] + , + null, + 'POST', + 'php://input', + [], + [ + 'oauth_authorize_request' => KeyCrypt::encrypt( + json_encode(['user_id' => 123]), + 'file://' . __DIR__ . '/../Utils/private.key' + ), + ], + [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'sdfsdf' + ], + [ + 'username' => 'alex', + 'password' => 'whisky', + 'action' => 'approve', + ] + ); + + try { + /** @var StubResponseType $response */ + $grant->respondToRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } catch (OAuthServerException $e) { + $this->assertEquals($e->getMessage(), 'Client authentication failed'); + } } /** @@ -516,4 +574,426 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $this->assertTrue($response instanceof ResponseInterface); $this->assertTrue(strstr($response->getHeader('set-cookie')[0], 'oauth_authorize_request') !== false); } + + public function testRespondToAccessTokenRequest() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + $userEntity = new UserEntity(); + $userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + $userRepositoryMock, + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setPathToPublicKey('file://' . __DIR__ . '/../Utils/public.key'); + $grant->setPathToPrivateKey('file://' . __DIR__ . '/../Utils/private.key'); + + $request = new ServerRequest( + [], + [] + , + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code' => KeyCrypt::encrypt( + json_encode( + [ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => 'http://foo/bar', + ] + ), + 'file://' . __DIR__ . '/../Utils/private.key' + ), + ] + ); + + /** @var StubResponseType $response */ + $response = $grant->respondToRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + + $this->assertTrue($response->getAccessToken() instanceof AccessTokenEntityInterface); + $this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 3 + */ + public function testRespondToAccessTokenRequestMissingRedirectUri() + { + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + $userRepositoryMock, + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setPathToPublicKey('file://' . __DIR__ . '/../Utils/public.key'); + $grant->setPathToPrivateKey('file://' . __DIR__ . '/../Utils/private.key'); + + $request = new ServerRequest( + [], + [] + , + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + ] + ); + + /** @var StubResponseType $response */ + $grant->respondToRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 3 + */ + public function testRespondToAccessTokenRequestMissingCode() + { + $client = new ClientEntity(); + $client->setSecret('bar'); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + $userRepositoryMock, + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setPathToPublicKey('file://' . __DIR__ . '/../Utils/public.key'); + $grant->setPathToPrivateKey('file://' . __DIR__ . '/../Utils/private.key'); + + $request = new ServerRequest( + [], + [] + , + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'redirect_uri' => 'http://foo/bar', + ] + ); + + /** @var StubResponseType $response */ + $grant->respondToRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } + + public function testRespondToAccessTokenRequestExpiredCode() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + $userEntity = new UserEntity(); + $userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + $userRepositoryMock, + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setPathToPublicKey('file://' . __DIR__ . '/../Utils/public.key'); + $grant->setPathToPrivateKey('file://' . __DIR__ . '/../Utils/private.key'); + + $request = new ServerRequest( + [], + [] + , + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code' => KeyCrypt::encrypt( + json_encode( + [ + 'auth_code_id' => uniqid(), + 'expire_time' => time() - 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => 'http://foo/bar', + ] + ), + 'file://' . __DIR__ . '/../Utils/private.key' + ), + ] + ); + + try { + /** @var StubResponseType $response */ + $grant->respondToRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } catch (OAuthServerException $e) { + $this->assertEquals($e->getHint(), 'Authorization code has expired'); + } + } + + public function testRespondToAccessTokenRequestRevokedCode() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + $userEntity = new UserEntity(); + $userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + + $authCodeRepositoryMock = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(); + $authCodeRepositoryMock->method('isAuthCodeRevoked')->willReturn(true); + + $grant = new AuthCodeGrant( + $authCodeRepositoryMock, + $this->getMock(RefreshTokenRepositoryInterface::class), + $userRepositoryMock, + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setPathToPublicKey('file://' . __DIR__ . '/../Utils/public.key'); + $grant->setPathToPrivateKey('file://' . __DIR__ . '/../Utils/private.key'); + + $request = new ServerRequest( + [], + [] + , + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code' => KeyCrypt::encrypt( + json_encode( + [ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => 'http://foo/bar', + ] + ), + 'file://' . __DIR__ . '/../Utils/private.key' + ), + ] + ); + + try { + /** @var StubResponseType $response */ + $grant->respondToRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } catch (OAuthServerException $e) { + $this->assertEquals($e->getHint(), 'Authorization code has been revoked'); + } + } + + public function testRespondToAccessTokenRequestClientMismatch() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + $userEntity = new UserEntity(); + $userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + $userRepositoryMock, + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setPathToPublicKey('file://' . __DIR__ . '/../Utils/public.key'); + $grant->setPathToPrivateKey('file://' . __DIR__ . '/../Utils/private.key'); + + $request = new ServerRequest( + [], + [] + , + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code' => KeyCrypt::encrypt( + json_encode( + [ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'bar', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => 'http://foo/bar', + ] + ), + 'file://' . __DIR__ . '/../Utils/private.key' + ), + ] + ); + + try { + /** @var StubResponseType $response */ + $grant->respondToRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } catch (OAuthServerException $e) { + $this->assertEquals($e->getHint(), 'Authorization code was not issued to this client'); + } + } + + public function testRespondToAccessTokenRequestBadCodeEncryption() + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + $userEntity = new UserEntity(); + $userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + + $grant = new AuthCodeGrant( + $this->getMock(AuthCodeRepositoryInterface::class), + $this->getMock(RefreshTokenRepositoryInterface::class), + $userRepositoryMock, + new \DateInterval('PT10M') + ); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); + $grant->setPathToPublicKey('file://' . __DIR__ . '/../Utils/public.key'); + $grant->setPathToPrivateKey('file://' . __DIR__ . '/../Utils/private.key'); + + $request = new ServerRequest( + [], + [] + , + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code' => 'sdfsfsd', + ] + ); + + try { + /** @var StubResponseType $response */ + $grant->respondToRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + } catch (OAuthServerException $e) { + $this->assertEquals($e->getHint(), 'Cannot decrypt the authorization code'); + } + } }