diff --git a/src/Controller/Oauth2Token.php b/src/Controller/Oauth2Token.php index 954cab875b938243cbb074c5d2d1b1c3c354e9d9..c0f31e20dcf5adc57be4724c5433f43554fbdfef 100644 --- a/src/Controller/Oauth2Token.php +++ b/src/Controller/Oauth2Token.php @@ -2,7 +2,10 @@ namespace Drupal\simple_oauth\Controller; +use Drupal\Component\Serialization\Json; +use Drupal\Component\Utility\Crypt; use Drupal\Core\Controller\ControllerBase; +use Drupal\Core\Lock\LockBackendInterface; use Drupal\simple_oauth\Server\AuthorizationServerFactoryInterface; use GuzzleHttp\Psr7\Response; use League\OAuth2\Server\Exception\OAuthServerException; @@ -40,6 +43,13 @@ class Oauth2Token extends ControllerBase { */ protected ClientRepositoryInterface $clientRepository; + /** + * The lock backend. + * + * @var \Drupal\Core\Lock\LockBackendInterface + */ + protected LockBackendInterface $lock; + /** * The simple_oauth logger channel. * @@ -56,6 +66,8 @@ class Oauth2Token extends ControllerBase { * The PSR-7 converter. * @param \League\OAuth2\Server\Repositories\ClientRepositoryInterface $client_repository * The client repository service. + * @param \Drupal\Core\Lock\LockBackendInterface $lock + * The lock backend. * @param \Psr\Log\LoggerInterface $logger * The simple_oauth logger channel. */ @@ -63,11 +75,13 @@ class Oauth2Token extends ControllerBase { AuthorizationServerFactoryInterface $authorization_server_factory, HttpMessageFactoryInterface $http_message_factory, ClientRepositoryInterface $client_repository, + LockBackendInterface $lock, LoggerInterface $logger, ) { $this->authorizationServerFactory = $authorization_server_factory; $this->httpMessageFactory = $http_message_factory; $this->clientRepository = $client_repository; + $this->lock = $lock; $this->logger = $logger; } @@ -79,6 +93,7 @@ class Oauth2Token extends ControllerBase { $container->get('simple_oauth.server.authorization_server.factory'), $container->get('psr7.http_message_factory'), $container->get('simple_oauth.repositories.client'), + $container->get('lock'), $container->get('logger.channel.simple_oauth') ); } @@ -99,7 +114,18 @@ class Oauth2Token extends ControllerBase { $server_response = new Response(); $client_id = $request->get('client_id'); + $lock_key = $this->createLockKey($request); + try { + // Try to acquire the lock. + while (!$this->lock->acquire($lock_key)) { + // If we can't acquire the lock, wait for it. + if ($this->lock->wait($lock_key)) { + // Timeout reached after 30 seconds. + throw OAuthServerException::accessDenied('Request timed out. Could not acquire lock.'); + } + } + if (empty($client_id)) { throw OAuthServerException::invalidRequest('client_id'); } @@ -120,8 +146,28 @@ class Oauth2Token extends ControllerBase { ); $response = $exception->generateHttpResponse($server_response); } + finally { + // Release the lock. + $this->lock->release($lock_key); + } return $response; } + /** + * Creates a lock key for the request. + * + * The key consists of the request content, in this case the + * grant payload. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request. + * + * @return string + * The lock key. + */ + protected function createLockKey(Request $request): string { + return Crypt::hashBase64(Json::encode($request->request->all())); + } + }