SSO Issue: Missing SSO or SIG parameters

Hi, I’m having an issue with Discourse SSO. What am I doing wrong? My code below extracts from my other platform with no issue. However Im getting no nonce.

<?php

// 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;

Error logs:

[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.

Not sure why?

I’m not sure either. Is DiscourseConnect configured on your Discourse site? How are users arriving at the URL that is handled by this code?

Going through the authentication process with your browser’s inspector opened to its Network tab might give some details about what’s going on.

Thanks for the reply.

I’m trying to integrate with WHMCS. I have everything setup in the code appropriately and the settings enabled inside Discourse.

I can get a part response but lack the sig, Was thinking it might be a decoding error as there seems to be a discrepancy between the two systems.

This actually isn’t my skillset, I’m just doing my best to piece it together but been scratching my head for two days now. I cant see the error of my ways.

Tried as much debugging as I can, rebuilt Discourse. Step by step logging. Tried chatgpt. Compared against the wordpress plugin code. Stumped

It seems likely that something isn’t configured correctly. Maybe try taking a step back. I don’t have an SSO provider site configured on my local setup right now, but this might get you part of the way.

On Discourse, make sure the following settings are configured:

The discourse connect url setting should be set to the URL that is handling the code that you have posted.

Set the discourse connect secret setting to a string of text that’s at least 10 characters long. Note that you have the 7 character long string keyhere hardcoded into the code you’ve posted. I’m assuming you are changing that value when you are running the code. Set it to the same value as you’ve entered on Discourse.

Now log out of your Discourse site. Open your browser’s web inspector to its network tab. Click the “Login” button on Discourse. You should see requests similar to the first two requests from the screenshot below:

The first request will be to http://forum.example.com/session/sso?return_path=%2F

The next request should be to https://example.com/?sso=<sso_payload_sent_from_discourse>&sig=<sso_signature>

example.com and forum.example.com should be set to the actual domains you are using.

If everything is configured correctly, I’d expect this to assign the values of the sso and sig parameters to the variables you’ve set here:

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

If it was me, I’d probably comment out the rest of the code and just confirm that you can receive the payload and assign it to the variables.

With DiscourseConnect enabled, you can log back into your Discourse site by visiting the /u/admin-login route. If you have access to the Discourse site’s Rails console, you can also log back in by disabling DiscourseConnect from the Rails console:

SiteSetting.enable_discourse_connect = false

It’s possible there are errors further down in the code you posted. For example, I think you need to call urldecode on the value of the sso parameter before generating the expected sig. Have a look at how the WP Discourse plugin handles it:

$payload in the function above is just the value of the sso query param, after its been sanitized here: wp-discourse/lib/sso-provider/discourse-sso.php at main · discourse/wp-discourse · GitHub.

1 Like