Utiliser Discourse comme fournisseur d'identité (SSO, DiscourseConnect)

Vous souhaitez utiliser Discourse comme fournisseur d’identité pour votre propre application web ? Excellent ! Commençons.

Activer le paramètre du fournisseur DiscourseConnect

Dans les paramètres du site d’administration de Discourse (/admin/site_settings), activez le paramètre enable discourse connect provider et ajoutez une chaîne secrète à discourse connect provider secrets (utilisée pour hacher les charges utiles SSO).

Implémenter DiscourseConnect dans votre application web :

  • Générez un nonce aléatoire. Appelons cette valeur NONCE. Sauvegardez-la temporairement afin de pouvoir la vérifier avec la valeur de nonce qui sera renvoyée dans la réponse.

  • Créez une nouvelle charge utile avec le NONCE et une RETURN_URL (vers laquelle Discourse redirigera l’utilisateur après vérification). La charge utile doit ressembler à : nonce=NONCE&return_sso_url=RETURN_URL. L’hôte de RETURN_URL doit correspondre au modèle de domaine que vous avez utilisé lors de la configuration de discourse connect provider secrets.

  • Encodez en Base64 la charge utile brute ci-dessus. Appelons cette charge utile BASE64_PAYLOAD

  • Encodez en URL la BASE64_PAYLOAD ci-dessus. Appelons cette charge utile URL_ENCODED_PAYLOAD

  • Générez une signature HMAC-SHA256 à partir de BASE64_PAYLOAD en utilisant votre secret de fournisseur sso comme clé, puis créez une chaîne hexadécimale en minuscules à partir de celle-ci. Appelons cette signature HEX_SIGNATURE

Envoyer la requête d’authentification à Discourse

Redirigez l’utilisateur vers DISCOURSE_ROOT_URL/session/sso_provider?sso=URL_ENCODED_PAYLOAD&sig=HEX_SIGNATURE

Obtenir la réponse de Discourse :

Si les étapes ci-dessus sont effectuées correctement, Discourse redirigera l’utilisateur connecté vers la RETURN_URL fournie. Vous obtiendrez des paramètres de chaîne de requête avec sig et sso ainsi que des informations utilisateur. Suivez maintenant les étapes ci-dessous :

  • Calculez le HMAC-SHA256 de sso en utilisant le secret du fournisseur sso comme clé.

  • Convertissez sig de sa représentation en chaîne hexadécimale en octets.

  • Assurez-vous que les deux valeurs ci-dessus sont égales.

  • Décodez en Base64 sso ; vous obtiendrez la chaîne de requête intégrée transmise. Celle-ci aura une clé appelée nonce dont la valeur doit correspondre au nonce passé à l’origine. Assurez-vous que c’est le cas et veillez à supprimer le nonce de votre système.

  • Vous constaterez que cette chaîne de requête contient également un ensemble d’informations utilisateur. Utilisez-les comme bon vous semble.

C’est tout. À ce stade, vous devriez avoir configuré votre application web pour utiliser Discourse comme fournisseur SSO !

Plus de paramètres, plus d’options

En plus de nonce et return_sso_url, la charge utile de la requête comporte plusieurs paramètres optionnels supplémentaires.

  • prompt : Si prompt=none, la requête SSO est traitée comme une requête « juste pour vérifier ». Si le navigateur/appareil est déjà connecté à Discourse, Discourse renverra une réponse SSO réussie portant les informations d’authentification de l’utilisateur, comme d’habitude. Si le navigateur/appareil n’est pas déjà connecté, Discourse ne demandera pas à l’utilisateur de se connecter et renverra immédiatement une réponse SSO portant le paramètre failed=true au lieu des informations utilisateur. Cela fournit un mécanisme pour interroger si l’utilisateur est connecté, sans jamais le diriger vers une boîte de dialogue de connexion s’il ne l’est pas.

  • logout : Si logout=true, la requête SSO devient une requête de déconnexion. Si un utilisateur est connecté à Discourse sur ce navigateur/appareil, il sera déconnecté de cet appareil. Dans tous les cas, Discourse redirigera immédiatement vers la return_sso_url, sans que sso ou sig ne soient ajoutés à la chaîne de requête.

  • require_2fa : Si require_2fa=true, Discourse exigera que l’utilisateur vérifie l’authentification à deux facteurs avant d’être redirigé. La charge utile de réponse inclura confirmed_2fa=true si l’utilisateur a terminé avec succès la vérification 2FA, ou no_2fa_methods=true si l’utilisateur n’a aucune méthode 2FA configurée.

