> 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/maiagent-user-guide/en/org/sso/sso-saml/sso-saml-adfs.md).

# ADFS Integration

Integrate enterprise ADFS (Active Directory Federation Services) via the SAML 2.0 protocol

ADFS (Active Directory Federation Services) is a federated identity service provided by Microsoft. Enterprises that have already deployed Windows Active Directory and ADFS can allow employees to log in to MaiAgent directly using their AD credentials without creating separate accounts.

**Target audience**: Enterprises with ADFS deployed (Windows Server 2016 / 2019 / 2022)

**Before you begin, prepare the following**:

* MaiAgent organization administrator permissions (for configuring the authentication source)
* Administrator permissions on the ADFS server (for creating the Relying Party Trust and claim rules)
* An AD account for testing, where the **`mail` attribute must have a value**

{% hint style="info" %}
The basic concept of integration is "two-way information exchange": enter the ADFS information (Metadata URL) into MaiAgent, then import the MaiAgent information (SP Metadata URL) into ADFS. For general SAML configuration instructions, refer to [SAML Integration](/maiagent-user-guide/en/org/sso/sso-saml.md) first.
{% endhint %}

## 1. Create an Authentication Source in the MaiAgent Backend <a href="#create-auth-source" id="create-auth-source"></a>

Go to <mark style="color:blue;">Organization Management</mark> > <mark style="color:blue;">Authentication Sources</mark>, and select the <mark style="color:blue;">SAML</mark> authentication type:

| Field                      | Value                                                                                      |
| -------------------------- | ------------------------------------------------------------------------------------------ |
| Authentication Source Name | e.g., `my-company` (lowercase alphanumeric and hyphens, used to generate SP endpoint URLs) |
| IdP Metadata URL           | `https://{ADFS domain}/FederationMetadata/2007-06/FederationMetadata.xml`                  |
| Email Domain               | (Optional) Enterprise Email domain, e.g., `company.com`                                    |

The ADFS Federation Metadata URL follows a fixed path — simply replace `{ADFS domain}` with your enterprise's ADFS server domain. When saving, MaiAgent fetches and parses this metadata in real time, automatically populating the Entity ID, SSO URL, and signing certificate.

{% hint style="warning" %}
The MaiAgent server must be able to reach this Metadata URL when saving. If ADFS is only accessible from the internal network and MaiAgent cannot connect, use manual mode instead: fill in the IdP Entity ID (typically `http://{ADFS domain}/adfs/services/trust`), IdP SSO URL (typically `https://{ADFS domain}/adfs/ls/`), and the **Token-Signing certificate** (Base64 format) exported from the ADFS Management Console.
{% endhint %}

## 2. Obtain MaiAgent's SP Information <a href="#get-sp-info" id="get-sp-info"></a>

After saving, the settings page displays the SP (Service Provider, i.e., MaiAgent side) information:

| SP Field                    | Format                                                     |
| --------------------------- | ---------------------------------------------------------- |
| Entity ID / SP Metadata URL | `https://{platform domain}/accounts/saml/{name}/metadata/` |
| ACS URL                     | `https://{platform domain}/accounts/saml/{name}/acs/`      |

Open the SP Metadata URL in a browser and confirm that it displays an XML document (starting with `<EntityDescriptor>`), indicating the authentication source is active. This URL is the information you need to provide to the ADFS administrator in the next step.

{% hint style="warning" %}
For private deployments using a non-standard port (e.g., `https://platform-domain:8000`), check whether the `AssertionConsumerService` `Location` in the XML **includes the port**. If the entityID includes the port but the ACS URL does not, the reverse proxy (Nginx) is stripping the port from the Host header — change `proxy_set_header Host $host;` to `proxy_set_header Host $http_host;` in your Nginx configuration, then reload Nginx (update every location block). Refresh and verify.
{% endhint %}

## 3. Create a Relying Party Trust in ADFS <a href="#create-relying-party-trust" id="create-relying-party-trust"></a>

The following steps are performed by the ADFS administrator on the ADFS server:

1. Open <mark style="color:blue;">Server Manager</mark> > <mark style="color:blue;">Tools</mark> > <mark style="color:blue;">AD FS Management</mark>
2. Select <mark style="color:blue;">Relying Party Trusts</mark> on the left, then click <mark style="color:blue;">Add Relying Party Trust...</mark> on the right
3. Select <mark style="color:blue;">Claims aware</mark> > Start
4. On the **Select Data Source** page, choose an import method (see below)
5. Set **Display name** to `MaiAgent`
6. For **Access Control Policy**, select <mark style="color:blue;">Permit everyone</mark> initially (restrict to specific AD groups after testing)
7. Click Next through the wizard to complete

**Two import methods for Select Data Source**:

| Method                                               | Use Case                                                                            | Steps                                                                                                 |
| ---------------------------------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| Import data about the relying party published online | ADFS server trusts MaiAgent's TLS certificate                                       | Paste the SP Metadata URL                                                                             |
| Import data from a file                              | MaiAgent uses a self-signed or internal certificate (common in private deployments) | First open the SP Metadata URL in a browser, save the XML as a file, then select that file for import |

{% hint style="info" %}
If online import shows a "Could not create SSL/TLS secure channel" error, it means the ADFS server does not trust MaiAgent's certificate — use file import instead. The metadata import is a one-time operation. During actual login, ADFS does not need to connect to MaiAgent (all redirects are handled by the user's browser), so file import has no side effects.
{% endhint %}

After successful import, the wizard automatically populates the Identifier (i.e., SP Metadata URL) and ACS Endpoint — no manual entry required.

