Problema de SSO: Faltan los parámetros SSO o SIG

Hola, tengo un problema con Discourse SSO. ¿Qué estoy haciendo mal? Mi código extrae de mi otra plataforma sin ningún problema. Sin embargo, no recibo ningún nonce.


// Habilitar el registro de errores para la depuración
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

// Cargar archivo init
require_once '/init.php'; // Actualiza esta ruta según la configuración de tu plataforma

// Clave secreta de Discourse Connect y URL para devolver el payload
$sso_secret = 'keyhere'; // Reemplaza con tu clave secreta compartida de Discourse
$discourse_url = 'https://urlhere.com/session/sso_login'; // Reemplaza con tu URL de Discourse

// Iniciar sesión para verificar el estado de inicio de sesión del usuario de la plataforma
session_start();

// Verificar si el usuario ha iniciado sesión
if (!isset($_SESSION['uid'])) {
    // Si el usuario no ha iniciado sesión, redirigir a la página de inicio de sesión de la plataforma
    header('Location: /clientarea.php');
    exit;
}

// Depuración: Registrar los parámetros de la solicitud entrante
error_log("Parámetros GET recibidos: " . print_r($_GET, true));

// Verificar si recibimos los parámetros 'sso' y 'sig' de Discourse
if (!isset($_GET['sso']) || !isset($_GET['sig'])) {
    error_log("Faltan los parámetros SSO o SIG.");
    header('Location: /clientarea.php');
    exit;
}

// Validar la firma SSO usando la clave secreta
$sso = $_GET['sso'];
$sig = $_GET['sig'];
$expected_sig = hash_hmac('sha256', $sso, $sso_secret);

if ($sig !== $expected_sig) {
    error_log("Firma SSO inválida. Esperada: $expected_sig, Recibida: $sig");
    header('Location: /clientarea.php');
    exit;
}

// Obtener detalles del cliente de la plataforma
$clientDetails = localAPI('getclientsdetails', ['clientid' => $_SESSION['uid']], 'admin');
if ($clientDetails['result'] !== 'success' || !isset($clientDetails['userid'])) {
    error_log("Error al obtener los detalles del cliente de la plataforma.");
    header('Location: /clientarea.php');
    exit;
}

// Extraer detalles del usuario
$userId = $clientDetails['userid'];
$email = $clientDetails['email'];
$firstName = $clientDetails['firstname'];
$lastName = $clientDetails['lastname'];
$username = $firstName . ' ' . $lastName;

// Decodificar el payload SSO
$sso_payload = base64_decode($sso);
parse_str($sso_payload, $sso_params);

if (!isset($sso_params['nonce'])) {
    error_log("Falta el nonce en el payload SSO.");
    header('Location: /clientarea.php');
    exit;
}

// Preparar el payload SSO de retorno con los detalles del usuario
$return_payload = [
    'nonce' => $sso_params['nonce'],
    'email' => $email,
    'external_id' => $userId,
    'username' => $username,
    'name' => $firstName . ' ' . $lastName,
    'admin' => 0, // Establecer en 1 si el usuario es administrador, de lo contrario dejar en 0
    'moderator' => 0 // Establecer en 1 si el usuario es moderador, de lo contrario dejar en 0
];

// Codificar el payload de retorno en Base64
$query_string = http_build_query($return_payload);
$encoded_payload = base64_encode($query_string);

// Firmar el payload codificado con la clave secreta de Discourse
$return_sig = hash_hmac('sha256', $encoded_payload, $sso_secret);

// Depuración: Registrar el payload y la firma
error_log("Payload de retorno: " . print_r($return_payload, true));
error_log("Payload codificado: $encoded_payload");
error_log("Firma de retorno: $return_sig");

// Redirigir a Discourse con el payload firmado
header("Location: $discourse_url?sso=" . urlencode($encoded_payload) . "&sig=" . urlencode($return_sig));
exit;

Registros de errores:

[25-Sep-2024 14:50:17 UTC] Faltan los parámetros SSO o SIG.
[25-Sep-2024 14:53:59 UTC] Parámetros GET recibidos: Array
(
[sso] => nonce_from_discourse
)

[25-Sep-2024 14:53:59 UTC] Faltan los parámetros SSO o SIG.

¿No estoy seguro de por qué?

Yo tampoco estoy seguro. ¿Está DiscourseConnect configurado en tu sitio de Discourse? ¿Cómo llegan los usuarios a la URL que es manejada por este código?

Pasar por el proceso de autenticación con el inspector de tu navegador abierto en su pestaña de Red podría dar algunos detalles sobre lo que está sucediendo.

Gracias por la respuesta.

Estoy intentando integrarme con WHMCS. Tengo todo configurado adecuadamente en el código y las configuraciones habilitadas dentro de Discourse.

Puedo obtener una respuesta parcial pero me falta la firma (sig). Pensé que podría ser un error de decodificación, ya que parece haber una discrepancia entre los dos sistemas.

En realidad, esto no es mi área de especialización, solo estoy haciendo mi mejor esfuerzo para armarlo, pero me he estado devanando los sesos durante dos días. No veo el error en mi forma de proceder.

He intentado depurar tanto como he podido, he reconstruido Discourse. He registrado paso a paso. He probado ChatGPT. Lo he comparado con el código del plugin de WordPress. Estoy atascado.

Parece probable que algo no esté configurado correctamente. Quizás intentes dar un paso atrás. No tengo un sitio proveedor de SSO configurado en mi configuración local en este momento, pero esto podría ayudarte un poco.

En Discourse, asegúrate de que se configuren los siguientes ajustes:

La configuración discourse connect url debe establecerse en la URL que está manejando el código que has publicado.

Establece la configuración discourse connect secret en una cadena de texto de al menos 10 caracteres de longitud. Ten en cuenta que tienes la cadena de 7 caracteres keyhere codificada de forma rígida en el código que has publicado. Supongo que cambias ese valor cuando ejecutas el código. Establécelo con el mismo valor que has introducido en Discourse.

Ahora cierra sesión en tu sitio de Discourse. Abre la pestaña de red del inspector web de tu navegador. Haz clic en el botón “Login” en Discourse. Deberías ver solicitudes similares a las dos primeras solicitudes de la siguiente captura de pantalla:

La primera solicitud será a http://forum.example.com/session/sso?return_path=%2F

La siguiente solicitud debería ser a https://example.com/?sso=<sso_payload_sent_from_discourse>&sig=<sso_signature>

example.com y forum.example.com deben establecerse en los dominios reales que estés utilizando.

Si todo está configurado correctamente, esperaría que esto asigne los valores de los parámetros sso y sig a las variables que has establecido aquí:

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

Si fuera yo, probablemente comentaría el resto del código y simplemente confirmaría que puedes recibir el payload y asignarlo a las variables.

Con DiscourseConnect habilitado, puedes volver a iniciar sesión en tu sitio de Discourse visitando la ruta /u/admin-login. Si tienes acceso a la consola Rails del sitio de Discourse, también puedes volver a iniciar sesión deshabilitando DiscourseConnect desde la consola Rails:

SiteSetting.enable_discourse_connect = false

Es posible que haya errores más abajo en el código que has publicado. Por ejemplo, creo que necesitas llamar a urldecode en el valor del parámetro sso antes de generar la firma esperada. Echa un vistazo a cómo lo maneja el plugin WP Discourse:

$payload en la función anterior es solo el valor del parámetro de consulta sso, después de haber sido sanitizado aquí: wp-discourse/lib/sso-provider/discourse-sso.php at main · discourse/wp-discourse · GitHub.

1 me gusta