Skip to content
Snippets Groups Projects

Issue #3498190 by chfoidl: Implement lock to avoid token request race conditions

Merged Issue #3498190 by chfoidl: Implement lock to avoid token request race conditions
All threads resolved!
All threads resolved!
@@ -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()));
}
}
Loading