Considering that none of the usual suggestions really work...
The standard user/logout
provided by Drupal has CSRF issues, as numerous questions describe, it shows a "'csrf_token' URL query argument is invalid" even if the appropriate token is provided (going deep into the core shows that it can't successfully validate the logout token provided, hence the error).
The obvious solution then is to create our own REST service to log out, especially if using REST for other purposes in a module, too, all it takes is just one more call. The service can simply call user_logout()
to have core perform it for us.
However, at least in current Drupal 10.1, while this results in a spurious "Warning: session_destroy(): Trying to destroy uninitialized session" warning. Core's SessionManager
actually calls PHP's session_destroy()
with a comment describing that it does it on purpose instead of calling the corresponding Symfony function. When checking the sessions
table in the database, these sessions actually stay there, so the user isn't really logged out at all.
Logging out in a browser the usual way doesn't leave a warning like that so this is clearly some problem with the programmatic approach. But no matter how I check the source, I can't see why...
For reference, the REST resource I try (slightly different, this one makes the relevant calls from user_logout()
directly but there's no difference by either calling that or doing this way):
class UserResource extends ResourceBase {
protected AccountProxyInterface $currentUser;
protected SessionManager $sessionManager;
public function __construct(array $config, $module_id, $module_definition, array $serializer_formats, LoggerInterface $logger, AccountProxyInterface $current_user, SessionManager $session_manager) {
parent::__construct($config, $module_id, $module_definition, $serializer_formats, $logger);
$this->currentUser = $current_user;
$this->sessionManager = $session_manager;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): UserResource {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->getParameter('serializer.formats'),
$container->get('logger.factory')->get('user'),
$container->get('current_user'),
$container->get('session_manager'),
);
}
/**
* Logs out a user.
* Replacement for the stock one that gives AccessDeniedHttpException: 'csrf_token' URL query argument is invalid.
* Based on user.module::user_logout()
*/
public function get(): ResourceResponse {
// user_logout();
$this->sessionManager->destroy();
$this->logger->info('Session closed for %name.', ['%name' => $this->currentUser->getAccountName()]);
return new ResourceResponse(NULL, 204);
}
public function permissions(): array {
return [];
}
}