Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for generic front logout channel #334

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions includes/openid-connect-generic-client-wrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,21 @@ public static function register( OpenID_Connect_Generic_Client $client, OpenID_C
*/
add_action( 'wp_ajax_openid-connect-authorize', array( $client_wrapper, 'authentication_request_callback' ) );
add_action( 'wp_ajax_nopriv_openid-connect-authorize', array( $client_wrapper, 'authentication_request_callback' ) );

add_action( 'wp_ajax_openid-connect-logout', array( $client_wrapper, 'frontchannel_logout_request' ) );
add_action( 'wp_ajax_nopriv_openid-connect-logout', array( $client_wrapper, 'frontchannel_logout_request' ) );
}

if ( $settings->alternate_redirect_uri ) {
// Provide an alternate route for authentication_request_callback.
add_rewrite_rule( '^openid-connect-authorize/?', 'index.php?openid-connect-authorize=1', 'top' );
add_rewrite_tag( '%openid-connect-authorize%', '1' );
add_action( 'parse_request', array( $client_wrapper, 'alternate_redirect_uri_parse_request' ) );

// Provide an alternate route for the frontchannel_logout_request.
add_rewrite_rule( '^openid-connect-logout/?', 'index.php?openid-connect-logout=1', 'top' );
add_rewrite_tag( '%openid-connect-logout%', '1' );
add_action( 'parse_request', array( $client_wrapper, 'alternate_redirect_uri_parse_request' ) );
}

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

if ( isset( $query->query_vars['openid-connect-logout'] ) &&
'1' === $query->query_vars['openid-connect-logout'] ) {
$this->frontchannel_logout_request();
exit;
}

return $query;
}

Expand Down Expand Up @@ -208,6 +222,7 @@ public function get_authentication_url( $atts = array() ) {
'endpoint_login' => $this->settings->endpoint_login,
'scope' => $this->settings->scope,
'client_id' => $this->settings->client_id,
'logout_uri' => $this->client->get_logout_uri(),
'redirect_uri' => $this->client->get_redirect_uri(),
'redirect_to' => $this->get_redirect_to(),
),
Expand Down Expand Up @@ -563,6 +578,65 @@ public function authentication_request_callback() {
exit;
}

/**
* Control the front channel logout endpoint as specified per openid standards.
*
* @return void
*/
public function frontchannel_logout_request() {
if ( ! is_user_logged_in() ) {
wp_send_json(
array(
'status' => 'User not logged in',
)
);
}

$user = wp_get_current_user();
$manager = WP_Session_Tokens::get_instance( $user->ID );
$token = wp_get_session_token();
$session = $manager->get( $token );

if ( ! isset( $session[ $this->cookie_token_refresh_key ] ) ) {
// Not an OpenID-based session.
wp_send_json(
array(
'status' => 'Not an OAUTH session',
)
);
}

$claim = $user->get( 'openid-connect-generic-last-id-token-claim' );

if ( ! isset( $_GET['iss'] ) || ! isset( $_GET['sid'] ) ) {
wp_send_json(
array(
'status' => 'Missing sid or iss parameter',
)
);
}

if ( $_GET['iss'] != $claim['iss'] || $_GET['sid'] != $claim['sid'] ) {
wp_send_json(
array(
'status' => 'Iss or sid not matching',
)
);
}

$refresh_token_info = $session[ $this->cookie_token_refresh_key ];
$refresh_token = $refresh_token_info['refresh_token'];
$this->client->revoke_refresh_token( $refresh_token );

wp_logout();

wp_send_json(
array(
'status' => 'Logout OK',
)
);
}

/**
* Validate the potential WP_User.
*
Expand Down
67 changes: 66 additions & 1 deletion includes/openid-connect-generic-client.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ class OpenID_Connect_Generic_Client {
*/
private $endpoint_userinfo;

/**
* The OIDC/oAuth token revocation endpoint URL.
*
* @see OpenID_Connect_Generic_Option_Settings::endpoint_revoke
*
* @var string
*/
private $endpoint_revoke;

/**
* The OIDC/oAuth token validation endpoint URL.
*
Expand All @@ -73,6 +82,15 @@ class OpenID_Connect_Generic_Client {
*/
private $endpoint_token;

/**
* The logout front channel flow "ajax" endpoint URI.
*
* @see OpenID_Connect_Generic_Option_Settings::logout_uri
*
* @var string
*/
private $logout_uri;

