Single Sign On can be used to handle Discourse user authentication from a separate site. The Official Single Sign On for Discourse topic has details about how to implement SSO.
The Problem
With SSO, Discourse users will be created or updated when they login to Discourse from your external website. What it doesn’t handle is when you need to create or update Discourse users without having them login to your site. For sites that are using SSO, these cases should be handled by making an authenticated POST
request to the sync_sso
route.
Note: if you are using the Discourse API gem, you can use the gem’s sync_sso
method instead of using the following code. See the examples directory for instructions on how to use the method.
As an example, we’ll take a case where a user is added to a group on the parent site, and they need to be added to a corresponding group on Discourse without having to first login with SSO. The name of the group on both the website and the forum is ‘eurorack’. The external_id
of the user is 1
and their email is bob@example.com
. The following code is using PHP
. The basic idea can be applied to any programming language.
Setup your API credentials and SSO secret key
$api_key = '4fe83002bb5fba8c9a61a65e5b4b0a3cf8233b0e4ccafc85ebd6607abab4651a';
$api_username = 'system';
$sso_secret = 'jdhb19*Xh3!nu(#k';
Setup the SSO parameters
To see what parameters are available, have a look at the ACCESSORS
section of single_sign_on.rb. The parameters that you must include are external_id
and email
. To add a user to a group, include the add_groups
parameter. To remove a user from a group, include the remove_groups
parameter. The value for either of these parameters needs to be set to a comma separated string of group names. Spaces are not allowed between the group names.
The require_activation
parameter is being included in the payload. This should be set to true
if the user’s email hasn’t been validated on the parent site. With PHP
the parameter needs to be set to the string ‘true’ to avoid it being converted to the number 1
. If you have validated the user’s email address, you do not need to include this parameter.
// Create an array of SSO parameters.
$sso_params = array(
'external_id' => 1,
'email' => 'bob@example.com',
'username' => 'bob',
'add_groups' => 'eurorack',
'require_activation' => 'true',
);
// Convert the SSO parameters into the SSO payload and generate the SSO signature.
$sso_payload = base64_encode( http_build_query( $sso_params ) );
$sig = hash_hmac( 'sha256', $sso_payload, $sso_secret );
Send the POST request
For this example I’ll use curl
, set the user_agent
to ‘WordPress/4.9.4’, and the forum URL to https://forum.example.com
$url = 'https://forum.example.com/admin/users/sync_sso';
$post_fields = array(
'sso' => $sso_payload,
'sig' => $sig,
'api_key' => $api_key,
'api_username' => $api_username,
);
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_POST, 1 );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_POSTFIELDS, http_build_query( $post_fields ) );
curl_setopt( $ch, CURLOPT_USERAGENT, 'WordPress/4.9.4' );
$result = curl_exec( $ch );
if ( curl_errno( $ch ) !== 0 ) {
// Handle error, call curl_close( $ch ) and return.
}
curl_close( $ch );
$discourse_user = json_decode( $result );
Further Reading
To see what is going on, have a look at the sync_sso code, the SingleSignOn
parse method, and the DiscourseSingleSignOn
lookup_or_create_user method.