Skip to content

Commit ad528fb

Browse files
committed
Adding support for generic front logout channel
1 parent 9dde05b commit ad528fb

7 files changed

+162
-1
lines changed
31 KB
Binary file not shown.

includes/openid-connect-generic-client-wrapper.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,21 @@ public static function register( OpenID_Connect_Generic_Client $client, OpenID_C
108108
*/
109109
add_action( 'wp_ajax_openid-connect-authorize', array( $client_wrapper, 'authentication_request_callback' ) );
110110
add_action( 'wp_ajax_nopriv_openid-connect-authorize', array( $client_wrapper, 'authentication_request_callback' ) );
111+
112+
add_action( 'wp_ajax_openid-connect-logout', array( $client_wrapper, 'frontchannel_logout_request' ) );
113+
add_action( 'wp_ajax_nopriv_openid-connect-logout', array( $client_wrapper, 'frontchannel_logout_request' ) );
111114
}
112115

113116
if ( $settings->alternate_redirect_uri ) {
114117
// Provide an alternate route for authentication_request_callback.
115118
add_rewrite_rule( '^openid-connect-authorize/?', 'index.php?openid-connect-authorize=1', 'top' );
116119
add_rewrite_tag( '%openid-connect-authorize%', '1' );
117120
add_action( 'parse_request', array( $client_wrapper, 'alternate_redirect_uri_parse_request' ) );
121+
122+
// Provide an alternate route for the frontchannel_logout_request.
123+
add_rewrite_rule( '^openid-connect-logout/?', 'index.php?openid-connect-logout=1', 'top' );
124+
add_rewrite_tag( '%openid-connect-logout%', '1' );
125+
add_action( 'parse_request', array( $client_wrapper, 'alternate_redirect_uri_parse_request' ) );
118126
}
119127

120128
// Verify token for any logged in user.
@@ -139,6 +147,12 @@ public function alternate_redirect_uri_parse_request( $query ) {
139147
exit;
140148
}
141149

150+
if ( isset( $query->query_vars['openid-connect-logout'] ) &&
151+
'1' === $query->query_vars['openid-connect-logout'] ) {
152+
$this->frontchannel_logout_request();
153+
exit;
154+
}
155+
142156
return $query;
143157
}
144158

@@ -208,6 +222,7 @@ public function get_authentication_url( $atts = array() ) {
208222
'endpoint_login' => $this->settings->endpoint_login,
209223
'scope' => $this->settings->scope,
210224
'client_id' => $this->settings->client_id,
225+
'logout_uri' => $this->client->get_logout_uri(),
211226
'redirect_uri' => $this->client->get_redirect_uri(),
212227
'redirect_to' => $this->get_redirect_to(),
213228
),
@@ -563,6 +578,65 @@ public function authentication_request_callback() {
563578
exit;
564579
}
565580

581+
/**
582+
* Control the front channel logout endpoint as specified per openid standards.
583+
*
584+
* @return void
585+
*/
586+
public function frontchannel_logout_request() {
587+
if ( ! is_user_logged_in() ) {
588+
wp_send_json(
589+
array(
590+
'status' => 'User not logged in',
591+
)
592+
);
593+
}
594+
595+
$user = wp_get_current_user();
596+
$manager = WP_Session_Tokens::get_instance( $user->ID );
597+
$token = wp_get_session_token();
598+
$session = $manager->get( $token );
599+
600+
if ( ! isset( $session[ $this->cookie_token_refresh_key ] ) ) {
601+
// Not an OpenID-based session.
602+
wp_send_json(
603+
array(
604+
'status' => 'Not an OAUTH session',
605+
)
606+
);
607+
}
608+
609+
$claim = $user->get( 'openid-connect-generic-last-id-token-claim' );
610+
611+
if ( ! isset( $_GET['iss'] ) || ! isset( $_GET['sid'] ) ) {
612+
wp_send_json(
613+
array(
614+
'status' => 'Missing sid or iss parameter',
615+
)
616+
);
617+
}
618+
619+
if ( $_GET['iss'] != $claim['iss'] || $_GET['sid'] != $claim['sid'] ) {
620+
wp_send_json(
621+
array(
622+
'status' => 'Iss or sid not matching',
623+
)
624+
);
625+
}
626+
627+
$refresh_token_info = $session[ $this->cookie_token_refresh_key ];
628+
$refresh_token = $refresh_token_info['refresh_token'];
629+
$this->client->revoke_refresh_token( $refresh_token );
630+
631+
wp_logout();
632+
633+
wp_send_json(
634+
array(
635+
'status' => 'Logout OK',
636+
)
637+
);
638+
}
639+
566640
/**
567641
* Validate the potential WP_User.
568642
*

includes/openid-connect-generic-client.php

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ class OpenID_Connect_Generic_Client {
6464
*/
6565
private $endpoint_userinfo;
6666

