将 Discourse 用作身份提供者 (SSO, DiscourseConnect)

那么你想使用 Discourse 作为你自己的 Web 应用程序的身份提供商?太棒了!让我们开始吧。

启用 DiscourseConnect 提供商设置

在 Discourse 管理网站设置(/admin/site_settings)中,启用设置 enable discourse connect provider,并在 discourse connect provider secrets 中添加一个密钥字符串(用于哈希 SSO 有效负载)。

在你的 Web 应用程序中实现 DiscourseConnect

  • 生成一个随机的 nonce。我们称这个值为 NONCE。暂时保存它,以便你可以用响应中返回的 nonce 值进行验证。

  • 创建一个包含 NONCERETURN_URL(Discourse 在验证后将用户重定向到的位置)的新有效负载。有效负载应如下所示:nonce=NONCE&return_sso_url=RETURN_URLRETURN_URL 的主机名必须与你在配置 discourse connect provider secrets 时使用的域模式匹配。

  • 对上述原始有效负载进行 Base64 编码。我们称这个有效负载为 BASE64_PAYLOAD

  • 对上述 BASE64_PAYLOAD 进行 URL 编码。我们称这个有效负载为 URL_ENCODED_PAYLOAD

  • 使用你的 sso 提供商密钥作为密钥,从 BASE64_PAYLOAD 生成一个 HMAC-SHA256 签名,然后将此签名转换为小写的十六进制字符串。我们称这个签名为 HEX_SIGNATURE

向 Discourse 发送身份验证请求

将用户重定向到 DISCOURSE_ROOT_URL/session/sso_provider?sso=URL_ENCODED_PAYLOAD&sig=HEX_SIGNATURE

从 Discourse 接收响应:

如果以上步骤正确完成,Discourse 将把已登录的用户重定向到提供的 RETURN_URL。你将收到带有 sigsso 以及一些用户信息查询字符串参数。现在请遵循以下步骤:

  • 使用 sso 提供商密钥作为密钥,对 sso 计算 HMAC-SHA256。

  • sig 从其十六进制字符串表示形式转换回字节。

  • 确保上述两个值相等。

  • Base64 解码 sso;你将得到传入的嵌入式查询字符串。这将有一个名为 nonce 的键,其值应与最初传入的 nonce 匹配。请确保情况属实,并务必从你的系统中删除该 nonce。

  • 你会发现此查询字符串还包含一堆用户信息。按你认为合适的方式使用它们。

就是这样。到目前为止,你应该已经设置好 Web 应用程序以使用 Discourse 作为 SSO 提供商了!

更多参数,更多选项

除了 noncereturn_sso_url 之外,请求有效负载还有几个可选参数。

  • prompt: 如果 prompt=none,则 SSO 请求被视为“仅检查”请求。如果浏览器/设备已登录到 Discourse,Discourse 将像往常一样返回带有用户身份验证信息的成功 SSO 响应。如果浏览器/设备尚未登录,Discourse 将不会要求用户登录,并将立即返回一个带有参数 failed=true 的 SSO 响应,而不是用户信息。这提供了一种查询用户是否已登录的机制,而无需在用户未登录时将用户重定向到登录对话框。

  • logout: 如果 logout=true,则 SSO 请求将变为_注销_请求。如果用户在该浏览器/设备上登录了 Discourse,他们将被在该设备上注销。无论哪种情况,Discourse 都将立即重定向回 return_sso_url在查询字符串中添加 ssosig

  • require_2fa: 如果 require_2fa=true,Discourse 将要求用户在重定向回之前验证双因素身份验证(2FA)。响应有效负载将包含 confirmed_2fa=true(如果用户成功完成 2FA 验证),或 no_2fa_methods=true(如果用户没有配置 2FA 方法)。

prompt=nonelogout=true 是互斥的;在同一请求中提供两者没有意义。

sso= 有效负载参考

请求参数:

  • nonce:(字符串,必需)一个安全生成的随机字符串
  • return_sso_url:(字符串,必需)重定向回时附带响应的 URL
  • prompt:(字符串,可选)如果为 none,则在不提示用户登录的情况下探测身份验证状态。
  • logout:(布尔值,默认为 false)如果为 true,则将用户从 Discourse 注销。
  • require_2fa:(布尔值,默认为 false)如果为 true,则要求用户在重定向回之前验证双因素身份验证。

结果参数:

  • 对于注销请求,响应中没有 sso= 有效负载或签名,只有一个重定向到请求的纯 return_sso_url
  • 登录请求的结果有效负载将始终包含从请求中反射回来的 nonce
  • 结果有效负载还将反映任何其他请求参数。不要依赖此行为;它不一定是故意的,也不是 API 的保证方面。(例如,为什么 return_sso_url 参数会被复制到发送到 return_sso_url 的有效负载中?)
  • 如果请求未能验证用户,结果有效负载将包含 failed=true
  • 如果请求成功验证了用户,结果有效负载将包含用户凭据/信息:
    • external_id:(字符串)Discourse 用户 ID
    • username:(字符串)用户名/句柄
    • name:(字符串)用户的真实姓名
    • email:(字符串)电子邮件地址
    • avatar_url:(字符串)用户上传的头像图片的完整 CDN URL
    • admin:(布尔值)如果用户是管理员,则为 true,否则为 false
    • moderator:(布尔值)如果用户是版主,则为 true,否则为 false
    • groups:(字符串)用户所属的用户组(按名称)的逗号分隔列表
    • profile_background_url:(字符串)用户个人资料背景图片的完整 CDN URL
    • card_background_url:(字符串)用户卡片背景图片的完整 CDN URL
    • confirmed_2fa:(布尔值)如果用户完成了 2FA 验证,则为 true(仅在请求 require_2fa=true 时存在)
    • no_2fa_methods:(布尔值)如果用户没有配置 2FA 方法,则为 true(仅在请求 require_2fa=true 时存在)

如果用户未设置这些项,则可能缺少 nameavatar_urlprofile_background_urlcard_background_url。(Discourse 中任何值为 nil 的元素都将从响应中省略。)

Discourse 官方“将 Discourse 用作身份提供商”实现:

社区贡献的“将 Discourse 用作 SSO 提供商”实现:

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

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

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

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

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

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

2 个赞

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

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

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

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

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

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

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

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