コンテンツにスキップ

シングルサインオン

シングルサインオンを使うと、外部の ID プロバイダー(IdP)が、あなたの Control Center サーバーに誰がサインインできるかを決めます。サポートされる プロトコルは SAML 2.0 と OpenID Connect の 2 つで、どちらも同じ場所に着地 します。すなわち、以降のすべての経路で、招待や QR ペアリングと区別がつかない できたてのデバイス認証情報です。

SAML ログインはブラウザ経由です。IdP があなたのサーバーへの接続を開く ことは決してなく、すべてを運ぶのはユーザーのブラウザです:

browser ──> /saml/login ──> IdP ──POST SAMLResponse──> /saml/acs
│ verify
▼
provision user (maybe)
│
▼
browser handoff: device PSK in the URL
fragment (never a query parameter)

運び手がブラウザであるため、ログインではNAT の背後にあるサーバーでも問題 なく動作します。localhost、LAN、Tailscale、VPN、トンネルのいずれでも かまいません。必要なのは、ユーザーのブラウザが ACS URL に到達できることだけ です。スマートフォンのリモートコントロールが使う WebSocket リレーは HTTP の 往復を運べないため、SSO のエントリーポイントは常にサーバーの HTTP オリジンを 対象にします。

OpenID Connect も同じ流れをたどり、認可コード + PKCE の往復を使います。 クレームは発行者のトークンエンドポイントから TLS 経由で読み取られるため、 ブラウザが身元データを渡すことは一切ありません。

Control Center は責任を意図的に分割しています:

構成要素 場所 担当
cc_saml ネイティブ packages/cc_natives/native/saml/ XML-DSig の検証、正規化、SAML プロファイルの検証 — ステートレスな C ABI の背後にある、固定バージョンの純 Rust saml クレートに委譲されます
SamlService / OidcService cc_server_core ログイン状態(保留中のリクエストトラッカー、リプレイの重複排除)、検証ポリシーの設定項目、共有のプロビジョニング呼び出し
SsoProvisioner cc_server_core ユーザーの検索または作成、プロバイダーのサブジェクトの固定、自動メンバーポリシーに基づくメンバーシップの付与
ScimService cc_server_core SCIM 2.0 のプッシュサーフェス(プロビジョニング/プロビジョニング解除のライフサイクル)
SsoSettingsService cc_server_core 保存済みの接続行、ライブでの再適用、SCIM トークン

意図的に、純 Dart の SAML 暗号フォールバックは存在しません。手書きの XML-DSig や正規化は、まさに署名ラッピング脆弱性が潜む場所だからです。そのため cc_saml ライブラリが見つからない場合、SAML ログインとサーバー起動の両方が 拒否されます。ネイティブは設計上ステートレスです。リクエストをまたぐ状態は Dart サービス側にあり、単回使用で短命です。

検証済みのネイティブとサービスの内部では、次の順序で検証します:

  1. アサーションの署名(常に必須。レスポンスエンベロープ側は設定により 任意)— 検証されたペイロードが検証済みの要素に束縛されるため、構造的に 署名ラッピングに耐えます,
  2. 発行者が設定済みの IdP と一致すること,
  3. オーディエンスがこのサービスプロバイダーを名指ししていること,
  4. 送信先が自社の ACS URL であること,
  5. InResponseTo が、こちらが発行した保留中の AuthnRequest と一致すること (未要求の、IdP 開始によるレスポンスはデフォルトで無効),
  6. 有効期間のウィンドウがクロックスキュー(デフォルト 90 秒)の範囲内に 収まっていること,
  7. そのアサーション ID がこれまでに見たことがないこと(リプレイ キャッシュ)。

失敗は型付きのコード — signature、expired、audience、destination、issuer、 request-match — として返され、エンドポイントは汎用エラーで応答します。その ため攻撃者は、偽造がなぜ失敗したのかを何も学べません。

最初の SSO ログイン時に、プロバイダーの不変のサブジェクト(issuer と、 SAML の NameID / OIDC の sub、または SCIM の externalId)がアカウントに 固定されます。以降は、メールアドレスではなくサブジェクトがログインを識別 します。後になって、同じ、あるいは別の IdP から同じメールアドレスを提示する 人が現れても、固定済みのアカウントはその人を拒否します。プロバイダーで再利用 されたメールアドレスがアカウント乗っ取りに変わるのを、これが食い止めます。

JIT プロビジョニングはログイン時に行われます(SsoProvisioner)。SCIM は 別個のプッシュサーフェス(ScimService)で、ユーザー行に同じサブジェクトを 固定しますが、ワークスペースのメンバーシップは付与しません。プロビジョニング 解除 — SCIM の active: false または DELETE — は、そのアカウントが ワークスペースのオーナーである場合に拒否します(先に所有権を譲渡して ください)。それ以外の場合は、まずすべてのデバイス認証情報を取り消し (デバイス監視によりセッションは数秒以内に切れます)、すべてのワークスペース メンバーシップを削除し、アカウントを無効化済みとして記録します。ユーザー行は 残ります。帰属は永続的です。再有効化はログインを復元しますが、メンバーシップ を再付与することは決してありません。

  • グループ→ロールのマッピングはログイン時に行われます — SAML 属性 / OIDC クレームからです。プッシュされた SCIM グループは保存されません。
  • オーナーロールは SSO からは到達できません — デフォルトロールとしても、 グループマッピングを通じても同様です。
  • WebSocket リレーが SSO のチャネルになることはありません。リレー経由でしか サーバーに到達できないスマートフォンは、サーバーに対するブラウザの往復を 完了できません。