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