1

I have a feature where if a user navigates to a specific url (www.mysite.com/special-content) they are redirected to the login page, where we've appended a destination to the request (www.mysite.com/user/login?destination=special-content).

We created a custom module using a checkAuthStatus event subscriber to detect anonymous users and redirect them appropriately:

$ cat src/EventSubscriber/RedirectAnonymousSubscriber.php
<?php

namespace Drupal\anon_redirect\EventSubscriber;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Event subscriber subscribing to KernelEvents::REQUEST.
 */
class RedirectAnonymousSubscriber implements EventSubscriberInterface {

  public function __construct() {
    $this->account = \Drupal::currentUser();
  }

  public function checkAuthStatus(GetResponseEvent $event) {
    $paths = [
      '/special-content',
      '/more-special-content'
    ];

    if ($this->account->isAnonymous()) {
      foreach ( $paths as $path ) {
        if ( substr($_SERVER['REQUEST_URI'], 0, strlen($path)) === $path ) {
          $url = \Drupal\Core\Url::fromUri('internal:/user/login',[
            'query'=>['destination'=>$_SERVER['REQUEST_URI']],
          ]);
          $response = new RedirectResponse('/user/login?destination='.$_SERVER['REQUEST_URI'], 302);
          $response->send();
          return;
        }
      }
    }

  }

  public static function getSubscribedEvents() {
    $events[KernelEvents::REQUEST][] = ['checkAuthStatus'];
    return $events;
  }
}

This has been working fine for a few months, but recently I got complains that people were not getting redirected. Upon investigation, I saw that they were getting 'access denied', as evidenced by the watchdog log. That is, when they are not logged in, they see our 301 page, instead of getting redirected.

Frustratingly, I've only been able to intermittently reproduce this problem. Sometimes I get the 301 page, sometimes I get the login page with the proper redirect query appended. I've tried both Firefox and Chrome, private sessions, clearing cookies, etc.

I suspect that whatever decides access also is listening to checkAuthStatus and sometimes having the final word about what page is ultimately sent to the end-user, and our module's behavior is not implemented, while other times, our event listener is invoked.

My question is if there is any way I can see what event listeners are subscribed to the checkAuthStatus event, and ideally, which one is having the final word as to the ultimate response from Drupal. I suspect another one is somehow getting prioritized over mine, and would like to confirm or deny that theory.

  • 1
    To stop the propagation of the event you have to return a response from the event subscriber, see drupal.stackexchange.com/questions/270597/… – 4k4 Mar 6 at 21:15
  • @4k4 Did you link the right question? I don't see anything about stop propagation in that page. – user1359 Mar 7 at 13:35
  • Yes, I did. When you send the response instead of returning it (see the answer to the linked question) you don't stop the event dispatcher and Drupal continues to build its own response. This causes the erratic behavior you describe. You might need to adjust the priority of your event, I think <300 to get executed after AuthenticationSubscriber (see the list shown in the answer from @johndevman), because your code depends only on the current user. – 4k4 Mar 7 at 13:51
  • @4k4 okay, but I don't see the return in the checkForRedirection() method in the answer to the question you linked. It does return $events in getSubscribedEvents(), but so does my code. Also I don't see where the priority is set in the linked question or accepted answer. – user1359 Mar 7 at 14:08
  • 1
    You return the response in the event object $event->setResponse($response) – 4k4 Mar 7 at 14:15
2

In the above code example the event is KernelEvents::Request and checkAuthStatus is the method called when the Request event is dispatched.

You can use Drupal Console debug:event command to list all subscribers for a given event:

vendor/bin/drupal debug:event kernel.request

And that will give you something like:

  Drupal\Core\EventSubscriber\OptionsRequestSubscriber                   onRequest: 1000
  Drupal\Core\EventSubscriber\RedirectLeadingSlashesSubscriber           redirect: 1000
  Drupal\Core\EventSubscriber\AuthenticationSubscriber                   onKernelRequestAuthenticate: 300
  Drupal\language\EventSubscriber\LanguageRequestSubscriber              onKernelRequestLanguage: 255
  Drupal\Core\EventSubscriber\RedirectResponseSubscriber                 sanitizeDestination: 100
  Drupal\Core\EventSubscriber\AjaxResponseSubscriber                     onRequest: 50
  Symfony\Component\HttpKernel\EventListener\RouterListener              onKernelRequest: 32
  Drupal\Core\EventSubscriber\AuthenticationSubscriber                   onKernelRequestFilterProvider: 31
  Drupal\user\EventSubscriber\MaintenanceModeSubscriber                  onKernelRequestMaintenance: 31
  Drupal\Core\EventSubscriber\MaintenanceModeSubscriber                  onKernelRequestMaintenance: 30
  Drupal\Core\Routing\RoutePreloader                                     onRequest: 0
  Drupal\Core\EventSubscriber\ReplicaDatabaseIgnoreSubscriber            checkReplicaServer: 0

If you use Drush and have the devel module installed, you can do:

vendor/bin/drush devel-event kernel.request
0

You can get a list of the subscribers to a particular event, sorted in order of priority, using the event dispatcher service:

$subscribers = \Drupal::service('event_dispatcher')
  ->getListeners(\Symfony\Component\HttpKernel\KernelEvents::REQUEST);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.