prompt=none et logout=true sont mutuellement exclusifs ; il n’est pas logique de fournir les deux dans la même requête.

Référence de la charge utile sso=

Paramètres de requête :

  • nonce : (string, requis) une chaîne aléatoire générée de manière sécurisée
  • return_sso_url : (string, requis) l’URL vers laquelle rediriger avec la réponse
  • prompt : (string, facultatif) Si none, sonde le statut d’authentification sans demander à l’utilisateur de se connecter.
  • logout : (boolean, défaut false) Si true, déconnecte l’utilisateur de Discourse.
  • require_2fa : (boolean, défaut false) Si true, exige que l’utilisateur vérifie l’authentification à deux facteurs avant d’être redirigé.

Paramètres de résultat :

  • Il n’y a pas de charge utile sso= ni de signature en réponse à une requête de déconnexion, juste une redirection vers la return_sso_url simple de la requête.

  • La charge utile de résultat pour une requête de connexion contiendra toujours le nonce, reflété à partir de la requête.

  • La charge utile de résultat reflétera également tous les autres paramètres de requête. Ne vous fiez pas à ce comportement ; il n’est pas nécessairement intentionnel et n’est pas un aspect garanti de l’API. (Par exemple, pourquoi le paramètre return_sso_url est-il copié dans la charge utile envoyée à return_sso_url ?)

  • Si la requête n’a pas réussi à authentifier un utilisateur, la charge utile de résultat contiendra failed=true.

  • Si la requête a réussi à authentifier un utilisateur, la charge utile de résultat contiendra les informations d’identification/informations de l’utilisateur :

    • external_id : (string) l’identifiant utilisateur Discourse
    • username : (string) nom d’utilisateur/identifiant
    • name : (string) nom réel de l’utilisateur
    • email : (string) adresse e-mail
    • avatar_url : (string) URL CDN complète de l’image d’avatar téléchargée par l’utilisateur
    • admin : (boolean) true si l’utilisateur est un administrateur, sinon false
    • moderator : (boolean) true si l’utilisateur est un modérateur, sinon false
    • groups : (string) liste séparée par des virgules des groupes (par nom) auxquels l’utilisateur appartient
    • profile_background_url : (string) URL CDN complète de l’image d’arrière-plan du profil de l’utilisateur
    • card_background_url : (string) URL CDN complète de l’image d’arrière-plan de la carte de l’utilisateur
    • confirmed_2fa : (boolean) true si l’utilisateur a terminé la vérification 2FA (uniquement présent lorsque require_2fa=true a été demandé)
    • no_2fa_methods : (boolean) true si l’utilisateur n’a aucune méthode 2FA configurée (uniquement présent lorsque require_2fa=true a été demandé)

    name, avatar_url, profile_background_url et card_background_url peuvent être absents si l’utilisateur ne les a pas définis. (Tout élément avec une valeur nil dans Discourse sera omis de la réponse.)

Implémentations officielles de Discourse pour « Utiliser Discourse comme fournisseur d’identité » :

Implémentations communautaires pour « Utiliser Discourse comme fournisseur SSO » :

