Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
cernify-openid-connect.patch 30.27 KiB
diff --git a/CERN_README.md b/CERN_README.md
new file mode 100644
index 0000000..96aeb9e
--- /dev/null
+++ b/CERN_README.md
@@ -0,0 +1,7 @@
+# Createing patch overview
+
+- In order to create a new patch for the opeind connect module execute the git diff command from the commit a52a7e98cf0e16cc85aa78ab51deff4ffd952d68 to the last one and redirect the output to a proper file in the following way:
+
+example:
+
+`git diff a52a7e98cf0e16cc85aa78ab51deff4ffd952d68 last_commit_hash > cernify-openid-connect.patch`
\ No newline at end of file
diff --git a/README.txt b/README.txt
index b0dff40..a698272 100644
--- a/README.txt
+++ b/README.txt
@@ -1,3 +1,5 @@
+MODIFIED PER 21-FEB-2023 TO ACCOMMODATE CERN OPENID_CONNECT IMPLEMENTATION
+
 CONTENTS OF THIS FILE
 ---------------------

diff --git a/config/install/openid_connect.settings.yml b/config/install/openid_connect.settings.yml
index 306410a..abed6ad 100644
--- a/config/install/openid_connect.settings.yml
+++ b/config/install/openid_connect.settings.yml
@@ -2,9 +2,11 @@ always_save_userinfo: true
 connect_existing_users: false
 override_registration_settings: false
 end_session_enabled: true
-user_login_display: 'hidden'
-redirect_login: 'user'
-redirect_logout: ''
+user_login_display: "hidden"
+redirect_login: "/"
+redirect_logout: ""
 userinfo_mappings:
   timezone: zoneinfo
 role_mappings: {}
+autostart_login: true
+role_eval_every_time: true
diff --git a/config/schema/openid_connect.schema.yml b/config/schema/openid_connect.schema.yml
index 4b93e0b..dc3ce8b 100644
--- a/config/schema/openid_connect.schema.yml
+++ b/config/schema/openid_connect.schema.yml
@@ -1,116 +1,122 @@
 openid_connect.settings:
   type: config_object
-  label: 'OpenID Connect settings'
+  label: "OpenID Connect settings"
   mapping:
     always_save_userinfo:
       type: boolean
-      label: 'Save user claims on every login'
+      label: "Save user claims on every login"
     connect_existing_users:
       type: boolean
-      label: 'Automatically connect existing users'
+      label: "Automatically connect existing users"
     override_registration_settings:
       type: boolean
-      label: 'Override registration settings'
+      label: "Override registration settings"
     end_session_enabled:
       type: boolean
-      label: 'Logout from identity provider'
+      label: "Logout from identity provider"
     user_login_display:
       type: string
-      label: 'Show external providers in user login form'
+      label: "Show external providers in user login form"
     redirect_login:
       type: string
-      label: 'Redirect on login'
+      label: "Redirect on login"
     redirect_logout:
       type: string
-      label: 'Redirect on logout'
+      label: "Redirect on logout"
     userinfo_mappings:
       type: sequence
-      label: 'User claims mapping'
+      label: "User claims mapping"
       sequence:
         type: string
     role_mappings:
       type: sequence
-      label: 'User role mapping'
+      label: "User role mapping"
       sequence:
         type: sequence
         sequence:
           type: string
+    autostart_login:
+      type: bool
+      label: "Autostart the login process"
+    role_eval_every_time:
+      type: bool
+      label: "Re-evaluate roles every time a user logs in"

 openid_connect.client.*:
   type: config_entity
-  label: 'OpenID Connect entity definitions'
+  label: "OpenID Connect entity definitions"
   mapping:
     id:
       type: string
-      label: 'OpenID Connect client entity ID'
+      label: "OpenID Connect client entity ID"
     label:
       type: string
-      label: 'OpenID Connect client name'
+      label: "OpenID Connect client name"
     plugin:
       type: string
