Symfony run process in background

I’m developing a small API for retrieving data based on OAuth 2 with PHP/Drupal/Symfony . This involves retrieving an access code, then retrieving a bearer token from this access code, and finally using this bearer token to retrieve user information. Everything works using a route on a controller, but I want this process to run in the background when the user is logged in to the site, meaning no manual interaction is required on a controller route. Here’s the code:

The controller :

namespace Drupalmy_moduleController;

use DrupalCoreControllerControllerBase;
use DrupalCoreRoutingTrustedRedirectResponse;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentDependencyInjectionContainerInterface;

class OAuthController extends ControllerBase {

  protected $oauthClient;

  public function __construct($oauth_client) {
    $this->oauthClient = $oauth_client;
  }

  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('my_module.oauth_client')
    );
  }

  /**
   * Initie le flux OAuth2
   */
  public function initiate() {
    $auth_url = $this->oauthClient->getAuthorizationUrl();
    return new TrustedRedirectResponse($auth_url);
  }

  /**
   * Callback après autorisation
   */
  public function callback(Request $request) {
    $code = $request->query->get('code');

    if (empty($code)) {
        $this->messenger()->addError($this->t('Authorization failed: no code received'));
        return $this->redirect('<front>');
    }

    // Échange le code contre un token
    $token = $this->oauthClient->getAccessToken($code);

    if (!$token || !isset($token['access_token'])) {
      $this->messenger()->addError($this->t('Failed to obtain access token'));
      return $this->redirect('<front>');
    }

    // Récupère les infos utilisateur
    $user_info = $this->oauthClient->getUserInfo($token['access_token']);
    dd($user_info);

    // Stocke le token en session
    $request->getSession()->set('oauth_userinfo', $user_info);

    // Redirige vers la page d'origine
    return $this->redirect('<front>');
  }

The route of the controller :

my_module.oauth_initiate:
  path: '/oauth/initiate'
  defaults:
    _controller: 'Drupalmy_moduleControllerOAuthController::initiate'
    _title: 'Initiate OAuth'
  requirements:
     _user_is_logged_in: 'TRUE'

my_module.oauth_callback:
  path: '/openid-connect/generic'
  defaults:
    _controller: 'Drupalmy_moduleControllerOAuthController::callback'
    _title: 'OAuth Callback'
  requirements:
     _user_is_logged_in: 'TRUE'

The service :

class OAuthClient {

  protected $httpClient;
  protected $openIdConfig;

  public function __construct(ClientFactory $http_client_factory, ConfigFactoryInterface $config_factory) {
    $this->httpClient = $http_client_factory->fromOptions([
      'headers' => [
        'Content-Type' => 'application/x-www-form-urlencoded',
        'Accept' => 'application/json',
      ],
    ]);

    // Charge la configuration du client OpenID Connect "generic"
    $this->openIdConfig = $config_factory->get('openid_connect.client.generic');
  }

  /**
   * Génère l'URL d'autorisation OAuth2
   */
  public function getAuthorizationUrl() {
    $settings = $this->openIdConfig->get('settings');
    $params = [
      'response_type' => 'code',
      'client_id' => $settings['client_id'],
      'scope' => 'openid profile email',
      'redirect_uri' => $this->getRedirectUri(),
    ];
    return $settings['authorization_endpoint']. '?' . http_build_query($params);
  }



  /**
   * Échange le code contre un token
   */
  public function getAccessToken($code) {
    $settings = $this->openIdConfig->get('settings');
    try {
      $clientId = $settings['client_id'];
      $clientSecret = $settings['client_secret'];

      // Configuration complète de la requête
      $options = [
        'headers' => [
          'Content-Type' => 'application/x-www-form-urlencoded',
          'Authorization' => 'Basic ' . base64_encode($clientId . ':' . $clientSecret),
        ],
        'form_params' => [
          'grant_type' => 'authorization_code',
          'code' => $code,
          'redirect_uri' => $this->getRedirectUri(),
        ],
      ];

      $response = $this->httpClient->post($settings['token_endpoint'], $options);

      $body = $response->getBody()->getContents();

      Drupal::logger('oauth')->debug('Token response: ' . $body);

      return Json::decode($body);

    } catch (RequestException $e) {
      $errorDetails = [
        'message' => $e->getMessage(),
        'response' => $e->hasResponse() ? $e->getResponse()->getBody()->getContents() : null,
        'request' => [
          'url' => $e->getRequest()->getUri(),
          'headers' => $e->getRequest()->getHeaders(),
          'body' => $e->getRequest()->getBody()->getContents()
        ]
      ];

      Drupal::logger('oauth')->error('Token error details: @details',
        ['@details' => print_r($errorDetails, true)]
      );

      return FALSE;
    }
  }


/**
 * Récupère les infos utilisateur avec le token
 */
  public function getUserInfo($access_token) {
    try {
      $settings = $this->openIdConfig->get('settings');

      $response = $this->httpClient->get($settings['userinfo_endpoint'], [
        'headers' => [
          'Authorization' => 'Bearer ' . $access_token,
          'Accept' => 'application/json',
        ],
      ]);

      return Json::decode($response->getBody()->getContents());

    } catch (RequestException $e) {
      Drupal::logger('oauth')->error('UserInfo error: @error', [
        '@error' => $e->getMessage()
      ]);
      return FALSE;
    }
  }

in an eventSubscriber I tried to implement something like this but it won’t manage the access code

public function onUserLogin(AccountInterface $account) {
    $session = $this->requestStack->getCurrentRequest()->getSession();
    
    // Si déjà authentifié OAuth, ne rien faire
    if ($session->has('oauth_userinfo')) {
      return;
    }

    // 1. Initier le flux OAuth silencieusement
    try {
      $auth_url = $this->oauthClient->getAuthorizationUrl();

      
      // 2. Faire la requête directement (sans redirection navigateur)
      $client = Drupal::httpClient();
      $response = $client->get($auth_url, ['allow_redirects' => false]);

      // 3. Traiter la réponse
      if ($response->getStatusCode() == 302) {
        $location = $response->getHeader('Location')[0];
        dd($location);
        parse_str(parse_url($location, PHP_URL_QUERY), $params);

        if (!empty($params['code'])) {
          $token = $this->oauthClient->getAccessToken($params['code']);
          
          if ($token && isset($token['access_token'])) {
            $user_info = $this->oauthClient->getUserInfo($token['access_token']);
            $session->set('oauth_userinfo', $user_info);
          }
        }
      }
    } catch (Exception $e) {
      Drupal::logger('log_oauth')->error('Auto-auth failed: @error', ['@error' => $e->getMessage()]);
    }
  }

Thanks in advance for your help