63 « J'aime »
Use discourse for SSO in a non-web app?
Can I require a Discourse login to view things on a different site?
SSO Redirect Problem
How-to setup a Discourse network?
Setting the session token '_t' on the entire domain, not just my subdomain
Login to Discourse with custom Oauth2 provider
Can I log into multiple instances of discourse simultaneously?
Reverse Single-Sign-On: Possible to use Discourse accounts on my other site?
Inter-Discourse
Login via discourse
Discourse a SSO provider for Wordpress
How to use SSO (Discourse on subdomain)
Discourse as an identity provider not working in localhost
Validate User Session on another custom site
Use the same user database and login credentials in multiple discourse instances
Login w/ Discourse w/o SSO?
Best way to use discourse as SSO - Wikimedia, Nextcloud, Immich
DiscourseConnect provider does not redirect to return_url after entering correct id and password
[Paid?] Migrate comments from several Blogger & Wordpress blogs to Discourse, under an SSO
Login error while trying to use Discourse as an identity provider (SSO, DiscourseConnect)
Is it possible?
Use Discourse SSO with Mediawiki
Use Discourse SSO with Mantis Bug Tracker
Use Discourse API to check whether username/password combination is valid
How to create a login on my front-end application to a specific Discourse site?
When Discourse is an identity provider, does it save the external user IDs?
How can I change the registration URL?
Should Discourse make an effort to become a viable comment platform?
Game Dev - User Registration & DB management (advice needed)
Game Dev - User Registration & DB management (advice needed)
Security/Privacy concern: Email exposed in DiscourseConnect Provider redirect URL
System deleting users: 'inactive user', 'Automatically deleted as abandoned, deactivated account', and 'staged unused'
Checking whether a user is logged in on Discourse from another website
Communities using discourse SSO for their in-app community experience
OAUTH flow to Integrate the discourse community account with the third party CRM tool where it can create tickets of community
OAUTH flow to Integrate the discourse community account with the third party CRM tool where it can create tickets of community
Setup DiscourseConnect - Official Single-Sign-On for Discourse (sso)
Is it Possible to Send Encrypted Email and Password in the Authentication Flow?
How Discourse ID works
Discourse doesn’t redirect to return_sso_url after user logs in on private site
Discourse as an SAML, OAUTH IDP?
What to do with response from Discourse SSO as provider
Discourse as SSO provider for Prosody
[Paid] WP + Discourse user authentication for IRC or other chat
User API keys specification
Discourse session variables
Enabling Login to Discourse from a third party api
How to use SSO (Discourse on subdomain)
Return_sso_url is blank but provided
Set up your DiscourseConnect(DiscourseSSO) to SimpleSAMLphp, use your Discourse forum as a SAML IDP (Identify Provider)
Discourse doesn't redirect to return_sso_url after user logs in on private site
Can Discourse be used as an OAuth provider?
Log in to Rocketchat with Discourse?
Authorization from a desktop application (and base domain site)
Can two Discourse Instances share and use One Database?
Nil secret on SSO
Can I authenticate to Drupal via Discourse?
Using Discourse as an authentication provider
Triggering account creation/login on external service when a user logs in on discourse
Using Discourse as SSO for desktop app
Triggering account creation/login on external service when a user logs in on discourse
Getting more discourse user data when using discourse as identity provider
Discourse as SSO Provider: Login Error
SSO between two Discourse instances
Could Discourse offer a StackExchange-like SSO/Federated login service?
Confirming a correct User+Pass combination via API?
How can I authenticate 3rd party website using discourse?
Share sign-up and login between Discourse and site?
How to configure Discourse as the sole user-facing login/sign-up provider for Wordpress site
Discourse SSO Provider doesn't redirect to return_sso_url as user logs in with custom SSO
OAuth 2 and other discourse sites?
Can I use the Discourse API to authenticate users in another app?
How can generate _forum_session and _t for an user through code/api call or without login to browser?
Setup DiscourseConnect - Official Single-Sign-On for Discourse (sso)

Great how to, thanks.

FYI, the term ‘nonce’ has an unfortunate meaning in Britain.

10 « J'aime »

I wrote a class to implement the process in Ruby. I use it to work with devise in my web application.

