Skip to content

Commit

Permalink
Cognito idp revoke token (#1296)
Browse files Browse the repository at this point in the history
* Add CognitoIdentityProvider RevokeToken

* Update CognitoIdentityProvider/CHANGELOG.md
  • Loading branch information
maxon755 authored Sep 6, 2022
1 parent a08cbcb commit 6782d5c
Show file tree
Hide file tree
Showing 9 changed files with 314 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
- Added operation `AdminDisableUser`
- Added operation `AdminEnableUser`
- AWS api-change: Add a new exception type, ForbiddenException, that is returned when request is not allowed
- Added operation `AdminAddUserToGroup`
- Added operation `AdminRemoveUserFromGroup`
- Added operation `CreateGroup`
- Added operation `ListGroups`
- Added operation `RevokeToken`

## 1.5.0

Expand Down
43 changes: 43 additions & 0 deletions src/CognitoIdentityProviderClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@
use AsyncAws\CognitoIdentityProvider\Exception\SoftwareTokenMFANotFoundException;
use AsyncAws\CognitoIdentityProvider\Exception\TooManyFailedAttemptsException;
use AsyncAws\CognitoIdentityProvider\Exception\TooManyRequestsException;
use AsyncAws\CognitoIdentityProvider\Exception\UnauthorizedException;
use AsyncAws\CognitoIdentityProvider\Exception\UnexpectedLambdaException;
use AsyncAws\CognitoIdentityProvider\Exception\UnsupportedOperationException;
use AsyncAws\CognitoIdentityProvider\Exception\UnsupportedTokenTypeException;
use AsyncAws\CognitoIdentityProvider\Exception\UnsupportedUserStateException;
use AsyncAws\CognitoIdentityProvider\Exception\UserLambdaValidationException;
use AsyncAws\CognitoIdentityProvider\Exception\UsernameExistsException;
Expand Down Expand Up @@ -62,6 +65,7 @@
use AsyncAws\CognitoIdentityProvider\Input\ListUsersRequest;
use AsyncAws\CognitoIdentityProvider\Input\ResendConfirmationCodeRequest;
use AsyncAws\CognitoIdentityProvider\Input\RespondToAuthChallengeRequest;
use AsyncAws\CognitoIdentityProvider\Input\RevokeTokenRequest;
use AsyncAws\CognitoIdentityProvider\Input\SetUserMFAPreferenceRequest;
use AsyncAws\CognitoIdentityProvider\Input\SignUpRequest;
use AsyncAws\CognitoIdentityProvider\Input\VerifySoftwareTokenRequest;
Expand All @@ -87,6 +91,7 @@
use AsyncAws\CognitoIdentityProvider\Result\ListUsersResponse;
use AsyncAws\CognitoIdentityProvider\Result\ResendConfirmationCodeResponse;
use AsyncAws\CognitoIdentityProvider\Result\RespondToAuthChallengeResponse;
use AsyncAws\CognitoIdentityProvider\Result\RevokeTokenResponse;
use AsyncAws\CognitoIdentityProvider\Result\SetUserMFAPreferenceResponse;
use AsyncAws\CognitoIdentityProvider\Result\SignUpResponse;
use AsyncAws\CognitoIdentityProvider\Result\VerifySoftwareTokenResponse;
Expand Down Expand Up @@ -1243,6 +1248,44 @@ public function respondToAuthChallenge($input): RespondToAuthChallengeResponse
return new RespondToAuthChallengeResponse($response);
}

