シングルサインオン
信頼できる ID プロバイダーで、セルフホスティングの Nodaro にユーザーをログインさせます。EXTERNAL_SSO_PROVIDERS の設定、アカウントのリンク規則、SSO のみの許可について説明します。
シングルサインオン(SSO)を使うと、信頼できる外部の ID プロバイダーを通じて、ユーザーがセルフホスティングの Nodaro にログインできます。各プロバイダーを EXTERNAL_SSO_PROVIDERS 変数に記述すると、ログインページに SSO ボタンが表示されます。ユーザーが最終的に使うアカウントは、環境の通常のユーザーです。SSO は、セッションを開始する方法の 1 つにすぎません。このページでは、環境を運用する側の設定を説明します。プロトコルについては、開発者向けのシングルサインオンを参照してください。
使うプロバイダーの種類
| 種類 | 使う場面 | 状態 |
|---|---|---|
assertion | 発行元が OpenID Connect や SAML に対応していなくても、有効期間の短い JWT に署名できる場合。たとえば、Nodaro を埋め込むアプリです。Nodaro はトークンを検証し、通常のセッションと交換します。 | 一連の流れがすべて動作します。 |
oidc | ID プロバイダーが OpenID Connect に対応していて、環境の Supabase Auth で設定されている場合。ブラウザーは通常の Supabase のログインリダイレクトを行い、Supabase が結果を検証します。 | プロバイダーの id は、keycloak や azure など、Supabase Auth での OAuth プロバイダーの名前と同じにする必要があります。 |
saml | — | エントリーは検証を通過しますが、ログインページが SAML のドメインを送信できないため、誰もログインさせられません。assertion か oidc を使ってください。 |
SSO は、デフォルトでは無効です。EXTERNAL_SSO_PROVIDERS が未設定の場合、ログインページに SSO ボタンは表示されず、GET /v1/sso/providers は空のリストを返し、すべてのプロバイダーのルートが 404 unknown_provider を返します。
プロバイダーを設定する
EXTERNAL_SSO_PROVIDERS に、JSON 配列を設定します。値を直接書くか、@/path/to/providers.json の形式でファイルから読み込みます。
[
{
"id": "librechat",
"label": "LibreChat",
"kind": "assertion",
"secret": "a-dedicated-32+char-hmac-key-not-the-idp-session-secret",
"audience": "nodaro",
"claimMap": { "email": "email", "emailVerified": "email_verified", "subject": "sub" },
"initiateUrl": "https://chat.example.com/oauth/nodaro",
"maxLifetimeSeconds": 300
},
{
"id": "keycloak",
"label": "Acme (Keycloak)",
"kind": "oidc",
"supabaseProvider": "keycloak"
}
]値の形式が正しくないと、起動が停止します。シークレットを含むエントリーに入力ミスがあっても、プロバイダーが黙って外されたり(SSO が 404 で失敗しているように見えます)、ログインが中途半端に設定されたりすることがあってはならないためです。
Compose スタックでは、EXTERNAL_SSO_PROVIDERS と EXTERNAL_SSO_LINK_EXISTING を、nodaro サービスの environment: に追加してください。Compose ファイルは、.env のこれらの変数をコンテナに渡さないためです。ファイルを使う場合は、ファイルをコンテナにマウントし、コンテナ内でのパスを指定します。
プロバイダーのフィールド
| フィールド | 対象 | 内容 |
|---|---|---|
id | すべて | URL で使える名前です。先頭は英小文字か数字で、以降は英小文字、数字、_、- を使えます。全体で 1〜63 文字で、ドットは使えません。ルート /v1/sso/<id> に使われ、ユーザーに保存されます。一意である必要があります。 |
label | すべて | プロバイダーの名前です。ログインボタンに表示されるのは、プロバイダーが 2 つ以上あり、サーフェスプロファイルで auth.ssoLabel が設定されていない場合だけです。 |
kind | すべて | assertion、oidc、saml のいずれかです。 |
secret | assertion(必須) | アサーションの署名を検証する HS256 のキーで、16 文字以上です。ID プロバイダー自身のセッションシークレットは使わず、専用のキーを使ってください。そうすれば、Nodaro 側でキーが漏れても、プロバイダーのセッションを偽造されることはありません。 |
audience | assertion(必須) | アサーションの aud クレームと一致する必要がある値です。 |
claimMap | assertion | メールアドレス、確認済みフラグ、サブジェクトを、それぞれどのクレームで受け取るかを指定します。デフォルトは { "email": "email", "emailVerified": "email_verified", "subject": "sub" } です。 |
initiateUrl | assertion | 絶対 URL です。ユーザーがログインボタンをクリックすると、Nodaro はこの URL にリダイレクトします。プロバイダーはユーザーをログインさせ、アサーションを持って戻ってきます。このフィールドがない場合、クリックすると 400 no_assertion が返ります。 |
initiateUrlByHost | assertion | ポートを含まない小文字のホスト名から、絶対 URL への対応表です。そのホストでクリックされた場合は、initiateUrl の代わりに対応する URL に移動します。複数のホスト名で応答する環境向けです。キーが正しくないと起動が停止し、この対応表が公開されることはありません。 |
maxLifetimeSeconds | assertion | アサーションに許される最長の有効期間(exp から iat を引いた値)です。デフォルトは 300(5 分)で、最大は 3600 です。 |
supabaseProvider | oidc | 検証はされますが、使われません。ログインページは、プロバイダーの id を Supabase に渡します。 |
domain | oidc、saml | 検証はされますが、使われません。oidc と saml のエントリーには、domain か supabaseProvider が必要です。 |
公開されるのは id、label、kind だけで、GET /v1/sso/providers を通じて公開されます。シークレットがサーバーの外に出ることはありません。
Nodaro がアサーションで確認すること
assertion プロバイダーの ID サーバーは JWT に署名し、ブラウザーを GET /v1/sso/<id>?assertion=<jwt> に送ります。Nodaro は、次の条件をすべて満たす場合にだけ、このアサーションを受け入れます。
- HS256 とプロバイダーの
secretで署名されていること。 audが、プロバイダーのaudienceと一致すること。expがあり、期限が過ぎていないこと(許容誤差は 5 秒)。また、有効期間がmaxLifetimeSeconds以内であること。jtiがあり、一度も使われていないこと。各アサーションは 1 回しか使えません。- メールアドレスのクレームがあること。アカウントを作成またはリンクするには、確認済みを示すクレームが
trueである必要があります。
確認に失敗すると、401 が返ります。このルートは、1 つの IP アドレスから 60 秒あたり 20 回までのリクエストを受け付け、それを超えると 429 を返します。仕様の全体、エラーコード、リダイレクトの流れは、開発者向けのシングルサインオンにあります。
アカウントのリンク方法
アサーションが確認を通過すると、Nodaro は該当するユーザーを探すか、新しく作成します。次のルールにより、メールアドレスが同じというだけで、アサーションが既存のアカウントを乗っ取ることはできません。
| 状況 | 結果 |
|---|---|
| そのメールアドレスのアカウントがなく、メールアドレスが確認済み | 新しいユーザーが作成され、プロバイダーにリンクされます。 |
| そのメールアドレスのアカウントがなく、メールアドレスが未確認 | 拒否されます:403 email_unverified。 |
| アカウントがすでにこのプロバイダーにリンクされている | ログインします。 |
| アカウントが別のプロバイダーにリンクされている | EXTERNAL_SSO_LINK_EXISTING=true の場合でも拒否されます:403 account_linked_other_provider。 |
SSO を使わないローカルアカウントがあり、EXTERNAL_SSO_LINK_EXISTING=true で、メールアドレスが確認済み | アカウントがプロバイダーにリンクされ、ログインします。 |
SSO を使わないローカルアカウントがあり、フラグが false か、メールアドレスが未確認 | 拒否されます:403 account_exists。 |
EXTERNAL_SSO_LINK_EXISTING のデフォルトは false で、乗っ取りに対して安全です。有効になるのは true か 1 の場合だけです(大文字と小文字は区別しません)。有効にするのは、プロバイダーの「メールアドレス確認済み」のクレームを、既存のアカウントに結び付けてよいと言えるほど信頼できる場合だけにしてください。
account_exists と email_unverified は、どのアカウントでも同じ文言を使います。そのため、ログインフォームを使って、どのアドレスが存在するかを探ることはできません。どのアカウントが対象になったかは、サーバーのログに記録されます。
ログインボタン
プロバイダーが 1 つ以上設定されていると、ログインページに SSO ボタンが表示されます。
- プロバイダーが 1 つの場合:ボタンにはシングルサインオン、またはサーフェスプロファイルの
auth.ssoLabelが表示されます。 - プロバイダーが複数の場合:サーフェスプロファイルで
auth.ssoLabelが設定されていなければ、各ボタンにはそのプロバイダーのlabelが表示されます。
ログイン後、ユーザーは /projects に移動します。プロバイダーは、リダイレクトで戻すときに &next=<path> を付けて、別のページを指定できます。Nodaro がこの指定に従うのは、同じオリジン上の相対パスで、/ で始まり、// で始まらない場合だけです。それ以外の場合は /projects に移動します。CORS_ORIGIN に列挙されたホストでは、ログイン後のリダイレクトは相対パスのままで、ユーザーがアクセスしてきたホストにとどまります。
SSO によるログインだけを許可する
Business エディションでは、サーフェスプロファイルで、ほかのログイン方法を無効にできます。
NODARO_SURFACE_PROFILE={"auth":{"methods":["sso"],"ssoLabel":"Sign in with Acme"}}リストに sso だけを指定すると、サーバー側のルールも有効になります。ログインするすべてのアカウントは、SSO を通じて作成またはリンクされている必要があります。それ以外のセッションは、最初の API 呼び出しで 403 sso_required によって拒否されます。そのため、ログインサービスに直接登録したアカウントは、この環境を使えません。リストに email を加えると、このルールは無効になります。エディションとサーフェスプロファイルを参照してください。
セキュリティのまとめ
- 専用のシークレット:プロバイダーごとに、プロバイダー自身のセッションシークレットとは別のシークレットを使います。
- 1 回限りのアサーション:有効期間は短く、サーバー側で強制されます。
- 同一オリジンへのリダイレクトのみ:ログイン後のリダイレクト先は、同じオリジンに限られます。
- シークレットはサーバーの外に出ません。アサーションとワンタイムトークンは、リクエストログから削除されます。
- レート制限:交換用のルートには、IP アドレスごとのレート制限があります。
- 通常のセッション:交換のあと、ユーザーは特別な認証情報を持たない通常のユーザーになります。
よくある質問
関連ページ
外部ログイン(SSO)
エディションとサーフェスプロファイル
設定
最初のユーザーと管理者
最終更新