https://github.com/gogo52cn/sso_with_discourse

3 « J'aime »

sso seems to have a trailing newline which needs to be included when sent to the HMAC function, so it’s important to make sure that SSO consumer applications don’t strip whitespace from these query arguments.

1 « J'aime »

Our app users get their own subdomains. Is there a way to implement this with variable SSO urls?

interesting, this is tricky you would need to monkey patch this in, or better still make core extensible in this way and add a plugin.

3 « J'aime »

Hello,

I have created an Erlang implementation for encoding/decoding the payloads described in this specification. You can find it here:
https://github.com/reverendpaco/discourse-as-sso-erlang
Feedback is welcome.

Thanks to @mpalmer on another thread for setting me straight.

As I put together this implementation, I realized that the specification does not say too much about a few things:

  1. the user information that is returned in the query parameters,
  2. what happens when a user is not found

When I got around to testing I found the answers, and then confirmed by looking at the code, here: discourse/app/controllers/session_controller.rb at main · discourse/discourse · GitHub

I shall assume that in regards to 1. that while new user information may be added, that none of these values (“name”,“username”,“email”,“external_id”, etc) will be removed. This is just as important, contractually as what is described in the main post.

One piece of feedback I’d like to give the Discourse team is that it would be nice to add a means to optionally return back to the calling application in the case of a missing user.

Currently, at line 51 a non-logged-in or non-registered user will be forwarded to the Discourse login page. While this can be useful, I would rather programmatically have the option to learn that this person has not yet logged in (or registered) and give them the opportunity on my site to continue anonymously.

I can imagine something like this:

DISCOURSE_ROOT_URL/session/sso_provider?
sso=URL_ENCODED_PAYLOAD&sig=HEX_SIGNATURE&
returnBackIfUserMissing=true

and then the Discourse site sending back to the return_sso_url with either a special header, or an attribute, rather than redirecting to /login.

This change should be backwards compatible.
I’m new here, so if this is something that could be contributed via a pull-request, please tell me and I could take a shot at it next week.

thanks,
daniel

5 « J'aime »

Sure, totally open to add another option to the payload you send us for create_new=false PR welcome.

2 « J'aime »

added a PR for this

Commit-Message:

Implemented an optional 'no_user_found_return_sso_url' parameter to be called
 by the client when client is using Discourse as an SSO and wants Discourse to 
redirect back to a place of the client's choosng when a user is not found.

Currently, the Discourse as an SSO implementation checks the cookies _t and
 _session_forum to see if the user is registered and present in the database 
(_t is the token that is located in the users table). If a user is not found, 
the current implemenation forwards to the forum's /login URL. This behavior 
may be what the client wants, but it would be good to give an option to the 
client to send somewhere else.

This commit allows the client to embed an optional 'no_user_found_return_sso_url' 
parameter in the payload, prior to base64 and URL-encoding. If the Discoure SSO
endpoint detects that this parameter is present in the payload (and has a non-empty
value) the Discourse server will redirect to this new location if it does not detect the 
user. If this parameter is not present, then the redirection to /login will take place 
as it currently does.

Additionally, as the client may choose to use the same URL for
'no_user_found_return_sso_url' as for 'return_url', this commit introduces a new 
query-string name-value pair to be sent back to the client 'no_user_found_return_sso_url' 
location. This parameter 'user_found' will ALWAYS be sent back to the client, either when 
the user is found and 'return_url' is used or when the user is not found 
'no_user_found_return_sso_url' is used (values will be 'true' and 'false' respectively).

thanks,
daniel

4 « J'aime »

Nice work :slight_smile:

redirect_to can be routed to a url with parameters. But isn’t a create_new=false enough? Or whatever name it is. You’ll get nonce and the flag back.

I wanted to give the client the flexibility of sending a failure to a different URL than the success url (return_url). With complicated SSO architectures, this might be a requirement. (Selfishly, I wanted to make my return endpoint code less convoluted – i.e. on my side I have two codebases at /sso for success and /sso_failure for failure).

