Skip to content

Commit

Permalink
Added method for revoking access and refresh tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickbussmann committed Jul 9, 2022
1 parent 3a4576b commit d9d1797
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ All Notable changes to `oauth2-apple` will be documented in this file
### Security
- Nothing

## 0.2.9 - 2022-07-09

### Added
- Method for revoking access and refresh tokens [#37](https://github.com/patrickbussmann/oauth2-apple/issues/37)

## 0.2.8 - 2022-05-10

### Fixed
Expand Down
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,34 @@ if (!isset($_POST['code'])) {
}
```

### Revoke Code Flow

```php
// $leeway is needed for clock skew
Firebase\JWT\JWT::$leeway = 60;

$provider = new League\OAuth2\Client\Provider\Apple([
'clientId' => '{apple-client-id}',
'teamId' => '{apple-team-id}', // 1A234BFK46 https://developer.apple.com/account/#/membership/ (Team ID)
'keyFileId' => '{apple-key-file-id}', // 1ABC6523AA https://developer.apple.com/account/resources/authkeys/list (Key ID)
'keyFilePath' => '{apple-key-file-path}', // __DIR__ . '/AuthKey_1ABC6523AA.p8' -> Download key above
'redirectUri' => 'https://example.com/callback-url',
]);

$token = $token->getToken(); // Use the token of "Authorization Code Flow" which you saved somewhere for the user


try {
$provider->revokeAccessToken($token /*, 'access_token' or 'refresh_token' */);
// Successfully revoked the token!

} catch (Exception $e) {

// Failed to revoke
exit(':-(');
}
```

### Managing Scopes

When creating your Apple authorization URL, you can specify the state and scopes your application may authorize.
Expand Down
58 changes: 58 additions & 0 deletions src/Provider/Apple.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ public function getBaseAccessTokenUrl(array $params)
return 'https://appleid.apple.com/auth/token';
}

/**
* Get revoke token url to revoke token
*
* @return string
*/
public function getBaseRevokeTokenUrl(array $params)
{
return 'https://appleid.apple.com/auth/revoke';
}

/**
* Get provider url to fetch user details
*
Expand Down Expand Up @@ -247,6 +257,54 @@ public function getAccessToken($grant, array $options = [])
return parent::getAccessToken($grant, $options);
}

/**
* Revokes an access or refresh token using a specified token.
*
* @param string $token
* @param string|null $tokenTypeHint
* @return \Psr\Http\Message\RequestInterface
*/
public function revokeAccessToken($token, $tokenTypeHint = null)
{
$configuration = $this->getConfiguration();
$time = new \DateTimeImmutable();
$time = $time->setTime($time->format('H'), $time->format('i'), $time->format('s'));
$expiresAt = $time->modify('+1 Hour');
$expiresAt = $expiresAt->setTime($expiresAt->format('H'), $expiresAt->format('i'), $expiresAt->format('s'));

$clientSecret = $configuration->builder()
->issuedBy($this->teamId)
->permittedFor('https://appleid.apple.com')
->issuedAt($time)
->expiresAt($expiresAt)
->relatedTo($this->clientId)
->withHeader('alg', 'ES256')
->withHeader('kid', $this->keyFileId)
->getToken($configuration->signer(), $configuration->signingKey());

$params = [
'client_id' => $this->clientId,
'client_secret' => $clientSecret->toString(),
'token' => $token
];
if ($tokenTypeHint !== null) {
$params += [
'token_type_hint' => $tokenTypeHint
];
}

$method = $this->getAccessTokenMethod();
$url = $this->getBaseRevokeTokenUrl($params);
if (property_exists($this, 'optionProvider')) {
$options = $this->optionProvider->getAccessTokenOptions(self::METHOD_POST, $params);
} else {
$options = $this->getAccessTokenOptions($params);
}
$request = $this->getRequest($method, $url, $options);

return $this->getParsedResponse($request);
}

/**
* @return Configuration
*/
Expand Down
43 changes: 43 additions & 0 deletions test/src/Provider/AppleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,49 @@ public function testGetAccessTokenFailedBecauseAppleHasError()
]);
}

public function testRevokeAccessToken()
{
$provider = new TestApple([
'clientId' => 'mock.example',
'teamId' => 'mock.team.id',
'keyFileId' => 'mock.file.id',
'keyFilePath' => __DIR__ . '/../../resources/p256-private-key.p8',
'redirectUri' => 'none'
]);
$provider = m::mock($provider);

$client = m::mock(ClientInterface::class);
$client->shouldReceive('send')
->times(1)
->andReturn(new Response(200, [], json_encode([])));
$provider->setHttpClient($client);

$this->assertEmpty($provider->revokeAccessToken('hello-world', 'access_token'));
}

public function testRevokeAccessTokenFailedBecauseAppleHasError()
{
$this->expectException('Exception');
$this->expectExceptionMessage('invalid_request');

$provider = new TestApple([
'clientId' => 'mock.example',
'teamId' => 'mock.team.id',
'keyFileId' => 'mock.file.id',
'keyFilePath' => __DIR__ . '/../../resources/p256-private-key.p8',
'redirectUri' => 'none'
]);
$provider = m::mock($provider);

$client = m::mock(ClientInterface::class);
$client->shouldReceive('send')
->times(1)
->andReturn(new Response(400, [], json_encode(['error' => 'invalid_request'])));
$provider->setHttpClient($client);

$provider->revokeAccessToken('hello-world');
}

public function testFetchingOwnerDetails()
{
$provider = $this->getProvider();
Expand Down

0 comments on commit d9d1797

Please sign in to comment.