-      label: 'OpenID Connect client plugin ID'
+      label: "OpenID Connect client plugin ID"
     settings:
       type: openid_connect.client.plugin.[%parent.plugin]

 openid_connect.client.plugin.*:
   type: mapping
-  label: 'OpenID Connect plugin base settings'
+  label: "OpenID Connect plugin base settings"
   mapping: &base
     client_id:
       type: string
-      label: 'Client ID'
+      label: "Client ID"
     client_secret:
       type: string
-      label: 'Client secret'
+      label: "Client secret"
     iss_allowed_domains:
       type: string
-      label: 'Domains that are allowed to initiate SSO using ISS'
+      label: "Domains that are allowed to initiate SSO using ISS"
 openid_connect.client.plugin.facebook:
   type: mapping
-  label: 'OpenID Connect Facebook settings'
+  label: "OpenID Connect Facebook settings"
   mapping:
     <<: *base
     api_version:
       type: string
-      label: 'API Version'
+      label: "API Version"

 openid_connect.client.plugin.generic:
   type: mapping
-  label: 'OpenID Connect Generic settings'
+  label: "OpenID Connect Generic settings"
   mapping:
     <<: *base
     issuer_url:
       type: string
-      label: 'Issuer URL'
+      label: "Issuer URL"
     authorization_endpoint:
       type: string
-      label: 'Authorization endpoint'
+      label: "Authorization endpoint"
     token_endpoint:
       type: string
-      label: 'Token endpoint'
+      label: "Token endpoint"
     userinfo_endpoint:
       type: string
-      label: 'Userinfo endpoint'
+      label: "Userinfo endpoint"
     end_session_endpoint:
       type: string
-      label: 'End Session endpoint'
+      label: "End Session endpoint"
     scopes:
       type: sequence
-      label: 'Scopes'
+      label: "Scopes"
       sequence:
         type: string

 openid_connect.client.plugin.okta:
   type: mapping
-  label: 'OpenID Connect Okta settings'
+  label: "OpenID Connect Okta settings"
   mapping:
     <<: *base
     okta_domain:
       type: string
-      label: 'Okta domain'
+      label: "Okta domain"
     scopes:
       type: sequence
-      label: 'Scopes'
+      label: "Scopes"
       sequence:
         type: string
