Use Discourse as an identity provider (SSO, DiscourseConnect)

In Settings > Login, the two DiscourseConnect provider settings are not consecutive, and appear interspersed with the DiscourseConnect settings:

Since the provider and the non-provider settings are for opposite use cases—using Discourse to manage users for something else versus using something else to manage users for Discourse—displaying these settings mixed together invites misconfiguration. It would be less confusing if the two provider settings were consecutive and either entirely before or entirely after the non-provider settings.


@techAPJ Can this be used with AWS Cognito? I want to create an app in AWS Amplify for my discourse community and want my app to be authenticated through discourse.

1 Like

Sorry for the very delayed reply. You’re absolutely right - it’s confusing that they’re interspersed like that. I’ve cleaned it up in this PR:


@mdoggydog Thanks for the recent update to the DiscourseSsoConsumer MediaWiki extension. We’d been puzzling over what to do about users being logged out of our wiki without having logged out of Discourse, and $wgPluggableAuth_EnableAutoLogin was definitely not what we wanted, since it prevents anonymous access to the wiki. The $wgDiscourseSsoConsumer_AutoRelogin setting you added is exactly what we needed.

1 Like

Hello :wave: there!

I am trying to use the PHP example from the original post, but the way they’re storing stuff doesn’t make sense. They’re just storing values in an SQL database with the keys login and nonce. If I want to use SQL to store the nonces what would my SQL database look like exactly?

Some other info that might help is what I’m using this for - I am hoping to link a Discourse user to a Minecraft account by generating an SSO link which is tied to their UUID. Upon successfully logging in with Discourse, I’m going to store their UUID and Discourse ID in a table.

So far, I was able to get the PHP example working, but I guess I’m not fully understanding how I’d have to modify it to work for my use-case. Ideally, I want to generate the link through a GET request and send it to the user, so the UUID is already associated with the nonce.

Thank you for this post, as I would be even more lost without it!

Edit: For nonces would I just be better off storing nonces in the table and look up by that? I know I need to match the nonce but, unless I can pass additional information through in the redirect URL (which I haven’t been successful doing so) I’m not sure how to reference the nonce properly.


I have created a guide that allow admin to configure DiscourseConnect into standard SAML protocol via SimpleSAMLphp.

1 Like

Link Detail


I’m not sure why it will 502 bad gateway.

I edited out some of the value of the sso param that you had provided. Unless someone knew your secret key they would not be able to decode the value, but it still seemed safer to not provide the full value. It won’t be related to the 502 error that you are getting.

1 Like

It seems a databse error. Because I try the same plugin and configuration in another discourse website, and it’s working. How can I fix this problem?

It seems that trying to authenticate into discourse-auth-proxy with non-ASCII usernames or group names (Chinese in my case) leads to an error due to cookies cannot contain these characters. This is my fix: (disclaimer: I’m not really familiar with golang)

