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

The ongoing one-time log in link saga #42

Open
Ambient-Impact opened this issue Sep 9, 2023 · 0 comments
Open

The ongoing one-time log in link saga #42

Ambient-Impact opened this issue Sep 9, 2023 · 0 comments
Labels
bug Something isn't working

Comments

@Ambient-Impact
Copy link
Member

Ambient-Impact commented Sep 9, 2023

Multiple users have reported that they can't use one-time log in links to reset their passwords or to verify their email address. There appear to be multiple reasons for this depending on the context.

Link invalidation

Initially, this seemed to be due to various crawlers from popular email services such as Gmail and Outlook visiting the URL to scan for malware, thus always invalidating the link before the user could use it. The Shy One-Time module was installed to prevent this in #30.

Password resets redirecting incorrectly

At this point, we expected the issue to be resolved, but have been getting occasional reports of password resets not working. The exact cause and exactly what was going wrong was difficult to pin down due the lack of information that the logs were providing and users' reports not yielding anything specific other than it not doing anything when they used the log in link.

The light bulb moment

I was finally able to both replicate the issue and narrow down where the cause may be on my local development copy of the site.

With the current set of installed modules, sending myself a password reset and then following the link resulted in a redirect to the user log in form (user/login) without offering the password reset or logging me in, which is unexpected. It's at this point that I wondered if one or more of the modules we use that alter the log in redirects may be conflicting with each other. I tried to uninstall various combinations, testing the one time log in links after each one. The combination that finally showed me the expected reset page (user/reset) instead of redirecting me to the log in form (user/login) on uninstalling were the Shy One-Time module and the Two-factor Authentication (TFA) module. After further investigation, it seems uninstalling Shy One-Time and simply disabling TFA in the TFA settings form also fixes it without the need to uninstall TFA. Actually, TFA may be a red herring and simply uninstalling the Shy One-Time module seems to restore the expected behaviour of the reset page (user/reset). 🙃

Shy One-Time

Update: opened Empty state user agent value results in false positive, redirects all non-crawlers to user/login [#3386334] which fixes this and adds a test to prevent regressions.

It looks like the issue originates in the 2.x branch of src/EventSubscriber/ShyEventSubscriber.php where they do the following which redirects to the log in form (user/login):

elseif ($this->shyOneTimeState->checkUserAgents($event->getRequest()->headers->get('User-Agent'))) {
  $redirectUrl = Url::fromRoute('user.login')->toString();
  // Create a redirect response to the special node.
  $response = new RedirectResponse($redirectUrl);
  // Set the redirect status code (optional).
  $response->setStatusCode(302);
  // Set any additional headers (optional).
  $response->headers->set('Cache-Control', 'no-cache');
  // Return the redirect response to stop further processing.
  $event->setResponse($response);
}

The event subscriber seems to have been originally introduced in Reset Password link is not working [#3349335] in 1.x without the above code; the 2.x branch introduced the above code in commit ccb3e1bab3ff797e69b8b8f8fba605b1ee84b7c3 as part of Exclusion of individual user agents [#3373315]; however, the event subscriber itself isn't the problem but rather src/ShyOneTimeState.php which doesn't sufficiently check for empty values returned from state and thus creates a regular expression that will always match if the state value is empty:

  public function checkUserAgents($currentUserAgent): bool|string {

    $isBlockedUserAgent = FALSE;

    foreach ($this->getUserAgents() as $userAgent) {

      $trimmedUserAgent = preg_replace('/\s+/', ' ', trim($userAgent));
      $patternUserAgent = "/" . preg_quote($trimmedUserAgent, '/') . "/i";

      if (preg_match($patternUserAgent, $currentUserAgent)) {
        $isBlockedUserAgent = TRUE;
        break;
      }
    }

    return $isBlockedUserAgent;
  }

Adding if (empty($trimmedUserAgent)) { like so seems to fix it:

  public function checkUserAgents($currentUserAgent): bool|string {

    $isBlockedUserAgent = FALSE;

    foreach ($this->getUserAgents() as $userAgent) {

      $trimmedUserAgent = preg_replace('/\s+/', ' ', trim($userAgent));

      // Skip empty user agent strings to avoid false positives. This can happen
      // when the state value is an empty string.
      if (empty($trimmedUserAgent)) {
        continue;
      }

      $patternUserAgent = "/" . preg_quote($trimmedUserAgent, '/') . "/i";

      if (preg_match($patternUserAgent, $currentUserAgent)) {
        $isBlockedUserAgent = TRUE;
        break;
      }
    }

    return $isBlockedUserAgent;
  }

Red herrings

While seemingly a potential culprit, the Login Destination module has no effect if uninstalled. Additionally, the Username Enumeration Prevention module also has no effect if uninstalled.

@Ambient-Impact Ambient-Impact added the bug Something isn't working label Sep 9, 2023
@Ambient-Impact Ambient-Impact pinned this issue Sep 9, 2023
@Ambient-Impact Ambient-Impact changed the title The ongoing one time log in link saga The ongoing one-time log in link saga Sep 12, 2023
Ambient-Impact added a commit that referenced this issue Sep 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Status: In Progress
Development

No branches or pull requests

1 participant