Prevent registration with duplicate email

Created on 30 April 2020, about 4 years ago
Updated 7 November 2023, 8 months ago

Problem/Motivation

External auth does not verify if a user account already exists with the provided email during registration. This is invalid - the user email field is supposed to be unique. Drupal's normal user registration system does not allow duplicate emails.

Proposed resolution

Verify that the email address provided does not already exist. Or, better yet, run the user through entity validation before saving it, just like core does with normal user registration. This would automatically run the unique email constraint validator. External auth could then throw an exception.

Remaining tasks

TBD

User interface changes

None

API changes

This should be explicitly mentioned in the release notes. There could be sites that are relying on the broken behavior.

Data model changes

None

🐛 Bug report
Status

Closed: won't fix

Version

2.0

Component

Code

Created by

🇩🇪Germany semiaddict

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • 🇺🇸United States John Franklin

    Looking to current External Authentication codebase, I see no routes, not configurations, no forms. That's great! It's pure API and it should stay as it is.

    The need to validate claims, both per se and against existing users, is a common enough use case that the functionality should exist. I'm running into this same issue on my own projects. Yes, the Drupal API allows for users with missing or duplicate email addresses, and the UI does a lot of the user validation work. However, with External Auth, that validation is bypassed, and it falls on each module building on External Auth to add it back in, which strikes me as duplication of effort.

    I can see the merits of keeping External Auth as an API-only module. That said, just as External Auth handles the common parts of any external authentication solution, IMHO it should also provide that common validation, while allowing it to be optional.

    There are two major ways I can see the optional validation happening while keeping External Auth as an API-only module:

    1) provide hooks/functions/API parameters/etc. to allow calling modules to trigger the validation.
    2) provide a separate sub-module that implements the validation which site builders can optionally enable if they need it.

    The first favors module developers who need to add a '#validate' => TRUE somewhere, or need to call $this->externalAuth->validateData() (or similar). The second favors site builders, but may require some refactoring of the events triggered by External Auth, perhaps incorporated into Redo authmap_alter / register events. Needs review .

    Thoughts?

    p.s. I'm intentionally not re-opening this without a clearer idea of where the community wants to go.

  • 🇨🇴Colombia julianmancera

    To accomplish this you can add an event subscriber to AUTHMAP_ALTER event like:

    <?php
    
    namespace Drupal\my_module\EventSubscriber;
    
    use Drupal\Core\Database\Connection;
    use Drupal\externalauth\Event\ExternalAuthAuthmapAlterEvent;
    use Drupal\externalauth\Event\ExternalAuthEvents;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    
    /**
     * Class ExternalAuthSubscriber.
     *
     * @package Drupal\my_module\EventSubscriber
     */
    class ExternalAuthSubscriber implements EventSubscriberInterface {
    
      /**
       * The database connection.
       *
       * @var \Drupal\Core\Database\Connection
       */
      protected Connection $database;
    
      /**
       * Constructor.
       *
       * We use dependency injection.
       *
       * @param \Drupal\Core\Database\Connection $database
       *   The database service.
       */
      public function __construct(Connection $database) {
        $this->database = $database;
      }
    
      /**
       * {@inheritdoc}
       */
      public static function getSubscribedEvents() {
        return [
          // Static class constant => method on this class.
          ExternalAuthEvents::AUTHMAP_ALTER => 'onUserRegisterMapAlter',
        ];
      }
    
      /**
       * Subscribe to the external auth map  event dispatched.
       *
       * @param \Drupal\externalauth\Event\ExternalAuthAuthmapAlterEvent $event
       *   Mapping event object.
       */
      public function onUserRegisterMapAlter(ExternalAuthAuthmapAlterEvent $event) {
        $account = $event->getAuthname();
    
     
    
        // Looking for user email.
        $query = $this->database->select('users_field_data', 'ufs')
          ->fields('ufs', ['name'])
          ->range(0, 1)
          ->condition('mail', $email)
          ->execute();
    
        $user_account = $query->fetchObject();
    
        if ($user_account) {
          $event->setUsername($user_account->name);
        }
      }
    
    }
    

    Though some times you have different username that the authname, for such case you can use the patch attached

  • 🇮🇹Italy apaderno Brescia, 🇮🇹

    I agree with claudiu.cristea.

Production build 0.69.0 2024