/**
* The login flow "ajax" endpoint URI.
*
Expand Down Expand Up @@ -106,23 +124,37 @@ class OpenID_Connect_Generic_Client {
* @param string $scope @see OpenID_Connect_Generic_Option_Settings::scope for description.
* @param string $endpoint_login @see OpenID_Connect_Generic_Option_Settings::endpoint_login for description.
* @param string $endpoint_userinfo @see OpenID_Connect_Generic_Option_Settings::endpoint_userinfo for description.
* @param string $endpoint_revoke @see OpenID_Connect_Generic_Option_Settings::endpoint_revoke for description.
* @param string $endpoint_token @see OpenID_Connect_Generic_Option_Settings::endpoint_token for description.
* @param string $logout_uri @see OpenID_Connect_Generic_Option_Settings::logout_uri for description.
* @param string $redirect_uri @see OpenID_Connect_Generic_Option_Settings::redirect_uri for description.
* @param int $state_time_limit @see OpenID_Connect_Generic_Option_Settings::state_time_limit for description.
* @param OpenID_Connect_Generic_Option_Logger $logger The plugin logging object instance.
*/
public function __construct( $client_id, $client_secret, $scope, $endpoint_login, $endpoint_userinfo, $endpoint_token, $redirect_uri, $state_time_limit, $logger ) {
public function __construct( $client_id, $client_secret, $scope, $endpoint_login, $endpoint_userinfo, $endpoint_revoke, $endpoint_token,
$logout_uri, $redirect_uri, $state_time_limit, $logger ) {
$this->client_id = $client_id;
$this->client_secret = $client_secret;
$this->scope = $scope;
$this->endpoint_login = $endpoint_login;
$this->endpoint_userinfo = $endpoint_userinfo;
$this->endpoint_revoke = $endpoint_revoke;
$this->endpoint_token = $endpoint_token;
$this->logout_uri = $logout_uri;
$this->redirect_uri = $redirect_uri;
$this->state_time_limit = $state_time_limit;
$this->logger = $logger;
}

/**
* Provides the configured logout URI supplied to the IDP.
*
* @return string
*/
public function get_logout_uri() {
return $this->logout_uri;
}

/**
* Provides the configured Redirect URI supplied to the IDP.
*
Expand Down Expand Up @@ -538,4 +570,37 @@ public function get_subject_identity( $id_token_claim ) {
return $id_token_claim['sub'];
}

/**
* Using the refresh token, revoke its usage
*
* @param string $refresh_token The refresh token previously obtained from token response.
*
* @return array<mixed>|WP_Error
*/
public function revoke_refresh_token( $refresh_token ) {
$request = array(
'headers' => array(
'Content-type: application/x-www-form-urlencoded',
),
'body' => array(
'client_id' => $this->client_id,
'client_secret' => $this->client_secret,
'token' => $refresh_token,
),
);

// Allow modifications to the request.
$request = apply_filters( 'openid-connect-generic-alter-request', $request, 'refresh-token' );

// Call the server and ask to revoke token.
$this->logger->log( $this->endpoint_revoke, 'revoke_refresh_token' );
$response = wp_remote_post( $this->endpoint_revoke, $request );

if ( is_wp_error( $response ) ) {
$response->add( 'revoke_refresh_token', __( 'Revoke refresh token failed.', 'daggerhart-openid-connect-generic' ) );
}

return $response;
}

}
2 changes: 2 additions & 0 deletions includes/openid-connect-generic-option-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
* @property string $scope The list of scopes this client should access.
* @property string $endpoint_login The IDP authorization endpoint URL.
* @property string $endpoint_userinfo The IDP User information endpoint URL.
* @property string $endpoint_revoke The IDP revoke endpoint URL.
* @property string $endpoint_token The IDP token validation endpoint URL.
* @property string $endpoint_end_session The IDP logout endpoint URL.
*
Expand Down Expand Up @@ -90,6 +91,7 @@ class OpenID_Connect_Generic_Option_Settings {
'client_secret' => 'OIDC_CLIENT_SECRET',
'endpoint_login' => 'OIDC_ENDPOINT_LOGIN_URL',
'endpoint_userinfo' => 'OIDC_ENDPOINT_USERINFO_URL',
'endpoint_revoke' => 'OIDC_ENDPOINT_REVOKE_URL',
'endpoint_token' => 'OIDC_ENDPOINT_TOKEN_URL',
'endpoint_end_session' => 'OIDC_ENDPOINT_LOGOUT_URL',
);
Expand Down
14 changes: 14 additions & 0 deletions includes/openid-connect-generic-settings-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,14 @@ private function get_settings_fields() {
'disabled' => defined( 'OIDC_ENDPOINT_USERINFO_URL' ),
'section' => 'client_settings',
),
'endpoint_revoke' => array(
'title' => __( 'Token Revocation Endpoint URL', 'daggerhart-openid-connect-generic' ),
'description' => __( 'Identify provider revoke endpoint.', 'daggerhart-openid-connect-generic' ),
'example' => 'https://example.com/oauth2/revoke',
'type' => 'text',
'disabled' => defined( 'OIDC_ENDPOINT_REVOKE_URL' ),
'section' => 'client_settings',
),
'endpoint_token' => array(
'title' => __( 'Token Validation Endpoint URL', 'daggerhart-openid-connect-generic' ),
'description' => __( 'Identify provider token endpoint.', 'daggerhart-openid-connect-generic' ),
Expand Down Expand Up @@ -414,9 +422,11 @@ public function sanitize_settings( $input ) {
* @return void
*/
public function settings_page() {
$logout_uri = admin_url( 'admin-ajax.php?action=openid-connect-logout' );
$redirect_uri = admin_url( 'admin-ajax.php?action=openid-connect-authorize' );

if ( $this->settings->alternate_redirect_uri ) {
$logout_uri = site_url( '/openid-connect-logout' );
$redirect_uri = site_url( '/openid-connect-authorize' );
}
?>
Expand All @@ -442,6 +452,10 @@ public function settings_page() {
<strong><?php esc_html_e( 'Redirect URI', 'daggerhart-openid-connect-generic' ); ?></strong>
<code><?php print esc_url( $redirect_uri ); ?></code>
</p>
<p class="description">
<strong><?php esc_html_e( 'Logout URI', 'daggerhart-openid-connect-generic' ); ?></strong>
<code><?php print esc_url( $logout_uri ); ?></code>
</p>
<p class="description">
<strong><?php esc_html_e( 'Login Button Shortcode', 'daggerhart-openid-connect-generic' ); ?></strong>
<code>[openid_connect_generic_login_button]</code>
Expand Down
5 changes: 5 additions & 0 deletions openid-connect-generic.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,11 @@ public function init() {

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

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

if ( $this->settings->alternate_redirect_uri ) {
$logout_uri = admin_url( '/openid-connect-logout' );
$redirect_uri = site_url( '/openid-connect-authorize' );
}

Expand All @@ -149,7 +151,9 @@ public function init() {
$this->settings->scope,
$this->settings->endpoint_login,
$this->settings->endpoint_userinfo,
$this->settings->endpoint_revoke,
$this->settings->endpoint_token,
$logout_uri,
$redirect_uri,
$state_time_limit,
$this->logger
Expand Down Expand Up @@ -333,6 +337,7 @@ public static function bootstrap() {
'scope' => '',
'endpoint_login' => defined( 'OIDC_ENDPOINT_LOGIN_URL' ) ? OIDC_ENDPOINT_LOGIN_URL : '',
'endpoint_userinfo' => defined( 'OIDC_ENDPOINT_USERINFO_URL' ) ? OIDC_ENDPOINT_USERINFO_URL : '',
'endpoint_revoke' => defined( 'OIDC_ENDPOINT_REVOKE_URL' ) ? OIDC_ENDPOINT_REVOKE_URL : '',
'endpoint_token' => defined( 'OIDC_ENDPOINT_TOKEN_URL' ) ? OIDC_ENDPOINT_TOKEN_URL : '',
'endpoint_end_session' => defined( 'OIDC_ENDPOINT_LOGOUT_URL' ) ? OIDC_ENDPOINT_LOGOUT_URL : '',

Expand Down
1 change: 1 addition & 0 deletions tests/phpstan-bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@
defined( 'OIDC_CLIENT_SECRET' ) || define( 'OIDC_CLIENT_SECRET', bin2hex( random_bytes( 16 ) ) );
defined( 'OIDC_ENDPOINT_LOGIN_URL' ) || define( 'OIDC_ENDPOINT_LOGIN_URL', 'https://oidc/oauth2/authorize' );
defined( 'OIDC_ENDPOINT_USERINFO_URL' ) || define( 'OIDC_ENDPOINT_USERINFO_URL', 'https://oidc/oauth2/userinfo' );
defined( 'OIDC_ENDPOINT_REVOKE_URL' ) || define( 'OIDC_ENDPOINT_REVOKE_URL', 'https://oidc/oauth2/revoke' );
defined( 'OIDC_ENDPOINT_TOKEN_URL' ) || define( 'OIDC_ENDPOINT_TOKEN_URL', 'https://oidc/oauth2/token' );
defined( 'OIDC_ENDPOINT_LOGOUT_URL' ) || define( 'OIDC_ENDPOINT_LOGOUT_URL', 'https://oidc/oauth2/logout' );