Verwenden Sie Discourse als Identitätsanbieter (SSO, DiscourseConnect)

Hi :wave:, I wrote this SimpleSAMLphp authentication module to be able to use Discourse as an SSO provider within a SimpleSAMLphp installation. I.e. you can use Discourse as an SSO provider for any services that supports SAML or Shibboleth authentication, which is really nice.

https://github.com/swcc/simplesamlphp-module-authdiscourse

Let me know what you think (if interested to comment on the code you can use Github Issues).

4 „Gefällt mir“

That’s great! If you’d like to make the module more visible, you could create a topic about it in our extras category. That category is a directory of all extensions & integrations for Discourse which are not Discourse plugins,

3 „Gefällt mir“

EDIT: Mockup image added


I’m implementing this SSO on an existing web site and I just want to check how people are “presenting” their login method to users.

For example, let’s say my web site at www.example.com has a Login button in the top nav.

Should that login button instantly take people to my discourse login auth page? Or is it preferable to display a page / modal of information first, something like:

I’m just wondering if people will get confused if they’re not told what’s about to happen? :thinking:

Does anyone have any user experiences or best practices to share?

And thanks too by the way to @techAPJ for the very detailed first post in this thread, I was able to successfully build this in to my ASP.NET web site from scratch following your steps :+1:t2:

2 „Gefällt mir“

Is it possible to include a state parameter that gets returned unchanged, like as is done in OAuth? Asp.net core authentication middleware depends on generating a correlation id to prevent CSRF attacks, and I don’t currently have an easy way to include this.

Could you not add one to the querystring of your callback URL before you wrap it up?

Eg:

nonce=NONCE&return_sso_url=https://www.example.com/my/callback_url.aspx?myparam=here

@jessicah I tried this today and yes, it works fine.

        ' Create a Return URL
        Dim strReturnURL As String = "https://www.example.com/authtestRETURNURL.aspx?myownparametershere=surewhynot"

        ' Generate a random nonce. Save it temporarily so that you can verify it with returned nonce value
        Dim strNonce As String = Guid.NewGuid().ToString("N")

        ' Create a new payload with nonce and return url (where the Discourse will redirect user after verification)
        '       Payload should look like: nonce=NONCE&return_sso_url=RETURN_URL
        Dim strPayload As String = "nonce=" & strNonce & "&return_sso_url=" & strReturnURL

Then on the page which is called back you’ll find this inside your decoded SSO query string:

&return_sso_url=http%3A%2F%2Fwww.example.com%2FauthtestRETURNURL.aspx%3Fmyownparametershere%3Dsurewhynot&username=Rich

Hopefully this is what you meant?

1 „Gefällt mir“

Multi-site approaches to using discourse-auth-proxy?

Are there any examples or recommendations for using Discourse as SSO provider for multi-site authentication?

It seems like the there are two basic multisite approaches:

  1. Use multiple instances of discourse-auth-proxy, one per site protected.
  2. Use a single instance of discourse-auth-proxy so the payload containing return_sso_url changes based upon the source of the login request.

I think either of these could work, but the issue with these two approaches, is
that you still require logging into each different site.
There is also the risk that something is stored in Postgres that will get overwritten by each login from the different sites. ie: site1.com. site2.com
(I don’t know the details of Discourse auth/PG schema, so I don’t know)

What would be ideal is a way to have login performed once, which gets you logged into all the sites in the multi-site group. ie, site1.com, site2.com, site3.com

Apparently Stackoverflow does this using a combination of localSession storage and Iframes as the main enabler. tech description

But I’d really love to know if someone has implemented any approach to
multisite login using Discourse as the SSO provider.
approach 1: multiple instances of discourse-auth-proxy
approach 2: hacked discourse-auth-proxy affecting return_sso_url in payload.
approach 3: #1 or #2 implemented such that logging in once, means you do not have to login again when moving from site1.com to site2.com

I am tagging you @sam, since you originally authored the Go discourse-auth-proxy program.

Hi.

I am stucking at how the provider handle the + sign in return sso url.

In my case, my DiscourseConnect consumer will create a return_sso_url like

  1. raw http://example.com/foo/bar?wpLoginToken=123+\
  2. PHP urlencoded http%3A%2F%2Fexample.com%2Ffoo%2Fbar%3FwpLoginToken%3D123%2B%5C

then DiscourseConnect will return to

  1. http://example.com/foo/bar?wpLoginToken=123%20\&sso=foo&sig=bar
  2. http://example.com/foo/bar?wpLoginToken=123+\&sso=foo&sig=bar

The problem is that the return to url will be handle by the urldecode function in PHP(it’s the core workflow in MediaWiki Authentication), and the wpLoginToken value will be unexpected change from 123+\ to 123 \.

