シングルサインオン
シングルサインオンを使うと、外部の 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 サービス側にあり、単回使用で短命です。
ログインが検証するもの
「ログインが検証するもの」というセクション検証済みのネイティブとサービスの内部では、次の順序で検証します:
- アサーションの署名(常に必須。レスポンスエンベロープ側は設定により 任意)— 検証されたペイロードが検証済みの要素に束縛されるため、構造的に 署名ラッピングに耐えます,
- 発行者が設定済みの IdP と一致すること,
- オーディエンスがこのサービスプロバイダーを名指ししていること,
- 送信先が自社の ACS URL であること,
InResponseToが、こちらが発行した保留中の AuthnRequest と一致すること (未要求の、IdP 開始によるレスポンスはデフォルトで無効),- 有効期間のウィンドウがクロックスキュー(デフォルト 90 秒)の範囲内に 収まっていること,
- そのアサーション 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 のチャネルになることはありません。リレー経由でしか サーバーに到達できないスマートフォンは、サーバーに対するブラウザの往復を 完了できません。