diff --git a/main.go b/main.go
index 1b1dc28..18f8c9e 100644
--- a/main.go
+++ b/main.go
@@ -154,7 +154,12 @@ func redirectIfNoCookie(handler http.Handler, r *http.Request, w http.ResponseWr
        var username, groups string
        if err == nil && cookie != nil {
-               username, groups, err = parseCookie(cookie.Value, config.CookieSecret)
+               var value string
+               value, err = url.QueryUnescape(cookie.Value)
+               if err != nil {
+                       return
+               }
+               username, groups, err = parseCookie(value, config.CookieSecret)
        if err == nil {
@@ -224,7 +229,7 @@ func redirectIfNoCookie(handler http.Handler, r *http.Request, w http.ResponseWr
                cookieData := strings.Join([]string{username, strings.Join(groups, "|")}, ",")
                http.SetCookie(w, &http.Cookie{
                        Name:     cookieName,
-                       Value:    signCookie(cookieData, config.CookieSecret),
+                       Value:    url.QueryEscape(signCookie(cookieData, config.CookieSecret)),
                        Expires:  expiration,
                        HttpOnly: true,
                        Path:     "/",
1 Like

Thanks for reporting! Would you mind making a PR against the github repository so that we can merge this change into the official version?


I was able to use this to create an account linking system for my Minecraft server! Thought I’d share what it looks like! This was my first time working with Discourse SSO, so I might’ve overcomplicated everything. However, it works, which is the main thing.


Hi i did all these correctly. but when the user is not logged in discourse it will show user login popup. when I fill username and password, it does not redirect me back to return_url. can you please help me?

I assume the nonce is there to prevent replay attacks. I read online that replay attacks aren’t possible with HTTPS, which is what I’m using. So do I still need to do the nonce? I ask because I’m not sure where to store it. Does it make sense to store it as a secure, plain text cookie in the user’s browser? and then read it from the browser along with the return payload?

This library, which is linked from the original post GitHub - ArmedGuy/discourse_sso_node: npm package for Discourse SSO login features. doesn’t use the nonce when validating the user.

Yes, you still need to validate the nonce because it prevents reusing payloads that Discourse sends when it redirects users back to your site.

For example, let’s your site has some content behind a paywall that only members of the subscribers group on Discourse can access and you use the groups field in the payload that Discourse sends to your site to display the paid content only to members of the subscribers group. If you don’t validate the nonce, a user who is no longer in the subscribers could use an old payload from when they were a member to login to your site and see the paid content.

It’s best to store the nonce in a database with a short expiration date and delete the nonce from the database as soon as it’s used. However, if you can’t use a database, then you can use a cookie to store the nonce, but you need to do some additional steps to prevent payload reuse:

  1. attach an expiration date to the nonce when you generate it, for example 10 minutes from the current time
  2. sign the whole cookie (nonce + expiration date) to prevent users from modifying the nonce and/or the expiration date
  3. verify the signature of the cookie and ensure the nonce isn’t expired

That should give you a good enough protection against payload reuse. Keep in mind that technically it remains possible to reuse a payload, but it’ll be limited to a 10 minutes window instead of forever.

A simpler solution that doesn’t need a cookie is to include the expiration date in a custom field in the payload that you generate. Then when Discourse redirects users back to your site with a payload, your custom fields will be included and you can retrieve the expiration date and you verify it’s not expired. To include a custom field in the payload, you need to include a field prefixed with custom., so your payload would look like this:


You could also store the nonce in the session, that will prevent the user from tampering with it as well.


Just circling back to this thread years later

Can someone tell me (@pfaffman or @tobiaseigen or @iamntz) what the Discourse SSO provider returns? I know I can “try it and see” but it would be nice to have it documented. The github PHP sample code doesn’t trally have any other fields even mentioned.

Ideally, it would send the same fields as when Discourse uses the external script for SSO, such as external id, email, username, name, avatar photo etc. So we can import this and create a user on our side!

Does it also tell Wordpress the email?

How about groups, badges etc? Can we find this information by making REST calls?

Finally, what about the user’s private messages and other stuff? I guess if Discord was an oAuth provider and allowed our apps to consume this sruff, that would be awesome.

When trying to enable Discourse Connect I get this error:
enable_discourse_connect: You cannot enable DiscourseConnect and invite only at the same time.

Any ideas?

I see you have asked the same question here: Setup DiscourseConnect - Official Single-Sign-On for Discourse (sso) - #537 by Roie_Natan. If you are trying to configure the enable discourse connect setting and not the enable discourse connect provider setting, the other topic is the correct place to ask your question.

The enable discourse connect provider setting is for when you want to use your Discourse site as the identity provider for another site. The enable discourse connect setting is for when you want to log users into Discourse via an external site.

1 Like

I’ve implemented the procedure in Python for a Flask application I am building. Here is some boilerplate code for anyone who needs it. The steps laid out in this topic were pretty simple to follow but I’m not a security specialist so if I overlooked anything please let me know!