## 4. Configure Claim Rules <a href="#configure-claim-rules" id="configure-claim-rules"></a>

{% hint style="danger" %}
This is the **most commonly overlooked step** in the integration. After the wizard completes, the claim rules are empty. Without configuration, the SAML response returned by ADFS will not contain any user attributes, and login will fail.
{% endhint %}

Right-click the newly created Relying Party > <mark style="color:blue;">Edit Claim Issuance Policy...</mark> (not Edit Access Control Policy) > <mark style="color:blue;">Add Rule</mark>. Select the <mark style="color:blue;">Send Claims Using a Custom Rule</mark> template and create the following two rules in order.

**Rule 1: Send user attributes** (Name: `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);
```

This rule reads the user's `mail` and `displayName` attributes from AD. Note: MaiAgent uses the **Name claim** (`.../claims/name`) to read the email, not the commonly used emailaddress claim. Therefore, this rule sends the Email to both claims simultaneously (see [SAML Integration attribute mapping](/maiagent-user-guide/en/org/sso/sso-saml.md#saml-attribute-mapping) for details).

**Rule 2: Transform Email to NameID** (Name: `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");
```

This rule transforms the Email into an Email-format NameID for login identification and single logout.

Three important notes when configuring:

* Rule order: Rule 1 must be above Rule 2 (Rule 2 depends on the emailaddress claim issued by Rule 1)
* Copy and paste the entire rule text — do not type it manually. A single character difference in the claim URI will render the rule ineffective
* No service restart is needed after saving — test login immediately

## 5. Check Relying Party Properties <a href="#check-relying-party-properties" id="check-relying-party-properties"></a>

Right-click the Relying Party > <mark style="color:blue;">Properties</mark>:

| Tab        | Checklist                                                                                                                                                                     |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Advanced   | Set Secure hash algorithm to **SHA-256**                                                                                                                                      |
| Encryption | Do **not** add any encryption certificate. MaiAgent does not support encrypted SAML assertions. Adding a certificate causes ADFS to encrypt the response, and login will fail |
| Signature  | Leave blank                                                                                                                                                                   |

## 6. Test Login <a href="#test-login" id="test-login"></a>

1. Use a test account whose AD `mail` attribute has a value
2. Click SSO login on the MaiAgent login page — the browser should redirect to `https://{ADFS domain}/adfs/ls/`
3. Enter AD credentials. After successful authentication, the browser automatically redirects back to MaiAgent

{% hint style="info" %}
Users logging in via SSO for the first time will have an account automatically created and added to the organization, but they **have no permissions by default** and will see a "No Access Permission" page — this is expected security behavior. The organization administrator should go to <mark style="color:blue;">Organization Management</mark> > <mark style="color:blue;">Members</mark>, find the member, and assign role permissions. The user can click <mark style="color:blue;">Check Permission</mark> or log in again to access the platform.
{% endhint %}

## Troubleshooting <a href="#troubleshooting" id="troubleshooting"></a>

| Symptom                                                                                  | Cause                                                                                       | Solution                                                                                                                         |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| ADFS shows "Could not create SSL/TLS secure channel" when importing metadata             | ADFS server does not trust MaiAgent's TLS certificate (self-signed or internal certificate) | Use "Import data from a file" instead                                                                                            |
| Login results in "Third-Party Login Failure"                                             | Most commonly a claim rule issue — check server logs to confirm                             | For private deployments, run `docker logs maiagent-django --since 5m 2>&1 \| grep -i saml` to view error messages                |
| Logs show `There is no AttributeStatement on the Response`                               | Claim rules not configured, or the test account's AD `mail` attribute is empty              | Add the two rules per Section 4; verify mail has a value with `Get-ADUser <account> -Properties mail`                            |
| Logs show `User email not found`                                                         | Claims are issued but Email is not in the Name claim                                        | Verify Rule 1's claim URIs match the documentation exactly                                                                       |
| Logs show assertion expired or not yet valid (NotBefore / NotOnOrAfter)                  | Time mismatch between MaiAgent server and ADFS server                                       | Configure NTP on both servers                                                                                                    |
| Login succeeds but shows "No Access Permission"                                          | New member has not been assigned permissions (expected behavior)                            | Administrator assigns role permissions in member management                                                                      |
| After ADFS authentication, the redirect URL is missing the port and the page cannot load | ACS URL in SP metadata is missing the port (reverse proxy configuration issue)              | Follow the instructions in Section 2 to fix Nginx's `proxy_set_header Host` setting, regenerate metadata, and reimport into ADFS |

**Use SAML-tracer for debugging**: Install the SAML-tracer browser extension and log in again. You can directly view the SAML response content returned by ADFS. If the `AttributeStatement` is empty or the Email is in the wrong claim, you can immediately confirm it is a claim rule issue on the ADFS side.

## Maintenance Notes <a href="#maintenance-notes" id="maintenance-notes"></a>

* **Token-Signing certificate rotation**: ADFS automatically rotates the Token-Signing certificate annually by default (AutoCertificateRollover). If MaiAgent is configured with the IdP Metadata URL mode, simply re-save the authentication source after the certificate change to update it. If using manual certificate mode, you must obtain the new certificate and update the configuration before rotation; otherwise, all users will be unable to log in
* **Restrict login scope**: After testing, change the ADFS Access Control Policy from Permit everyone to a specific AD group to control which employees can log in to MaiAgent — no changes needed on the MaiAgent side
* **Suspend SSO**: Simply disable the authentication source's login toggle in the MaiAgent backend — no changes needed on the ADFS side


---

# 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/maiagent-user-guide/en/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.
