Problema de SSO: Parâmetros SSO ou SIG ausentes

Olá, estou tendo um problema com o Discourse SSO. O que estou fazendo de errado? Meu código abaixo extrai de minha outra plataforma sem problemas. No entanto, não estou recebendo nenhum nonce.


// Habilita o registro de erros para depuração
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

// Carrega o arquivo init
require_once '/init.php'; // Atualize este caminho com base na configuração da sua plataforma

// Chave secreta do Discourse Connect e URL para retornar o payload
$sso_secret = 'keyhere'; // Substitua pela sua chave secreta compartilhada do Discourse
$discourse_url = 'https://urlhere.com/session/sso_login'; // Substitua pela sua URL do Discourse

// Inicia a sessão para verificar o estado de login do usuário da plataforma
session_start();

// Verifica se o usuário está logado
if (!isset($_SESSION['uid'])) {
    // Se o usuário não estiver logado, redireciona para a página de login da plataforma
    header('Location: /clientarea.php');
    exit;
}

// Depuração: Registra os parâmetros da solicitação de entrada
error_log("Parâmetros GET recebidos: " . print_r($_GET, true));

// Verifica se recebemos os parâmetros 'sso' e 'sig' do Discourse
if (!isset($_GET['sso']) || !isset($_GET['sig'])) {
    error_log("Parâmetros SSO ou SIG ausentes.");
    header('Location: /clientarea.php');
    exit;
}

// Valida a assinatura SSO usando a chave secreta
$sso = $_GET['sso'];
$sig = $_GET['sig'];
$expected_sig = hash_hmac('sha256', $sso, $sso_secret);

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

// Busca detalhes do cliente da plataforma
$clientDetails = localAPI('getclientsdetails', ['clientid' => $_SESSION['uid']], 'admin');
if ($clientDetails['result'] !== 'success' || !isset($clientDetails['userid'])) {
    error_log("Falha ao buscar detalhes do cliente da plataforma.");
    header('Location: /clientarea.php');
    exit;
}

// Extrai detalhes do usuário
$userId = $clientDetails['userid'];
$email = $clientDetails['email'];
$firstName = $clientDetails['firstname'];
$lastName = $clientDetails['lastname'];
$username = $firstName . ' ' . $lastName;

// Decodifica o payload SSO
$sso_payload = base64_decode($sso);
parse_str($sso_payload, $sso_params);

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

// Prepara o payload SSO de retorno com os detalhes do usuário
$return_payload = [
    'nonce' => $sso_params['nonce'],
    'email' => $email,
    'external_id' => $userId,
    'username' => $username,
    'name' => $firstName . ' ' . $lastName,
    'admin' => 0, // Defina como 1 se o usuário for um administrador, caso contrário, deixe como 0
    'moderator' => 0 // Defina como 1 se o usuário for um moderador, caso contrário, deixe como 0
];

// Codifica o payload de retorno em Base64
$query_string = http_build_query($return_payload);
$encoded_payload = base64_encode($query_string);

// Assina o payload codificado com a chave secreta do Discourse
$return_sig = hash_hmac('sha256', $encoded_payload, $sso_secret);

// Depuração: Registra o payload e a assinatura
error_log("Payload de Retorno: " . print_r($return_payload, true));
error_log("Payload Codificado: $encoded_payload");
error_log("Assinatura de Retorno: $return_sig");

// Redireciona para o Discourse com o payload assinado
header("Location: $discourse_url?sso=" . urlencode($encoded_payload) . "&sig=" . urlencode($return_sig));
exit;

Registros de erro:

[25-Sep-2024 14:50:17 UTC] Parâmetros SSO ou SIG ausentes.
[25-Sep-2024 14:53:59 UTC] Parâmetros GET recebidos: Array
(
[sso] => nonce_from_discourse
)

[25-Sep-2024 14:53:59 UTC] Parâmetros SSO ou SIG ausentes.

Não tenho certeza por quê?

Eu também não tenho certeza. O DiscourseConnect está configurado no seu site Discourse? Como os usuários chegam ao URL que é tratado por este código?

Passar pelo processo de autenticação com o inspetor do seu navegador aberto na aba Rede pode fornecer alguns detalhes sobre o que está acontecendo.

Obrigado pela resposta.

Estou tentando integrar com o WHMCS. Tenho tudo configurado no código apropriadamente e as configurações ativadas dentro do Discourse.

Consigo obter uma resposta parcial, mas falta a assinatura (sig). Pensei que poderia ser um erro de decodificação, pois parece haver uma discrepância entre os dois sistemas.

Na verdade, isso não é minha área de especialização, estou apenas fazendo o meu melhor para juntar as peças, mas estou quebrando a cabeça há dois dias. Não consigo ver o erro em minhas ações.

Tentei o máximo de depuração que pude, reconstruí o Discourse. Registros passo a passo. Tentei o ChatGPT. Comparei com o código do plugin do WordPress. Empacado.

Parece provável que algo não esteja configurado corretamente. Talvez tente dar um passo atrás. Eu não tenho um site provedor SSO configurado na minha configuração local no momento, mas isso pode te ajudar em parte.

No Discourse, certifique-se de que as seguintes configurações estejam configuradas:

A configuração discourse connect url deve ser definida para a URL que está lidando com o código que você postou.

Defina a configuração discourse connect secret para uma string de texto com pelo menos 10 caracteres. Note que você tem a string de 7 caracteres keyhere codificada diretamente no código que você postou. Presumo que você esteja mudando esse valor quando estiver executando o código. Defina-o para o mesmo valor que você inseriu no Discourse.

Agora saia do seu site Discourse. Abra a aba de rede do inspetor web do seu navegador. Clique no botão “Login” no Discourse. Você deverá ver requisições semelhantes às duas primeiras requisições da captura de tela abaixo:

A primeira requisição será para http://forum.example.com/session/sso?return_path=%2F

A próxima requisição deve ser para https://example.com/?sso=<payload_sso_enviado_do_discourse>&sig=<assinatura_sso>

example.com e forum.example.com devem ser definidos para os domínios reais que você está usando.

Se tudo estiver configurado corretamente, eu esperaria que isso atribuísse os valores dos parâmetros sso e sig às variáveis que você definiu aqui:

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

Se fosse eu, provavelmente comentaria o resto do código e apenas confirmaria que você pode receber o payload e atribuí-lo às variáveis.

Com o DiscourseConnect ativado, você pode fazer login novamente no seu site Discourse visitando a rota /u/admin-login. Se você tiver acesso ao console Rails do site Discourse, também poderá fazer login desativando o DiscourseConnect do console Rails:

SiteSetting.enable_discourse_connect = false

É possível que haja erros mais adiante no código que você postou. Por exemplo, eu acho que você precisa chamar urldecode no valor do parâmetro sso antes de gerar a sig esperada. Dê uma olhada em como o plugin WP Discourse lida com isso:

$payload na função acima é apenas o valor do parâmetro de consulta sso, após ter sido sanitizado aqui: wp-discourse/lib/sso-provider/discourse-sso.php at main · discourse/wp-discourse · GitHub.

1 curtida