Look like i has found the cause:
https://ruby-doc.org/stdlib-2.7.1/libdoc/uri/rdoc/URI.html#method-c-decode_www_form
https://www.php.net/manual/en/function.urlencode.php

Well, the client and server implement encode/decode but follow different specification.

I’m not asking Discourse to change but looking for advice to resolve this.

Thanks.

Edit:
I resolve it just urlencode the + sign twice.

3 „Gefällt mir“

Thanks!

I’m trying this presently. I sent you two PRs on GitHub, one for refreshing the docs and one for fixing a bug I hit.

1 „Gefällt mir“

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.

4 „Gefällt mir“

@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 „Gefällt mir“

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:

6 „Gefällt mir“

@mdoggydog Danke für das kürzliche Update der DiscourseSsoConsumer MediaWiki-Erweiterung. Wir haben uns gefragt, was wir tun sollen, da Benutzer von unserem Wiki abgemeldet wurden, ohne sich von Discourse abgemeldet zu haben, und $wgPluggableAuth_EnableAutoLogin war definitiv nicht das, was wir wollten, da es den anonymen Zugriff auf das Wiki verhindert. Die von Ihnen hinzugefügte Einstellung $wgDiscourseSsoConsumer_AutoRelogin ist genau das, was wir brauchten.

1 „Gefällt mir“

Hallo :wave:!

Ich versuche, das PHP-Beispiel aus dem ursprünglichen Beitrag zu verwenden, aber die Art und Weise, wie sie Dinge speichern, ergibt keinen Sinn. Sie speichern Werte nur in einer SQL-Datenbank mit den Schlüsseln login und nonce. Wenn ich SQL verwenden möchte, um die Nonces zu speichern, wie würde meine SQL-Datenbank genau aussehen?

Einige andere Informationen, die helfen könnten, sind, wofür ich dies verwende – ich hoffe, einen Discourse-Benutzer mit einem Minecraft-Konto zu verknüpfen, indem ich einen SSO-Link generiere, der an ihre UUID gebunden ist. Nach erfolgreichem Login mit Discourse werde ich ihre UUID und ihre Discourse-ID in einer Tabelle speichern.

Bisher konnte ich das PHP-Beispiel zum Laufen bringen, aber ich verstehe nicht ganz, wie ich es für meinen Anwendungsfall anpassen müsste. Idealerweise möchte ich den Link über eine GET-Anfrage generieren und an den Benutzer senden, sodass die UUID bereits mit der Nonce verknüpft ist.

Vielen Dank für diesen Beitrag, ohne ihn wäre ich noch mehr verloren!

Bearbeiten: Wäre es für Nonces besser, Nonces in der Tabelle zu speichern und danach zu suchen? Ich weiß, dass ich die Nonce abgleichen muss, aber es sei denn, ich kann zusätzliche Informationen in der Weiterleitungs-URL übergeben (was mir bisher nicht gelungen ist), bin ich mir nicht sicher, wie ich die Nonce richtig referenzieren kann.

2 „Gefällt mir“

Ich habe eine Anleitung erstellt, die es Administratoren ermöglicht, DiscourseConnect über SimpleSAMLphp in das Standard-SAML-Protokoll zu integrieren.

1 „Gefällt mir“

Link Detail

/session/sso_provider?sso=bm9uY2U9NDA3YTVlNjg1ZTEwMDlh...

Ich bin mir nicht sicher, warum es zu einem 502 Bad Gateway kommt.

Ich habe einen Teil des Werts des sso-Parameters, den Sie angegeben haben, heraus editiert. Solange niemand Ihren geheimen Schlüssel kennt, kann er den Wert nicht entschlüsseln, aber es schien trotzdem sicherer zu sein, nicht den vollständigen Wert anzugeben. Dies hat nichts mit dem 502-Fehler zu tun, den Sie erhalten.

1 „Gefällt mir“

Es scheint ein Datenbankfehler zu sein. Denn ich habe dasselbe Plugin und dieselbe Konfiguration auf einer anderen Discourse-Website ausprobiert, und es funktioniert. Wie kann ich dieses Problem beheben?

Es scheint, dass der Versuch, sich bei discourse-auth-proxy mit Nicht-ASCII-Benutzernamen oder Gruppennamen (in meinem Fall Chinesisch) zu authentifizieren, zu einem Fehler führt, da Cookies diese Zeichen nicht enthalten können. Hier ist meine Korrektur: (Haftungsausschluss: Ich bin mit Golang nicht wirklich vertraut)

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 „Gefällt mir“

Danke für die Meldung! Würden Sie bitte einen PR gegen das GitHub-Repository erstellen, damit wir diese Änderung in die offizielle Version übernehmen können?

4 „Gefällt mir“