/**
* Revokes all of the access tokens generated by the specified refresh token. After the token is revoked, you can't use
* the revoked token to access Amazon Cognito authenticated APIs.
*
* @see https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_RevokeToken.html
* @see https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-cognito-idp-2016-04-18.html#revoketoken
*
* @param array{
* Token: string,
* ClientId: string,
* ClientSecret?: string,
* @region?: string,
* }|RevokeTokenRequest $input
*
* @throws TooManyRequestsException
* @throws InternalErrorException
* @throws UnauthorizedException
* @throws InvalidParameterException
* @throws UnsupportedOperationException
* @throws UnsupportedTokenTypeException
* @throws ForbiddenException
*/
public function revokeToken($input): RevokeTokenResponse
{
$input = RevokeTokenRequest::create($input);
$response = $this->getResponse($input->request(), new RequestContext(['operation' => 'RevokeToken', 'region' => $input->getRegion(), 'exceptionMapping' => [
'TooManyRequestsException' => TooManyRequestsException::class,
'InternalErrorException' => InternalErrorException::class,
'UnauthorizedException' => UnauthorizedException::class,
'InvalidParameterException' => InvalidParameterException::class,
'UnsupportedOperationException' => UnsupportedOperationException::class,
'UnsupportedTokenTypeException' => UnsupportedTokenTypeException::class,
'ForbiddenException' => ForbiddenException::class,
]]));

return new RevokeTokenResponse($response);
}

/**
* Set the user's multi-factor authentication (MFA) method preference, including which MFA factors are activated and if
* any are preferred. Only one factor can be set as preferred. The preferred MFA factor will be used to authenticate a
Expand Down
22 changes: 22 additions & 0 deletions src/Exception/UnauthorizedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace AsyncAws\CognitoIdentityProvider\Exception;

use AsyncAws\Core\Exception\Http\ClientException;
use Symfony\Contracts\HttpClient\ResponseInterface;

/**
* Exception that is thrown when the request isn't authorized. This can happen due to an invalid access token in the
* request.
*/
final class UnauthorizedException extends ClientException
{
protected function populateResult(ResponseInterface $response): void
{
$data = $response->toArray(false);

if (null !== $v = (isset($data['message']) ? (string) $data['message'] : null)) {
$this->message = $v;
}
}
}
21 changes: 21 additions & 0 deletions src/Exception/UnsupportedOperationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace AsyncAws\CognitoIdentityProvider\Exception;

use AsyncAws\Core\Exception\Http\ClientException;
use Symfony\Contracts\HttpClient\ResponseInterface;

/**
* Exception that is thrown when you attempt to perform an operation that isn't enabled for the user pool client.
*/
final class UnsupportedOperationException extends ClientException
{
protected function populateResult(ResponseInterface $response): void
{
$data = $response->toArray(false);

if (null !== $v = (isset($data['message']) ? (string) $data['message'] : null)) {
$this->message = $v;
}
}
}
21 changes: 21 additions & 0 deletions src/Exception/UnsupportedTokenTypeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace AsyncAws\CognitoIdentityProvider\Exception;

use AsyncAws\Core\Exception\Http\ClientException;
use Symfony\Contracts\HttpClient\ResponseInterface;

/**
* Exception that is thrown when an unsupported token is passed to an operation.
*/
final class UnsupportedTokenTypeException extends ClientException
{
protected function populateResult(ResponseInterface $response): void
{
$data = $response->toArray(false);

if (null !== $v = (isset($data['message']) ? (string) $data['message'] : null)) {
$this->message = $v;
}
}
}
136 changes: 136 additions & 0 deletions src/Input/RevokeTokenRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?php

namespace AsyncAws\CognitoIdentityProvider\Input;

use AsyncAws\Core\Exception\InvalidArgument;
use AsyncAws\Core\Input;
use AsyncAws\Core\Request;
use AsyncAws\Core\Stream\StreamFactory;

