3

I have an application that has a custom SSO with a third party service. We are porting Drupal 7 code to Drupal 10 to continue the functionality. Previously we had a hook_init function that did the following:

  • Check if the user is authenticated with specific role
  • Check that the user has two valid cookies (used in API calls to the third party)
  • If the user is authenticated with specific role but accesses /user paths, redirect them
  • If the user is authenticated but no cookies, redirect to custom login
  • If the user is anonymous but has cookies, unset the cookies to force a login

I am running into two issues, either I get an endless redirect loop or the cookies are not unset. On top of that it seems like the responses get cached - as long as the user is authenticated as this specific role I want to keep evaluating this in every request.

Here is the event subscriber:

  /**
   * Kernel request event handler.
   *
   * @param \Symfony\Component\HttpKernel\Event\RequestEvent $event
   *   Response event.
   */
  public function checkMemberPresence(RequestEvent $event) {
    if (!$event->isMainRequest()) {
      return;
    }

    $uri = $this->requestStack->getCurrentRequest()->getPathInfo();

    if ($this->isMember() && !$this->requestStack->getCurrentRequest()->cookies->has('session')) {
      user_logout();
      $response = new RedirectResponse('/member/login', 302);
      $response->send();
      $event->stopPropagation();
    }

    if ($this->isMember() && preg_match('/^\/(user$|user\/).+/i', $uri)) {
      $response = new RedirectResponse('/member', 302);
      $response->send();
      $event->stopPropagation();
    }

    if ($this->currentUser->isAnonymous() && ($this->requestStack->getCurrentRequest()->cookies->has('session') || $this->requestStack->getCurrentRequest()->cookies->has('session_expires'))) {
      $options = $this->sessionConfiguration->getOptions($event->getRequest());
      $cookie_domain = $options['cookie_domain'];

      $response = new RedirectResponse('/', 302);
      $response->headers->clearCookie('session', '/', $cookie_domain);
      $response->headers->clearCookie('session_expires', '/', $cookie_domain);
      $response->send();
      $event->stopPropagation();
    }
  }

  /**
   * Helper method to check if the current user is a member.
   *
   * @return bool
   *   If the current user is a member.
   */
  protected function isMember() {
    return $this->currentUser->hasRole('ticketure_member');
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    return [
      KernelEvents::REQUEST => ['checkMemberPresence'],
    ];
  }

The first two cases appear to work AFAICT (haven't written the tests yet) but the third does not.

How can I fix this to work and not cache the response? Furthermore, is this a proper porting of hook_init and should it be done this way in Drupal 10?

Update:

The cookie removal logic now appears to work (domain must be sent) - still unclear on if this is a good implementation.

1 Answer 1

1

You could start with setting a higher priority because you only depend on user authentication (priority 300) and want to run before other event subscribers like routing and dynamic caching:

KernelEvents::REQUEST => ['checkMemberPresence', 290],

Then don't send, return the response with the event:

$event->setResponse($response);

This stops propagation automatically.

However, this doesn't override the Internal Page Cache for anonymous requests with no Drupal session cookie but third-party cookies. If you want to handle those requests within the Drupal kernel you need to uninstall this module.

1
  • IPC is uninstalled, yeah.
    – Kevin
    Oct 16, 2023 at 15:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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