67+
/**
68+
* The OIDC/oAuth token revocation endpoint URL.
69+
*
70+
* @see OpenID_Connect_Generic_Option_Settings::endpoint_revoke
71+
*
72+
* @var string
73+
*/
74+
private $endpoint_revoke;
75+
6776
/**
6877
* The OIDC/oAuth token validation endpoint URL.
6978
*
@@ -73,6 +82,15 @@ class OpenID_Connect_Generic_Client {
7382
*/
7483
private $endpoint_token;
7584

85+
/**
86+
* The logout front channel flow "ajax" endpoint URI.
87+
*
88+
* @see OpenID_Connect_Generic_Option_Settings::logout_uri
89+
*
90+
* @var string
91+
*/
92+
private $logout_uri;
93+
7694
/**
7795
* The login flow "ajax" endpoint URI.
7896
*
@@ -106,23 +124,37 @@ class OpenID_Connect_Generic_Client {
106124
* @param string $scope @see OpenID_Connect_Generic_Option_Settings::scope for description.
107125
* @param string $endpoint_login @see OpenID_Connect_Generic_Option_Settings::endpoint_login for description.
108126
* @param string $endpoint_userinfo @see OpenID_Connect_Generic_Option_Settings::endpoint_userinfo for description.
127+
* @param string $endpoint_revoke @see OpenID_Connect_Generic_Option_Settings::endpoint_revoke for description.
109128
* @param string $endpoint_token @see OpenID_Connect_Generic_Option_Settings::endpoint_token for description.
129+
* @param string $logout_uri @see OpenID_Connect_Generic_Option_Settings::logout_uri for description.
110130
* @param string $redirect_uri @see OpenID_Connect_Generic_Option_Settings::redirect_uri for description.
111131
* @param int $state_time_limit @see OpenID_Connect_Generic_Option_Settings::state_time_limit for description.
112132
* @param OpenID_Connect_Generic_Option_Logger $logger The plugin logging object instance.
113133
*/
114-
public function __construct( $client_id, $client_secret, $scope, $endpoint_login, $endpoint_userinfo, $endpoint_token, $redirect_uri, $state_time_limit, $logger ) {
134+
public function __construct( $client_id, $client_secret, $scope, $endpoint_login, $endpoint_userinfo, $endpoint_revoke, $endpoint_token,
135+
$logout_uri, $redirect_uri, $state_time_limit, $logger ) {
115136
$this->client_id = $client_id;
116137
$this->client_secret = $client_secret;
117138
$this->scope = $scope;
118139
$this->endpoint_login = $endpoint_login;
119140
$this->endpoint_userinfo = $endpoint_userinfo;
141+
$this->endpoint_revoke = $endpoint_revoke;
120142
$this->endpoint_token = $endpoint_token;
143+
$this->logout_uri = $logout_uri;
121144
$this->redirect_uri = $redirect_uri;
122145
$this->state_time_limit = $state_time_limit;
123146
$this->logger = $logger;
124147
}
125148

149+
/**
150+
* Provides the configured logout URI supplied to the IDP.
151+
*
152+
* @return string
153+
*/
154+
public function get_logout_uri() {
155+
return $this->logout_uri;
156+
}
157+
126158
/**
127159
* Provides the configured Redirect URI supplied to the IDP.
128160
*
@@ -538,4 +570,37 @@ public function get_subject_identity( $id_token_claim ) {
538570
return $id_token_claim['sub'];
539571
}
540572

573+
/**
574+
* Using the refresh token, revoke its usage
575+
*
576+
* @param string $refresh_token The refresh token previously obtained from token response.
577+
*
578+
* @return array<mixed>|WP_Error
579+
*/
580+
public function revoke_refresh_token( $refresh_token ) {
581+
$request = array(
582+
'headers' => array(
583+
'Content-type: application/x-www-form-urlencoded',
584+
),
585+
'body' => array(
586+
'client_id' => $this->client_id,
587+
'client_secret' => $this->client_secret,
588+
'token' => $refresh_token,
589+
),
590+
);
591+
592+
// Allow modifications to the request.
593+
$request = apply_filters( 'openid-connect-generic-alter-request', $request, 'refresh-token' );
594+
595+
// Call the server and ask to revoke token.
596+
$this->logger->log( $this->endpoint_revoke, 'revoke_refresh_token' );
597+
$response = wp_remote_post( $this->endpoint_revoke, $request );
598+
599+
if ( is_wp_error( $response ) ) {
600+
$response->add( 'revoke_refresh_token', __( 'Revoke refresh token failed.', 'daggerhart-openid-connect-generic' ) );
601+
}
602+
603+
return $response;
604+
}
605+
541606
}