Implicitly there are already two URLs (return_url for success and ‘/login’ for failure) – I didn’t want to lose that.

So just to be clear, the payload you send to the Discourse endpoint should have both return_url and no_user_found_return_sso_url if you want Discourse to send it back when no user is found. It should have return_url only if you are ok with Discourse forwarding to /login.

Should look like this:

1.If you want Discourse to forward to login if no user found, then:

PAYLOAD = return_url=mydomain.com/clientendpoint&nonce=xE787euK
2. If you want Discourse to send back to you to the same endpoint for failure as success:

PAYLOAD = no_user_found_return_sso_url=mydomain.com/clientendpoint&return_url=mydomain.com/clientendpoint&nonce=xE787euK
3. If you want Discourse to send back to you to a different endpoint as success:

PAYLOAD = no_user_found_return_sso_url=sub.mydomain.com/handleNewUser&return_url=mydomain.com/clientendpoint&nonce=xE787euK

Potentially a bit over-engineered, but that was my thought process.

1 « J'aime »

To be clear, it’s not user_not_found, it’s not logged in.

It’s about protocol not engineering in the first place. Given two endpoints, you have to deal with two possible endpoints. In both cases you must validate the nonce and destroy it. It’s a hurdle not benefit.

In extreme case, a client can ask Discourse several times with/without session (current_user), thus you would get n responses in each endpoints. It would be twice as hard to secure it due to changing cookies on your end.

2 « J'aime »

Completely agree on protocol over engineering. Hence, my submission.

The semantics are equivalent to an if-else block, or more generally a case/switch block. result_url is our true condition, and user_not_found_url is our else block. If we didn’t provide it, we would force the client to have to deal with the ‘failure’ condition in the same codebase/endpoint – and for 90% of the people, this will be fine.

90% of people who want Discourse to return back to them will set return_url and user_not_found_url to be exact same thing. This bears repeating, and really renders the motivations for the 10% moot.

90% of the time you will make return_url and user_not_found_url the exact same thing. These 90%-ers will use their JSESSIONID/ PHPSESSID/ ASPSESSIONID /_session to look up the returned encoded nonce in their framework-supplied session-store and engineer accordingly.

7% of people will be happy to pass it to a different URL on their same app-server, which does some decoration (servlet-chains anyone?) or routing, but still looks up the nonce in their session.

3% will have some complicated polyglotish system that uses a distributed session store (like memcache) to store sessions for different app servers implemented in different legacy codebases. It’s up to them to store/invalidate the nonce across these different systems.

I realize I might not have been completely clear, but the user_not_found_url still receives the sso and sig parameters, just like the return_url.

So, if you are the 90% scenario, when you get the payload and verify/decode it, you will find the parameter user_found=false|true to know why it’s coming back to you.

2 « J'aime »

@fantasticfears, as per your recommendation on the PR, I have renamed the attribute from user_not_found_url to return_sso_unlogged_in_url.

1 « J'aime »

3 posts were split to a new topic: Can Discourse be used as an OAuth provider?

Great job !

Do you know how it possible to adapt it to GitLab in order to allow the Discourse users to directly login there?

2 « J'aime »

Hey folks, I could not find anything, so I created a npm for Discourse auth using Passport for node.js.

It’s here, and in npm if anyone needs it:

(really appreciate the team’s work, we’ve been a user since the very early days @ talk.wigwag.com)

5 « J'aime »

@sam,

I have submitted a reply back to your rejection of the PR:
Optional 'no_user_found_return_sso_url' parameter for using Discourse as SSO by reverendpaco · Pull Request #4211 · discourse/discourse · GitHub

As I have been relying on my patched version of discourse for these months, I would be interested in convincing you as to my need.

If this PR is going to be rejected, could you explain what I can do to get the functionality that I need?

Does anyone know of a wordpress plugin that works with discourse as the SSO provider?

1 « J'aime »