将 Discourse 用作身份提供者(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 个赞

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 个赞

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 个赞

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 个赞

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 个赞

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 个赞

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 个赞

@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 个赞

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 个赞

@mdoggydog 感谢您最近更新了 DiscourseSsoConsumer MediaWiki 扩展。我们一直在琢磨如何处理用户在未退出 Discourse 的情况下退出我们 wiki 的问题,而 $wgPluggableAuth_EnableAutoLogin 绝对不是我们想要的,因为它阻止了对 wiki 的匿名访问。您添加的 $wgDiscourseSsoConsumer_AutoRelogin 设置正是我们所需要的。

1 个赞

你好:wave:!

我正在尝试使用原始帖子的 PHP 示例,但他们存储数据的方式没有意义。他们只是将值存储在 SQL 数据库中,键为 loginnonce。如果我想使用 SQL 来存储 nonces,我的 SQL 数据库应该是什么样的?

一些可能有助于我理解的额外信息是我的用途——我希望通过生成一个与他们的 UUID 相关联的 SSO 链接来将 Discourse 用户与 Minecraft 账户关联起来。在成功使用 Discourse 登录后,我将在一个表中存储他们的 UUID 和 Discourse ID。

到目前为止,我已经设法让 PHP 示例正常工作,但我似乎没有完全理解我必须如何修改它才能适用于我的用例。理想情况下,我想通过 GET 请求生成链接并将其发送给用户,这样 UUID 就已经与 nonce 相关联了。

感谢这篇帖子,没有它我将更加迷茫!

编辑:对于 nonces,我是否最好将 nonces 存储在表中并通过它进行查找?我知道我需要匹配 nonce,但除非我能在重定向 URL 中传递额外信息(我尚未成功做到这一点),否则我不确定如何正确引用 nonce。

2 个赞

我创建了一个指南,允许管理员通过 SimpleSAMLphp 将 DiscourseConnect 配置为标准的 SAML 协议。

1 个赞

链接详情

/session/sso_provider?sso=bm9uY2U9NDA3YTVlNjg1ZTEwMDlh...

我不确定为什么会出现 502 错误(网关错误)。

我编辑掉了你提供的 sso 参数的一些值。除非有人知道你的密钥,否则他们无法解码该值,但仍然提供完整值似乎不那么安全。这与你遇到的 502 错误无关。

1 个赞

看起来是数据库错误。因为我在另一个 Discourse 网站上尝试了相同的插件和配置,并且它能正常工作。我该如何解决这个问题?

尝试使用非 ASCII 用户名或组名(在本例中为中文)进行 discourse-auth-proxy 身份验证似乎会导致错误,因为 cookie 无法包含这些字符。这是我的修复方法:(免责声明:我并不真正熟悉 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 个赞

感谢您的反馈!您能否针对 GitHub 存储库创建一个 PR,以便我们将此更改合并到官方版本中?

4 个赞