From 20474386e0d32594576c7aae858d8a63527c5024 Mon Sep 17 00:00:00 2001 From: Fred Cox Date: Thu, 10 Aug 2017 16:57:08 +0300 Subject: [PATCH] Authorize based on grant type - Allows specific user/client combo to only use certain grant types - E.g. This mean server can allow certain clients access to password grant, but others only authorization code grants --- src/GrantType/AbstractGrantTypeHandler.php | 12 ++++++++ .../AuthorizationCodeGrantTypeHandler.php | 2 ++ .../ClientCredentialsGrantTypeHandler.php | 2 ++ src/GrantType/PasswordGrantTypeHandler.php | 2 ++ .../RefreshTokenGrantTypeHandler.php | 2 ++ src/Model/AuthorizeInterface.php | 7 +++++ .../AbstractResponseTypeHandler.php | 11 +++++++ src/ResponseType/CodeResponseTypeHandler.php | 3 ++ src/ResponseType/TokenResponseTypeHandler.php | 2 ++ .../DataFixtures/ORM/AuthorizeFixture.php | 16 ++++++++++ tests/TestBundle/Entity/Authorize.php | 29 +++++++++++++++++++ 11 files changed, 88 insertions(+) diff --git a/src/GrantType/AbstractGrantTypeHandler.php b/src/GrantType/AbstractGrantTypeHandler.php index 4bb7ab98..34ada779 100644 --- a/src/GrantType/AbstractGrantTypeHandler.php +++ b/src/GrantType/AbstractGrantTypeHandler.php @@ -11,9 +11,11 @@ namespace AuthBucket\OAuth2\GrantType; +use AuthBucket\OAuth2\Exception\InvalidGrantException; use AuthBucket\OAuth2\Exception\InvalidRequestException; use AuthBucket\OAuth2\Exception\InvalidScopeException; use AuthBucket\OAuth2\Exception\ServerErrorException; +use AuthBucket\OAuth2\Model\AuthorizeInterface; use AuthBucket\OAuth2\Model\ModelManagerFactoryInterface; use AuthBucket\OAuth2\Symfony\Component\Security\Core\Authentication\Token\ClientCredentialsToken; use AuthBucket\OAuth2\TokenType\TokenTypeHandlerFactoryInterface; @@ -30,6 +32,8 @@ */ abstract class AbstractGrantTypeHandler implements GrantTypeHandlerInterface { + const GRANT_TYPE = null; + protected $tokenStorage; protected $encoderFactory; protected $validator; @@ -122,19 +126,27 @@ protected function checkScope( // Compare if given scope within all authorized scopes. $scopeAuthorized = []; + $grantTypeAuthorized = []; $authorizeManager = $this->modelManagerFactory->getModelManager('authorize'); + /** @var AuthorizeInterface $result */ $result = $authorizeManager->readModelOneBy([ 'clientId' => $clientId, 'username' => $username, ]); if ($result !== null) { $scopeAuthorized = $result->getScope(); + $grantTypeAuthorized = $result->getGrantType(); } if (array_intersect($scope, $scopeAuthorized) !== $scope) { throw new InvalidScopeException([ 'error_description' => 'The requested scope exceeds the scope granted by the resource owner.', ]); } + if (!in_array(static::GRANT_TYPE, $grantTypeAuthorized)) { + throw new InvalidGrantException([ + 'error_description' => 'The requested grant is invalid.', + ]); + } return $scope; } diff --git a/src/GrantType/AuthorizationCodeGrantTypeHandler.php b/src/GrantType/AuthorizationCodeGrantTypeHandler.php index ae464e48..4ace85be 100644 --- a/src/GrantType/AuthorizationCodeGrantTypeHandler.php +++ b/src/GrantType/AuthorizationCodeGrantTypeHandler.php @@ -23,6 +23,8 @@ */ class AuthorizationCodeGrantTypeHandler extends AbstractGrantTypeHandler { + const GRANT_TYPE = 'authorization_code'; + public function handle(Request $request) { // Fetch client_id from authenticated token. diff --git a/src/GrantType/ClientCredentialsGrantTypeHandler.php b/src/GrantType/ClientCredentialsGrantTypeHandler.php index 16279885..94f3a9ef 100644 --- a/src/GrantType/ClientCredentialsGrantTypeHandler.php +++ b/src/GrantType/ClientCredentialsGrantTypeHandler.php @@ -21,6 +21,8 @@ */ class ClientCredentialsGrantTypeHandler extends AbstractGrantTypeHandler { + const GRANT_TYPE = 'client_credentials'; + public function handle(Request $request) { // Fetch client_id from authenticated token. diff --git a/src/GrantType/PasswordGrantTypeHandler.php b/src/GrantType/PasswordGrantTypeHandler.php index 6c4d6297..c57aba22 100644 --- a/src/GrantType/PasswordGrantTypeHandler.php +++ b/src/GrantType/PasswordGrantTypeHandler.php @@ -30,6 +30,8 @@ */ class PasswordGrantTypeHandler extends AbstractGrantTypeHandler { + const GRANT_TYPE = 'password'; + public function handle(Request $request) { // Fetch client_id from authenticated token. diff --git a/src/GrantType/RefreshTokenGrantTypeHandler.php b/src/GrantType/RefreshTokenGrantTypeHandler.php index e2d8dc9b..fd2c92e7 100644 --- a/src/GrantType/RefreshTokenGrantTypeHandler.php +++ b/src/GrantType/RefreshTokenGrantTypeHandler.php @@ -24,6 +24,8 @@ */ class RefreshTokenGrantTypeHandler extends AbstractGrantTypeHandler { + const GRANT_TYPE = 'refresh_token'; + public function handle(Request $request) { // Fetch client_id from authenticated token. diff --git a/src/Model/AuthorizeInterface.php b/src/Model/AuthorizeInterface.php index 37798645..73ff22f5 100644 --- a/src/Model/AuthorizeInterface.php +++ b/src/Model/AuthorizeInterface.php @@ -65,4 +65,11 @@ public function setScope($scope); * @return array */ public function getScope(); + + /** + * Get Grant types that this client is allowed to use. + * + * @return array + */ + public function getGrantType(); } diff --git a/src/ResponseType/AbstractResponseTypeHandler.php b/src/ResponseType/AbstractResponseTypeHandler.php index 09955b21..30429c03 100644 --- a/src/ResponseType/AbstractResponseTypeHandler.php +++ b/src/ResponseType/AbstractResponseTypeHandler.php @@ -11,10 +11,12 @@ namespace AuthBucket\OAuth2\ResponseType; +use AuthBucket\OAuth2\Exception\InvalidGrantException; use AuthBucket\OAuth2\Exception\InvalidRequestException; use AuthBucket\OAuth2\Exception\InvalidScopeException; use AuthBucket\OAuth2\Exception\ServerErrorException; use AuthBucket\OAuth2\Exception\UnauthorizedClientException; +use AuthBucket\OAuth2\Model\AuthorizeInterface; use AuthBucket\OAuth2\Model\ModelManagerFactoryInterface; use AuthBucket\OAuth2\TokenType\TokenTypeHandlerFactoryInterface; use Symfony\Component\HttpFoundation\Request; @@ -30,6 +32,7 @@ */ abstract class AbstractResponseTypeHandler implements ResponseTypeHandlerInterface { + const GRANT_TYPE = null; protected $tokenStorage; protected $validator; protected $modelManagerFactory; @@ -230,6 +233,7 @@ protected function checkScope( // Compare if given scope within all authorized scopes. $scopeAuthorized = []; $authorizeManager = $this->modelManagerFactory->getModelManager('authorize'); + /** @var AuthorizeInterface $result */ $result = $authorizeManager->readModelOneBy([ 'clientId' => $clientId, 'username' => $username, @@ -245,6 +249,13 @@ protected function checkScope( ]); } + if (!in_array(static::GRANT_TYPE, $result->getGrantType())) { + throw new InvalidGrantException([ + 'state' => $state, + 'error_description' => 'The requested grant is invalid.', + ]); + } + return $scope; } } diff --git a/src/ResponseType/CodeResponseTypeHandler.php b/src/ResponseType/CodeResponseTypeHandler.php index bfe84d58..ca1818ac 100644 --- a/src/ResponseType/CodeResponseTypeHandler.php +++ b/src/ResponseType/CodeResponseTypeHandler.php @@ -11,6 +11,7 @@ namespace AuthBucket\OAuth2\ResponseType; +use AuthBucket\OAuth2\GrantType\AuthorizationCodeGrantTypeHandler; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -21,6 +22,8 @@ */ class CodeResponseTypeHandler extends AbstractResponseTypeHandler { + const GRANT_TYPE = AuthorizationCodeGrantTypeHandler::GRANT_TYPE; + public function handle(Request $request) { // Fetch username from authenticated token. diff --git a/src/ResponseType/TokenResponseTypeHandler.php b/src/ResponseType/TokenResponseTypeHandler.php index 68250d1b..8f30ff28 100644 --- a/src/ResponseType/TokenResponseTypeHandler.php +++ b/src/ResponseType/TokenResponseTypeHandler.php @@ -22,6 +22,8 @@ */ class TokenResponseTypeHandler extends AbstractResponseTypeHandler { + const GRANT_TYPE = 'implicit'; + public function handle(Request $request) { // Fetch username from authenticated token. diff --git a/tests/TestBundle/DataFixtures/ORM/AuthorizeFixture.php b/tests/TestBundle/DataFixtures/ORM/AuthorizeFixture.php index c0d8f1c0..c2766441 100644 --- a/tests/TestBundle/DataFixtures/ORM/AuthorizeFixture.php +++ b/tests/TestBundle/DataFixtures/ORM/AuthorizeFixture.php @@ -77,6 +77,11 @@ public function load(ObjectManager $manager) ->setUsername('demousername1') ->setScope([ 'demoscope1', + ]) + ->setGrantType([ + 'authorization_code', + 'password', + 'implicit', ]); $manager->persist($model); @@ -86,6 +91,9 @@ public function load(ObjectManager $manager) ->setScope([ 'demoscope1', 'demoscope2', + ]) + ->setGrantType([ + 'authorization_code', ]); $manager->persist($model); @@ -96,6 +104,11 @@ public function load(ObjectManager $manager) 'demoscope1', 'demoscope2', 'demoscope3', + ]) + ->setGrantType([ + 'authorization_code', + 'password', + 'implicit', ]); $manager->persist($model); @@ -106,6 +119,9 @@ public function load(ObjectManager $manager) 'demoscope1', 'demoscope2', 'demoscope3', + ]) + ->setGrantType([ + 'client_credentials', ]); $manager->persist($model); diff --git a/tests/TestBundle/Entity/Authorize.php b/tests/TestBundle/Entity/Authorize.php index 02a8d0d6..d8659e64 100644 --- a/tests/TestBundle/Entity/Authorize.php +++ b/tests/TestBundle/Entity/Authorize.php @@ -52,6 +52,13 @@ class Authorize implements AuthorizeInterface */ protected $scope; + /** + * @var array + * + * @ORM\Column(name="grant_type", type="array") + */ + protected $grantType; + /** * Get id. * @@ -133,4 +140,26 @@ public function getScope() { return $this->scope; } + + /** + * Set grant type. + * + * @param array $grantType + * + * @return Authorize + */ + public function setGrantType($grantType) + { + $this->grantType = $grantType; + } + + /** + * Get grant type. + * + * @return array + */ + public function getGrantType() + { + return $this->grantType; + } }