Skip to content

Commit

Permalink
feat(SDK-4732): Implement support for Back-Channel Logout (#435)
Browse files Browse the repository at this point in the history
  • Loading branch information
evansims authored Dec 7, 2023
1 parent 30aafed commit a5d172a
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 2 deletions.
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@
"source": "https://github.com/auth0/laravel-auth0"
},
"require": {
"php": "^8.0",
"php": "^8.1",
"ext-json": "*",
"auth0/auth0-php": "^8.7",
"auth0/auth0-php": "^8.10",
"illuminate/contracts": "^9 || ^10",
"illuminate/http": "^9 || ^10",
"illuminate/support": "^9 || ^10",
Expand Down
2 changes: 2 additions & 0 deletions config/auth0.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
Configuration::CONFIG_CLIENT_ASSERTION_SIGNING_KEY => Configuration::get(Configuration::CONFIG_CLIENT_ASSERTION_SIGNING_KEY),
Configuration::CONFIG_CLIENT_ASSERTION_SIGNING_ALGORITHM => Configuration::get(Configuration::CONFIG_CLIENT_ASSERTION_SIGNING_ALGORITHM),
Configuration::CONFIG_PUSHED_AUTHORIZATION_REQUEST => Configuration::get(Configuration::CONFIG_PUSHED_AUTHORIZATION_REQUEST),
Configuration::CONFIG_BACKCHANNEL_LOGOUT_CACHE => Configuration::get(Configuration::CONFIG_BACKCHANNEL_LOGOUT_CACHE),
Configuration::CONFIG_BACKCHANNEL_LOGOUT_EXPIRES => Configuration::get(Configuration::CONFIG_BACKCHANNEL_LOGOUT_EXPIRES),
],

