diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 35e745d3..c375af47 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -71,6 +71,11 @@ class AuthorizationServer implements EmitterAwareInterface */ private $encryptionKey; + /** + * @var string + */ + private $defaultScope = ''; + /** * New server instance. * @@ -97,7 +102,6 @@ class AuthorizationServer implements EmitterAwareInterface $privateKey = new CryptKey($privateKey); } $this->privateKey = $privateKey; - $this->encryptionKey = $encryptionKey; $this->responseType = $responseType; } @@ -117,6 +121,7 @@ class AuthorizationServer implements EmitterAwareInterface $grantType->setAccessTokenRepository($this->accessTokenRepository); $grantType->setClientRepository($this->clientRepository); $grantType->setScopeRepository($this->scopeRepository); + $grantType->setDefaultScope($this->defaultScope); $grantType->setPrivateKey($this->privateKey); $grantType->setEmitter($this->getEmitter()); $grantType->setEncryptionKey($this->encryptionKey); @@ -205,4 +210,14 @@ class AuthorizationServer implements EmitterAwareInterface return $this->responseType; } + + /** + * Set the default scope for the authorization server. + * + * @param string $defaultScope + */ + public function setDefaultScope($defaultScope) + { + $this->defaultScope = $defaultScope; + } } diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 3ac98cf4..419631a4 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -81,6 +81,11 @@ abstract class AbstractGrant implements GrantTypeInterface */ protected $privateKey; + /** + * @string + */ + protected $defaultScope; + /** * @param ClientRepositoryInterface $clientRepository */ @@ -147,6 +152,14 @@ abstract class AbstractGrant implements GrantTypeInterface $this->privateKey = $key; } + /** + * @param string $scope + */ + public function setDefaultScope($scope) + { + $this->defaultScope = $scope; + } + /** * Validate the client. * @@ -211,18 +224,14 @@ abstract class AbstractGrant implements GrantTypeInterface * * @return ScopeEntityInterface[] */ - public function validateScopes( - $scopes, - $redirectUri = null - ) { - $scopesList = array_filter( - explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), - function ($scope) { - return !empty($scope); - } - ); + public function validateScopes($scopes, $redirectUri = null) + { + $scopesList = array_filter(explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), function ($scope) { + return !empty($scope); + }); + + $validScopes = []; - $scopes = []; foreach ($scopesList as $scopeItem) { $scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem); @@ -230,10 +239,14 @@ abstract class AbstractGrant implements GrantTypeInterface throw OAuthServerException::invalidScope($scopeItem, $redirectUri); } - $scopes[] = $scope; + $validScopes[] = $scope; } - return $scopes; + if (empty($validScopes)) { + throw OAuthServerException::invalidScope($redirectUri); + } + + return $validScopes; } /** diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index a138366f..a8528bb5 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -243,7 +243,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant } $scopes = $this->validateScopes( - $this->getQueryStringParameter('scope', $request), + $this->getQueryStringParameter('scope', $request, $this->defaultScope), is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] : $client->getRedirectUri() diff --git a/src/Grant/ClientCredentialsGrant.php b/src/Grant/ClientCredentialsGrant.php index b5b968d4..ed157aaf 100644 --- a/src/Grant/ClientCredentialsGrant.php +++ b/src/Grant/ClientCredentialsGrant.php @@ -29,13 +29,13 @@ class ClientCredentialsGrant extends AbstractGrant ) { // Validate request $client = $this->validateClient($request); - $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); + $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); // Finalize the requested scopes - $scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client); + $finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client); // Issue and persist access token - $accessToken = $this->issueAccessToken($accessTokenTTL, $client, null, $scopes); + $accessToken = $this->issueAccessToken($accessTokenTTL, $client, null, $finalizedScopes); // Inject access token into response type $responseType->setAccessToken($accessToken); diff --git a/src/Grant/GrantTypeInterface.php b/src/Grant/GrantTypeInterface.php index 7aa98242..0e721435 100644 --- a/src/Grant/GrantTypeInterface.php +++ b/src/Grant/GrantTypeInterface.php @@ -119,6 +119,13 @@ interface GrantTypeInterface extends EmitterAwareInterface */ public function setScopeRepository(ScopeRepositoryInterface $scopeRepository); + /** + * Set the default scope. + * + * @param string $scope + */ + public function setDefaultScope($scope); + /** * Set the path to the private key. * diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 4a16314f..870e930a 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -152,14 +152,14 @@ class ImplicitGrant extends AbstractAuthorizeGrant } $scopes = $this->validateScopes( - $this->getQueryStringParameter('scope', $request), + $this->getQueryStringParameter('scope', $request, $this->defaultScope), is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] : $client->getRedirectUri() ); // Finalize the requested scopes - $scopes = $this->scopeRepository->finalizeScopes( + $finalizedScopes = $this->scopeRepository->finalizeScopes( $scopes, $this->getIdentifier(), $client @@ -172,7 +172,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant $authorizationRequest->setClient($client); $authorizationRequest->setRedirectUri($redirectUri); $authorizationRequest->setState($stateParameter); - $authorizationRequest->setScopes($scopes); + $authorizationRequest->setScopes($finalizedScopes); return $authorizationRequest; } diff --git a/src/Grant/PasswordGrant.php b/src/Grant/PasswordGrant.php index 31755613..cfd7e9fe 100644 --- a/src/Grant/PasswordGrant.php +++ b/src/Grant/PasswordGrant.php @@ -49,14 +49,14 @@ class PasswordGrant extends AbstractGrant ) { // Validate request $client = $this->validateClient($request); - $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); + $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); $user = $this->validateUser($request, $client); // Finalize the requested scopes - $scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier()); + $finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier()); // Issue and persist new tokens - $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $scopes); + $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes); $refreshToken = $this->issueRefreshToken($accessToken); // Inject tokens into response diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php index 53dfdf7d..66a3b266 100644 --- a/src/Grant/RefreshTokenGrant.php +++ b/src/Grant/RefreshTokenGrant.php @@ -44,28 +44,17 @@ class RefreshTokenGrant extends AbstractGrant // Validate request $client = $this->validateClient($request); $oldRefreshToken = $this->validateOldRefreshToken($request, $client->getIdentifier()); - $scopes = $this->validateScopes($this->getRequestParameter('scope', $request)); + $scopes = $this->validateScopes($this->getRequestParameter( + 'scope', + $request, + implode(self::SCOPE_DELIMITER_STRING, $oldRefreshToken['scopes'])) + ); - // If no new scopes are requested then give the access token the original session scopes - if (count($scopes) === 0) { - $scopes = array_map(function ($scopeId) use ($client) { - $scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId); - - if ($scope instanceof ScopeEntityInterface === false) { - // @codeCoverageIgnoreStart - throw OAuthServerException::invalidScope($scopeId); - // @codeCoverageIgnoreEnd - } - - return $scope; - }, $oldRefreshToken['scopes']); - } else { - // The OAuth spec says that a refreshed access token can have the original scopes or fewer so ensure - // the request doesn't include any new scopes - foreach ($scopes as $scope) { - if (in_array($scope->getIdentifier(), $oldRefreshToken['scopes']) === false) { - throw OAuthServerException::invalidScope($scope->getIdentifier()); - } + // The OAuth spec says that a refreshed access token can have the original scopes or fewer so ensure + // the request doesn't include any new scopes + foreach ($scopes as $scope) { + if (in_array($scope->getIdentifier(), $oldRefreshToken['scopes']) === false) { + throw OAuthServerException::invalidScope($scope->getIdentifier()); } } diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 6fc59715..40458059 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -16,6 +16,7 @@ use League\OAuth2\Server\ResponseTypes\BearerTokenResponse; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\AuthCodeEntity; use LeagueTests\Stubs\ClientEntity; +use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; use Psr\Http\Message\ResponseInterface; @@ -26,6 +27,9 @@ use Zend\Diactoros\ServerRequestFactory; class AuthorizationServerTest extends TestCase { + + const DEFAULT_SCOPE = 'basic'; + public function setUp() { // Make sure the keys have the correct permissions. @@ -59,7 +63,9 @@ class AuthorizationServerTest extends TestCase $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepository->method('getClientEntity')->willReturn(new ClientEntity()); + $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -74,6 +80,7 @@ class AuthorizationServerTest extends TestCase new StubResponseType() ); + $server->setDefaultScope(self::DEFAULT_SCOPE); $server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M')); $_POST['grant_type'] = 'client_credentials'; @@ -142,6 +149,10 @@ class AuthorizationServerTest extends TestCase $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $scope = new ScopeEntity(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), @@ -152,10 +163,12 @@ class AuthorizationServerTest extends TestCase $server = new AuthorizationServer( $clientRepositoryMock, $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), - $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), + $scopeRepositoryMock, 'file://' . __DIR__ . '/Stubs/private.key', 'file://' . __DIR__ . '/Stubs/public.key' ); + + $server->setDefaultScope(self::DEFAULT_SCOPE); $server->enableGrantType($grant); $request = new ServerRequest( diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 213b4a03..6b60f2b0 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -27,6 +27,8 @@ use Zend\Diactoros\ServerRequest; class AuthCodeGrantTest extends TestCase { + const DEFAULT_SCOPE = 'basic'; + /** * @var CryptTraitStub */ @@ -77,15 +79,22 @@ class AuthCodeGrantTest extends TestCase { $client = new ClientEntity(); $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $scope = new ScopeEntity(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $request = new ServerRequest( [], @@ -112,12 +121,18 @@ class AuthCodeGrantTest extends TestCase $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $scope = new ScopeEntity(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $request = new ServerRequest( [], @@ -144,6 +159,10 @@ class AuthCodeGrantTest extends TestCase $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $scope = new ScopeEntity(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), @@ -151,6 +170,8 @@ class AuthCodeGrantTest extends TestCase ); $grant->enableCodeExchangeProof(); $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $request = new ServerRequest( [], @@ -429,6 +450,10 @@ class AuthCodeGrantTest extends TestCase $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $scope = new ScopeEntity(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), @@ -436,6 +461,8 @@ class AuthCodeGrantTest extends TestCase ); $grant->enableCodeExchangeProof(); $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $request = new ServerRequest( [], @@ -466,6 +493,10 @@ class AuthCodeGrantTest extends TestCase $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $scope = new ScopeEntity(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), @@ -473,6 +504,8 @@ class AuthCodeGrantTest extends TestCase ); $grant->enableCodeExchangeProof(); $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $request = new ServerRequest( [], @@ -1634,4 +1667,47 @@ class AuthCodeGrantTest extends TestCase $grant->completeAuthorizationRequest(new AuthorizationRequest()); } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 5 + */ + public function testValidateAuthorizationRequestFailsWithoutScope() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $scope = new ScopeEntity(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + + $grant = new AuthCodeGrant( + $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), + $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), + new \DateInterval('PT10M') + ); + + $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + [], + [], + [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + ] + ); + + $grant->validateAuthorizationRequest($request); + } } diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index 96d8d578..cfcdfba5 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -9,12 +9,15 @@ use League\OAuth2\Server\Repositories\ClientRepositoryInterface; use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; +use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use PHPUnit\Framework\TestCase; use Zend\Diactoros\ServerRequest; class ClientCredentialsGrantTest extends TestCase { + const DEFAULT_SCOPE = 'basic'; + public function testGetIdentifier() { $grant = new ClientCredentialsGrant(); @@ -31,7 +34,48 @@ class ClientCredentialsGrantTest extends TestCase $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); + + $grant = new ClientCredentialsGrant(); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + + $this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 5 + */ + public function testRespondToRequestFailsWithoutScope() + { + $client = new ClientEntity(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $scope = new ScopeEntity(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $grant = new ClientCredentialsGrant(); @@ -49,7 +93,5 @@ class ClientCredentialsGrantTest extends TestCase $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); - - $this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface); } -} +} \ No newline at end of file diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index f962024a..dff2c341 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -23,6 +23,8 @@ use Zend\Diactoros\ServerRequest; class ImplicitGrantTest extends TestCase { + const DEFAULT_SCOPE = 'basic'; + /** * CryptTrait stub */ @@ -97,6 +99,7 @@ class ImplicitGrantTest extends TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $request = new ServerRequest( [], @@ -131,6 +134,7 @@ class ImplicitGrantTest extends TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $request = new ServerRequest( [], @@ -408,4 +412,42 @@ class ImplicitGrantTest extends TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->completeAuthorizationRequest(new AuthorizationRequest()); } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 5 + */ + public function testValidateAuthorizationRequestFailsWithoutScope() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeEntity = new ScopeEntity(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); + $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); + + $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + $headers = [], + $cookies = [], + $queryParams = [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + ] + ); + + $grant->validateAuthorizationRequest($request); + } } diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index 0c1be581..469044af 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -13,6 +13,7 @@ use League\OAuth2\Server\Repositories\UserRepositoryInterface; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\RefreshTokenEntity; +use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use LeagueTests\Stubs\UserEntity; use PHPUnit\Framework\TestCase; @@ -20,6 +21,8 @@ use Zend\Diactoros\ServerRequest; class PasswordGrantTest extends TestCase { + const DEFAULT_SCOPE = 'basic'; + public function testGetIdentifier() { $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); @@ -47,13 +50,16 @@ class PasswordGrantTest extends TestCase $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); + $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $serverRequest = new ServerRequest(); $serverRequest = $serverRequest->withParsedBody( @@ -168,4 +174,50 @@ class PasswordGrantTest extends TestCase $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + * @expectedExceptionCode 5 + */ + public function testRespondToRequestFailsWithoutScope() + { + $client = new ClientEntity(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); + $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); + + $userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock(); + $userEntity = new UserEntity(); + $userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity); + + $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); + $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); + + $scope = new ScopeEntity(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); + + $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); + $grant->setClientRepository($clientRepositoryMock); + $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); + + $serverRequest = new ServerRequest(); + $serverRequest = $serverRequest->withParsedBody( + [ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'username' => 'foo', + 'password' => 'bar', + ] + ); + + $responseType = new StubResponseType(); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + } } diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index f7f803f7..d7b3a8fd 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -46,21 +46,18 @@ class RefreshTokenGrantTest extends TestCase $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); - $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); + $scopeEntity->setIdentifier('foo'); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); - $accessTokenRepositoryMock - ->expects($this->once()) - ->method('persistNewAccessToken')->willReturnSelf(); + $accessTokenRepositoryMock->expects($this->once())->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); - $refreshTokenRepositoryMock - ->expects($this->once()) - ->method('persistNewRefreshToken')->willReturnSelf(); + $refreshTokenRepositoryMock->expects($this->once())->method('persistNewRefreshToken')->willReturnSelf(); $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); @@ -83,13 +80,12 @@ class RefreshTokenGrantTest extends TestCase ); $serverRequest = new ServerRequest(); - $serverRequest = $serverRequest->withParsedBody( - [ - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, - ] - ); + $serverRequest = $serverRequest->withParsedBody([ + 'client_id' => 'foo', + 'client_secret' => 'bar', + 'refresh_token' => $oldRefreshToken, + 'scopes' => ['foo'], + ]); $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); diff --git a/tests/Middleware/AuthorizationServerMiddlewareTest.php b/tests/Middleware/AuthorizationServerMiddlewareTest.php index bc860770..99118736 100644 --- a/tests/Middleware/AuthorizationServerMiddlewareTest.php +++ b/tests/Middleware/AuthorizationServerMiddlewareTest.php @@ -11,6 +11,7 @@ use League\OAuth2\Server\Repositories\ClientRepositoryInterface; use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; +use LeagueTests\Stubs\ScopeEntity; use LeagueTests\Stubs\StubResponseType; use PHPUnit\Framework\TestCase; use Zend\Diactoros\Response; @@ -18,12 +19,16 @@ use Zend\Diactoros\ServerRequestFactory; class AuthorizationServerMiddlewareTest extends TestCase { + const DEFAULT_SCOPE = 'basic'; + public function testValidResponse() { $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepository->method('getClientEntity')->willReturn(new ClientEntity()); + $scopeEntity = new ScopeEntity; $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); $accessRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -38,6 +43,7 @@ class AuthorizationServerMiddlewareTest extends TestCase new StubResponseType() ); + $server->setDefaultScope(self::DEFAULT_SCOPE); $server->enableGrantType(new ClientCredentialsGrant()); $_POST['grant_type'] = 'client_credentials';