mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
All 181 docs markdown files translated into 20 languages (apps/docs/<locale>/**). Companion to the i18n code PR; admin-merged because the file count exceeds GitHub's per-PR CI trigger limit. Validated by pnpm i18n:check (all surfaces, 0 stale/missing) and a clean all-locale docs build.
225 lines
12 KiB
Markdown
225 lines
12 KiB
Markdown
---
|
||
description: "為 SnapOtter 設定 SAML 2.0 單一登入。針對 Okta、Azure AD / Entra ID、Google Workspace 及其他 SAML 身分供應商的逐步指南。"
|
||
i18n_source_hash: 33dfb8b02a22
|
||
i18n_provenance: human
|
||
i18n_output_hash: 8816672cd13a
|
||
---
|
||
|
||
# SAML SSO {#saml-sso}
|
||
|
||
SnapOtter 支援 SAML 2.0 進行單一登入。使用者可以透過外部身分供應商(Okta、Azure AD / Entra ID、Google Workspace,或任何標準 SAML 2.0 IdP)登入,取代本機使用者名稱/密碼驗證。
|
||
|
||
::: tip 企業版功能
|
||
SAML SSO 需要具備 `saml_sso` 功能的 **team** 或 **enterprise** 授權。若設定了 `SAML_ENABLED=true` 卻沒有有效授權,SAML 路由會被靜默略過並記錄一則警告。
|
||
:::
|
||
|
||
## 先決條件 {#prerequisites}
|
||
|
||
- 一個可透過公開 URL 存取的執行中 SnapOtter 執行個體
|
||
- 將 `EXTERNAL_URL` 設為該公開 URL(例如 `https://photos.example.com`)
|
||
- 一個具備 `saml_sso` 功能的 team 或 enterprise 授權金鑰
|
||
- 你 SAML 身分供應商的管理員存取權
|
||
|
||
## 快速開始 {#quick-start}
|
||
|
||
將這些環境變數加入你的 `docker-compose.yml`:
|
||
|
||
```yaml
|
||
services:
|
||
snapotter:
|
||
image: snapotter/snapotter:latest
|
||
environment:
|
||
EXTERNAL_URL: "https://photos.example.com"
|
||
SNAPOTTER_LICENSE_KEY: "your-license-key"
|
||
SAML_ENABLED: "true"
|
||
SAML_IDP_SSO_URL: "https://idp.example.com/sso/saml"
|
||
SAML_IDP_CERTIFICATE: |
|
||
MIICpDCCAYwCCQDU+pQ4pHgSpDANBgkqhkiG9w0BAQsFADAUMRIw
|
||
...your IdP's signing certificate in PEM format...
|
||
EAYHKoZIzj0CAQYFK4EEACIDYgAE
|
||
```
|
||
|
||
重新啟動容器。登入頁面會出現「使用 SAML 登入」按鈕(或由 `SAML_PROVIDER_NAME` 設定的標籤)。
|
||
|
||
## 設定參考 {#configuration-reference}
|
||
|
||
| 變數 | 預設值 | 說明 |
|
||
|---|---|---|
|
||
| `SAML_ENABLED` | `false` | 啟用 SAML 登入。 |
|
||
| `SAML_IDP_SSO_URL` | | IdP 的 SSO 端點 URL。啟用 SAML 時為**必填**。 |
|
||
| `SAML_IDP_CERTIFICATE` | | IdP 的 PEM 格式 X.509 簽章憑證(憑證文字本身,而非檔案路徑)。啟用 SAML 時為**必填**。 |
|
||
| `EXTERNAL_URL` | | SnapOtter 可被存取的公開 URL。啟用 SAML 時為**必填**。 |
|
||
| `SAML_ENTITY_ID` | `${EXTERNAL_URL}/api/auth/saml/metadata` | 送往 IdP 的 SP Entity ID / Audience URI。 |
|
||
| `SAML_CALLBACK_URL` | `${EXTERNAL_URL}/api/auth/saml/callback` | Assertion Consumer Service(ACS)URL。 |
|
||
| `SAML_AUTO_CREATE_USERS` | `true` | 在首次 SAML 登入時自動建立本機使用者帳號。 |
|
||
| `SAML_AUTO_LINK_USERS` | `false` | 若電子郵件地址相符,將 SAML 身分連結到現有的本機使用者。 |
|
||
| `SAML_DEFAULT_ROLE` | `user` | 指派給自動建立的 SAML 使用者的角色。可為 `admin`、`editor` 或 `user` 其中之一。 |
|
||
| `SAML_PROVIDER_NAME` | | 前端 SAML 登入按鈕的顯示標籤(例如「Okta」、「Azure AD」)。若留空,按鈕會顯示「SAML」。 |
|
||
| `SAML_USERNAME_ATTRIBUTE` | | 用作使用者名稱的 SAML 斷言屬性。若留空,會回退到電子郵件的本機部分,再回退到 NameID。 |
|
||
| `SAML_EMAIL_ATTRIBUTE` | `email` | 用作使用者電子郵件地址的 SAML 斷言屬性。 |
|
||
|
||
若 `SAML_ENABLED=true` 且缺少三個必填變數(`SAML_IDP_SSO_URL`、`SAML_IDP_CERTIFICATE`、`EXTERNAL_URL`)中的任何一個,伺服器會拒絕啟動。
|
||
|
||
::: details 安全性注意事項
|
||
`wantAuthnResponseSigned` 與 `wantAssertionsSigned` 兩者都硬式編碼為 `true`。SnapOtter 會拒絕未簽章或簽章不正確的 SAML 回應。來自受信任 IdP 的斷言會被視為電子郵件已驗證。
|
||
|
||
僅支援 SP 發起的登入。SnapOtter 不支援 IdP 發起(未經請求)的登入或單一登出(SLO)。登出 SnapOtter 不會將使用者從 IdP 登出。
|
||
:::
|
||
|
||
## SP 中繼資料與 URL {#sp-metadata-and-urls}
|
||
|
||
你的 IdP 需要來自 SnapOtter 的三個值:
|
||
|
||
| 欄位 | 值 |
|
||
|---|---|
|
||
| **ACS URL**(Assertion Consumer Service) | `${EXTERNAL_URL}/api/auth/saml/callback` |
|
||
| **Entity ID** / **Audience URI** | `${EXTERNAL_URL}/api/auth/saml/metadata` |
|
||
| **SP Metadata**(XML) | `GET ${EXTERNAL_URL}/api/auth/saml/metadata` |
|
||
|
||
舉例來說,如果 `EXTERNAL_URL` 是 `https://photos.example.com`:
|
||
|
||
- ACS URL:`https://photos.example.com/api/auth/saml/callback`
|
||
- Entity ID:`https://photos.example.com/api/auth/saml/metadata`
|
||
- 中繼資料端點:`https://photos.example.com/api/auth/saml/metadata`(回傳 XML)
|
||
|
||
某些 IdP 可以直接匯入 SP 中繼資料 URL,這會自動填入 ACS URL 與 Entity ID。
|
||
|
||
## 供應商設定 {#provider-setup}
|
||
|
||
### Okta {#okta}
|
||
|
||
1. 在 Okta 管理主控台中,前往 **Applications > Create App Integration**。
|
||
2. 選擇 **SAML 2.0** 並點按 **Next**。
|
||
3. 設定名稱(例如「SnapOtter」)並點按 **Next**。
|
||
4. 設定 SAML 設定:
|
||
- **Single sign-on URL**:你的 ACS URL(例如 `https://photos.example.com/api/auth/saml/callback`)
|
||
- **Audience URI (SP Entity ID)**:你的 Entity ID(例如 `https://photos.example.com/api/auth/saml/metadata`)
|
||
- **Name ID format**:EmailAddress
|
||
- **Application username**:Email
|
||
5. 在 **Attribute Statements** 下,新增對應到 `user.email` 的 `email`。
|
||
6. 點按 **Next**,再點按 **Finish**。
|
||
7. 前往 **Sign On** 分頁,點按 **View SAML setup instructions**,並複製:
|
||
- 將 **Identity Provider Single Sign-On URL** 填入 `SAML_IDP_SSO_URL`
|
||
- 將 **X.509 Certificate** 填入 `SAML_IDP_CERTIFICATE`
|
||
|
||
### Azure AD / Entra ID {#azure-ad-entra-id}
|
||
|
||
1. 在 Azure 入口網站中,前往 **Microsoft Entra ID > Enterprise applications > New application**。
|
||
2. 點按 **Create your own application**,命名為「SnapOtter」,並選擇 **Integrate any other application you don't find in the gallery**。
|
||
3. 前往 **Single sign-on > SAML** 並在 **Basic SAML Configuration** 區段點按 **Edit**:
|
||
- **Identifier (Entity ID)**:你的 Entity ID(例如 `https://photos.example.com/api/auth/saml/metadata`)
|
||
- **Reply URL (ACS URL)**:你的 ACS URL(例如 `https://photos.example.com/api/auth/saml/callback`)
|
||
4. 在 **SAML Certificates** 下,下載 **Certificate (Base64)**。
|
||
5. 在 **Set up SnapOtter** 下,複製 **Login URL**。
|
||
6. 將 `SAML_IDP_SSO_URL` 設為 Login URL,並將 `SAML_IDP_CERTIFICATE` 設為下載的憑證內容。
|
||
7. 在 **Users and groups** 下將使用者或群組指派給此應用程式。
|
||
|
||
### Google Workspace {#google-workspace}
|
||
|
||
1. 在 Google 管理主控台中,前往 **Apps > Web and mobile apps > Add app > Add custom SAML app**。
|
||
2. 將應用程式命名為「SnapOtter」並點按 **Continue**。
|
||
3. 在 **Google Identity Provider details** 頁面,複製 **SSO URL** 並下載 **Certificate**。點按 **Continue**。
|
||
4. 設定 Service Provider 詳細資訊:
|
||
- **ACS URL**:你的 ACS URL(例如 `https://photos.example.com/api/auth/saml/callback`)
|
||
- **Entity ID**:你的 Entity ID(例如 `https://photos.example.com/api/auth/saml/metadata`)
|
||
- **Name ID format**:EMAIL
|
||
- **Name ID**:Basic Information > Primary email
|
||
5. 點按 **Continue**,再點按 **Finish**。
|
||
6. 為你的組織單位將應用程式**開啟(ON)**。
|
||
7. 將 `SAML_IDP_SSO_URL` 設為步驟 3 的 SSO URL,並將 `SAML_IDP_CERTIFICATE` 設為下載的憑證內容。
|
||
|
||
### 通用 SAML 2.0 IdP {#generic-saml-2-0-idp}
|
||
|
||
對於任何符合 SAML 2.0 規範的身分供應商:
|
||
|
||
1. 在你的 IdP 中建立新的 SAML 應用程式/服務供應商。
|
||
2. 將 **ACS URL** 設為 `${EXTERNAL_URL}/api/auth/saml/callback`。
|
||
3. 將 **Entity ID** / **Audience** 設為 `${EXTERNAL_URL}/api/auth/saml/metadata`。
|
||
4. 設定 IdP 在名為 `email` 的屬性中送出使用者的電子郵件(或設定 `SAML_EMAIL_ATTRIBUTE` 以符合你 IdP 的屬性名稱)。
|
||
5. 將 **IdP SSO URL** 與 **signing certificate** 複製到 `SAML_IDP_SSO_URL` 與 `SAML_IDP_CERTIFICATE`。
|
||
|
||
## 使用者佈建 {#user-provisioning}
|
||
|
||
### 自動建立 {#auto-create}
|
||
|
||
當 `SAML_AUTO_CREATE_USERS` 為 `true`(預設)時,會在有人首次透過 SAML 登入時建立本機使用者帳號。角色會設為 `SAML_DEFAULT_ROLE`。
|
||
|
||
使用者名稱按以下順序衍生:
|
||
|
||
1. 由 `SAML_USERNAME_ATTRIBUTE` 指定的斷言屬性值(若已設定且存在)
|
||
2. 電子郵件地址的本機部分(`@` 之前的所有內容)
|
||
3. SAML NameID
|
||
|
||
若發生使用者名稱衝突,會附加一個數字後綴(例如 `jane` 會變成 `jane_2`)。
|
||
|
||
### 自動連結 {#auto-link}
|
||
|
||
當 `SAML_AUTO_LINK_USERS` 為 `true` 時,若電子郵件地址相符,SnapOtter 會將 SAML 身分連結到現有的本機帳號。當你已預先建立使用者帳號,並希望他們在不遺失資料的情況下開始使用 SSO 時,這很有用。
|
||
|
||
::: warning
|
||
只有在你信任你的 SAML IdP 會驗證電子郵件地址時,才啟用自動連結。來自設定錯誤的 IdP 的未經驗證電子郵件,可能讓某人接管其他使用者的帳號。
|
||
:::
|
||
|
||
### 屬性對應 {#attribute-mapping}
|
||
|
||
| SnapOtter 欄位 | 來源 | 設定 |
|
||
|---|---|---|
|
||
| Email | 斷言屬性 | `SAML_EMAIL_ATTRIBUTE`(預設:`email`) |
|
||
| Username | 斷言屬性、電子郵件或 NameID | `SAML_USERNAME_ATTRIBUTE`(請參閱上方的衍生順序) |
|
||
| External ID | NameID | 一律為 SAML NameID,不可設定 |
|
||
|
||
## SSO 強制 {#sso-enforcement}
|
||
|
||
如果你想要求所有使用者透過 SAML(或 OIDC)登入,並封鎖本機密碼登入,請啟用 SSO 強制:
|
||
|
||
1. 確保 `sso_enforcement` 企業版功能已獲授權(team 與 enterprise 方案提供)。
|
||
2. 在 **Admin Settings > Security** 中,開啟 **SSO Enforcement**。
|
||
3. 設定一個 **break-glass 使用者名稱**:這是唯一在 IdP 無法連線時仍可用密碼登入以進行緊急存取的本機帳號。
|
||
|
||
當 SSO 強制啟用時,任何本機登入嘗試(break-glass 使用者除外)都會回傳 403 錯誤,訊息為「Local password login is disabled. Please use SSO.」
|
||
|
||
::: tip
|
||
在啟用 SSO 強制之前,一律要先設定 break-glass 使用者名稱。若沒有它,當你的 IdP 停機時,你可能會被鎖在 SnapOtter 之外。
|
||
:::
|
||
|
||
## 將 SAML 與 OIDC 並用 {#using-saml-alongside-oidc}
|
||
|
||
SAML 與 OIDC 可以同時啟用。當兩者都啟用時,登入頁面會為每個供應商顯示各自的按鈕(由 `SAML_PROVIDER_NAME` 與 `OIDC_PROVIDER_NAME` 標示)。使用者可以用任一方法登入。
|
||
|
||
兩個供應商各自獨立共用相同的自動建立、自動連結與 SSO 強制設定:每個都有自己的 `*_AUTO_CREATE_USERS`、`*_AUTO_LINK_USERS` 與 `*_DEFAULT_ROLE` 變數。
|
||
|
||
## 疑難排解 {#troubleshooting}
|
||
|
||
### 斷言驗證失敗 {#assertion-validation-failed}
|
||
|
||
SAML 回應簽章或斷言簽章無法驗證。請檢查:
|
||
|
||
- `SAML_IDP_CERTIFICATE` 中的憑證與你 IdP 中目前的簽章憑證相符(憑證會輪替,所以請檢查是否過期)
|
||
- 憑證為 PEM 格式(以 `-----BEGIN CERTIFICATE-----` 開頭)
|
||
- 憑證為完整文字,而非檔案路徑
|
||
- 你 IdP 中設定的 ACS URL 與 Entity ID 與 SnapOtter 的值完全相符(配置方式、主機、連接埠、路徑)
|
||
|
||
### 缺少屬性 {#missing-attributes}
|
||
|
||
如果登入後使用者名稱或電子郵件為空,你的 IdP 可能沒有送出預期的屬性。請檢查:
|
||
|
||
- 你的 IdP 已設定為釋出 `email` 屬性(或 `SAML_EMAIL_ATTRIBUTE` 所設定的任何值)
|
||
- 若使用 `SAML_USERNAME_ATTRIBUTE`,請驗證該屬性已包含在斷言中
|
||
- 某些 IdP 需要明確的屬性對應設定,才會釋出宣告
|
||
|
||
### 時鐘偏差 {#clock-skew}
|
||
|
||
SAML 斷言包含時間戳記條件(`NotBefore`、`NotOnOrAfter`)。如果你的伺服器時鐘與 IdP 時鐘不同步,斷言驗證會失敗。在兩台機器上都執行 NTP 以保持時鐘一致。
|
||
|
||
### 「SAML is enabled via env but saml_sso enterprise feature is not licensed」 {#saml-is-enabled-via-env-but-saml-sso-enterprise-feature-is-not-licensed}
|
||
|
||
當 `SAML_ENABLED=true` 但授權不包含 `saml_sso` 功能時,此警告會出現在伺服器記錄中。請驗證你的授權金鑰與方案。`saml_sso` 功能在 team 與 enterprise 方案提供。
|
||
|
||
### 登入重新導向後帶著錯誤返回 {#login-redirects-back-with-error}
|
||
|
||
如果點按 SAML 登入按鈕後帶著錯誤重新導向回登入頁面,請檢查伺服器記錄以取得詳細資訊。常見原因:
|
||
|
||
- 從伺服器無法連線到 IdP SSO URL
|
||
- IdP 拒絕了驗證請求(請檢查 IdP 的稽核記錄)
|
||
- IdP 回傳了未簽章的回應(SnapOtter 要求回應與斷言兩者都必須簽章)
|