Skip to content

Commit

Permalink
Support complete_verification_uri
Browse files Browse the repository at this point in the history
  • Loading branch information
Sephster committed Jan 31, 2024
1 parent 15c214d commit 4e9de6e
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 16 deletions.
4 changes: 3 additions & 1 deletion src/Entities/DeviceCodeEntityInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public function getVerificationUri(): string;

public function setVerificationUri(string $verificationUri): void;

public function getVerificationUriComplete(): string;

public function getLastPolledAt(): ?DateTimeImmutable;

public function setLastPolledAt(DateTimeImmutable $lastPolledAt): void;
Expand All @@ -34,7 +36,7 @@ public function setInterval(int $interval): void;

public function getIntervalInAuthResponse(): bool;

public function setIntervalInAuthResponse(bool $intervalInAuthResponse): bool;
public function setIntervalInAuthResponse(bool $intervalInAuthResponse): void;

public function getUserApproved(): bool;

Expand Down
20 changes: 18 additions & 2 deletions src/Entities/Traits/DeviceCodeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ trait DeviceCodeTrait
{
private bool $userApproved = false;
private bool $intervalInAuthResponse = false;
private bool $includeVerificationUriComplete = false;
private int $interval = 5;
private string $userCode;
private string $verificationUri;
Expand All @@ -45,6 +46,11 @@ public function setVerificationUri(string $verificationUri): void
$this->verificationUri = $verificationUri;
}

public function getVerificationUriComplete(): string
{
return $this->verificationUri . '?user_code=' . $this->userCode;
}

abstract public function getClient(): ClientEntityInterface;

abstract public function getExpiryDateTime(): DateTimeImmutable;
Expand Down Expand Up @@ -81,9 +87,9 @@ public function getIntervalInAuthResponse(): bool
return $this->intervalInAuthResponse;
}

public function setIntervalInAuthResponse(bool $intervalInAuthResponse): bool
public function setIntervalInAuthResponse(bool $intervalInAuthResponse): void
{
return $this->intervalInAuthResponse = $intervalInAuthResponse;
$this->intervalInAuthResponse = $intervalInAuthResponse;
}

public function getUserApproved(): bool
Expand All @@ -95,4 +101,14 @@ public function setUserApproved(bool $userApproved): void
{
$this->userApproved = $userApproved;
}

public function getVerificationUriCompleteInAuthResponse(): bool
{
return $this->includeVerificationUriComplete;
}