final class RevokeTokenRequest extends Input
{
/**
* The refresh token that you want to revoke.
*
* @required
*
* @var string|null
*/
private $token;

/**
* The client ID for the token that you want to revoke.
*
* @required
*
* @var string|null
*/
private $clientId;

/**
* The secret for the client ID. This is required only if the client ID has a secret.
*
* @var string|null
*/
private $clientSecret;

/**
* @param array{
* Token?: string,
* ClientId?: string,
* ClientSecret?: string,
* @region?: string,
* } $input
*/
public function __construct(array $input = [])
{
$this->token = $input['Token'] ?? null;
$this->clientId = $input['ClientId'] ?? null;
$this->clientSecret = $input['ClientSecret'] ?? null;
parent::__construct($input);
}

public static function create($input): self
{
return $input instanceof self ? $input : new self($input);
}

public function getClientId(): ?string
{
return $this->clientId;
}

public function getClientSecret(): ?string
{
return $this->clientSecret;
}

public function getToken(): ?string
{
return $this->token;
}

/**
* @internal
*/
public function request(): Request
{
// Prepare headers
$headers = [
'Content-Type' => 'application/x-amz-json-1.1',
'X-Amz-Target' => 'AWSCognitoIdentityProviderService.RevokeToken',
];

// Prepare query
$query = [];

// Prepare URI
$uriString = '/';

// Prepare Body
$bodyPayload = $this->requestBody();
$body = empty($bodyPayload) ? '{}' : json_encode($bodyPayload, 4194304);

// Return the Request
return new Request('POST', $uriString, $query, $headers, StreamFactory::create($body));
}

public function setClientId(?string $value): self
{
$this->clientId = $value;

return $this;
}

public function setClientSecret(?string $value): self
{
$this->clientSecret = $value;

return $this;
}

public function setToken(?string $value): self
{
$this->token = $value;

return $this;
}

private function requestBody(): array
{
$payload = [];
if (null === $v = $this->token) {
throw new InvalidArgument(sprintf('Missing parameter "Token" for "%s". The value cannot be null.', __CLASS__));
}
$payload['Token'] = $v;
if (null === $v = $this->clientId) {
throw new InvalidArgument(sprintf('Missing parameter "ClientId" for "%s". The value cannot be null.', __CLASS__));
}
$payload['ClientId'] = $v;
if (null !== $v = $this->clientSecret) {
$payload['ClientSecret'] = $v;
}

return $payload;
}
}
9 changes: 9 additions & 0 deletions src/Result/RevokeTokenResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace AsyncAws\CognitoIdentityProvider\Result;

use AsyncAws\Core\Result;

class RevokeTokenResponse extends Result
{
}
33 changes: 33 additions & 0 deletions tests/Unit/Input/RevokeTokenRequestTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace AsyncAws\CognitoIdentityProvider\Tests\Unit\Input;

use AsyncAws\CognitoIdentityProvider\Input\RevokeTokenRequest;
use AsyncAws\Core\Test\TestCase;

class RevokeTokenRequestTest extends TestCase
{
public function testRequest(): void
{
$input = new RevokeTokenRequest([
'Token' => 'refresh_token',
'ClientId' => 'client_id',
'ClientSecret' => 'client_secret',
]);

// see https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_RevokeToken.html
$expected = '
POST / HTTP/1.0
Content-Type: application/x-amz-json-1.1
X-AMZ-Target: AWSCognitoIdentityProviderService.RevokeToken
{
"Token": "refresh_token",
"ClientId": "client_id",
"ClientSecret": "client_secret"
}
';

self::assertRequestEqualsHttpRequest($expected, $input->request());
}
}
24 changes: 24 additions & 0 deletions tests/Unit/Result/RevokeTokenResponseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace AsyncAws\CognitoIdentityProvider\Tests\Unit\Result;

use AsyncAws\CognitoIdentityProvider\Result\RevokeTokenResponse;
use AsyncAws\Core\Response;
use AsyncAws\Core\Test\Http\SimpleMockedResponse;
use AsyncAws\Core\Test\TestCase;
use Psr\Log\NullLogger;
use Symfony\Component\HttpClient\MockHttpClient;

class RevokeTokenResponseTest extends TestCase
{
public function testRevokeTokenResponse(): void
{
self::markTestSkipped('The response has no body to test');

// see https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_RevokeToken.html
$response = new SimpleMockedResponse('{}');

$client = new MockHttpClient($response);
$result = new RevokeTokenResponse(new Response($client->request('POST', 'http://localhost'), $client, new NullLogger()));
}
}

0 comments on commit 6782d5c

Please sign in to comment.