diff --git a/openid_connect.install b/openid_connect.install
index b7a1efd..e6832d8 100644
--- a/openid_connect.install
+++ b/openid_connect.install
@@ -221,7 +221,7 @@ function openid_connect_update_8201() {
  */
 function openid_connect_update_8202() {
   $config = \Drupal::configFactory()->getEditable('openid_connect.settings');
-  $config->set('redirect_login', 'user');
+  $config->set('redirect_login', '/');
   $config->save(TRUE);
 }

diff --git a/openid_connect.services.yml b/openid_connect.services.yml
index 00c04bb..5b1e91e 100644
--- a/openid_connect.services.yml
+++ b/openid_connect.services.yml
@@ -2,20 +2,20 @@ services:
   openid_connect.openid_connect:
     class: Drupal\openid_connect\OpenIDConnect
     arguments:
-      - '@config.factory'
-      - '@externalauth.authmap'
-      - '@externalauth.externalauth'
-      - '@entity_type.manager'
-      - '@entity_field.manager'
-      - '@current_user'
-      - '@user.data'
-      - '@email.validator'
-      - '@messenger'
-      - '@module_handler'
-      - '@logger.factory'
-      - '@file_system'
-      - '@openid_connect.session'
-      - '@file.repository'
+      - "@config.factory"
+      - "@externalauth.authmap"
+      - "@externalauth.externalauth"
+      - "@entity_type.manager"
+      - "@entity_field.manager"
+      - "@current_user"
+      - "@user.data"
+      - "@email.validator"
+      - "@messenger"
+      - "@module_handler"
+      - "@logger.factory"
+      - "@file_system"
+      - "@openid_connect.session"
+      - "@file.repository"

   plugin.manager.openid_connect_client:
     class: Drupal\openid_connect\Plugin\OpenIDConnectClientManager
@@ -23,20 +23,33 @@ services:

   openid_connect.claims:
     class: Drupal\openid_connect\OpenIDConnectClaims
-    arguments: ['@config.factory', '@module_handler']
+    arguments: ["@config.factory", "@module_handler"]

   openid_connect.state_token:
     class: Drupal\openid_connect\OpenIDConnectStateToken
-    arguments: ['@openid_connect.session']
+    arguments: ["@openid_connect.session"]
+
+  openid_connect.auto_login:
+    class: Drupal\openid_connect\EventSubscriber\OpenIDConnectAutoLogin
+    arguments:
+      [
+        "@current_user",
+        "@plugin.manager.openid_connect_client",
+        "@config.factory",
+      ]
+    tags:
+      - { name: event_subscriber }

   openid_connect.session:
     class: Drupal\openid_connect\OpenIDConnectSession
     arguments:
-      - '@config.factory'
-      - '@redirect.destination'
-      - '@session'
-      - '@language_manager'
+      [
+        "@config.factory",
+        "@redirect.destination",
+        "@session",
+        "@language_manager",
+      ]

   openid_connect.autodiscover:
     class: Drupal\openid_connect\OpenIDConnectAutoDiscover
-    arguments: ['@http_client_factory', '@logger.factory']
+    arguments: ["@http_client_factory", "@logger.factory"]
diff --git a/src/EventSubscriber/OpenIDConnectAutoLogin.php b/src/EventSubscriber/OpenIDConnectAutoLogin.php
new file mode 100644
index 0000000..7d359f3
--- /dev/null
+++ b/src/EventSubscriber/OpenIDConnectAutoLogin.php
@@ -0,0 +1,235 @@
+<?php
+
+namespace Drupal\openid_connect\EventSubscriber;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\openid_connect\Plugin\OpenIDConnectClientManager;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Event\RequestEvent;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Drupal\Core\Routing\RouteObjectInterface;
+
+/**
+ * Auto login process.
+ *
+ * When user is requesting user login, register or password reset
+ * page as anonymous, OpenID Connect client login process should auto start.
+ *
+ * Login auto start can be disabled in configuration of plugin and
+ * will only start, if only one OpenID Connect client is enabled.
+ *
+ * If user, as anonymous will request page with 'showcore'
+ * parameter, standard Drupal login page should be displayed
+ * instead of OpenID Connect client login page.
+ */
+class OpenIDConnectAutoLogin implements EventSubscriberInterface {
+
+  /**
+   * Current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * OpenID Connect Client Plugin Manager.
+   *
+   * @var \Drupal\openid_connect\Plugin\OpenIDConnectClientManager
+   */
+  protected $pluginManager;
+
+  /**
+   * Config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * OpenID Client to use in login process.
+   *
+   * @var \Drupal\openid_connect\Plugin\OpenIDConnectClientInterface
+   */
+  protected $client;
+
+  /**
+   * The constructor.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $user
+   *   Current user.
+   * @param \Drupal\openid_connect\Plugin\OpenIDConnectClientManager $plugin_manager
+   *   The plugin manager.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   Config factory.
+   */
+  public function __construct(AccountInterface $user, OpenIDConnectClientManager $plugin_manager, ConfigFactoryInterface $config_factory) {
+    $this->currentUser = $user;
+    $this->pluginManager = $plugin_manager;
+    $this->configFactory = $config_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    return [
+      KernelEvents::REQUEST => [
+        ['login', 28],
+      ],
+    ];
+  }
+
+  /**
+   * Auto start OpenID Connect client login process.
+   *
+   * The process will start, if there is only one client enabled.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\RequestEvent $event
+   *   Response event.
+   */
+  public function login(RequestEvent $event) {
+    // Get current request.
+    $request = $event->getRequest();
+    // Check if user is anonymous and login or register page was requested.
+    if ($this->isAutostartEnabled() && $this->currentUser->isAnonymous() && $this->isLoginRequested($request)) {
+      // If there is no login errors and login process is not in progress
+      // and openid_connect_bypass is not provided, then start login process.
+      if (!$this->hasErrors() && !$this->bypassAutoLogin($request)) {
+        // Start OpenID Connect login process.
+        \Drupal::service('openid_connect.session')->saveDestination();
+        $_SESSION['openid_connect_op'] = 'login';
+        $client = $this->getClient();
+        if ($client) {
+          $response = $client->authorize();
+          // Redirect to given response.
+          $event->setResponse($response);
+        }
+      }
+    }
+  }
+
+  /**
+   * Detect if there is error during OpenID Connect login process.
+   *
+   * @return bool
+   *   TRUE in case of error, FALSE otherwise.
+   */
+  protected function hasErrors() {
+    if (isset($_SESSION['messages']) && isset($_SESSION['messages']['error'])) {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Check if auto start login process is enabled.
+   *
+   * Autostart means, that if user tries to access login, register or reset
+   * password pages as anonymous, it will be redirected to OpenID Connect
+   * client login process.
+   *
+   * This function also checks if openid client configuration has been provided.
+   *
+   * @return bool
+   *   TRUE if autostart login is enabled, FALSE otherwise.
+   */
+  protected function isAutostartEnabled() {
+    // Check if autostart is enabled.
+    $auto_start = (bool) $this->configFactory
+      ->get('openid_connect.settings')
+      ->get('autostart_login');
+    if ($auto_start) {
+      $client = $this->getClient();
+      // Check if client endpoints are configured.
+      if ($client) {
+        foreach ($client->getEndpoints() as $endpoint) {
+          if ($endpoint === NULL) {
+            return FALSE;
+          }
+        }
+      }
+    }
+    return $auto_start;
+  }
+
+  /**
+   * Check if login or register page was requested.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   Request.
+   *
+   * @return bool
+   *   TRUE if login or register page was requested, FALSE otherwise.
+   */
+  protected function isLoginRequested(Request $request) {
+    // Get route name of current page.
+    $route_name = $request->get(RouteObjectInterface::ROUTE_NAME);
+    // If route name is empty, return true to prevent further actions,
+    // as we don't know yet page, we are viewing.
+    return !empty($route_name) && in_array($route_name, [
+      'user.login',
+      'user.register',
+      'user.pass',
+    ]);
+  }
+
+  /**
+   * Set OpenID Connect Client.
+   *
+   * Get all definitions of OpenID Connect clients and return the one,
+   * we should use in auto start login process. If there is more than one
+   * clients enabled, return null.
+   *
+   * @return null|\Drupal\openid_connect\Plugin\OpenIDConnectClientInterface
+   *   NULL if no client or client object.
+   */
+  protected function getClient() {
+    // If client isset, don't do that again.
+    if (!$this->client) {
+      // Find enabled Generic OpenID Connect clients. since we allow only generic openid_connect id plugins we will look only for that.
+      // However if in the future we want to allow other plugin we have to load all
+      $clients = \Drupal::entityTypeManager()->getStorage('openid_connect_client')->loadByProperties(['plugin' => 'generic']);
+
+      foreach ($clients as $client){
+        // checking if the status is equal to enabled, status could be 1 or 0 meaning enabled or not enabled
+        if ((bool) $client->get('status')) {
+          $plugin = $client->getPlugin();
+          $configuration = $plugin->getConfiguration();
+          // Check if client is not set yet.
+          if (!$this->client) {
+            $this->client = $this->pluginManager->createInstance(
+              $plugin->getPluginId(),
+              $configuration
+            );
+        }
+          // If there is more than one enabled client,
+          // we can't auto start login process.
+          else {
+            $this->client = NULL;
+            break;
+          }
+      }
+    }
+  }
+  return $this->client;
+}
+
+  /**
+   * Check if OpenID connect or Drupal login process were requested.
+   *
+   * If we should display Drupal login/register/password reset page,
+   * the query contains the 'showcore' parameter in the request. Otherwise we are
+   * starting OpenID Connect client login process.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   Request.
+   *
+   * @return bool
+   *   TRUE if regular Drupal login process should start, FALSE otherwise.
+   */
+  protected function bypassAutoLogin(Request $request) {
+    return $request->query->has('showcore');
+  }
+}
diff --git a/src/Form/OpenIDConnectSettingsForm.php b/src/Form/OpenIDConnectSettingsForm.php
index 79d2cef..07d5f3c 100644
--- a/src/Form/OpenIDConnectSettingsForm.php
+++ b/src/Form/OpenIDConnectSettingsForm.php
@@ -123,6 +123,13 @@ class OpenIDConnectSettingsForm extends ConfigFormBase {
       '#default_value' => $settings->get('end_session_enabled'),
     ];

+    $form['autostart_login'] = [
+          '#title' => $this->t('Autostart login process'),
+          '#type' => 'checkbox',
+          '#default_value' => $settings->get('autostart_login'),
+          '#description' => $this->t('Auto start login process when login, register or password reset page was requested as anonymous.'),
+    ];
+
     $form['user_login_display'] = [
       '#type' => 'radios',
       '#title' => $this->t('OpenID buttons display in user login form'),
@@ -192,30 +199,32 @@ class OpenIDConnectSettingsForm extends ConfigFormBase {
       '#title' => 'EXPERIMENTAL - ' . $this->t('User role mapping'),
       '#type' => 'fieldset',
       '#description' => $this->t('For each Drupal role, provide the sets of equivalent external groups, separated by spaces. A user belonging to one of the provided groups will be assigned the configured Drupal role.') .
-                        $this->t("<br/><strong>Note:</strong> The module will not update user roles with no mapped external groups. If all mappings to one of the roles are removd, users will keep that role until it is removed in the Drupal user administration."),
+                        $this->t("<br/><strong>Note:</strong> The module will not update user roles with no mapped external groups. If all mappings to one of the roles are removed, users will keep that role until it is removed in the Drupal user administration."),
       '#tree' => TRUE,
     ];
     // phpcs:enable

-    foreach ($roles as $role_id => $role) {
-      $default = '';
-      if (is_array($role_mappings[$role_id])) {
-        // Surround any mappings with spaces with double quotes.
-        foreach ($role_mappings[$role_id] as $key => $mapping) {
-          if (strpos($mapping, ' ') !== FALSE) {
-            $role_mappings[$role_id][$key] = '"' . $mapping . '"';
+    if (isset($role_mappings)) {
+      foreach ($roles as $role_id => $role) {
+        $default = '';
+        if (is_array($role_mappings[$role_id])) {
+          // Surround any mappings with spaces with double quotes.
+          foreach ($role_mappings[$role_id] as $key => $mapping) {
+            if (strpos($mapping, ' ') !== FALSE) {
+              $role_mappings[$role_id][$key] = '"' . $mapping . '"';
+            }
           }
+          $default = implode(' ', $role_mappings[$role_id]);
         }
-        $default = implode(' ', $role_mappings[$role_id]);
-      }

-      $form['role_mappings'][$role_id] = [
-        '#title' => $role->label(),
-        '#type' => 'textfield',
-        '#default_value' => $default,
-      ];
+        $form['role_mappings'][$role_id] = [
+          '#title' => $role->label(),
+          '#type' => 'textfield',
+          '#default_value' => $default,
+        ];
+      }
     }
-
+
     $form['advanced'] = [
       '#title' => $this->t('Advanced'),
       '#type' => 'details',
@@ -246,6 +255,7 @@ class OpenIDConnectSettingsForm extends ConfigFormBase {
       ->set('always_save_userinfo', $form_state->getValue('always_save_userinfo'))
       ->set('connect_existing_users', $form_state->getValue('connect_existing_users'))
       ->set('override_registration_settings', $form_state->getValue('override_registration_settings'))
+      ->set('autostart_login', $form_state->getValue('autostart_login'))
       ->set('end_session_enabled', $form_state->getValue('end_session_enabled'))
       ->set('user_login_display', $form_state->getValue('user_login_display'))
       ->set('redirect_login', $form_state->getValue('redirect_login'))
@@ -254,5 +264,5 @@ class OpenIDConnectSettingsForm extends ConfigFormBase {
       ->set('role_mappings', $role_mappings)
       ->save();
   }
-
+
 }
diff --git a/src/OpenIDConnect.php b/src/OpenIDConnect.php
index b21e2d5..ef0712b 100644
--- a/src/OpenIDConnect.php
+++ b/src/OpenIDConnect.php
@@ -20,6 +20,7 @@ use Drupal\externalauth\ExternalAuthInterface;
 use Drupal\file\FileRepositoryInterface;
 use Drupal\user\UserDataInterface;
 use Drupal\user\UserInterface;
+use Drupal\user\Entity\Role;

 /**
  * Main service of the OpenID Connect module.
@@ -432,6 +433,10 @@ class OpenIDConnect {
       $this->session->saveAccessToken($tokens['access_token']);
     }

+    // We evaluate roles each time a user signs in.
+    // This way, changes to e-groups / Grappa will be accounted for.
+    $this->roleMatchSync($client, $account, $context);
+
     $this->moduleHandler
       ->invokeAll('openid_connect_post_authorize', [$account, $context]);

@@ -490,6 +495,99 @@ class OpenIDConnect {
     return FALSE;
   }

+  /**
+   * Synchronizes (adds/removes) user account roles.
+   *
+   * @param \Drupal\openid_connect\Plugin\OpenIDConnectClientInterface $client
+   *   The client.
+   * @param \Drupal\user\UserInterface $account
+   *   The Drupal user to sync roles for.
+   * @param array $context
+   *   An associative array with context information:
+   *   - tokens:         An array of tokens.
+   *   - user_data:      An array of user and session data.
+   *   - userinfo:       An array of user information.
+   *   - plugin_id:      The plugin identifier.
+   *   - sub:            The remote user identifier.
+   *
+   * @throws EntityStorageException
+   */
+  public function roleMatchSync(OpenIDConnectClientEntityInterface $client, UserInterface $account, array $context) {
+    // Get user's current roles, excluding locked roles (e.g. Authenticated).
+    $current_roles = $account->getRoles(TRUE);
+    // Get OICD user roles
+    $openid_roles = $this->getOIDCRoles($client, $context);
+
+    if ($this->configFactory->get('openid_connect.settings')->get('debug')) {
+      $this->logger->debug('Current user roles: %roles', [
+        '%roles' => json_encode($current_roles),
+      ]);
+      $this->logger->debug('OICD matched roles: %roles', ['%roles' => json_encode($openid_roles)]);
+    }
+
+    // Set boolean to only update account when needed.
+    $account_updated = FALSE;
+
+    // Remove non-locked roles not mapped to the user via OIDC.
+    foreach (array_diff($current_roles, $openid_roles) as $role_id) {
+      if ($this->configFactory->get('openid_connect.settings')->get('debug')) {
+        $this->logger->debug('Removing role %role from user %name', [
+          '%role' => $role_id,
+          '%name' => $account->getAccountName(),
+        ]);
+      }
+      $account->removeRole($role_id);
+      $account_updated = TRUE;
+    }
+
+    // Add roles mapped to the user via OIDC.
+    foreach (array_diff($openid_roles, $current_roles) as $role_id) {
+      if ($this->configFactory->get('openid_connect.settings')->get('debug')) {
+        $this->logger->debug('Adding role %role to user %name', [
+          '%role' => $role_id,
+          '%name' => $account->getAccountName(),
+          ]);
+      }
+      $account->addRole($role_id);
+      $account_updated = TRUE;
+    }
+    if ($account_updated) {
+      $account->save();
+    }
+
+    if ($this->configFactory->get('openid_connect.settings')->get('debug')) {
+      $this->logger->debug('User final roles: %roles', ['%roles' => json_encode($account->getRoles())]);
+    }
+  }
+
+  /**
+   * Returns OpenID Connect Role claims.
+   *
+   * @param \Drupal\openid_connect\Plugin\OpenIDConnectClientInterface $client
+   *   The client.
+   * @param array $context
+   *   An associative array with context information:
+   *   - tokens:         An array of tokens.
+   *   - user_data:      An array of user and session data.
+   *   - userinfo:       An array of user information.
+   *   - plugin_id:      The plugin identifier.
+   *   - sub:            The remote user identifier.
+   *
+   * @return array
+   *   List of Role claims.
+   */
+  public function getOIDCRoles(OpenIDConnectClientEntityInterface $client, array $context): array {
+    $user_data_roles = $context['user_data']['cern_roles'];
+    $all_roles = array_keys(Role::loadMultiple());
+
+    if ($this->configFactory->get('openid_connect.settings')->get('debug')) {
+      $this->logger->debug('User roles: %roles', ['%roles' => json_encode($user_data_roles)]);
+      $this->logger->debug('Drupal roles: %roles', ['%roles' => json_encode($all_roles)]);
+    }
+
+    return array_values(array_intersect($all_roles, $user_data_roles));
+  }
+
   /**
    * Find whether a user is allowed to change the own password.
    *
diff --git a/src/OpenIDConnectSession.php b/src/OpenIDConnectSession.php
index ea79bf1..6e699d6 100644
--- a/src/OpenIDConnectSession.php
+++ b/src/OpenIDConnectSession.php
@@ -92,18 +92,27 @@ class OpenIDConnectSession implements OpenIDConnectSessionInterface {
    * {@inheritdoc}
    */
   public function saveDestination() {
-    // If the current request includes a 'destination' query parameter we'll use
-    // that in the redirection. Otherwise use the current request path and
-    // query.
-    $destination = ltrim($this->redirectDestination->get(), '/');
-    $langcode = $this->languageManager->getCurrentLanguage()->getId();
-
-    // Don't redirect to user/login. In this case redirect to the user profile.
-    if (strpos($destination, ltrim(Url::fromRoute('user.login')->toString(), '/')) === 0) {
-      $redirect_login = $this->configFactory->get('openid_connect.settings')->get('redirect_login');
-      $destination = $redirect_login ?: 'user';
+    // If the current request includes a 'destination' query parameter and openid setting is empty or '/' we'll use
+    // that in the redirection.
+    // Otherwise use the current openid setting
+
+    $redirect_login_setting = $this->configFactory->get('openid_connect.settings')->get('redirect_login');
+
+    if(empty($redirect_login_setting) || $redirect_login_setting === "/"){
+      // use the destination query parameter
+      $destination = ltrim($this->redirectDestination->get(), '/');
+
+      //if destination is empty or is set to user/login we redirect the user to home page
+      if (empty($destination) || strpos($destination, ltrim(Url::fromRoute('user.login')->toString(), '/')) === 0){
+        $destination = '/';
+      }
+    }else{
+      // use the openid setting. if this is empty redirect to home
+      $destination = $redirect_login_setting ?: '/';
     }

+    $langcode = $this->languageManager->getCurrentLanguage()->getId();
+
     $this->session->set('openid_connect_destination', $destination);
     $this->session->set('openid_connect_langcode', $langcode);
   }
@@ -207,4 +216,4 @@ class OpenIDConnectSession implements OpenIDConnectSessionInterface {
     $this->session->set('openid_connect_state', $token);
   }

-}
+}
\ No newline at end of file
diff --git a/src/Plugin/OpenIDConnectClient/OpenIDConnectGenericClient.php b/src/Plugin/OpenIDConnectClient/OpenIDConnectGenericClient.php
index 268c5ed..ff7d9fd 100644
--- a/src/Plugin/OpenIDConnectClient/OpenIDConnectGenericClient.php
+++ b/src/Plugin/OpenIDConnectClient/OpenIDConnectGenericClient.php
@@ -27,7 +27,7 @@ class OpenIDConnectGenericClient extends OpenIDConnectClientBase {
       'authorization_endpoint' => 'https://example.com/oauth2/authorize',
       'token_endpoint' => 'https://example.com/oauth2/token',
       'userinfo_endpoint' => 'https://example.com/oauth2/userinfo',
-      'end_session_endpoint' => '',
+      'end_session_endpoint' => '/',
       'scopes' => ['openid', 'email'],
     ] + parent::defaultConfiguration();
   }
@@ -66,6 +66,7 @@ class OpenIDConnectGenericClient extends OpenIDConnectClientBase {
         'visible' => [':input[name="settings[use_well_known]"]' => ['checked' => FALSE]],
       ],
     ];
+
     $form['token_endpoint'] = [
       '#title' => $this->t('Token endpoint'),
       '#type' => 'url',
@@ -74,6 +75,7 @@ class OpenIDConnectGenericClient extends OpenIDConnectClientBase {
         'visible' => [':input[name="settings[use_well_known]"]' => ['checked' => FALSE]],
       ],
     ];
+
     $form['userinfo_endpoint'] = [
       '#title' => $this->t('UserInfo endpoint'),
       '#type' => 'url',
@@ -82,6 +84,7 @@ class OpenIDConnectGenericClient extends OpenIDConnectClientBase {
         'visible' => [':input[name="settings[use_well_known]"]' => ['checked' => FALSE]],
       ],
     ];
+
     $form['end_session_endpoint'] = [
       '#title' => $this->t('End Session endpoint'),
       '#type' => 'url',
@@ -98,6 +101,12 @@ class OpenIDConnectGenericClient extends OpenIDConnectClientBase {
       '#default_value' => implode(' ', $this->configuration['scopes']),
     ];

+    $form['role_eval_every_time'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Re-evaluate roles every time the user logs in'),
+      '#default_value' => $this->configuration['role_eval_every_time'],
+    ];
+
     return $form;
   }

@@ -185,6 +194,7 @@ class OpenIDConnectGenericClient extends OpenIDConnectClientBase {
       'token' => $this->configuration['token_endpoint'],
       'userinfo' => $this->configuration['userinfo_endpoint'],
       'end_session' => $this->configuration['end_session_endpoint'],
+      'role_eval_every_time' => $this->configuration['role_eval_every_time']
     ];
   }

diff --git a/src/Plugin/OpenIDConnectClientBase.php b/src/Plugin/OpenIDConnectClientBase.php
index 9ec17e0..601cd86 100644
--- a/src/Plugin/OpenIDConnectClientBase.php
+++ b/src/Plugin/OpenIDConnectClientBase.php
@@ -131,7 +131,9 @@ abstract class OpenIDConnectClientBase extends PluginBase implements OpenIDConne
     $this->languageManager = $language_manager;
     $this->stateToken = $state_token;
     $this->autoDiscover = $auto_discover;
-    $this->parentEntityId = '';
+    // this function is called sometimes at login (maybe bug of the library?)
+    // and overwrites the settings done, so we ensure that the default falue for the parentID is the generic one.
+    $this->parentEntityId = 'generic';
     $this->setConfiguration($configuration);
   }