From 7b063eb71c4eab77ea78c92df2aa77291e2a1d87 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 22 May 2024 05:42:48 +0330 Subject: [PATCH 01/16] fix including interval in response --- examples/src/Repositories/DeviceCodeRepository.php | 2 +- src/Grant/DeviceCodeGrant.php | 9 +++++---- src/Repositories/DeviceCodeRepositoryInterface.php | 2 +- src/ResponseTypes/DeviceCodeResponse.php | 9 +++++++-- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/examples/src/Repositories/DeviceCodeRepository.php b/examples/src/Repositories/DeviceCodeRepository.php index 9495c9a15..4151c7bfe 100644 --- a/examples/src/Repositories/DeviceCodeRepository.php +++ b/examples/src/Repositories/DeviceCodeRepository.php @@ -39,7 +39,7 @@ public function persistDeviceCode(DeviceCodeEntityInterface $deviceCodeEntity): /** * {@inheritdoc} */ - public function getDeviceCodeEntityByDeviceCode($deviceCode): ?DeviceCodeEntityInterface + public function getDeviceCodeEntityByDeviceCode(string $deviceCode): ?DeviceCodeEntityInterface { $clientEntity = new ClientEntity(); $clientEntity->setIdentifier('myawesomeapp'); diff --git a/src/Grant/DeviceCodeGrant.php b/src/Grant/DeviceCodeGrant.php index 3804e2027..16b3fb414 100644 --- a/src/Grant/DeviceCodeGrant.php +++ b/src/Grant/DeviceCodeGrant.php @@ -101,6 +101,10 @@ public function respondToDeviceAuthorizationRequest(ServerRequestInterface $requ $response->includeVerificationUriComplete(); } + if ($this->intervalVisibility === true) { + $response->includeInterval(); + } + $response->setDeviceCodeEntity($deviceCodeEntity); return $response; @@ -259,10 +263,7 @@ protected function issueDeviceCode( $deviceCode->setExpiryDateTime((new DateTimeImmutable())->add($deviceCodeTTL)); $deviceCode->setClient($client); $deviceCode->setVerificationUri($verificationUri); - - if ($this->getIntervalVisibility() === true) { - $deviceCode->setInterval($this->retryInterval); - } + $deviceCode->setInterval($this->retryInterval); foreach ($scopes as $scope) { $deviceCode->addScope($scope); diff --git a/src/Repositories/DeviceCodeRepositoryInterface.php b/src/Repositories/DeviceCodeRepositoryInterface.php index 09575ab18..453ba3bb7 100644 --- a/src/Repositories/DeviceCodeRepositoryInterface.php +++ b/src/Repositories/DeviceCodeRepositoryInterface.php @@ -33,7 +33,7 @@ public function persistDeviceCode(DeviceCodeEntityInterface $deviceCodeEntity): * Get a device code entity. */ public function getDeviceCodeEntityByDeviceCode( - string $deviceCodeEntity + string $deviceCode ): ?DeviceCodeEntityInterface; /** diff --git a/src/ResponseTypes/DeviceCodeResponse.php b/src/ResponseTypes/DeviceCodeResponse.php index 91a8df69a..0a988db10 100644 --- a/src/ResponseTypes/DeviceCodeResponse.php +++ b/src/ResponseTypes/DeviceCodeResponse.php @@ -25,7 +25,7 @@ class DeviceCodeResponse extends AbstractResponseType { protected DeviceCodeEntityInterface $deviceCodeEntity; private bool $includeVerificationUriComplete = false; - private const DEFAULT_INTERVAL = 5; + private bool $includeInterval = false; /** * {@inheritdoc} @@ -45,7 +45,7 @@ public function generateHttpResponse(ResponseInterface $response): ResponseInter $responseParams['verification_uri_complete'] = $this->deviceCodeEntity->getVerificationUriComplete(); } - if ($this->deviceCodeEntity->getInterval() !== self::DEFAULT_INTERVAL) { + if ($this->includeInterval === true) { $responseParams['interval'] = $this->deviceCodeEntity->getInterval(); } @@ -79,6 +79,11 @@ public function includeVerificationUriComplete(): void $this->includeVerificationUriComplete = true; } + public function includeInterval(): void + { + $this->includeInterval = true; + } + /** * Add custom fields to your Bearer Token response here, then override * AuthorizationServer::getResponseType() to pull in your version of From e382fa49ba09607542053d121e294938d31c4532 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 11 Oct 2024 15:40:34 +0330 Subject: [PATCH 02/16] add tests and changelog --- CHANGELOG.md | 1 + tests/Grant/DeviceCodeGrantTest.php | 46 +++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85c99ac6c..ecfa8205e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed - Auto-generated event emitter is now persisted. Previously, a new emitter was generated every time (PR #1428) - Fixed bug where you could not omit a redirect uri even if one had not been specified during the auth request (PR #1428) +- Fixed bug on setting interval visibility of device authorization grant (PR #1410) ## [9.0.0] - released 2024-05-13 ### Added diff --git a/tests/Grant/DeviceCodeGrantTest.php b/tests/Grant/DeviceCodeGrantTest.php index 396ea760f..3a516879a 100644 --- a/tests/Grant/DeviceCodeGrantTest.php +++ b/tests/Grant/DeviceCodeGrantTest.php @@ -730,6 +730,52 @@ public function testSettingDeviceCodeIntervalRate(): void $this::assertEquals(self::INTERVAL_RATE, $deviceCode->interval); } + public function testSettingInternalVisibility(): void + { + $client = new ClientEntity(); + $client->setIdentifier('foo'); + + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $deviceCode = new DeviceCodeEntity(); + + $deviceCodeRepository = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock(); + $deviceCodeRepository->method('getNewDeviceCode')->willReturn($deviceCode); + + $scope = new ScopeEntity(); + $scope->setIdentifier('basic'); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope); + + $grant = new DeviceCodeGrant( + $deviceCodeRepository, + $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), + new DateInterval('PT10M'), + 'http://foo/bar', + ); + + $grant->setClientRepository($clientRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); + $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setScopeRepository($scopeRepositoryMock); + $grant->setIntervalVisibility(true); + + $request = (new ServerRequest())->withParsedBody([ + 'client_id' => 'foo', + 'scope' => 'basic', + ]); + + $deviceCodeResponse = $grant + ->respondToDeviceAuthorizationRequest($request) + ->generateHttpResponse(new Response()); + + $deviceCode = json_decode((string) $deviceCodeResponse->getBody()); + + $this::assertObjectHasProperty('interval', $deviceCode); + $this::assertEquals(5, $deviceCode->interval); + } + public function testIssueAccessDeniedError(): void { $client = new ClientEntity(); From 25be7a39941fa09d022e89a09ccbe10bebfd382a Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 28 Oct 2024 04:18:53 +0330 Subject: [PATCH 03/16] increase interval by 5 seconds on slow down error --- src/Grant/DeviceCodeGrant.php | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Grant/DeviceCodeGrant.php b/src/Grant/DeviceCodeGrant.php index 16b3fb414..887b1e3b4 100644 --- a/src/Grant/DeviceCodeGrant.php +++ b/src/Grant/DeviceCodeGrant.php @@ -51,7 +51,7 @@ public function __construct( RefreshTokenRepositoryInterface $refreshTokenRepository, private DateInterval $deviceCodeTTL, string $verificationUri, - private int $retryInterval = 5 + private readonly int $defaultInterval = 5 ) { $this->setDeviceCodeRepository($deviceCodeRepository); $this->setRefreshTokenRepository($refreshTokenRepository); @@ -143,10 +143,21 @@ public function respondToAccessTokenRequest( $client = $this->validateClient($request); $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); $deviceCodeEntity = $this->validateDeviceCode($request, $client); + $shouldSlowDown = false; + + if ($this->deviceCodePolledTooSoon($deviceCodeEntity) === true) { + $deviceCodeEntity->setInterval($deviceCodeEntity->getInterval() + 5); + + $shouldSlowDown = true; + } $deviceCodeEntity->setLastPolledAt(new DateTimeImmutable()); $this->deviceCodeRepository->persistDeviceCode($deviceCodeEntity); + if ($shouldSlowDown) { + throw OAuthServerException::slowDown(); + } + // If device code has no user associated, respond with pending if (is_null($deviceCodeEntity->getUserIdentifier())) { throw OAuthServerException::authorizationPending(); @@ -210,16 +221,14 @@ protected function validateDeviceCode(ServerRequestInterface $request, ClientEnt throw OAuthServerException::invalidRequest('device_code', 'Device code was not issued to this client'); } - if ($this->deviceCodePolledTooSoon($deviceCodeEntity->getLastPolledAt()) === true) { - throw OAuthServerException::slowDown(); - } - return $deviceCodeEntity; } - private function deviceCodePolledTooSoon(?DateTimeImmutable $lastPoll): bool + private function deviceCodePolledTooSoon(DeviceCodeEntityInterface $deviceCodeEntity): bool { - return $lastPoll !== null && $lastPoll->getTimestamp() + $this->retryInterval > time(); + $lastPoll = $deviceCodeEntity->getLastPolledAt(); + + return $lastPoll !== null && $lastPoll->getTimestamp() + $deviceCodeEntity->getInterval() > time(); } /** @@ -263,7 +272,7 @@ protected function issueDeviceCode( $deviceCode->setExpiryDateTime((new DateTimeImmutable())->add($deviceCodeTTL)); $deviceCode->setClient($client); $deviceCode->setVerificationUri($verificationUri); - $deviceCode->setInterval($this->retryInterval); + $deviceCode->setInterval($this->defaultInterval); foreach ($scopes as $scope) { $deviceCode->addScope($scope); From bc6b12f90ce653da4ad4a83320649bfbc344d8a7 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 28 Oct 2024 04:48:52 +0330 Subject: [PATCH 04/16] no need to throw error if user already completed the auth --- src/Grant/DeviceCodeGrant.php | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Grant/DeviceCodeGrant.php b/src/Grant/DeviceCodeGrant.php index 887b1e3b4..1a3bd1d3e 100644 --- a/src/Grant/DeviceCodeGrant.php +++ b/src/Grant/DeviceCodeGrant.php @@ -143,24 +143,25 @@ public function respondToAccessTokenRequest( $client = $this->validateClient($request); $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); $deviceCodeEntity = $this->validateDeviceCode($request, $client); - $shouldSlowDown = false; - if ($this->deviceCodePolledTooSoon($deviceCodeEntity) === true) { - $deviceCodeEntity->setInterval($deviceCodeEntity->getInterval() + 5); + // If device code has no user associated, respond with pending or slow down + if (is_null($deviceCodeEntity->getUserIdentifier())) { + $shouldSlowDown = false; - $shouldSlowDown = true; - } + if ($this->deviceCodePolledTooSoon($deviceCodeEntity) === true) { + $deviceCodeEntity->setInterval($deviceCodeEntity->getInterval() + 5); - $deviceCodeEntity->setLastPolledAt(new DateTimeImmutable()); - $this->deviceCodeRepository->persistDeviceCode($deviceCodeEntity); + $shouldSlowDown = true; + } - if ($shouldSlowDown) { - throw OAuthServerException::slowDown(); - } + $deviceCodeEntity->setLastPolledAt(new DateTimeImmutable()); + $this->deviceCodeRepository->persistDeviceCode($deviceCodeEntity); - // If device code has no user associated, respond with pending - if (is_null($deviceCodeEntity->getUserIdentifier())) { - throw OAuthServerException::authorizationPending(); + if ($shouldSlowDown) { + throw OAuthServerException::slowDown($deviceCodeEntity->getInterval()); + } + + throw OAuthServerException::authorizationPending($deviceCodeEntity->getInterval()); } if ($deviceCodeEntity->getUserApproved() === false) { From 7c2c8d0e06cee8fd7505ad5dbe6c255cb581ddcc Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 28 Oct 2024 04:50:04 +0330 Subject: [PATCH 05/16] include interval on authorization_pending and slow_down error responses --- src/Exception/OAuthServerException.php | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 24a38d3fe..6953575eb 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -215,9 +215,9 @@ public static function expiredToken(?string $hint = null, ?Throwable $previous = return new static($errorMessage, 11, 'expired_token', 400, $hint, null, $previous); } - public static function authorizationPending(string $hint = '', ?Throwable $previous = null): static + public static function authorizationPending(?int $interval = null, string $hint = '', ?Throwable $previous = null): static { - return new static( + $exception = new static( 'The authorization request is still pending as the end user ' . 'hasn\'t yet completed the user interaction steps. The client ' . 'SHOULD repeat the Access Token Request to the token endpoint', @@ -228,6 +228,15 @@ public static function authorizationPending(string $hint = '', ?Throwable $previ null, $previous ); + + if (!is_null($interval)) { + $exception->setPayload([ + ...$exception->getPayload(), + 'interval' => $interval, + ]); + } + + return $exception; } /** @@ -236,9 +245,9 @@ public static function authorizationPending(string $hint = '', ?Throwable $previ * * @return static */ - public static function slowDown(string $hint = '', ?Throwable $previous = null): static + public static function slowDown(?int $interval = null, string $hint = '', ?Throwable $previous = null): static { - return new static( + $exception = new static( 'The authorization request is still pending and polling should ' . 'continue, but the interval MUST be increased ' . 'by 5 seconds for this and all subsequent requests.', @@ -249,6 +258,15 @@ public static function slowDown(string $hint = '', ?Throwable $previous = null): null, $previous ); + + if (!is_null($interval)) { + $exception->setPayload([ + ...$exception->getPayload(), + 'interval' => $interval, + ]); + } + + return $exception; } /** From 9df89c78c0244d976e50e02868e6fda35d793ef6 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 28 Oct 2024 05:00:36 +0330 Subject: [PATCH 06/16] fix phpdoc types --- src/Exception/OAuthServerException.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 6953575eb..dfe45d59c 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -24,7 +24,7 @@ class OAuthServerException extends Exception { /** - * @var array + * @var array */ private array $payload; @@ -49,7 +49,7 @@ final public function __construct(string $message, int $code, private string $er /** * Returns the current payload. * - * @return array + * @return array */ public function getPayload(): array { @@ -59,7 +59,7 @@ public function getPayload(): array /** * Updates the current payload. * - * @param array $payload + * @param array $payload */ public function setPayload(array $payload): void { From 68512844ccf7e2e9acfb0fc7ef5ddfca233eb089 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 28 Oct 2024 05:06:13 +0330 Subject: [PATCH 07/16] formatting --- tests/Grant/DeviceCodeGrantTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Grant/DeviceCodeGrantTest.php b/tests/Grant/DeviceCodeGrantTest.php index 3a516879a..72df2801a 100644 --- a/tests/Grant/DeviceCodeGrantTest.php +++ b/tests/Grant/DeviceCodeGrantTest.php @@ -757,7 +757,6 @@ public function testSettingInternalVisibility(): void $grant->setClientRepository($clientRepositoryMock); $grant->setDefaultScope(self::DEFAULT_SCOPE); - $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setScopeRepository($scopeRepositoryMock); $grant->setIntervalVisibility(true); From b53b0b70167df1fb1070f4a681f1e4657d70a901 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 28 Oct 2024 05:22:33 +0330 Subject: [PATCH 08/16] fix example --- examples/src/Repositories/DeviceCodeRepository.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/src/Repositories/DeviceCodeRepository.php b/examples/src/Repositories/DeviceCodeRepository.php index 4151c7bfe..434159080 100644 --- a/examples/src/Repositories/DeviceCodeRepository.php +++ b/examples/src/Repositories/DeviceCodeRepository.php @@ -49,6 +49,8 @@ public function getDeviceCodeEntityByDeviceCode(string $deviceCode): ?DeviceCode $deviceCodeEntity->setIdentifier($deviceCode); $deviceCodeEntity->setExpiryDateTime(new DateTimeImmutable('now +1 hour')); $deviceCodeEntity->setClient($clientEntity); + $deviceCodeEntity->setLastPolledAt(new DateTimeImmutable()); + $deviceCodeEntity->setInterval(5); // The user identifier should be set when the user authenticates on the // OAuth server, along with whether they approved the request From 1d9c1214c11242f3e63effeb0ea8783f9a9758db Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 28 Oct 2024 20:29:14 +0330 Subject: [PATCH 09/16] include interval on errors when visible --- src/Grant/DeviceCodeGrant.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Grant/DeviceCodeGrant.php b/src/Grant/DeviceCodeGrant.php index 1a3bd1d3e..12797fa9f 100644 --- a/src/Grant/DeviceCodeGrant.php +++ b/src/Grant/DeviceCodeGrant.php @@ -158,10 +158,14 @@ public function respondToAccessTokenRequest( $this->deviceCodeRepository->persistDeviceCode($deviceCodeEntity); if ($shouldSlowDown) { - throw OAuthServerException::slowDown($deviceCodeEntity->getInterval()); + throw OAuthServerException::slowDown( + $this->intervalVisibility ? $deviceCodeEntity->getInterval() : null + ); } - throw OAuthServerException::authorizationPending($deviceCodeEntity->getInterval()); + throw OAuthServerException::authorizationPending( + $this->intervalVisibility ? $deviceCodeEntity->getInterval() : null + ); } if ($deviceCodeEntity->getUserApproved() === false) { From 0dc16579a00ecae0db9171223853e40f39841230 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 30 Oct 2024 20:31:35 +0330 Subject: [PATCH 10/16] fix example --- examples/src/Repositories/DeviceCodeRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/Repositories/DeviceCodeRepository.php b/examples/src/Repositories/DeviceCodeRepository.php index 434159080..ccb166d4b 100644 --- a/examples/src/Repositories/DeviceCodeRepository.php +++ b/examples/src/Repositories/DeviceCodeRepository.php @@ -49,7 +49,7 @@ public function getDeviceCodeEntityByDeviceCode(string $deviceCode): ?DeviceCode $deviceCodeEntity->setIdentifier($deviceCode); $deviceCodeEntity->setExpiryDateTime(new DateTimeImmutable('now +1 hour')); $deviceCodeEntity->setClient($clientEntity); - $deviceCodeEntity->setLastPolledAt(new DateTimeImmutable()); + $deviceCodeEntity->setLastPolledAt(new DateTimeImmutable('now -5 second')); $deviceCodeEntity->setInterval(5); // The user identifier should be set when the user authenticates on the From 05821f9470ebc31476b87202e73436d1c7ec7f6b Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 14 Nov 2024 22:05:45 +0330 Subject: [PATCH 11/16] move interval argument to the end --- src/Exception/OAuthServerException.php | 4 ++-- src/Grant/DeviceCodeGrant.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index dfe45d59c..43d042647 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -215,7 +215,7 @@ public static function expiredToken(?string $hint = null, ?Throwable $previous = return new static($errorMessage, 11, 'expired_token', 400, $hint, null, $previous); } - public static function authorizationPending(?int $interval = null, string $hint = '', ?Throwable $previous = null): static + public static function authorizationPending(string $hint = '', ?Throwable $previous = null, ?int $interval = null): static { $exception = new static( 'The authorization request is still pending as the end user ' . @@ -245,7 +245,7 @@ public static function authorizationPending(?int $interval = null, string $hint * * @return static */ - public static function slowDown(?int $interval = null, string $hint = '', ?Throwable $previous = null): static + public static function slowDown(string $hint = '', ?Throwable $previous = null, ?int $interval = null): static { $exception = new static( 'The authorization request is still pending and polling should ' . diff --git a/src/Grant/DeviceCodeGrant.php b/src/Grant/DeviceCodeGrant.php index 12797fa9f..91bc8df5f 100644 --- a/src/Grant/DeviceCodeGrant.php +++ b/src/Grant/DeviceCodeGrant.php @@ -159,12 +159,12 @@ public function respondToAccessTokenRequest( if ($shouldSlowDown) { throw OAuthServerException::slowDown( - $this->intervalVisibility ? $deviceCodeEntity->getInterval() : null + interval: $this->intervalVisibility ? $deviceCodeEntity->getInterval() : null ); } throw OAuthServerException::authorizationPending( - $this->intervalVisibility ? $deviceCodeEntity->getInterval() : null + interval: $this->intervalVisibility ? $deviceCodeEntity->getInterval() : null ); } From bb92dfca2c8521881ebb875bf77982f202eed6b7 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 14 Nov 2024 22:17:45 +0330 Subject: [PATCH 12/16] revert interval on authorization_pending error --- src/Exception/OAuthServerException.php | 13 ++----------- src/Grant/DeviceCodeGrant.php | 4 +--- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 43d042647..9cfcf3cf8 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -215,9 +215,9 @@ public static function expiredToken(?string $hint = null, ?Throwable $previous = return new static($errorMessage, 11, 'expired_token', 400, $hint, null, $previous); } - public static function authorizationPending(string $hint = '', ?Throwable $previous = null, ?int $interval = null): static + public static function authorizationPending(string $hint = '', ?Throwable $previous = null): static { - $exception = new static( + return new static( 'The authorization request is still pending as the end user ' . 'hasn\'t yet completed the user interaction steps. The client ' . 'SHOULD repeat the Access Token Request to the token endpoint', @@ -228,15 +228,6 @@ public static function authorizationPending(string $hint = '', ?Throwable $previ null, $previous ); - - if (!is_null($interval)) { - $exception->setPayload([ - ...$exception->getPayload(), - 'interval' => $interval, - ]); - } - - return $exception; } /** diff --git a/src/Grant/DeviceCodeGrant.php b/src/Grant/DeviceCodeGrant.php index 91bc8df5f..a826cb9a8 100644 --- a/src/Grant/DeviceCodeGrant.php +++ b/src/Grant/DeviceCodeGrant.php @@ -163,9 +163,7 @@ public function respondToAccessTokenRequest( ); } - throw OAuthServerException::authorizationPending( - interval: $this->intervalVisibility ? $deviceCodeEntity->getInterval() : null - ); + throw OAuthServerException::authorizationPending(); } if ($deviceCodeEntity->getUserApproved() === false) { From ca53defc6010f1f7225b4121c1b968e441188dcc Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 15 Nov 2024 02:41:09 +0330 Subject: [PATCH 13/16] revert increasing interval on slow_down error --- src/Exception/OAuthServerException.php | 19 +++++-------------- src/Grant/DeviceCodeGrant.php | 14 ++------------ 2 files changed, 7 insertions(+), 26 deletions(-) diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 9cfcf3cf8..24a38d3fe 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -24,7 +24,7 @@ class OAuthServerException extends Exception { /** - * @var array + * @var array */ private array $payload; @@ -49,7 +49,7 @@ final public function __construct(string $message, int $code, private string $er /** * Returns the current payload. * - * @return array + * @return array */ public function getPayload(): array { @@ -59,7 +59,7 @@ public function getPayload(): array /** * Updates the current payload. * - * @param array $payload + * @param array $payload */ public function setPayload(array $payload): void { @@ -236,9 +236,9 @@ public static function authorizationPending(string $hint = '', ?Throwable $previ * * @return static */ - public static function slowDown(string $hint = '', ?Throwable $previous = null, ?int $interval = null): static + public static function slowDown(string $hint = '', ?Throwable $previous = null): static { - $exception = new static( + return new static( 'The authorization request is still pending and polling should ' . 'continue, but the interval MUST be increased ' . 'by 5 seconds for this and all subsequent requests.', @@ -249,15 +249,6 @@ public static function slowDown(string $hint = '', ?Throwable $previous = null, null, $previous ); - - if (!is_null($interval)) { - $exception->setPayload([ - ...$exception->getPayload(), - 'interval' => $interval, - ]); - } - - return $exception; } /** diff --git a/src/Grant/DeviceCodeGrant.php b/src/Grant/DeviceCodeGrant.php index 87b79385f..0a6eaaf7d 100644 --- a/src/Grant/DeviceCodeGrant.php +++ b/src/Grant/DeviceCodeGrant.php @@ -145,21 +145,11 @@ public function respondToAccessTokenRequest( // If device code has no user associated, respond with pending or slow down if (is_null($deviceCodeEntity->getUserIdentifier())) { - $shouldSlowDown = false; - - if ($this->deviceCodePolledTooSoon($deviceCodeEntity) === true) { - $deviceCodeEntity->setInterval($deviceCodeEntity->getInterval() + 5); - - $shouldSlowDown = true; - } - $deviceCodeEntity->setLastPolledAt(new DateTimeImmutable()); $this->deviceCodeRepository->persistDeviceCode($deviceCodeEntity); - if ($shouldSlowDown) { - throw OAuthServerException::slowDown( - interval: $this->intervalVisibility ? $deviceCodeEntity->getInterval() : null - ); + if ($this->deviceCodePolledTooSoon($deviceCodeEntity) === true) { + throw OAuthServerException::slowDown(); } throw OAuthServerException::authorizationPending(); From 3f09114f54bf84a0a3c756215fba7b7942a850dd Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 15 Nov 2024 02:59:38 +0330 Subject: [PATCH 14/16] revert getting interval from device code entity --- examples/src/Repositories/DeviceCodeRepository.php | 3 +-- src/Grant/DeviceCodeGrant.php | 10 +++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/src/Repositories/DeviceCodeRepository.php b/examples/src/Repositories/DeviceCodeRepository.php index d61fe4e8a..ccfb84351 100644 --- a/examples/src/Repositories/DeviceCodeRepository.php +++ b/examples/src/Repositories/DeviceCodeRepository.php @@ -50,8 +50,7 @@ public function getDeviceCodeEntityByDeviceCode($deviceCode): ?DeviceCodeEntityI $deviceCodeEntity->setIdentifier($deviceCode); $deviceCodeEntity->setExpiryDateTime(new DateTimeImmutable('now +1 hour')); $deviceCodeEntity->setClient($clientEntity); - $deviceCodeEntity->setLastPolledAt(new DateTimeImmutable('now -5 second')); - $deviceCodeEntity->setInterval(5); + $deviceCodeEntity->setLastPolledAt(new DateTimeImmutable()); $scopes = []; foreach ($scopes as $scope) { diff --git a/src/Grant/DeviceCodeGrant.php b/src/Grant/DeviceCodeGrant.php index 0a6eaaf7d..12f56b161 100644 --- a/src/Grant/DeviceCodeGrant.php +++ b/src/Grant/DeviceCodeGrant.php @@ -145,10 +145,12 @@ public function respondToAccessTokenRequest( // If device code has no user associated, respond with pending or slow down if (is_null($deviceCodeEntity->getUserIdentifier())) { + $shouldSlowDown = $this->deviceCodePolledTooSoon($deviceCodeEntity->getLastPolledAt()); + $deviceCodeEntity->setLastPolledAt(new DateTimeImmutable()); $this->deviceCodeRepository->persistDeviceCode($deviceCodeEntity); - if ($this->deviceCodePolledTooSoon($deviceCodeEntity) === true) { + if ($shouldSlowDown) { throw OAuthServerException::slowDown(); } @@ -216,11 +218,9 @@ protected function validateDeviceCode(ServerRequestInterface $request, ClientEnt return $deviceCodeEntity; } - private function deviceCodePolledTooSoon(DeviceCodeEntityInterface $deviceCodeEntity): bool + private function deviceCodePolledTooSoon(?DateTimeImmutable $lastPoll): bool { - $lastPoll = $deviceCodeEntity->getLastPolledAt(); - - return $lastPoll !== null && $lastPoll->getTimestamp() + $deviceCodeEntity->getInterval() > time(); + return $lastPoll !== null && $lastPoll->getTimestamp() + $this->defaultInterval > time(); } /** From d4742e83476fbb43e8166ff3e2fde07004b0f038 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 4 Dec 2024 03:06:15 +0330 Subject: [PATCH 15/16] revert changed argument name --- src/Grant/DeviceCodeGrant.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Grant/DeviceCodeGrant.php b/src/Grant/DeviceCodeGrant.php index 12f56b161..aa8cba0f5 100644 --- a/src/Grant/DeviceCodeGrant.php +++ b/src/Grant/DeviceCodeGrant.php @@ -51,7 +51,7 @@ public function __construct( RefreshTokenRepositoryInterface $refreshTokenRepository, private DateInterval $deviceCodeTTL, string $verificationUri, - private readonly int $defaultInterval = 5 + private readonly int $retryInterval = 5 ) { $this->setDeviceCodeRepository($deviceCodeRepository); $this->setRefreshTokenRepository($refreshTokenRepository); @@ -220,7 +220,7 @@ protected function validateDeviceCode(ServerRequestInterface $request, ClientEnt private function deviceCodePolledTooSoon(?DateTimeImmutable $lastPoll): bool { - return $lastPoll !== null && $lastPoll->getTimestamp() + $this->defaultInterval > time(); + return $lastPoll !== null && $lastPoll->getTimestamp() + $this->retryInterval > time(); } /** @@ -264,7 +264,7 @@ protected function issueDeviceCode( $deviceCode->setExpiryDateTime((new DateTimeImmutable())->add($deviceCodeTTL)); $deviceCode->setClient($client); $deviceCode->setVerificationUri($verificationUri); - $deviceCode->setInterval($this->defaultInterval); + $deviceCode->setInterval($this->retryInterval); foreach ($scopes as $scope) { $deviceCode->addScope($scope); From 03dcdd78f6e695e173432878879a3e3140665189 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 4 Dec 2024 03:16:02 +0330 Subject: [PATCH 16/16] revert changed argument name --- src/Repositories/DeviceCodeRepositoryInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Repositories/DeviceCodeRepositoryInterface.php b/src/Repositories/DeviceCodeRepositoryInterface.php index 453ba3bb7..09575ab18 100644 --- a/src/Repositories/DeviceCodeRepositoryInterface.php +++ b/src/Repositories/DeviceCodeRepositoryInterface.php @@ -33,7 +33,7 @@ public function persistDeviceCode(DeviceCodeEntityInterface $deviceCodeEntity): * Get a device code entity. */ public function getDeviceCodeEntityByDeviceCode( - string $deviceCode + string $deviceCodeEntity ): ?DeviceCodeEntityInterface; /**