public function setVerificationUriCompleteInAuthResponse(bool $includeVerificationUriComplete): void
{
$this->includeVerificationUriComplete = $includeVerificationUriComplete;
}
}
25 changes: 16 additions & 9 deletions src/Grant/DeviceCodeGrant.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
use League\OAuth2\Server\Repositories\DeviceCodeRepositoryInterface;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\RequestEvent;
use League\OAuth2\Server\RequestTypes\DeviceAuthorizationRequestInterface;
use League\OAuth2\Server\ResponseTypes\DeviceCodeResponse;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use LogicException;
Expand All @@ -48,6 +47,7 @@
class DeviceCodeGrant extends AbstractGrant
{
protected DeviceCodeRepositoryInterface $deviceCodeRepository;
private bool $includeVerificationUriComplete = false;
private bool $intervalVisibility = false;
private string $verificationUri;

Expand Down Expand Up @@ -100,7 +100,7 @@ public function respondToDeviceAuthorizationRequest(ServerRequestInterface $requ
$scopes
);

// TODO: Check payload generation
// TODO: Check payload generation. Is this the best way to handle things?
$payload = [
'device_code_id' => $deviceCode->getIdentifier(),
'user_code' => $deviceCode->getUserCode(),
Expand All @@ -110,9 +110,15 @@ public function respondToDeviceAuthorizationRequest(ServerRequestInterface $requ
'scopes' => $deviceCode->getScopes(),
];

$response = new DeviceCodeResponse();

if ($this->includeVerificationUriComplete === true) {
$response->includeVerificationUriComplete();
$payload['verification_uri_complete'] = $deviceCode->getVerificationUriComplete();
}

$jsonPayload = json_encode($payload, JSON_THROW_ON_ERROR);

$response = new DeviceCodeResponse();
$response->setDeviceCode($deviceCode);
$response->setPayload($this->encrypt($jsonPayload));

Expand Down Expand Up @@ -187,9 +193,7 @@ public function respondToAccessTokenRequest(
}

/**
*
* @throws OAuthServerException
*
*/
protected function validateDeviceCode(ServerRequestInterface $request, ClientEntityInterface $client): DeviceCodeEntityInterface
{
Expand Down Expand Up @@ -240,9 +244,7 @@ private function deviceCodePolledTooSoon(?DateTimeImmutable $lastPoll): bool
}

/**
*
* @throws OAuthServerException
*
*/
protected function decodeDeviceCode(string $encryptedDeviceCode): stdClass
{
Expand All @@ -255,7 +257,6 @@ protected function decodeDeviceCode(string $encryptedDeviceCode): stdClass

/**
* Set the verification uri
*
*/
public function setVerificationUri(string $verificationUri): void
{
Expand Down Expand Up @@ -288,7 +289,7 @@ protected function issueDeviceCode(
DateInterval $deviceCodeTTL,
ClientEntityInterface $client,
string $verificationUri,
array $scopes = []
array $scopes = [],
): DeviceCodeEntityInterface {
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;

Expand Down Expand Up @@ -354,6 +355,7 @@ protected function generateUniqueUserCode(int $length = 8): string
// @codeCoverageIgnoreEnd
}

// TODO: Check interface
public function setIntervalVisibility(bool $intervalVisibility): void
{
$this->intervalVisibility = $intervalVisibility;
Expand All @@ -363,4 +365,9 @@ public function getIntervalVisibility(): bool
{
return $this->intervalVisibility;
}

public function setIncludeVerificationUriComplete(bool $includeVerificationUriComplete): void
{
$this->includeVerificationUriComplete = $includeVerificationUriComplete;
}
}
2 changes: 0 additions & 2 deletions src/Grant/GrantTypeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ public function canRespondToDeviceAuthorizationRequest(ServerRequestInterface $r
*/
public function respondToDeviceAuthorizationRequest(ServerRequestInterface $request): DeviceCodeResponse;

// TODO: Check DeviceAuthorizationRequest

/**
* If the grant can respond to a device authorization request this method should be called to validate the parameters of
* the request.
Expand Down
11 changes: 10 additions & 1 deletion src/ResponseTypes/DeviceCodeResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class DeviceCodeResponse extends AbstractResponseType
{
protected DeviceCodeEntityInterface $deviceCode;
protected string $payload;
private bool $includeVerificationUriComplete = false;

/**
* {@inheritdoc}
Expand All @@ -38,8 +39,11 @@ public function generateHttpResponse(ResponseInterface $response): ResponseInter
'user_code' => $this->deviceCode->getUserCode(),
'verification_uri' => $this->deviceCode->getVerificationUri(),
'expires_in' => $expireDateTime - time(),
// TODO: Potentially add in verification_uri_complete - it is optional
];

if ($this->includeVerificationUriComplete === true) {
$responseParams['verification_uri_complete'] = $this->deviceCode->getVerificationUriComplete();
}

if ($this->deviceCode->getIntervalInAuthResponse() === true) {
$responseParams['interval'] = $this->deviceCode->getInterval();
Expand Down Expand Up @@ -75,6 +79,11 @@ public function setDeviceCode(DeviceCodeEntityInterface $deviceCode): void
$this->deviceCode = $deviceCode;
}

public function includeVerificationUriComplete(): void
{
$this->includeVerificationUriComplete = true;
}

/**
* Add custom fields to your Bearer Token response here, then override
* AuthorizationServer::getResponseType() to pull in your version of
Expand Down
48 changes: 47 additions & 1 deletion tests/Grant/DeviceCodeGrantTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,53 @@ public function testRespondToDeviceAuthorizationRequest(): void
self::assertEquals('http://foo/bar', $responseJson->verification_uri);
}

public function testRespondToDeviceAuthorizationRequestWithVerificationUriComplete(): void
{
$client = new ClientEntity();
$client->setIdentifier('foo');

$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);

$deviceCodeRepository = $this->getMockBuilder(DeviceCodeRepositoryInterface::class)->getMock();
$deviceCodeRepository->method('getNewDeviceCode')->willReturn(new DeviceCodeEntity());

$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->setIncludeVerificationUriComplete(true);

$grant->setClientRepository($clientRepositoryMock);
$grant->setDefaultScope(self::DEFAULT_SCOPE);
$grant->setEncryptionKey($this->cryptStub->getKey());
$grant->setScopeRepository($scopeRepositoryMock);

$request = (new ServerRequest())->withParsedBody([
'client_id' => 'foo',
'scope' => 'basic',
]);

$deviceCodeResponse = $grant->respondToDeviceAuthorizationRequest($request);

$responseJson = json_decode($deviceCodeResponse->generateHttpResponse(new Response())->getBody()->__toString());

self::assertObjectHasProperty('device_code', $responseJson);
self::assertObjectHasProperty('user_code', $responseJson);
self::assertObjectHasProperty('verification_uri', $responseJson);
self::assertObjectHasProperty('verification_uri_complete', $responseJson);
self::assertEquals('http://foo/bar', $responseJson->verification_uri);
self::assertEquals('http://foo/bar?user_code=' . $responseJson->user_code, $responseJson->verification_uri_complete);
}

public function testValidateDeviceAuthorizationRequestMissingClient(): void
{
$client = new ClientEntity();
Expand Down Expand Up @@ -287,7 +334,6 @@ public function testDeviceAuthorizationResponse(): void
$this::assertObjectHasProperty('device_code', $responseObject);
$this::assertObjectHasProperty('user_code', $responseObject);
$this::assertObjectHasProperty('verification_uri', $responseObject);
// TODO: $this->assertObjectHasAttribute('verification_uri_complete', $responseObject);
$this::assertObjectHasProperty('expires_in', $responseObject);
// TODO: $this->assertObjectHasAttribute('interval', $responseObject);
}
Expand Down

0 comments on commit 4e9de6e

Please sign in to comment.