Проблема SSO: отсутствуют параметры SSO или SIG

Привет, у меня возникла проблема с SSO в Discourse. Что я делаю не так? Мой код ниже без проблем извлекает данные с другой платформы, но nonce не приходит.

<?php

// Включить логирование ошибок для отладки
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

// Загрузить файл инициализации
require_once '/init.php'; // Обновите этот путь в зависимости от настроек вашей платформы

// Секретный ключ Discourse Connect и URL для возврата полезной нагрузки
$sso_secret = 'keyhere'; // Замените на ваш общий секретный ключ Discourse
$discourse_url = 'https://urlhere.com/session/sso_login'; // Замените на ваш URL Discourse

// Запустить сессию для проверки состояния входа пользователя на платформе
session_start();

// Проверить, вошел ли пользователь в систему
if (!isset($_SESSION['uid'])) {
    // Если пользователь не вошел, перенаправить на страницу входа на платформу
    header('Location: /clientarea.php');
    exit;
}

// Отладка: записать входящие параметры запроса
error_log("Получены параметры GET: " . print_r($_GET, true));

// Проверить, получены ли параметры 'sso' и 'sig' от Discourse
if (!isset($_GET['sso']) || !isset($_GET['sig'])) {
    error_log("Отсутствуют параметры SSO или SIG.");
    header('Location: /clientarea.php');
    exit;
}

// Проверить подпись SSO с помощью секретного ключа
$sso = $_GET['sso'];
$sig = $_GET['sig'];
$expected_sig = hash_hmac('sha256', $sso, $sso_secret);

if ($sig !== $expected_sig) {
    error_log("Неверная подпись SSO. Ожидалось: $expected_sig, Получено: $sig");
    header('Location: /clientarea.php');
    exit;
}

// Получить данные клиента платформы
$clientDetails = localAPI('getclientsdetails', ['clientid' => $_SESSION['uid']], 'admin');
if ($clientDetails['result'] !== 'success' || !isset($clientDetails['userid'])) {
    error_log("Не удалось получить данные клиента платформы.");
    header('Location: /clientarea.php');
    exit;
}

// Извлечь данные пользователя
$userId = $clientDetails['userid'];
$email = $clientDetails['email'];
$firstName = $clientDetails['firstname'];
$lastName = $clientDetails['lastname'];
$username = $firstName . ' ' . $lastName;

// Декодировать полезную нагрузку SSO
$sso_payload = base64_decode($sso);
parse_str($sso_payload, $sso_params);

if (!isset($sso_params['nonce'])) {
    error_log("Nonce отсутствует в полезной нагрузке SSO.");
    header('Location: /clientarea.php');
    exit;
}

// Подготовить возвращаемую полезную нагрузку SSO с данными пользователя
$return_payload = [
    'nonce' => $sso_params['nonce'],
    'email' => $email,
    'external_id' => $userId,
    'username' => $username,
    'name' => $firstName . ' ' . $lastName,
    'admin' => 0, // Установите 1, если пользователь является администратором, иначе оставьте 0
    'moderator' => 0 // Установите 1, если пользователь является модератором, иначе оставьте 0
];

// Закодировать возвращаемую полезную нагрузку в base64
$query_string = http_build_query($return_payload);
$encoded_payload = base64_encode($query_string);

// Подписать закодированную полезную нагрузку секретным ключом Discourse
$return_sig = hash_hmac('sha256', $encoded_payload, $sso_secret);

// Отладка: записать полезную нагрузку и подпись
error_log("Возвращаемая полезная нагрузка: " . print_r($return_payload, true));
error_log("Закодированная полезная нагрузка: $encoded_payload");
error_log("Возвращаемая подпись: $return_sig");

// Перенаправить в Discourse с подписанной полезной нагрузкой
header("Location: $discourse_url?sso=" . urlencode($encoded_payload) . "&sig=" . urlencode($return_sig));
exit;

Логи ошибок:

[25-сен-2024 14:50:17 UTC] Отсутствуют параметры SSO или SIG.
[25-сен-2024 14:53:59 UTC] Получены параметры GET: Массив
(
[sso] => nonce_from_discourse
)

[25-сен-2024 14:53:59 UTC] Отсутствуют параметры SSO или SIG.

Непонятно, почему так?"}

Я тоже не уверен. Настроен ли DiscourseConnect на вашем сайте Discourse? Как пользователи попадают на URL, обрабатываемый этим кодом?

Прохождение процесса аутентификации с открытой вкладкой Network в инструментах разработчика вашего браузера может дать некоторые детали о том, что происходит.

Спасибо за ответ.

Я пытаюсь интегрироваться с WHMCS. Всё правильно настроено в коде, и соответствующие параметры включены в настройках Discourse.

Я получаю частичный ответ, но не хватает сигнатуры. Думал, что это может быть ошибка декодирования, так как, похоже, есть несоответствие между двумя системами.

Это не моя специализация, я просто делаю всё возможное, чтобы разобраться, но уже два дня ломаю голову. Не могу найти ошибку в своих действиях.

Провёл столько отладки, сколько мог, пересобрал Discourse, добавил пошаговое логирование. Пробовал ChatGPT. Сравнил с кодом плагина для WordPress. Запутался.

Похоже, что что-то настроено неправильно. Возможно, стоит отступить на шаг назад. У меня сейчас в локальной среде не настроен провайдер SSO, но это может помочь вам продвинуться вперёд.

В Discourse убедитесь, что настроены следующие параметры:

Параметр discourse connect url должен быть установлен в URL, который обрабатывает код, который вы опубликовали.

Параметр discourse connect secret установите в строку текста длиной не менее 10 символов. Обратите внимание, что в опубликованном вами коде жёстко прописана строка keyhere длиной всего 7 символов. Предполагаю, что при запуске кода вы меняете это значение. Установите его в то же значение, которое вы ввели в Discourse.

Теперь выйдите из своего сайта Discourse. Откройте вкладку «Network» (Сеть) в веб-инспекторе вашего браузера. Нажмите кнопку «Login» (Войти) на Discourse. Вы должны увидеть запросы, похожие на первые два запроса на скриншоте ниже:

Первый запрос будет по адресу http://forum.example.com/session/sso?return_path=%2F.

Следующий запрос должен быть по адресу https://example.com/?sso=<sso_payload_sent_from_discourse>&sig=<sso_signature>.

example.com и forum.example.com должны быть заменены на фактические домены, которые вы используете.

Если всё настроено правильно, я ожидаю, что это присвоит значения параметров sso и sig переменным, которые вы установили здесь:

$sso = $_GET['sso'];
$sig = $_GET['sig'];

Если бы это был я, я бы, вероятно, закомментировал остальную часть кода и просто подтвердил бы, что вы можете получать полезную нагрузку и присваивать её переменным.

С включённым DiscourseConnect вы можете снова войти на свой сайт Discourse, перейдя по маршруту /u/admin-login. Если у вас есть доступ к консоли Rails сайта Discourse, вы также можете снова войти, отключив DiscourseConnect из консоли Rails:

SiteSetting.enable_discourse_connect = false

Возможно, в коде, который вы опубликовали, есть ошибки ниже по ходу выполнения. Например, я думаю, что вам нужно вызвать urldecode для значения параметра sso перед генерацией ожидаемой сигнатуры. Посмотрите, как это обрабатывает плагин WP Discourse:

$payload в функции выше — это просто значение параметра запроса sso после его очистки здесь: wp-discourse/lib/sso-provider/discourse-sso.php at main · discourse/wp-discourse · GitHub.