SSO问题:缺少SSO或SIG参数

您好,我在使用 Discourse SSO 时遇到问题。我哪里做错了?我的代码可以从我的其他平台提取信息,但没有收到 nonce。


// Enable error logging for debugging
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

// Load init file
require_once '/init.php'; // Update this path based on your Platform setup

// Discourse Connect secret key and URL to return the payload
$sso_secret = 'keyhere'; // Replace with your Discourse shared secret key
$discourse_url = 'https://urlhere.com/session/sso_login'; // Replace with your Discourse URL

// Start session to check Platform user login state
session_start();

// Check if the user is logged in
if (!isset($_SESSION['uid'])) {
    // If the user is not logged in, redirect to the Platform login page
    header('Location: /clientarea.php');
    exit;
}

// Debug: Log incoming request parameters
error_log("Received GET parameters: " . print_r($_GET, true));

// Check if we received the 'sso' and 'sig' parameters from Discourse
if (!isset($_GET['sso']) || !isset($_GET['sig'])) {
    error_log("Missing SSO or SIG parameters.");
    header('Location: /clientarea.php');
    exit;
}

// Validate the SSO signature using the secret key
$sso = $_GET['sso'];
$sig = $_GET['sig'];
$expected_sig = hash_hmac('sha256', $sso, $sso_secret);

if ($sig !== $expected_sig) {
    error_log("Invalid SSO signature. Expected: $expected_sig, Received: $sig");
    header('Location: /clientarea.php');
    exit;
}

// Fetch Platform client details
$clientDetails = localAPI('getclientsdetails', ['clientid' => $_SESSION['uid']], 'admin');
if ($clientDetails['result'] !== 'success' || !isset($clientDetails['userid'])) {
    error_log("Failed to fetch Platform client details.");
    header('Location: /clientarea.php');
    exit;
}

// Extract user details
$userId = $clientDetails['userid'];
$email = $clientDetails['email'];
$firstName = $clientDetails['firstname'];
$lastName = $clientDetails['lastname'];
$username = $firstName . ' ' . $lastName;

// Decode the SSO payload
$sso_payload = base64_decode($sso);
parse_str($sso_payload, $sso_params);

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

// Prepare the return SSO payload with user details
$return_payload = [
    'nonce' => $sso_params['nonce'],
    'email' => $email,
    'external_id' => $userId,
    'username' => $username,
    'name' => $firstName . ' ' . $lastName,
    'admin' => 0, // Set to 1 if the user is an admin, otherwise leave as 0
    'moderator' => 0 // Set to 1 if the user is a moderator, otherwise leave as 0
];

// Base64-encode the return payload
$query_string = http_build_query($return_payload);
$encoded_payload = base64_encode($query_string);

// Sign the encoded payload with the Discourse secret
$return_sig = hash_hmac('sha256', $encoded_payload, $sso_secret);

// Debug: Log the payload and signature
error_log("Return Payload: " . print_r($return_payload, true));
error_log("Encoded Payload: $encoded_payload");
error_log("Return Signature: $return_sig");

// Redirect to Discourse with the signed payload
header("Location: $discourse_url?sso=" . urlencode($encoded_payload) . "&sig=" . urlencode($return_sig));
exit;

错误日志:

[25-Sep-2024 14:50:17 UTC] Missing SSO or SIG parameters.
[25-Sep-2024 14:53:59 UTC] Received GET parameters: Array
(
[sso] => nonce_from_discourse
)

[25-Sep-2024 14:53:59 UTC] Missing SSO or SIG parameters.

不确定为什么?

我也不确定。您的 Discourse 站点是否配置了 DiscourseConnect?用户是如何到达由该代码处理的 URL 的?

使用打开了浏览器检查器网络选项卡的浏览器进行身份验证过程可能会提供有关正在发生的事情的一些详细信息。

感谢您的回复。

我正在尝试与 WHMCS 集成。我已经将所有内容都正确设置在代码中,并在 Discourse 中启用了相关设置。

我能获得部分响应,但缺少签名 (sig)。我猜想这可能是解码错误,因为两个系统之间似乎存在差异。

这其实不是我的专长,我只是在尽力拼凑,但已经为此冥思苦想了两天。我找不到自己的错误所在。

我已经尽力调试,重建了 Discourse。逐步记录。尝试了 ChatGPT。与 WordPress 插件代码进行了比较。我现在很困惑。

看起来配置不正确。也许可以退一步看看。我现在本地设置没有配置SSO提供商站点,但这或许能帮到你一部分。

在 Discourse 中,请确保配置了以下设置:

discourse connect url 设置应为处理你已发布代码的URL。

discourse connect secret 设置应为一个至少10个字符的字符串。请注意,你已将7个字符的字符串 keyhere 硬编码到你已发布的代码中。我假设你在运行代码时会更改该值。将其设置为你在 Discourse 上输入的值。

现在注销你的 Discourse 站点。打开浏览器的 Web 检查器到其网络选项卡。点击 Discourse 上的“登录”按钮。你应该会看到类似下面截图的前两个请求:

第一个请求将是 http://forum.example.com/session/sso?return_path=%2F

下一个请求应该是 https://example.com/?sso=<sso_payload_sent_from_discourse>&sig=<sso_signature>

example.comforum.example.com 应设置为你正在使用的实际域名。

如果一切配置正确,我预计这会将 ssosig 参数的值分配给你在此处设置的变量:

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

如果是我,我可能会注释掉其余代码,只确认你能接收到 payload 并将其分配给变量。

启用 DiscourseConnect 后,你可以通过访问 /u/admin-login 路由重新登录你的 Discourse 站点。如果你可以访问 Discourse 站点的 Rails 控制台,你也可以通过在 Rails 控制台中禁用 DiscourseConnect 来重新登录:

SiteSetting.enable_discourse_connect = false

你已发布代码的其余部分可能存在错误。例如,我认为在生成预期的 sig 之前,你需要对 sso 参数的值调用 urldecode。看看 WP Discourse 插件是如何处理的:

上面函数中的 $payload 只是 sso 查询参数的值,在经过此处的清理后:wp-discourse/lib/sso-provider/discourse-sso.php at main · discourse/wp-discourse · GitHub

1 个赞