includes/openid-connect-generic-option-settings.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
* @property string $scope The list of scopes this client should access.
3232
* @property string $endpoint_login The IDP authorization endpoint URL.
3333
* @property string $endpoint_userinfo The IDP User information endpoint URL.
34+
* @property string $endpoint_revoke The IDP revoke endpoint URL.
3435
* @property string $endpoint_token The IDP token validation endpoint URL.
3536
* @property string $endpoint_end_session The IDP logout endpoint URL.
3637
*
@@ -90,6 +91,7 @@ class OpenID_Connect_Generic_Option_Settings {
9091
'client_secret' => 'OIDC_CLIENT_SECRET',
9192
'endpoint_login' => 'OIDC_ENDPOINT_LOGIN_URL',
9293
'endpoint_userinfo' => 'OIDC_ENDPOINT_USERINFO_URL',
94+
'endpoint_revoke' => 'OIDC_ENDPOINT_REVOKE_URL',
9395
'endpoint_token' => 'OIDC_ENDPOINT_TOKEN_URL',
9496
'endpoint_end_session' => 'OIDC_ENDPOINT_LOGOUT_URL',
9597
);

includes/openid-connect-generic-settings-page.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,14 @@ private function get_settings_fields() {
256256
'disabled' => defined( 'OIDC_ENDPOINT_USERINFO_URL' ),
257257
'section' => 'client_settings',
258258
),
259+
'endpoint_revoke' => array(
260+
'title' => __( 'Token Revocation Endpoint URL', 'daggerhart-openid-connect-generic' ),
261+
'description' => __( 'Identify provider revoke endpoint.', 'daggerhart-openid-connect-generic' ),
262+
'example' => 'https://example.com/oauth2/revoke',
263+
'type' => 'text',
264+
'disabled' => defined( 'OIDC_ENDPOINT_REVOKE_URL' ),
265+
'section' => 'client_settings',
266+
),
259267
'endpoint_token' => array(
260268
'title' => __( 'Token Validation Endpoint URL', 'daggerhart-openid-connect-generic' ),
261269
'description' => __( 'Identify provider token endpoint.', 'daggerhart-openid-connect-generic' ),
@@ -414,9 +422,11 @@ public function sanitize_settings( $input ) {
414422
* @return void
415423
*/
416424
public function settings_page() {
425+
$logout_uri = admin_url( 'admin-ajax.php?action=openid-connect-logout' );
417426
$redirect_uri = admin_url( 'admin-ajax.php?action=openid-connect-authorize' );
418427

419428
if ( $this->settings->alternate_redirect_uri ) {
429+
$logout_uri = site_url( '/openid-connect-logout' );
420430
$redirect_uri = site_url( '/openid-connect-authorize' );
421431
}
422432
?>
@@ -442,6 +452,10 @@ public function settings_page() {
442452
<strong><?php esc_html_e( 'Redirect URI', 'daggerhart-openid-connect-generic' ); ?></strong>
443453
<code><?php print esc_url( $redirect_uri ); ?></code>
444454
</p>
455+
<p class="description">
456+
<strong><?php esc_html_e( 'Logout URI', 'daggerhart-openid-connect-generic' ); ?></strong>
457+
<code><?php print esc_url( $logout_uri ); ?></code>
458+
</p>
445459
<p class="description">
446460
<strong><?php esc_html_e( 'Login Button Shortcode', 'daggerhart-openid-connect-generic' ); ?></strong>
447461
<code>[openid_connect_generic_login_button]</code>

openid-connect-generic.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,11 @@ public function init() {
132132

133133
wp_enqueue_style( 'daggerhart-openid-connect-generic-admin', plugin_dir_url( __FILE__ ) . 'css/styles-admin.css', array(), self::VERSION, 'all' );
134134

135+
$logout_uri = admin_url( 'admin-ajax.php?action=openid-connect-logout' );
135136
$redirect_uri = admin_url( 'admin-ajax.php?action=openid-connect-authorize' );
136137

137138
if ( $this->settings->alternate_redirect_uri ) {
139+
$logout_uri = admin_url( '/openid-connect-logout' );
138140
$redirect_uri = site_url( '/openid-connect-authorize' );
139141
}
140142

@@ -149,7 +151,9 @@ public function init() {
149151
$this->settings->scope,
150152
$this->settings->endpoint_login,
151153
$this->settings->endpoint_userinfo,
154+
$this->settings->endpoint_revoke,
152155
$this->settings->endpoint_token,
156+
$logout_uri,
153157
$redirect_uri,
154158
$state_time_limit,
155159
$this->logger
@@ -333,6 +337,7 @@ public static function bootstrap() {
333337
'scope' => '',
334338
'endpoint_login' => defined( 'OIDC_ENDPOINT_LOGIN_URL' ) ? OIDC_ENDPOINT_LOGIN_URL : '',
335339
'endpoint_userinfo' => defined( 'OIDC_ENDPOINT_USERINFO_URL' ) ? OIDC_ENDPOINT_USERINFO_URL : '',
340+
'endpoint_revoke' => defined( 'OIDC_ENDPOINT_REVOKE_URL' ) ? OIDC_ENDPOINT_REVOKE_URL : '',
336341
'endpoint_token' => defined( 'OIDC_ENDPOINT_TOKEN_URL' ) ? OIDC_ENDPOINT_TOKEN_URL : '',
337342
'endpoint_end_session' => defined( 'OIDC_ENDPOINT_LOGOUT_URL' ) ? OIDC_ENDPOINT_LOGOUT_URL : '',
338343

tests/phpstan-bootstrap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@
2121
defined( 'OIDC_CLIENT_SECRET' ) || define( 'OIDC_CLIENT_SECRET', bin2hex( random_bytes( 16 ) ) );
2222
defined( 'OIDC_ENDPOINT_LOGIN_URL' ) || define( 'OIDC_ENDPOINT_LOGIN_URL', 'https://oidc/oauth2/authorize' );
2323
defined( 'OIDC_ENDPOINT_USERINFO_URL' ) || define( 'OIDC_ENDPOINT_USERINFO_URL', 'https://oidc/oauth2/userinfo' );
24+
defined( 'OIDC_ENDPOINT_REVOKE_URL' ) || define( 'OIDC_ENDPOINT_REVOKE_URL', 'https://oidc/oauth2/revoke' );
2425
defined( 'OIDC_ENDPOINT_TOKEN_URL' ) || define( 'OIDC_ENDPOINT_TOKEN_URL', 'https://oidc/oauth2/token' );
2526
defined( 'OIDC_ENDPOINT_LOGOUT_URL' ) || define( 'OIDC_ENDPOINT_LOGOUT_URL', 'https://oidc/oauth2/logout' );

0 commit comments

Comments
 (0)