SSO Issue: Missing SSO or SIG parameters

AI-generated summary

SSO Issue: Missing SSO or SIG parameters

A user, John_Smith, is experiencing an issue with Discourse SSO, receiving the error “Missing SSO or SIG parameters.” They have provided their code, which extracts from their other platform (WHMCS) with no issues, but are getting no nonce.

Simon suggests that the issue might be due to a misconfiguration of DiscourseConnect on the Discourse site. He recommends checking the settings, including the discourse connect url and discourse connect secret, and ensuring that they match the values in the code.

John_Smith confirms that they have everything set up correctly and have tried debugging, but are still stumped. Simon provides further guidance, suggesting that they take a step back and re-check their configuration. He also recommends commenting out the rest of the code and confirming that they can receive the payload and assign it to the variables.

Additionally, Simon points out that there may be an error in the code, specifically with regards to decoding the sso parameter. He suggests looking at the WP Discourse plugin’s handling of SSO to resolve the issue.

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.