單一登入
單一登入讓外部身分識別供應商(IdP)決定誰可以登入你的 Control Center 伺服器。系統支援兩種協定——SAML 2.0 與 OpenID Connect——而兩者最終都 匯入同一個地方:一份全新簽發的裝置憑證,它在所有下游環節都與邀請或 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 設定檔驗證——委由固定版本的純 Rust saml crate 處理,位於無狀態 C ABI 之後 |
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 起始的回應預設為關閉),- 有效期間窗期落在時鐘偏差範圍內(預設 90s),以及
- 判斷提示 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 通道;只能透過該轉送連上伺服器的手機,無法 對它完成瀏覽器往返。