Files
varlog/public/oidc/callback.php
T

163 lines
4.9 KiB
PHP

<?php
declare(strict_types=1);
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once dirname(__DIR__, 2) . '/bootstrap.php';
require_once dirname(__DIR__, 2) . '/config/config.php';
if (!function_exists('env')) {
function env(string $key, ?string $default = null): ?string
{
if (array_key_exists($key, $_ENV) && $_ENV[$key] !== '') {
return (string)$_ENV[$key];
}
$v = getenv($key);
if ($v !== false && $v !== '') {
return (string)$v;
}
return $default;
}
}
$debug = (env('APP_DEBUG', '0') === '1');
$OIDC_ISSUER = rtrim((string)(env('OIDC_ISSUER') ?? ''), '/');
$OIDC_CLIENT_ID = (string)(env('OIDC_CLIENT_ID') ?? '');
$OIDC_CLIENT_SECRET = (string)(env('OIDC_CLIENT_SECRET') ?? '');
$OIDC_REDIRECT_URI = (string)(env('OIDC_REDIRECT_URI') ?: url('oidc/callback.php'));
if (!$OIDC_ISSUER || !$OIDC_CLIENT_ID || !$OIDC_REDIRECT_URI) {
http_response_code(500);
echo $debug ? 'OIDC config manquante.' : 'Erreur.';
exit;
}
$tokenEndpoint = $OIDC_ISSUER . '/protocol/openid-connect/token';
$userInfoEndpoint = $OIDC_ISSUER . '/protocol/openid-connect/userinfo';
if (!isset($_GET['state'], $_SESSION['oidc_state']) || !hash_equals((string)$_SESSION['oidc_state'], (string)$_GET['state'])) {
http_response_code(400);
echo $debug ? 'State invalide.' : 'Requête invalide.';
exit;
}
unset($_SESSION['oidc_state']);
if (empty($_GET['code'])) {
http_response_code(400);
echo $debug ? 'Code manquant.' : 'Requête invalide.';
exit;
}
$code = (string)$_GET['code'];
$codeVerifier = $_SESSION['oidc_code_verifier'] ?? null;
unset($_SESSION['oidc_code_verifier'], $_SESSION['oidc_nonce']);
if (!$codeVerifier) {
http_response_code(400);
echo $debug ? 'PKCE code_verifier manquant.' : 'Requête invalide.';
exit;
}
// Échange code → tokens
$post = [
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $OIDC_REDIRECT_URI,
'client_id' => $OIDC_CLIENT_ID,
'code_verifier' => $codeVerifier,
];
if ($OIDC_CLIENT_SECRET !== '') {
$post['client_secret'] = $OIDC_CLIENT_SECRET;
}
$ch = curl_init($tokenEndpoint);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($post, '', '&', PHP_QUERY_RFC3986),
CURLOPT_TIMEOUT => 15,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
]);
$tokenResponse = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErr = curl_error($ch);
curl_close($ch);
if ($tokenResponse === false || $httpCode !== 200) {
http_response_code(500);
echo $debug ? 'Échec échange token : ' . htmlspecialchars($curlErr ?: (string)$tokenResponse) : 'Erreur d\'authentification.';
exit;
}
$tokens = json_decode((string)$tokenResponse, true) ?: [];
$accessToken = $tokens['access_token'] ?? null;
$idToken = $tokens['id_token'] ?? null;
if (!$accessToken) {
http_response_code(500);
echo $debug ? 'Access token manquant.' : 'Erreur d\'authentification.';
exit;
}
// UserInfo
$ch = curl_init($userInfoEndpoint);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $accessToken],
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
]);
$userInfoResponse = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($userInfoResponse === false || $httpCode !== 200) {
http_response_code(500);
echo $debug ? 'Échec UserInfo.' : 'Erreur d\'authentification.';
exit;
}
$claims = json_decode((string)$userInfoResponse, true) ?: [];
$email = $claims['email'] ?? null;
// Fallback : lire l'email depuis le payload du id_token
if (!$email && $idToken && substr_count($idToken, '.') === 2) {
[, $p, ] = explode('.', $idToken, 3);
$payload = json_decode((string)base64_decode(strtr($p, '-_', '+/'), true), true);
if (is_array($payload) && !empty($payload['email'])) {
$email = $payload['email'];
}
}
if (!$email) {
http_response_code(400);
echo $debug ? 'Email non fourni par l\'IdP.' : 'Impossible de récupérer votre email.';
exit;
}
// Ouvre la session authentifiée
session_regenerate_id(true);
$_SESSION['user_email'] = strtolower(trim($email));
$_SESSION['oidc'] = [
'issuer' => $OIDC_ISSUER,
'sub' => $claims['sub'] ?? null,
'access_token' => $accessToken,
'id_token' => $idToken,
'expires_at' => time() + (int)($tokens['expires_in'] ?? 3600),
];
$target = $_SESSION['oidc_return_to'] ?? '/';
unset($_SESSION['oidc_return_to'], $_SESSION['oidc_flow']);
if (!is_string($target) || $target === '' || $target[0] !== '/') {
$target = '/';
}
header('Location: ' . $target, true, 303);
exit;