> For the complete documentation index, see [llms.txt](https://docs.maiagent.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maiagent.ai/org/sso/sso-saml/sso-saml-adfs.md).

# ADFS 整合

ADFS（Active Directory Federation Services）是微軟提供的企業身分聯合服務。已建置 Windows Active Directory 與 ADFS 的企業，可以讓員工直接使用 AD 帳號密碼登入 MaiAgent，不需另外建立帳號。

**適用對象**：已建置 ADFS（Windows Server 2016 / 2019 / 2022）的企業

**開始前請準備**：

* MaiAgent 組織管理員權限（用於設定認證來源）
* ADFS 伺服器的管理員權限（用於建立信賴憑證者信任與宣告規則）
* 一個 AD 帳號作為測試用，且該帳號的 **`mail` 屬性必須有值**

{% hint style="info" %}
整合的基本概念是「雙向交換資訊」：把 ADFS 的資訊（Metadata URL）填入 MaiAgent，再把 MaiAgent 的資訊（SP Metadata URL）匯入 ADFS。SAML 的通用設定說明請先參閱 [SAML 整合](/org/sso/sso-saml.md)。
{% endhint %}

## 一、在 MaiAgent 後台建立認證來源 <a href="#create-auth-source" id="create-auth-source"></a>

進入 <mark style="color:blue;">組織管理</mark> > <mark style="color:blue;">認證來源</mark>，選擇 <mark style="color:blue;">SAML</mark> 認證類型：

| 欄位               | 填入值                                                                   |
| ---------------- | --------------------------------------------------------------------- |
| 認證來源名稱           | 例如 `my-company`（小寫英數與連字號，用於產生 SP 端點 URL）                              |
| IdP Metadata URL | `https://{ADFS 網域}/FederationMetadata/2007-06/FederationMetadata.xml` |
| Email Domain     | （選填）企業 Email 網域，例如 `company.com`                                      |

ADFS 的 Federation Metadata URL 是固定路徑，只要把 `{ADFS 網域}` 換成企業的 ADFS 主機網域即可。儲存時 MaiAgent 會即時抓取並解析這份中繼資料，自動帶入 Entity ID、SSO URL 與簽章憑證。

{% hint style="warning" %}
儲存時 MaiAgent 伺服器必須能連線到這個 Metadata URL。如果 ADFS 只開放內部網路存取、MaiAgent 無法連線，請改用手動模式：分別填入 IdP Entity ID（通常是 `http://{ADFS 網域}/adfs/services/trust`）、IdP SSO URL（通常是 `https://{ADFS 網域}/adfs/ls/`），以及從 ADFS 管理主控台匯出的 **Token-Signing 憑證**（Base64 格式）。
{% endhint %}

## 二、取得 MaiAgent 的 SP 資訊 <a href="#get-sp-info" id="get-sp-info"></a>

儲存後，設定頁面會顯示 SP（Service Provider，即 MaiAgent 端）資訊：

| SP 欄位                       | 格式                                            |
| --------------------------- | --------------------------------------------- |
| Entity ID / SP Metadata URL | `https://{平台網域}/accounts/saml/{名稱}/metadata/` |
| ACS URL                     | `https://{平台網域}/accounts/saml/{名稱}/acs/`      |

用瀏覽器開啟 SP Metadata URL，確認會顯示一份 XML（以 `<EntityDescriptor>` 開頭），代表認證來源已生效。這個 URL 就是下一步要交給 ADFS 管理員的資料。

{% hint style="warning" %}
私有化部署若使用非標準連接埠（例如 `https://平台網域:8000`），請檢查 XML 中 `AssertionConsumerService` 的 `Location` 是否**包含連接埠**。如果 entityID 有連接埠、ACS URL 卻沒有，代表反向代理（Nginx）把 Host 標頭的連接埠去掉了——請將 Nginx 設定中的 `proxy_set_header Host $host;` 改為 `proxy_set_header Host $http_host;` 後重新載入 Nginx（注意每一個 location 區塊都要改），再重新整理確認。
{% endhint %}

## 三、在 ADFS 建立信賴憑證者信任 <a href="#create-relying-party-trust" id="create-relying-party-trust"></a>

以下由 ADFS 管理員在 ADFS 伺服器上操作：

1. 開啟 <mark style="color:blue;">Server Manager</mark> > <mark style="color:blue;">Tools</mark> > <mark style="color:blue;">AD FS Management</mark>
2. 左側選擇 <mark style="color:blue;">Relying Party Trusts</mark>，右側點擊 <mark style="color:blue;">Add Relying Party Trust...</mark>
3. 選擇 <mark style="color:blue;">Claims aware</mark> > Start
4. 在 **Select Data Source** 頁面選擇匯入方式（見下方說明）
5. **Display name** 填入 `MaiAgent`
6. **Access Control Policy** 先選擇 <mark style="color:blue;">Permit everyone</mark>（測試通過後再改為指定的 AD 群組）
7. 一路 Next 完成精靈

**Select Data Source 的兩種匯入方式**：

| 方式                                                   | 適用情境                           | 操作                                           |
| ---------------------------------------------------- | ------------------------------ | -------------------------------------------- |
| Import data about the relying party published online | ADFS 伺服器信任 MaiAgent 的 TLS 憑證   | 貼入 SP Metadata URL                           |
| Import data from a file                              | MaiAgent 使用自簽憑證或內部憑證（常見於私有化部署） | 先用瀏覽器開啟 SP Metadata URL、將 XML 另存成檔案，再選擇該檔案匯入 |

{% hint style="info" %}
如果線上匯入出現「Could not create SSL/TLS secure channel」錯誤，代表 ADFS 伺服器不信任 MaiAgent 的憑證，改用檔案匯入即可。中繼資料匯入是一次性的動作，正式登入流程中 ADFS 不需要連線到 MaiAgent（所有跳轉都由使用者的瀏覽器完成），因此用檔案匯入沒有任何後遺症。
{% endhint %}

匯入成功後，精靈會自動帶入 Identifier（即 SP Metadata URL）與 ACS Endpoint，不需手動輸入。

## 四、設定宣告規則（Claim Rules） <a href="#configure-claim-rules" id="configure-claim-rules"></a>

{% hint style="danger" %}
這是整合中**最容易遺漏的步驟**。精靈跑完後宣告規則是空的，若不設定，ADFS 回傳的 SAML 回應不會帶任何使用者屬性，登入會直接失敗。
{% endhint %}

在剛建立的 Relying Party 上按右鍵 > <mark style="color:blue;">Edit Claim Issuance Policy...</mark>（注意不是 Edit Access Control Policy）> <mark style="color:blue;">Add Rule</mark>，範本選擇 <mark style="color:blue;">Send Claims Using a Custom Rule</mark>，依序建立以下兩條規則。

**規則一：發送使用者屬性**（名稱：`MaiAgent attributes`）

```
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"]
 => issue(store = "Active Directory", types = ("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", "http://schemas.microsoft.com/identity/claims/displayname"), query = ";mail,mail,displayName;{0}", param = c.Value);
```

這條規則從 AD 讀取使用者的 `mail` 與 `displayName` 屬性。特別注意：MaiAgent 讀取電子郵件用的是 **Name 宣告**（`.../claims/name`），不是一般慣用的 emailaddress 宣告，因此這條規則會把 Email 同時發送到兩個宣告中（屬性對應詳見 [SAML 整合的屬性對應說明](/org/sso/sso-saml.md#saml-attribute-mapping)）。

**規則二：Email 轉換為 NameID**（名稱：`Email to NameID`）

```
c:[Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
 => issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType, Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/format"] = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress");
```

這條規則把 Email 轉為 Email 格式的 NameID，作為登入識別與單一登出之用。

設定時的三個注意事項：

* 規則順序：規則一在上、規則二在下（規則二依賴規則一發出的 emailaddress 宣告）
* 規則文字請整段複製貼上，不要手動輸入——宣告 URI 差一個字元整條規則就無效
* 儲存後不需重新啟動任何服務，直接測試登入即可

## 五、檢查信賴憑證者屬性 <a href="#check-relying-party-properties" id="check-relying-party-properties"></a>

在 Relying Party 上按右鍵 > <mark style="color:blue;">Properties</mark>：

| 分頁         | 檢查項目                                                           |
| ---------- | -------------------------------------------------------------- |
| Advanced   | Secure hash algorithm 選擇 **SHA-256**                           |
| Encryption | **不要**放置任何加密憑證。MaiAgent 不支援加密的 SAML 判斷提示，放了憑證 ADFS 會加密回應，登入將失敗 |
| Signature  | 保持空白即可                                                         |

## 六、測試登入 <a href="#test-login" id="test-login"></a>

1. 使用 AD `mail` 屬性有值的測試帳號
2. 從 MaiAgent 登入頁點選 SSO 登入，瀏覽器應跳轉到 `https://{ADFS 網域}/adfs/ls/`
3. 輸入 AD 帳號密碼，驗證成功後自動跳轉回 MaiAgent

{% hint style="info" %}
首次透過 SSO 登入的使用者會自動建立帳號並加入組織，但**預設沒有任何權限**，會看到「No Access Permission」頁面——這是正常的安全設計。請組織管理員到 <mark style="color:blue;">組織管理</mark> > <mark style="color:blue;">成員</mark> 找到該成員並指派角色權限，使用者按下 <mark style="color:blue;">Check Permission</mark> 或重新登入後即可使用。
{% endhint %}

## 疑難排解 <a href="#troubleshooting" id="troubleshooting"></a>

| 症狀                                                      | 原因                                     | 解法                                                                            |
| ------------------------------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------- |
| ADFS 匯入中繼資料時顯示「Could not create SSL/TLS secure channel」 | ADFS 伺服器不信任 MaiAgent 的 TLS 憑證（自簽或內部憑證） | 改用「Import data from a file」檔案匯入                                               |
| 登入後停在「Third-Party Login Failure」                        | 最常見是宣告規則問題，需查看伺服器記錄確認                  | 私有化部署可執行 `docker logs maiagent-django --since 5m 2>&1 \| grep -i saml` 查看錯誤訊息 |
| 記錄顯示 `There is no AttributeStatement on the Response`   | 宣告規則未設定，或測試帳號的 AD `mail` 屬性為空          | 依第四節補上兩條規則；用 `Get-ADUser <帳號> -Properties mail` 確認 mail 有值                    |
| 記錄顯示 `User email not found`                             | 宣告有發出，但 Email 沒有放在 Name 宣告             | 檢查規則一的宣告 URI 是否與文件完全一致                                                        |
| 記錄顯示判斷提示過期或尚未生效（NotBefore / NotOnOrAfter）               | MaiAgent 伺服器與 ADFS 伺服器時間不同步            | 兩邊主機都設定 NTP 校時                                                                |
| 登入成功但顯示「No Access Permission」                           | 新成員尚未被指派權限（正常行為）                       | 管理員到成員管理指派角色權限                                                                |
| ADFS 驗證成功後跳轉的網址缺少連接埠、頁面無法開啟                             | SP 中繼資料中的 ACS URL 缺少連接埠（反向代理設定問題）      | 依第二節的提示修正 Nginx 的 `proxy_set_header Host` 設定，重新產生中繼資料並重新匯入 ADFS               |

**善用 SAML-tracer 除錯**：在瀏覽器安裝 SAML-tracer 擴充功能後重新登入一次，可以直接看到 ADFS 回傳的 SAML 回應內容。如果 `AttributeStatement` 是空的或 Email 放在錯誤的宣告中，一眼就能確認是 ADFS 端的宣告規則問題。

## 維運注意事項 <a href="#maintenance-notes" id="maintenance-notes"></a>

* **Token-Signing 憑證輪替**：ADFS 預設每年自動更換 Token-Signing 憑證（AutoCertificateRollover）。若 MaiAgent 端使用 IdP Metadata URL 模式設定，憑證更換後重新儲存一次認證來源即可更新；若使用手動憑證模式，必須在輪替前取得新憑證並更新設定，否則屆時所有使用者將無法登入
* **限縮登入對象**：測試完成後，將 ADFS 的 Access Control Policy 從 Permit everyone 改為指定的 AD 群組，即可控制哪些員工能登入 MaiAgent，不需變更 MaiAgent 端設定
* **暫停 SSO**：在 MaiAgent 後台關閉該認證來源的登入開關即可，ADFS 端不需變更


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.maiagent.ai/org/sso/sso-saml/sso-saml-adfs.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
