From e1b3c35b9680a5c6851d806c8926b0cd68a3f461 Mon Sep 17 00:00:00 2001 From: Colan Schwartz <colan@58704.no-reply.drupal.org> Date: Tue, 17 Jan 2023 14:44:34 -0500 Subject: [PATCH 01/10] Issue #3327237 by colan: Cloned the Okta client plugin for use by Auth0. --- .../OpenIDConnectAuth0Client.php | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php diff --git a/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php b/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php new file mode 100644 index 0000000..68ad0bb --- /dev/null +++ b/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php @@ -0,0 +1,85 @@ +<?php + +namespace Drupal\openid_connect\Plugin\OpenIDConnectClient; + +use Drupal\Core\Form\FormStateInterface; +use Drupal\openid_connect\Plugin\OpenIDConnectClientBase; + +/** + * Okta OpenID Connect client. + * + * Implements OpenID Connect Client plugin for Okta. + * + * @OpenIDConnectClient( + * id = "okta", + * label = @Translation("Okta") + * ) + */ +class OpenIDConnectOktaClient extends OpenIDConnectClientBase { + + /** + * {@inheritdoc} + */ + public function defaultConfiguration(): array { + return [ + 'okta_domain' => '', + 'scopes' => ['openid', 'email'], + ] + parent::defaultConfiguration(); + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { + $form = parent::buildConfigurationForm($form, $form_state); + + $form['okta_domain'] = [ + '#title' => $this->t('Okta domain'), + '#type' => 'textfield', + '#default_value' => $this->configuration['okta_domain'], + ]; + + $form['scopes'] = [ + '#title' => $this->t('Scopes'), + '#type' => 'textfield', + '#description' => $this->t('Custom scopes, separated by spaces, for example: openid email'), + '#default_value' => implode(' ', $this->configuration['scopes']), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function getEndpoints(): array { + // From https://developer.okta.com/docs/reference/api/oidc and + // https://${yourOktaDomain}/.well-known/openid-configuration + return [ + 'authorization' => 'https://' . $this->configuration['okta_domain'] . '/oauth2/v1/authorize', + 'token' => 'https://' . $this->configuration['okta_domain'] . '/oauth2/v1/token', + 'userinfo' => 'https://' . $this->configuration['okta_domain'] . '/oauth2/v1/userinfo', + 'end_session' => 'https://' . $this->configuration['okta_domain'] . '/oauth2/v1/logout', + ]; + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + $configuration = $form_state->getValues(); + if (!empty($configuration['scopes'])) { + $this->setConfiguration(['scopes' => explode(' ', $configuration['scopes'])]); + } + + parent::submitConfigurationForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function getClientScopes(): ?array { + return $this->configuration['scopes']; + } + +} -- GitLab From f3e086b7d16d4c16e317a83737197fab11a467ea Mon Sep 17 00:00:00 2001 From: Colan Schwartz <colan@58704.no-reply.drupal.org> Date: Tue, 17 Jan 2023 14:53:05 -0500 Subject: [PATCH 02/10] Issue #3327237 by colan: Replaced Okta references with Auth0. --- .../OpenIDConnectAuth0Client.php | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php b/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php index 68ad0bb..cac58db 100644 --- a/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php +++ b/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php @@ -6,23 +6,23 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\openid_connect\Plugin\OpenIDConnectClientBase; /** - * Okta OpenID Connect client. + * Auth0 OpenID Connect client. * - * Implements OpenID Connect Client plugin for Okta. + * Implements OpenID Connect Client plugin for Auth0. * * @OpenIDConnectClient( - * id = "okta", - * label = @Translation("Okta") + * id = "auth0", + * label = @Translation("Auth0") * ) */ -class OpenIDConnectOktaClient extends OpenIDConnectClientBase { +class OpenIDConnectAuth0Client extends OpenIDConnectClientBase { /** * {@inheritdoc} */ public function defaultConfiguration(): array { return [ - 'okta_domain' => '', + 'auth0_domain' => '', 'scopes' => ['openid', 'email'], ] + parent::defaultConfiguration(); } @@ -33,10 +33,10 @@ class OpenIDConnectOktaClient extends OpenIDConnectClientBase { public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { $form = parent::buildConfigurationForm($form, $form_state); - $form['okta_domain'] = [ - '#title' => $this->t('Okta domain'), + $form['auth0_domain'] = [ + '#title' => $this->t('Auth0 domain'), '#type' => 'textfield', - '#default_value' => $this->configuration['okta_domain'], + '#default_value' => $this->configuration['auth0_domain'], ]; $form['scopes'] = [ @@ -53,13 +53,13 @@ class OpenIDConnectOktaClient extends OpenIDConnectClientBase { * {@inheritdoc} */ public function getEndpoints(): array { - // From https://developer.okta.com/docs/reference/api/oidc and - // https://${yourOktaDomain}/.well-known/openid-configuration + // From https://developer.auth0.com/docs/reference/api/oidc and + // https://${yourAuth0Domain}/.well-known/openid-configuration return [ - 'authorization' => 'https://' . $this->configuration['okta_domain'] . '/oauth2/v1/authorize', - 'token' => 'https://' . $this->configuration['okta_domain'] . '/oauth2/v1/token', - 'userinfo' => 'https://' . $this->configuration['okta_domain'] . '/oauth2/v1/userinfo', - 'end_session' => 'https://' . $this->configuration['okta_domain'] . '/oauth2/v1/logout', + 'authorization' => 'https://' . $this->configuration['auth0_domain'] . '/oauth2/v1/authorize', + 'token' => 'https://' . $this->configuration['auth0_domain'] . '/oauth2/v1/token', + 'userinfo' => 'https://' . $this->configuration['auth0_domain'] . '/oauth2/v1/userinfo', + 'end_session' => 'https://' . $this->configuration['auth0_domain'] . '/oauth2/v1/logout', ]; } -- GitLab From 71da7b73b2ab997a2898e7f1664d8a986a4ac8e3 Mon Sep 17 00:00:00 2001 From: Colan Schwartz <colan@58704.no-reply.drupal.org> Date: Wed, 18 Jan 2023 11:10:10 -0500 Subject: [PATCH 03/10] Issue #3327237 by colan: Updated endpoint URLs. --- .../OpenIDConnectAuth0Client.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php b/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php index cac58db..866e3ec 100644 --- a/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php +++ b/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php @@ -51,15 +51,19 @@ class OpenIDConnectAuth0Client extends OpenIDConnectClientBase { /** * {@inheritdoc} + * + * @see https://${yourAuth0Domain}/.well-known/openid-configuration */ public function getEndpoints(): array { - // From https://developer.auth0.com/docs/reference/api/oidc and - // https://${yourAuth0Domain}/.well-known/openid-configuration return [ - 'authorization' => 'https://' . $this->configuration['auth0_domain'] . '/oauth2/v1/authorize', - 'token' => 'https://' . $this->configuration['auth0_domain'] . '/oauth2/v1/token', - 'userinfo' => 'https://' . $this->configuration['auth0_domain'] . '/oauth2/v1/userinfo', - 'end_session' => 'https://' . $this->configuration['auth0_domain'] . '/oauth2/v1/logout', + # https://auth0.com/docs/api/authentication#authorize-application + 'authorization' => 'https://' . $this->configuration['auth0_domain'] . '/authorize', + # https://auth0.com/docs/api/authentication#get-code-or-link + 'token' => 'https://' . $this->configuration['auth0_domain'] . '/oauth/token', + # https://auth0.com/docs/api/authentication#get-user-info + 'userinfo' => 'https://' . $this->configuration['auth0_domain'] . '/userinfo', + # https://auth0.com/docs/api/authentication#logout + 'end_session' => 'https://' . $this->configuration['auth0_domain'] . '/v2/logout', ]; } -- GitLab From e6ad49bf703f82f0c1a1c22def6e9612633ee1ab Mon Sep 17 00:00:00 2001 From: Colan Schwartz <colan@58704.no-reply.drupal.org> Date: Wed, 25 Jan 2023 13:12:49 -0500 Subject: [PATCH 04/10] Issue #3327237 by colan: Added schema for Auth0 config. --- config/schema/openid_connect.schema.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/config/schema/openid_connect.schema.yml b/config/schema/openid_connect.schema.yml index 4b93e0b..babf079 100644 --- a/config/schema/openid_connect.schema.yml +++ b/config/schema/openid_connect.schema.yml @@ -114,3 +114,17 @@ openid_connect.client.plugin.okta: label: 'Scopes' sequence: type: string + +openid_connect.client.plugin.auth0: + type: mapping + label: 'OpenID Connect Auth0 settings' + mapping: + <<: *base + okta_domain: + type: string + label: 'Okta domain' + scopes: + type: sequence + label: 'Scopes' + sequence: + type: string -- GitLab From ecb15fbe616b9eb5b73692f40c98ab034ef7fb08 Mon Sep 17 00:00:00 2001 From: Colan Schwartz <colan@58704.no-reply.drupal.org> Date: Wed, 25 Jan 2023 13:14:15 -0500 Subject: [PATCH 05/10] Issue #3327237 by colan: Added Auth0 to the list of supported backends in in-line help. --- openid_connect.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openid_connect.module b/openid_connect.module index 6ec8f0f..89f47b1 100644 --- a/openid_connect.module +++ b/openid_connect.module @@ -30,7 +30,7 @@ function openid_connect_help($route_name, RouteMatchInterface $route_match) : st $output .= '<dt>' . t('Login to Drupal using OpenID Connect/OAuth2 providers') . '</dt>'; $output .= '<dd>' . t('Drupal users can use external OpenID Connect authentication providers to register and login to the Drupal site.') . '</dd>'; $output .= '<dt>' . t('Default providers') . '</dt>'; - $output .= '<dd>' . t('The default clients provided by the module for Google, Facebook, Github, LinkedIn and Okta can be used out-of-the box.') . '</dd>'; + $output .= '<dd>' . t('The default clients provided by the module for Google, Facebook, Github, LinkedIn, Auth0 and Okta can be used out-of-the box.') . '</dd>'; $output .= '<dt>' . t('Custom OpenID Connect/OAuth2 providers') . '</dt>'; $output .= '<dd>' . t('Easily add an own provider by using the provided Generic client, or use a custom provider client plugin.') . '</dd>'; $output .= '<dt>' . t('Synchronize user properties/fields with OpenID Connect claims') . '</dt>'; -- GitLab From f0755156322e3449d458767edffc7bbe16290097 Mon Sep 17 00:00:00 2001 From: Colan Schwartz <colan@58704.no-reply.drupal.org> Date: Wed, 25 Jan 2023 13:16:08 -0500 Subject: [PATCH 06/10] Issue #3327237 by colan: Added alter method for the query. --- src/Controller/OpenIDConnectRedirectController.php | 1 + src/Plugin/OpenIDConnectClientBase.php | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/Controller/OpenIDConnectRedirectController.php b/src/Controller/OpenIDConnectRedirectController.php index 3f724e4..bbf19a6 100644 --- a/src/Controller/OpenIDConnectRedirectController.php +++ b/src/Controller/OpenIDConnectRedirectController.php @@ -445,6 +445,7 @@ class OpenIDConnectRedirectController implements ContainerInjectionInterface, Ac if ($redirect_logout_url) { $url_options['query']['post_logout_redirect_uri'] = $redirect_logout_url->setAbsolute()->toString(TRUE)->getGeneratedUrl(); } + $url_options['query'] = $entity->getPlugin()->alterLogoutRedirectionQuery($url_options['query']); $redirect = Url::fromUri($endpoints['end_session'], $url_options)->toString(TRUE); $response = new TrustedRedirectResponse($redirect->getGeneratedUrl()); $response->addCacheableDependency($redirect); diff --git a/src/Plugin/OpenIDConnectClientBase.php b/src/Plugin/OpenIDConnectClientBase.php index 9ec17e0..64634db 100644 --- a/src/Plugin/OpenIDConnectClientBase.php +++ b/src/Plugin/OpenIDConnectClientBase.php @@ -453,4 +453,16 @@ abstract class OpenIDConnectClientBase extends PluginBase implements OpenIDConne return Url::fromRoute('openid_connect.redirect_controller_redirect', $route_parameters, $options); } + /** + * Allows for alterations to the logout redirection query. + * + * @param array $query + * The query. + * @return array + * The query, possibly with alterations. + */ + public function alterLogoutRedirectionQuery(array $query): array { + return $query; + } + } -- GitLab From df90d20ed6903698c3306f51b24c3385acb8305b Mon Sep 17 00:00:00 2001 From: Colan Schwartz <colan@58704.no-reply.drupal.org> Date: Wed, 25 Jan 2023 13:17:06 -0500 Subject: [PATCH 07/10] Issue #3327237 by colan: Overrode the query alter method to do Auth0-specific stuff. --- .../OpenIDConnectClient/OpenIDConnectAuth0Client.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php b/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php index 866e3ec..691b2aa 100644 --- a/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php +++ b/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php @@ -86,4 +86,15 @@ class OpenIDConnectAuth0Client extends OpenIDConnectClientBase { return $this->configuration['scopes']; } + /** + * {@inheritdoc} + */ + public function alterLogoutRedirectionQuery(array $query): array + { + $query['client_id'] = $this->configuration['client_id']; + $query['returnTo'] = $query['post_logout_redirect_uri']; + unset($query['post_logout_redirect_uri'], $query['id_token_hint']); + return $query; + } + } -- GitLab From 999bba04df7db964ca649c201e095057a17ab720 Mon Sep 17 00:00:00 2001 From: Colan Schwartz <colan@58704.no-reply.drupal.org> Date: Fri, 27 Jan 2023 12:36:28 -0500 Subject: [PATCH 08/10] Issue #3327237 by colan: Provide users with the reason why they can't log in if available. --- src/Controller/OpenIDConnectRedirectController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Controller/OpenIDConnectRedirectController.php b/src/Controller/OpenIDConnectRedirectController.php index bbf19a6..dd3b5df 100644 --- a/src/Controller/OpenIDConnectRedirectController.php +++ b/src/Controller/OpenIDConnectRedirectController.php @@ -281,11 +281,11 @@ class OpenIDConnectRedirectController implements ContainerInjectionInterface, Ac // Any other error should be logged. E.g. invalid scope. $variables = [ '@error' => $request->get('error'), - '@details' => $request->get('error_description') ? $request->get('error_description') : $this->t('Unknown error.'), + '@details' => $request->get('error_description') ?: $this->t('Unknown error: Could not authenticate with @provider.', $provider_param), ]; $message = 'Authorization failed: @error. Details: @details'; $this->getLogger('openid_connect_' . $openid_connect_client->getPluginId())->error($message, $variables); - $this->messenger()->addError($this->t('Could not authenticate with @provider.', $provider_param)); + $this->messenger()->addError($variables['@details']); } } else { -- GitLab From 644ff7b3d371f4432fc7a50f7f493633352d1d78 Mon Sep 17 00:00:00 2001 From: Colan Schwartz <colan@58704.no-reply.drupal.org> Date: Fri, 27 Jan 2023 12:37:46 -0500 Subject: [PATCH 09/10] Issue #3327237 by colan: Tell admins how to enforce e-mail verification at the IDP. --- .../OpenIDConnectClient/OpenIDConnectAuth0Client.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php b/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php index 691b2aa..53a130b 100644 --- a/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php +++ b/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php @@ -39,6 +39,18 @@ class OpenIDConnectAuth0Client extends OpenIDConnectClientBase { '#default_value' => $this->configuration['auth0_domain'], ]; + $form['email_verification'] = [ + '#title' => $this->t('E-mail verification'), + '#type' => 'item', + '#description' => $this->t( + 'By default, requiring mandatory e-mail verification for logins as configured at <em>Administration -> + Configuration -> People -> Registration and Cancellation</em> is not enforced with this login method. To enable + it in your Auth0 tenant, navigate to Actions -> Flows -> Login, add a custom action with the code block at + <a href="https://community.auth0.com/t/user-can-sign-in-before-email-confirmation/62730/3">Auth0 Community: User + can sign in before email confirmation</a>, and then place it between <em>Start</em> and <em>Complete</em>.' + ), + ]; + $form['scopes'] = [ '#title' => $this->t('Scopes'), '#type' => 'textfield', -- GitLab From 9a818668d6447823994d5f0b7fe4135d01e8dfa6 Mon Sep 17 00:00:00 2001 From: Colan Schwartz <colan@58704.no-reply.drupal.org> Date: Wed, 29 Mar 2023 12:36:30 -0400 Subject: [PATCH 10/10] Issue #3327237 by colan: Use the federated-logout URL if requested by admins. That is, log out of the IDP as well as Drupal. --- .../OpenIDConnectAuth0Client.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php b/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php index 53a130b..0caf304 100644 --- a/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php +++ b/src/Plugin/OpenIDConnectClient/OpenIDConnectAuth0Client.php @@ -75,10 +75,25 @@ class OpenIDConnectAuth0Client extends OpenIDConnectClientBase { # https://auth0.com/docs/api/authentication#get-user-info 'userinfo' => 'https://' . $this->configuration['auth0_domain'] . '/userinfo', # https://auth0.com/docs/api/authentication#logout - 'end_session' => 'https://' . $this->configuration['auth0_domain'] . '/v2/logout', + 'end_session' => 'https://' . $this->configuration['auth0_domain'] . $this->getLogoutPath(), ]; } + /** + * Fetches the logout path at the IDP. + * + * @return string + * + * @see https://auth0.com/docs/authenticate/login/logout/log-users-out-of-idps + */ + protected function getLogoutPath(): string { + $base_logout_path = '/v2/logout'; + if (\Drupal::configFactory()->get('openid_connect.settings')->get('end_session_enabled')) { + return $base_logout_path . '?federated'; + } + return $base_logout_path; + } + /** * {@inheritdoc} */ -- GitLab