DiscourseConnect は、Discourse のコア機能の一つで、ユーザー登録とログインをすべて別のサイトに完全に委譲する「シングルサインオン(SSO)」を設定できるようにします。これは、プロ、ビジネス、エンタープライズホスティングのお客様 に提供されています。
(2021 年 2 月)「Discourse SSO」は現在「DiscourseConnect」となっています。古いバージョンの Discourse を実行している場合、以下の設定名は
discourse_connect_...ではなくsso_...となります。
問題点
Discourse サイトと連携しようとする多くのサイトでは、すべてのユーザー登録を別のサイトに保持したいと考えています。このような設定では、すべてのログイン操作をその異なるサイトに委譲する必要があります。
既存の認証と併用して SSO を使用したい場合はどうすればよいですか?
DiscourseConnect の目的は、Discourse の認証を置き換えることです。新しいプロバイダーを追加したい場合は、既存のプラグイン(例:https://meta.discourse.org/t/vk-com-login-vkontakte/12987)を参照してください。
DiscourseConnect の有効化
DiscourseConnect を有効にするには、以下の 3 つの設定を記入する必要があります。
enable_discourse_connect:有効にする必要があります(グローバルスイッチ)
discourse_connect_url:ログイン時にユーザーが転送される外部URL
discourse_connect_secret:SSO ペイロードをハッシュ化するために使用される秘密の文字列。ペイロードの真正性を保証します。
enable_discourse_connect を true に設定すると、以下のようになります。
- ログインまたはアバターをクリックすると、
/session/ssoにリダイレクトされ、さらにユーザーが署名付きペイロードとともにdiscourse_connect_urlにリダイレクトされます。 - ユーザーは「パスワードの変更」を行うことができません。このフィールドはユーザープロフィールから削除されます。
- ユーザーはもはや Discourse 認証(ユーザー名/パスワード、Google など)を使用できなくなります。
誤ってチェックしてしまった場合はどうすればよいですか?
参照:Log back in as admin after locking yourself out with read-only mode or an invalid SSO configuration
サイトでの DiscourseConnect の実装
Discourse は、外部ユーザーを Discourse ユーザーにマッピングするために電子メールを使用し、外部電子メールが安全であると仮定しています。外部電子メールを Discourse に送信する前に検証していない場合、サイトは極めて脆弱になります!
または、検証されていない電子メールを送信することを強く主張する場合は、必ず require_activation=true を設定してください。これにより、すべての電子メールが Discourse によって検証されるようになります。ただし、この設定を有効にすることは強く推奨されません。この設定を有効にして進める場合は、大きなリスクを引き受けることになります。
Discourse は、署名付きペイロードとともにクライアントを discourse_connect_url にリダイレクトします(例:discourse_connect_url が https://somesite.com/sso の場合)。
以下のような受信トラフィックを受け取ります。
https://somesite.com/sso?sso=PAYLOAD&sig=SIG
ペイロードは、nonce と return_sso_url で構成される Base64 エンコードされた文字列です。ペイロードは常に有効なクエリ文字列です。
例えば、nonce が ABCD の場合、raw_payload は以下のようになります。
nonce=ABCD&return_sso_url=https%3A%2F%2Fdiscourse_site%2Fsession%2Fsso_login。この raw_payload は Base64 エンコードされています。
呼び出されるエンドポイントでは、以下の処理を行う必要があります。
- 署名を検証する:
discourse_connect_secretを鍵として使用したPAYLOADの HMAC-SHA256 がsigと等しいことを確認する(sigは hex エンコードされる)。 - 必要な認証を実行する。
- 少なくとも nonce、email、external_id を含む新しい URL エンコードされたペイロードを作成する。追加データも提供可能で、Discourse が理解するすべてのキーのリストは以下の通りです。
- nonce は入力ペイロードからコピーする必要があります。
- email は検証済みの電子メールアドレスである必要があります。電子メールアドレスが検証されていない場合は、require_activation を「true」に設定してください。
- external_id は、ユーザー固有の一意の文字列で、電子メールや名前などが変更されても決して変更されない値です。推奨値は、データベースの「id」行番号です。
- username は、ユーザーが新規の場合、または
SiteSetting.auth_overrides_usernameが設定されている場合に、Discourse 上のユーザー名になります。 - name は、ユーザーが新規の場合、または
SiteSetting.auth_overrides_nameが設定されている場合に、Discourse 上のフルネームになります。 - avatar_url は、ユーザーが新規の場合、または
SiteSetting.discourse_connect_overrides_avatarが設定されている場合に、ダウンロードされてユーザーのアバターとして設定されます。 - avatar_force_update はブール値フィールドです。true に設定すると、
avatar_urlが変更されたかどうかに関わらず、Discourse がユーザーのアバターを更新することを強制します。 - bio は、ユーザーが新規の場合、またはユーザーのバイオが空の場合、または
SiteSetting.discourse_connect_overrides_bioが設定されている場合に、ユーザーのバイオの内容になります。 - title は、ユーザーのタイトルを設定します。
- website は、ユーザーのプロフィール上のウェブサイトを設定します。
- location は、ユーザーのプロフィール上の所在地を設定します。
- profile_background_url は、ユーザーが新規の場合、または
SiteSetting.discourse_connect_overrides_profile_backgroundが設定されている場合に、ダウンロードされてユーザーのプロフィール背景として設定されます。 - card_background_url は、ユーザーが新規の場合、または
SiteSetting.discourse_connect_overrides_card_backgroundが設定されている場合に、ダウンロードされてユーザーのカード背景として設定されます。 - locale は、ユーザーが新規の場合、かつ
SiteSetting.allow_user_localeが有効になっている場合に、ユーザーのロケールを設定します。 - locale_force_update はブール値フィールドです。locale とともに true に設定すると、既存のユーザーのロケールを更新することを強制します(
SiteSetting.allow_user_localeが必要)。 - 追加のブール値(「true」または「false」)フィールドには、admin、moderator、suppress_welcome_message、logout があります。
- ペイロードを Base64 エンコードする。
discourse_connect_secretを鍵として、Base64 エンコードされたペイロードをテキストとして HMAC-SHA256 ハッシュを計算する。ssoとsigクエリパラメータとともにreturn_sso_urlにリダイレクトする(http://discourse_site/session/sso_login?sso=payload&sig=sig)。
Discourse は nonce が有効であることを検証し、有効であればすぐに期限切れにして再利用できないようにします。その後、以下の処理を試みます。
SingleSignOnRecordモデルで既に連携されている external_id を検索してユーザーをログインさせる。- 提供された電子メールを使用してユーザーをログインさせる(require_activation = true でない限り)。
- ユーザーに対して新しいアカウントを作成する(email、username、name を提供し、external_id を更新する)。
セキュリティ上の懸念
nonce(ワンタイムトークン)は 30 分後に自動的に期限切れになります。つまり、ユーザーがサイトにリダイレクトされてから 30 分以内にログインまたは新規アカウントを作成する必要があります。
このプロトコルは、nonce は一度しか使用できないため、リプレイ攻撃に対して安全です。また、nonce は現在のブラウザセッションに紐付けられているため、CSRF 攻撃から保護されます。
グループメンバーシップの指定
「discourse connect overrides groups」オプションが指定されている場合、Discourse は groups に渡されたカンマ区切りのグループリストを考慮します。
groups 以外にも、SSO ペイロード内で add_groups と remove_groups 属性を使用してグループメンバーシップを指定できます。これは「discourse connect overrides groups」オプションの有無に関係なく機能します。
add_groups は、ユーザーがメンバーであることを保証するグループ名のカンマ区切りリストです。
remove_groups は、ユーザーがメンバーでないことを保証するグループ名のカンマ区切りリストです。
リファレンス実装
Discourse には、SSO クラスのリファレンス実装が含まれています。
簡単な実装例は以下の通りです。
class DiscourseSsoController < ApplicationController
def sso
secret = "MY_SECRET_STRING"
sso = DiscourseApi::SingleSignOn.parse(request.query_string, secret)
sso.email = "user@email.com"
sso.name = "Bill Hicks"
sso.username = "bill@hicks.com"
sso.external_id = "123" # アプリケーションの各ユーザー固有の ID
sso.sso_secret = secret
redirect_to sso.to_url("http://l.discourse/session/sso_login")
end
end
シングルサインオンへの移行と移行後
リクエストペイロードで require_activation パラメータが true に設定されていない限り、システムはシングルサインオンエンドポイントから提供された電子メールを信頼します。つまり、過去に DiscourseConnect が無効な状態で Discourse に既存のアカウントを持っていた場合、DiscourseConnect はそれを再利用し、新しいアカウントを作成することはありません。
DiscourseConnect を無効にした場合、ユーザーはパスワードをリセットしてアカウントにアクセスできるようになります。
実例
以下の設定を想定します。
Discourse ドメイン:http://discuss.example.com
DiscourseConnect URL:http://www.example.com/discourse/sso
DiscourseConnect シークレット:d836444a9e4084d5b224a60c208dce14
電子メール検証:なし(ペイロードに require_activation=true を追加)
ユーザーがログインを試みる
-
nonce が生成される:
cb68251eefb5211e58c00ff1395f0c0b -
raw ペイロードが生成される:
nonce=cb68251eefb5211e58c00ff1395f0c0b -
ペイロードが Base64 エンコードされる:
bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI= -
ペイロードが URL エンコードされる:
bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D -
Base64 エンコードされたペイロードに対して HMAC-SHA256 が生成される:
1ce1494f94484b6f6a092be9b15ccc1cdafb1f8460a3838fbb0e0883c4390471
最後に、ブラウザは以下にリダイレクトされます。
http://www.example.com/discourse/sso?sso=bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D&sig=1ce1494f94484b6f6a092be9b15ccc1cdafb1f8460a3838fbb0e0883c4390471
他端での処理
- ペイロードが HMAC-SHA256 を使用して検証される。sig が不一致の場合、処理は中止される。
- 上記の手順を逆順に実行して nonce を抽出する。
ユーザーがログインする。
name: sam
external_id: hello123
email: test@test.com
username: samsam
require_activation: true
署名なしペイロードが生成される。
nonce=cb68251eefb5211e58c00ff1395f0c0b&name=sam&username=samsam&email=test%40test.com&external_id=hello123&require_activation=true
順序は関係なく、値は URL エンコードされます
ペイロードが Base64 エンコードされる。
bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1zYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRlcm5hbF9pZD1oZWxsbzEyMyZyZXF1aXJlX2FjdGl2YXRpb249dHJ1ZQ==
ペイロードが URL エンコードされる。
bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1zYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRlcm5hbF9pZD1oZWxsbzEyMyZyZXF1aXJlX2FjdGl2YXRpb249dHJ1ZQ%3D%3D
Base64 エンコードされたペイロードが署名される。
3d7e5ac755a87ae3ccf90272644ed2207984db03cf020377c8b92ff51be3abc3
ブラウザは以下にリダイレクトされる。
http://discuss.example.com/session/sso_login?sso=bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1zYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRlcm5hbF9pZD1oZWxsbzEyMyZyZXF1aXJlX2FjdGl2YXRpb249dHJ1ZQ%3D%3D&sig=3d7e5ac755a87ae3ccf90272644ed2207984db03cf020377c8b92ff51be3abc3
DiscourseConnect レコードの同期
POST 管理エンドポイント /admin/users/sync_sso を使用して DiscourseConnect レコードを同期できます。DiscourseConnect エンドポイントに渡すのと同じレコードを渡せばよく、nonce は関係ありません。
他のサイトから admin/users/sync_sso を呼び出す場合は、リクエストのヘッダーに有効な管理者 api_key と api_username を含める必要があります。リクエストの構造についての詳細は、Sync DiscourseConnect user data with the sync_sso route を参照してください。
DiscourseConnect レコードの削除
DiscourseConnect プロバイダーからの external_id 値が変更された場合(生成アルゴリズムを変更した場合や、異なるエンドポイントの場合など)、Rails コンソールを使用して既存のレコードをすべて安全に削除できます。
SingleSignOnRecord.destroy_all
ユーザーのログアウト
必要に応じて、システム内の任意のユーザーをログアウトさせるために、POST 管理エンドポイント /admin/users/{USER_ID}/log_out を使用できます。
ログアウト時に Discourse がリダイレクトするエンドポイントを構成するには、「logout redirect」設定を検索してください。ここで URL が設定されていない場合、discourse connect url で設定された URL にリダイレクトされます。
external_id によるユーザー検索
ユーザープロフィールデータは /users/by-external/{EXTERNAL_ID}.json エンドポイントを使用してアクセスできます。これにより、user_id を含むユーザー情報を含む JSON ペイロードが返されます。この user_id は log_out エンドポイントで使用できます。
既存の実装
-
discourse_apigem を SSO に使用できます。基本的な実装を確認するには、examples ディレクトリ の SSO コードを参照してください。 -
WordPress プラグイン を使用すると、WordPress と Discourse 間の SSO 設定が簡単になります。設定の詳細は、プラグインのオプションページの SSO タブにあります。
今後の課題
- 他のプラットフォーム向けの SSO のリファレンス実装をさらに収集したいと考えています。実装をお持ちの場合は、Dev / SSO カテゴリー に投稿してください。
高度な機能
- フィールド名の前に
customをプレフィックスすることで、カスタムユーザーフィールドを渡すことができます。例えば、custom.user_field_1は、名前がuser_field_1のUserCustomFieldの値を設定するために使用できます。 avatar_urlを渡してユーザーのアバターを上書きできます(SiteSetting.discourse_connect_overrides_avatarを有効にする必要があります)。アバターはキャッシュされるため、URL が同じでも更新を強制するにはavatar_force_update=trueを渡してください。現在、ユーザーのアバターを無効にするために空の URL を渡すことはできません。- デフォルトでは、SSO 経由で作成されたすべての新規ユーザーにウェルカムメッセージが送信されます。これを抑制したい場合は、
suppress_welcome_message=trueを渡してください。 - Discourse インスタンスを Discourse Connect プロバイダーとして設定するには、DiscourseConnect をアイデンティティプロバイダーとして使用する を参照してください。
DiscourseConnect プロバイダーのデバッグ
DiscourseConnect のデバッグを支援するために、サイト設定 verbose_discourse_connect_logging を有効にできます。この設定を有効にすると、YOURSITE.com/logs に詳細な診断情報が表示されます。YOURSITE.com/logs の下部にある「warnings」ボックスに
することをお忘れなく。
SSO ペイロードの完全なダンプを含む警告をログに記録します。
- DiscourseConnect プロセスが開始されるたびに、DiscourseConnect ペイロードの完全なダンプを含む警告をログに記録します。
- ユーザーが DiscourseConnect の完了に失敗するたびに(nonce の期限切れや IP ブロックによる)
ユーザーのサインアップを自動化する必要がありますか?Auto-provisioning user accounts when SSO is enabled を参照してください。

