Skip to content

Commit 35d05a6

Browse files
authored
feat: Support for Back-Channel Logout (#882)
1 parent f7352e2 commit 35d05a6

File tree

6 files changed

+236
-18
lines changed

6 files changed

+236
-18
lines changed

README.md

+13-8
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@ WordPress Plugin for [Auth0](https://auth0.com) Authentication
66

77
:rocket: [Getting Started](#getting-started) - :computer: [SDK Usage](#sdk-usage) - 📆 [Support Policy](#support-policy) - :speech_balloon: [Feedback](#feedback)
88

9-
## Plugin Overview
9+
## Overview
1010

1111
The Auth0 WordPress plugin replaces the standard WordPress login flow with a new authentication process using Auth0's Universal Login experience. This enables you to secure your WordPress site with Auth0's advanced features, such as MFA, SSO, Passwordless, PassKey, and so on.
1212

1313
> [!IMPORTANT]
14-
> This plugin is **NOT** a SDK (Software Development Kit.) We do not provide support for customizing the plugin's behavior or integrating it into WordPress in any way beyond what is expressly explained here. If you are looking for an SDK, please build a custom solution from the [Auth0-PHP SDK](https://github.com/auth0/auth0-php) instead.
14+
> This plugin is **NOT** a SDK (Software Development Kit.) It's APIs are internal and not intended for developers to extend directly. We do not support altering the plugin's behavior or integrating it in any way beyond what is outlined in this README. If you're looking to build a more extensive integration, please create a solution using the [Auth0-PHP SDK](https://github.com/auth0/auth0-php) instead.
15+
16+
> [!WARNING]
17+
> v4 of the plugin is no longer supported as of June 2023. We are no longer providing new features or bugfixes for that release. Please upgrade to v5 as soon as possible.
1518
1619
## Getting Started
1720

@@ -25,9 +28,6 @@ The Auth0 WordPress plugin replaces the standard WordPress login flow with a new
2528
2629
### Installation
2730

28-
> [!WARNING]
29-
> v4 of the plugin is no longer supported as of June 2023. We are no longer providing new features or bugfixes for that release. Please upgrade to v5 as soon as possible.
30-
3131
<!-- // Disabled while we complete this distribution configuration
3232
#### Release Package
3333
Releases are available from the GitHub repository [github.com/auth0/wordpress/releases](https://github.com/auth0/wordpress/releases), packaged as ZIP archives. Every release has an accompanying signature file for verification if desired.
@@ -54,9 +54,11 @@ openssl dgst -verify signing.key.pub -keyform PEM -sha256 -signature RELEASE.zip
5454

5555
#### Composer
5656

57-
The plugin supports installation through [Composer](https://getcomposer.org/), and is [WPackagist](https://wpackagist.org/) compatible. This approach is preferred when using [Bedrock](https://roots.io/bedrock/) or [WordPress Core](https://github.com/johnpbloch/wordpress-core-installer), but will work with virtually any WordPress installation.
57+
The plugin supports installation through [Composer](https://getcomposer.org/), and is [WPackagist](https://wpackagist.org/) compatible. This approach is preferred when using [Bedrock](https://roots.io/bedrock/), but will work with virtually any WordPress installation.
5858

59-
When using Composer-based WordPress configurations like Bedrock, you'll usually run this command from the root WordPress installation directory. Still, it's advisable to check the documentation the project's maintainers provided for the best guidance. This command can be run from the `wp-content/plugins` sub-directory for standard WordPress installations.
59+
For [Bedrock](https://roots.io/bedrock/) installations, you'll usually run this command from the root WordPress installation directory, but check the documentation the project's maintainers provide for the best guidance.
60+
61+
For standard WordPress installations, this command can be run from the `wp-content/plugins` sub-directory.
6062

6163
```
6264
composer require symfony/http-client nyholm/psr7 auth0/wordpress:^5.0
@@ -76,7 +78,10 @@ If you are using Bedrock or another Composer-based configuration, you can try in
7678
<!-- // Disabled while we complete this distribution configuration
7779
#### WordPress Dashboard
7880
79-
Installation from your WordPress dashboard is also supported. This approach first installs a small setup script that will verify that your host environment is compatible. Afterward, the latest plugin release will be downloaded from the GitHub repository, have its file signature verified, and ultimately installed.
81+
> [!CAUTION]
82+
> We recommend against using the WordPress Dashboard or Marketplace to install or update the plugin. Automattic does not implement reliable security measures to protect plugins from tampering, and this approach presents a supply chain risk. It is not recommended for production sites.
83+
84+
Installation from your WordPress dashboard is supported. This approach first installs a small setup script that will verify that your host environment is compatible. Afterward, the latest plugin release will be downloaded from the GitHub repository, have its file signature verified, and ultimately installed.
8085
8186
- Open your WordPress Dashboard.
8287
- Click 'Plugins", then 'Add New,' and search for 'Auth0'.

src/Actions/Authentication.php

+29
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Auth0\SDK\Exception\StateException;
88
use Auth0\SDK\Store\CookieStore;
99
use Auth0\WordPress\Database;
10+
use Auth0\WordPress\Utilities\Sanitize;
1011
use Throwable;
1112
use WP_Error;
1213
use WP_User;
@@ -415,6 +416,34 @@ public function onLogin(): void
415416
return;
416417
}
417418

419+
if (isset($_GET['auth0_fb'])) {
420+
$incomingFallbackRequest = Sanitize::string($_GET['auth0_fb']);
421+
$fallbackSecret = $this->getPlugin()->getOptionString('authentication', 'fallback_secret');
422+
423+
if ($incomingFallbackRequest === $fallbackSecret) {
424+
return;
425+
}
426+
427+
// Ignore invalid requests; continue as normal.
428+
}
429+
430+
if (isset($_GET['auth0_bcl']) && isset($_POST['logout_token'])) {
431+
$incomingBackchannelLogoutRequest = Sanitize::string($_GET['auth0_bcl']);
432+
$backchannelLogoutSecret = $this->getPlugin()->getOptionString('authentication', 'backchannel_logout_secret');
433+
434+
if ($incomingBackchannelLogoutRequest === $backchannelLogoutSecret) {
435+
$logoutToken = Sanitize::string($_POST['logout_token']);
436+
437+
try {
438+
$this->getSdk()->handleBackchannelLogout($logoutToken);
439+
exit();
440+
} catch (Throwable) {
441+
}
442+
}
443+
444+
// Ignore invalid requests; continue as normal.
445+
}
446+
418447
// Don't allow caching of this route
419448
nocache_headers();
420449

src/Actions/Configuration.php

+164-7
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ final class Configuration extends Base
2727
'description' => '',
2828
'options' => [
2929
'enable' => [
30-
'title' => 'Manage Authentication',
30+
'title' => 'Enable Authentication',
3131
'type' => 'boolean',
3232
'enabled' => 'isPluginReady',
3333
'description' => ['getOptionDescription', 'enable'],
@@ -39,7 +39,7 @@ final class Configuration extends Base
3939
],
4040
],
4141
'accounts' => [
42-
'title' => 'WordPress Account Management',
42+
'title' => 'WordPress Users Management',
4343
'description' => '',
4444
'options' => [
4545
'matching' => [
@@ -48,15 +48,15 @@ final class Configuration extends Base
4848
'enabled' => 'isPluginReady',
4949
'description' => '<b>Flexible</b> allows users to sign in using more than one connection type.<br /><b>Strict</b> is more secure, but may lead to confusion for users who forget their sign in method.',
5050
'select' => [
51-
'flexible' => 'Flexible: Match Verified Email Addresses to Accounts',
52-
'strict' => 'Strict: Match Unique Connections to Accounts',
51+
'flexible' => 'Flexible: Match Verified Email Addresses to Users',
52+
'strict' => 'Strict: Match Unique Connections to Users',
5353
],
5454
],
5555
'missing' => [
56-
'title' => 'Absentee Accounts',
56+
'title' => 'Missing Users',
5757
'type' => 'text',
5858
'enabled' => 'isPluginReady',
59-
'description' => 'What to do after a successful sign in, but there is no matching WordPress account.<br />For Database Connections, the "Disable Sign Ups" setting will be honored prior to this.',
59+
'description' => 'What to do after a successful sign in, but there is no matching WordPress account.<br />For Database Connections, the "Disable Sign Ups" setting takes priority over this option.',
6060
'select' => [
6161
'reject' => 'Deny access',
6262
'create' => 'Create account',
@@ -66,7 +66,7 @@ final class Configuration extends Base
6666
'title' => 'Default Role',
6767
'type' => 'text',
6868
'enabled' => 'isPluginReady',
69-
'description' => 'The role to assign new WordPress accounts created by the plugin.',
69+
'description' => 'The role to assign new WordPress users created by the plugin.',
7070
'select' => 'getRoleOptions',
7171
],
7272
'passwordless' => [
@@ -219,6 +219,12 @@ final class Configuration extends Base
219219
'false' => 'Disabled',
220220
],
221221
],
222+
'fallback_secret' => [
223+
'title' => 'Fallback Secret',
224+
'type' => 'password',
225+
'enabled' => 'isPluginReady',
226+
'description' => ['getOptionDescription', 'fallback_secret'],
227+
],
222228
],
223229
],
224230
'client_advanced' => [
@@ -382,10 +388,60 @@ final class Configuration extends Base
382388
],
383389
],
384390
],
391+
'backchannel_logout' => [
392+
'title' => 'Back-Channel Logout',
393+
'description' => 'You must configure your <a href="https://auth0.com/docs/authenticate/login/logout/back-channel-logout/configure-back-channel-logout" target="_blank">Auth0 tenant</a> to enable this feature.',
394+
'options' => [
395+
'enabled' => [
396+
'title' => 'Enabled',
397+
'type' => 'boolean',
398+
'enabled' => 'isPluginReady',
399+
'description' => 'Enable this if your site is <b>exclusively</b> served over HTTPS.',
400+
'select' => [
401+
'false' => 'Disabled',
402+
'true' => 'Enabled',
403+
],
404+
],
405+
'ttl' => [
406+
'title' => 'Logout Expiration',
407+
'type' => 'int',
408+
'enabled' => 'isPluginReady',
409+
'description' => 'How long before unclaimed Back-Channel Logout tokens expire.',
410+
'select' => [
411+
0 => 'Default (1 month)',
412+
1800 => '30 minutes',
413+
3600 => '1 hour',
414+
3600 * 6 => '6 hours',
415+
3600 * 12 => '12 hours',
416+
3600 * 24 => '1 day',
417+
86400 * 2 => '2 days',
418+
86400 * 4 => '4 days',
419+
86400 * 7 => '1 week',
420+
86400 * 14 => '2 weeks',
421+
86400 * 30 => '1 month',
422+
],
423+
],
424+
'secret' => [
425+
'title' => 'Secret',
426+
'type' => 'password',
427+
'enabled' => 'isPluginReady',
428+
'description' => ['getOptionDescription', 'backchannel_logout_secret'],
429+
],
430+
],
431+
],
385432
],
386433
],
434+
self::CONST_PAGE_TOOLS => [
435+
'title' => 'Auth0 — Tools',
436+
'sections' => []
437+
],
387438
];
388439

440+
/**
441+
* @var string
442+
*/
443+
public const CONST_PAGE_TOOLS = 'auth0_tools';
444+
389445
/**
390446
* @var string
391447
*/
@@ -415,6 +471,7 @@ final class Configuration extends Base
415471
'auth0_ui_configuration' => 'renderConfiguration',
416472
'auth0_ui_sync' => 'renderSyncConfiguration',
417473
'auth0_ui_advanced' => 'renderAdvancedConfiguration',
474+
'auth0_ui_tools' => 'renderToolsConfiguration',
418475
];
419476

420477
public function onMenu(): void
@@ -463,6 +520,18 @@ public function onMenu(): void
463520
},
464521
position: $this->getPriority('MENU_POSITION_ADVANCED', 2, 'AUTH0_ADMIN'),
465522
);
523+
524+
add_submenu_page(
525+
parent_slug: 'auth0',
526+
page_title: 'Auth0 — Tools',
527+
menu_title: 'Tools',
528+
capability: 'manage_options',
529+
menu_slug: 'auth0_tools',
530+
callback: static function (): void {
531+
do_action('auth0_ui_tools');
532+
},
533+
position: $this->getPriority('MENU_POSITION_ADVANCED', 3, 'AUTH0_ADMIN'),
534+
);
466535
}
467536

468537
public function onSetup(): void
@@ -651,8 +720,15 @@ public function onUpdateAuthentication(?array $input): ?array
651720
$sanitized = [
652721
'pair_sessions' => Sanitize::integer((string) ($input['pair_sessions'] ?? 0), 2, 0) ?? 0,
653722
'allow_fallback' => Sanitize::boolean((string) ($input['allow_fallback'] ?? '')) ?? '',
723+
'fallback_secret' => Sanitize::string((string) ($input['fallback_secret'] ?? '')) ?? '',
654724
];
655725

726+
if ($sanitized['fallback_secret'] === '') {
727+
$sanitized['fallback_secret'] = bin2hex(random_bytes(64));
728+
}
729+
730+
set_site_transient('auth0_updated_fallback', true, 60);
731+
656732
return array_filter($sanitized, static fn ($value): bool => '' !== $value);
657733
}
658734

@@ -905,6 +981,43 @@ public function onUpdateTokens(?array $input): ?array
905981
return array_filter($sanitized, static fn ($value): bool => '' !== $value);
906982
}
907983

984+
/**
985+
* @param null|array<null|bool|int|string> $input
986+
*
987+
* @return null|array<mixed>
988+
*/
989+
public function onUpdateBackchannelLogout(?array $input): ?array
990+
{
991+
if (null === $input) {
992+
return null;
993+
}
994+
995+
$sanitized = [
996+
'enabled' => Sanitize::string((string) ($input['secret'] ?? '')) ?? '',
997+
'secret' => Sanitize::string((string) ($input['secret'] ?? '')) ?? '',
998+
'ttl' => Sanitize::integer((string) ($input['ttl'] ?? 0), 2_592_000, 0) ?? 0,
999+
];
1000+
1001+
if ($sanitized['secret'] === '') {
1002+
$sanitized['secret'] = bin2hex(random_bytes(64));
1003+
}
1004+
1005+
set_site_transient('auth0_updated_backchannel', true, 60);
1006+
1007+
return array_filter($sanitized, static fn ($value): bool => '' !== $value);
1008+
}
1009+
1010+
public function renderToolsConfiguration(): void
1011+
{
1012+
Render::pageBegin(self::PAGES[self::CONST_PAGE_TOOLS]['title']);
1013+
1014+
// settings_fields(self::CONST_PAGE_ADVANCED);
1015+
// do_settings_sections(self::CONST_PAGE_ADVANCED);
1016+
// submit_button();
1017+
1018+
Render::pageEnd();
1019+
}
1020+
9081021
public function renderAdvancedConfiguration(): void
9091022
{
9101023
Render::pageBegin(self::PAGES[self::CONST_PAGE_ADVANCED]['title']);
@@ -944,6 +1057,50 @@ private function getOptionDescription(string $context): string
9441057
return sprintf('Must include origin domain of <code>`%s`</code>', Sanitize::domain(site_url()) ?? '');
9451058
}
9461059

1060+
if ('fallback_secret' === $context) {
1061+
if ($this->isPluginReady()) {
1062+
$fallbackAllowed = $this->getPlugin()->getOption('authentication', 'allow_fallback', 0);
1063+
1064+
if (1 === $fallbackAllowed) {
1065+
$updated = get_site_transient('auth0_updated_fallback');
1066+
1067+
if (! $updated) {
1068+
return 'Save your changes to view your fallback URI. Erase the secret to generate a new one.';
1069+
} else {
1070+
delete_site_transient('auth0_updated_fallback');
1071+
}
1072+
1073+
$fallbackSecret = $this->getPlugin()->getOption('authentication', 'fallback_secret');
1074+
1075+
if (null !== $fallbackSecret) {
1076+
return sprintf('Your fallback URI is <code>`%s?auth0_fb=%s`</code>', wp_login_url(), $fallbackSecret);
1077+
}
1078+
}
1079+
}
1080+
}
1081+
1082+
if ('backchannel_logout_secret' === $context) {
1083+
if ($this->isPluginReady()) {
1084+
$backchannelLogoutEnabled = $this->getPlugin()->getOption('backchannel_logout', 'enabled', 0);
1085+
1086+
if (0 !== $backchannelLogoutEnabled) {
1087+
$updated = get_site_transient('auth0_updated_backchannel');
1088+
1089+
if (! $updated) {
1090+
return 'Save your changes to view your Back-Channel Logout URI. Erase the secret to generate a new one.';
1091+
} else {
1092+
delete_site_transient('auth0_updated_backchannel');
1093+
}
1094+
1095+
$backchannelLogoutSecret = $this->getPlugin()->getOption('backchannel_logout', 'secret');
1096+
1097+
if (null !== $backchannelLogoutSecret) {
1098+
return sprintf('Your Back-Channel Logout URI is <code>`%s?auth0_bcl=%s`</code>', wp_login_url(), $backchannelLogoutSecret);
1099+
}
1100+
}
1101+
}
1102+
}
1103+
9471104
if ('enable' === $context) {
9481105
if ($this->isPluginReady()) {
9491106
return 'Manage WordPress authentication with Auth0.';

src/Actions/Tools.php

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Auth0\WordPress\Actions;
6+
7+
final class Tools extends Base
8+
{
9+
}

src/Plugin.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Auth0\WordPress\Actions\Base as Actions;
1111
use Auth0\WordPress\Actions\Configuration as ConfigurationActions;
1212
use Auth0\WordPress\Actions\Sync as SyncActions;
13+
use Auth0\WordPress\Actions\Tools as ToolsActions;
1314
use Auth0\WordPress\Cache\WpObjectCachePool;
1415
use Auth0\WordPress\Filters\Authentication as AuthenticationFilters;
1516
use Auth0\WordPress\Filters\Base as Filters;
@@ -20,7 +21,7 @@ final class Plugin
2021
/**
2122
* @var array<class-string<Actions>>
2223
*/
23-
private const ACTIONS = [AuthenticationActions::class, ConfigurationActions::class, SyncActions::class];
24+
private const ACTIONS = [AuthenticationActions::class, ConfigurationActions::class, SyncActions::class, ToolsActions::class];
2425

2526
/**
2627
* @var array<class-string<Filters>>
@@ -309,6 +310,7 @@ private function importConfiguration(): SdkConfiguration
309310
if ($caching !== 'disable') {
310311
$wpObjectCachePool = new WpObjectCachePool();
311312
$sdkConfiguration->setTokenCache($wpObjectCachePool);
313+
$sdkConfiguration->setBackchannelLogoutCache($wpObjectCachePool);
312314
}
313315

314316
return $sdkConfiguration;

0 commit comments

Comments
 (0)