'api' => [
Expand Down
17 changes: 17 additions & 0 deletions docs/BackchannelLogout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Backchannel Logout

The Auth0 Laravel SDK supports [Backchannel Logout](https://auth0.com/docs/authenticate/login/logout/back-channel-logout) from v7.12 onward. To use this feature, some additional configuration is necessary:

1. **Add a new route to your application.** This route must be publicly accessible. Auth0 will use it to send backchannel logout requests to your application. For example:

```php
Route::post('/backchannel', function (Request $request) {
if ($request->has('logout_token')) {
app('auth0')->handleBackchannelLogout($request->string('logout_token', '')->trim());
}
});
```

2. **Configure your Auth0 tenant to use Backchannel Logout.** See the [Auth0 documentation](https://auth0.com/docs/authenticate/login/logout/back-channel-logout/configure-back-channel-logout) for more information on how to do this. Please ensure you point the Logout URI to the backchannel route we just added to your application.

Note: If your application's configuration assigns `false` to the `backchannelLogoutCache` SDK configuration property, this feature will be disabled entirely.
2 changes: 2 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ The following environment variables are supported, but should not be adjusted un
| `AUTH0_CLIENT_ASSERTION_SIGNING_KEY` | `String` The key to use for signing client assertions. |
| `AUTH0_CLIENT_ASSERTION_SIGNING_ALGORITHM` | `String` The algorithm to use for signing client assertions. Defaults to `RS256`. |
| `AUTH0_PUSHED_AUTHORIZATION_REQUEST` | `Boolean` Whether the SDK should use Pushed Authorization Requests during authentication. Note that your tenant must have this feature enabled. Defaults to `false`. |
| `AUTH0_BACKCHANNEL_LOGOUT_CACHE` | `String (class name)` A PSR-6 class to use for caching backchannel logout tokens. |
| `AUTH0_BACKCHANNEL_LOGOUT_EXPIRES` | `Integer` How long (in seconds) to cache a backchannel logout token. Defaults to `2592000` (30 days). |

### Order of Priority

Expand Down
11 changes: 11 additions & 0 deletions src/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,24 @@ final class Configuration implements ConfigurationContract
self::CONFIG_TOKEN_CACHE_TTL,
self::CONFIG_HTTP_MAX_RETRIES,
self::CONFIG_COOKIE_EXPIRES,
self::CONFIG_BACKCHANNEL_LOGOUT_EXPIRES,
];

/**
* @var string
*/
public const CONFIG_AUDIENCE = 'audience';

/**
* @var string
*/
public const CONFIG_BACKCHANNEL_LOGOUT_CACHE = 'backchannelLogoutCache';

/**
* @var string
*/
public const CONFIG_BACKCHANNEL_LOGOUT_EXPIRES = 'backchannelLogoutExpires';

/**
* @var string
*/
Expand Down
33 changes: 33 additions & 0 deletions src/Entities/InstanceEntityAbstract.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public function __construct(
protected ?CacheItemPoolInterface $tokenCachePool = null,
protected ?CacheItemPoolInterface $managementTokenCachePool = null,
protected ?string $guardConfigurationKey = null,
protected ?CacheItemPoolInterface $backchannelLogoutCachePool = null,
) {
}

Expand Down Expand Up @@ -119,6 +120,29 @@ abstract public function setConfiguration(
SdkConfiguration | array | null $configuration = null,
): self;

protected function bootBackchannelLogoutCache(array $config): array
{
$backchannelLogoutCache = $config['backchannelLogoutCache'] ?? null;

if (false === $backchannelLogoutCache) {
unset($config['backchannelLogoutCache']);

return $config;
}

if (null === $backchannelLogoutCache) {
$backchannelLogoutCache = $this->getBackchannelLogoutCachePool();
}

if (is_string($backchannelLogoutCache)) {
$backchannelLogoutCache = app(trim($backchannelLogoutCache));
}

$config['backchannelLogoutCache'] = $backchannelLogoutCache instanceof CacheItemPoolInterface ? $backchannelLogoutCache : null;

return $config;
}

protected function bootManagementTokenCache(array $config): array
{
$managementTokenCache = $config['managementTokenCache'] ?? null;
Expand Down Expand Up @@ -256,6 +280,15 @@ protected function createConfiguration(
return $sdkConfiguration;
}

protected function getBackchannelLogoutCachePool(): CacheItemPoolInterface
{
if (! $this->backchannelLogoutCachePool instanceof CacheItemPoolInterface) {
$this->backchannelLogoutCachePool = app(CacheBridge::class);
}

return $this->backchannelLogoutCachePool;
}

protected function getManagementTokenCachePool(): CacheItemPoolInterface
{
if (! $this->managementTokenCachePool instanceof CacheItemPoolInterface) {
Expand Down
23 changes: 23 additions & 0 deletions tests/Unit/ServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,29 @@
->tokenCache->toBeInstanceOf(CacheItemPoolInterface::class);
});

test('bootBackchannelLogoutCache() behaves as expected', function (): void {
$method = new ReflectionMethod(Service::class, 'bootBackchannelLogoutCache');
$method->setAccessible(true);

expect($method->invoke($this->laravel, []))
->backchannelLogoutCache->toBeInstanceOf(CacheBridgeContract::class);

expect($method->invoke($this->laravel, ['backchannelLogoutCache' => null]))
->backchannelLogoutCache->toBeInstanceOf(CacheBridgeContract::class);

expect($method->invoke($this->laravel, ['backchannelLogoutCache' => CacheBridge::class]))
->backchannelLogoutCache->toBeInstanceOf(CacheBridgeContract::class);

expect($method->invoke($this->laravel, ['backchannelLogoutCache' => false]))
->backchannelLogoutCache->toBeNull();

expect($method->invoke($this->laravel, ['backchannelLogoutCache' => MemoryStore::class]))
->backchannelLogoutCache->toBeNull();

expect($method->invoke($this->laravel, ['backchannelLogoutCache' => 'cache.psr6']))
->backchannelLogoutCache->toBeInstanceOf(CacheItemPoolInterface::class);
});

// test('bootManagementTokenCache() behaves as expected', function (): void {
// $method = new ReflectionMethod(Service::class, 'bootManagementTokenCache');
// $method->setAccessible(true);
Expand Down

0 comments on commit a5d172a

Please sign in to comment.