> For the complete documentation index, see [llms.txt](https://docs.stackai.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.stackai.com/welcome-to-stackai/security-and-governance/security-in-stackai/system-for-cross-domain-identity-management-scim/scim-through-entra.md).

# SCIM through Entra

This guide covers the **core SCIM setup**: enabling SCIM in Stack AI, creating the Entra enterprise app, configuring attribute mappings for users, assigning users, and verifying that provisioning works.

***

### Prerequisites

* Stack AI organization admin access
* Microsoft Entra tenant with privileges to create Enterprise applications and edit App Registrations
* A modern browser tab open to each side

***

### Step 1 — Enable SCIM in Stack AI

1. Sign in to Stack AI as an org admin.
2. Open **Settings → SSO and Provisioning**.
3. Ensure SSO is enabled. If not, enable SSO for the org first.
4. Scroll down to **SCIM Provisioning** and select your IdP and the default role for users SCIM will provision
5. Generate a bearer token and save it for your reference.
6. Click "Enable SCIM"

Leave this page open — you'll come back to verify provisioned users.

***

### Step 2 — Create the Enterprise application in Entra

1. Open the [Microsoft Entra admin center](https://entra.microsoft.com/).
2. **Identity → Applications → Enterprise applications** → **+ New application**.
3. Click **+ Create your own application**.
4. Name it (e.g. `Stack AI`).
5. Choose **"Integrate any other application you don't find in the gallery (Non-gallery)"** → **Create**.

Entra will build the app from its generic SCIM template — that's why provisioning logs and job names will reference `customappsso`. It's cosmetic and expected.

***

### Step 3 — Configure SCIM credentials and test the connection

1. In the Enterprise App you registered in Entra for SSO, open the **Provisioning** blade → **Get started**.
2. Set **Provisioning Mode** = **Automatic**.
3. Expand **Admin Credentials**:
   * **Tenant URL**: paste the Stack AI Base URL from Step 1 (`https://api.stack-ai.com/scim/v2`)
   * **Secret Token**: paste the SCIM token from Step 1
4. Click **Test Connection**.
   * **401**: token is wrong or expired — regenerate in Stack AI and re-paste.
   * **404**: Tenant URL is wrong — double-check the trailing `/scim/v2`.
   * **5xx**: Stack AI side issue; check Stack AI status before continuing.
5. Click **Save**.

***

### Step 4 — Configure user attribute mappings

After saving credentials, the **Mappings** section appears with two object types: **Users** and **Groups**. Start with users — groups are covered in the optional section at the end.

1. Click **Provision Microsoft Entra ID Users**.
2. **Enabled**: Yes.
3. **Target Object Actions**: leave Create / Update / Delete all checked.
4. Verify (and trim) the attribute mappings. The minimum useful set:

   <table data-search="false"><thead><tr><th>Target SCIM attribute</th><th width="266.6171875">Microsoft Entra attribute</th><th>Notes</th></tr></thead><tbody><tr><td><code>userName</code></td><td><code>userPrincipalName</code></td><td>Identity key</td></tr><tr><td><code>active</code></td><td><code>Switch([IsSoftDeleted], , "False", "True", "True", "False")</code></td><td>Disables suspended users</td></tr><tr><td><code>displayName</code></td><td><code>displayName</code></td><td></td></tr><tr><td><code>emails[type eq "work"].value</code></td><td><code>mail</code></td><td>Required for invites/SSO match</td></tr><tr><td><code>name.givenName</code></td><td><code>givenName</code></td><td></td></tr><tr><td><code>name.familyName</code></td><td><code>surname</code></td><td></td></tr><tr><td><code>externalId</code></td><td><code>objectId</code></td><td>Note: you'll need to edit the Microsoft Entra ID Attribute to be objectId</td></tr></tbody></table>

   You can delete the rest of Entra's defaults (manager, addresses, phone numbers, etc.) — Stack AI ignores them and removing them reduces sync errors. Your final attribute should look as shown below

<figure><img src="/files/XAzJu9CB0BSkPYltoGZ0" alt=""><figcaption></figcaption></figure>

5. Save the mappings.

***

### Step 5 — Assign users to the app

Provisioning **scope** defaults to **"Sync only assigned users and groups"**. Leave this — broader scopes provision your entire tenant, which you almost never want.

1. In the same Enterprise application, open the **Users and groups** blade[^1].
2. **+ Add user/group** → pick users (or, later, groups — see the optional group section).
3. Save.

***

### Step 6 — Start provisioning and verify

1. Back to the **Provisioning** blade.
2. Toggle Provisioning Status to On and click save.

<figure><img src="/files/VQe0faj0vtMZMXnf721K" alt=""><figcaption></figcaption></figure>

3. In Stack AI → **Settings → Members**, confirm the user appears with the expected name, email, and active state. **NOTE**: Entra runs a sync cycle every **\~40 minutes**. This interval is set by Microsoft and cannot be lowered. To manually trigger a sync,&#x20;

***

### What to expect operationally

* **Adding a user to the app** → next cycle they appear in Stack AI.
* **Removing a user from the app** (or soft-deleting them in Entra) → `active=false` is sent, Stack AI deactivates them. The user record is preserved.
* **Hard-deleting a user in Entra** after 30 days → SCIM `DELETE /Users/{id}` is sent and the user is removed from Stack AI.
* **Renaming a user** → Stack AI updates the existing record, identified via `externalId`. No duplicate is created.
* **Quarantine** (red banner on the Provisioning blade) → too many failures or auth errors. Fix the underlying issue, then **Restart provisioning** to clear it.

***

### Troubleshooting checklist

If provisioning isn't behaving:

1. **Provisioning logs** (Under "Montior" on the left-side panel). Filter by Status = Failure. Each row shows the request, response, and exact error.
2. **401 Unauthorized** on every call → token expired or revoked in Stack AI. Regenerate and update Entra credentials.
3. **403 Forbidden** on `/Users` → SCIM was disabled on the Stack AI side. Re-enable from the SCIM Provisioning page.
4. **400 invalidValue** on `userName` → almost always a missing `userPrincipalName` for that user. Check the user's profile in Entra.
5. **No log rows at all for a user you expected** → they're not assigned to the app, or they're in an unassigned group.
6. **Provision on demand** is the fastest debug tool. Use it after every config change to validate without waiting for the cycle.

***

## Optional: Group sync

By default, Stack AI mirrors Entra groups one-to-one — same name, same membership. There's no group → role / group → permission translation; the synced group is just a Stack AI group. Skip this section if you only need user provisioning.

### Enable group sync in Stack AI

1. Stack AI → **Settings → SCIM Provisioning**.
2. Toggle **Sync groups** to **ON**.

If this toggle is off, every Entra `/Groups` call returns **403 Forbidden** and Entra logs will be flooded with group-create failures.

### Confirm the Group mapping is enabled in Entra

1. Provisioning blade → **Mappings**.
2. Click **Provision Microsoft Entra ID Groups** → **Enabled = Yes**.
3. Verify the minimum mapping set:

   | Entra attribute | SCIM attribute |
   | --------------- | -------------- |
   | `displayName`   | `displayName`  |
   | `objectId`      | `externalId`   |
   | `members`       | `members`      |

   The Entra group's `displayName` becomes the Stack AI group name verbatim. Pick clean, human-readable group names in Entra; they show up directly in Stack AI's UI.

### Assign the group to the app

In **Users and groups**, **+ Add user/group** → pick the **group itself** (not just its members). Assigning a member's parent group does not implicitly provision the group; the group must be assigned explicitly.

> **Note**: Entra does **not** expand nested or dynamic groups for SCIM. If you have a group of groups, assign each leaf group separately.

### Verify

After the next sync (or via **Provision on demand** on the group):

* The group appears in Stack AI → **Settings → Groups**.
* Members are populated. Adding/removing a user from the Entra group propagates as a SCIM `PATCH` (add/remove members) on the next cycle.
* Renaming the Entra group updates the Stack AI group's name (it's matched by `externalId`).
* Deleting the Entra group removes the Stack AI group; member users are kept.

***

## Optional: Role mapping

By default, provisioned users have **no role**. To grant roles automatically, you create a translation table in Stack AI ("when Entra sends role X, set Stack AI role Y") and configure Entra to send a role value per user.

### How role resolution works

When StackAI receives a SCIM user payload with a `roles` array, it:

1. Picks the role marked `primary: true` (or the first if none flagged).
2. Lowercases and trims the value.
3. Looks for an exact match in your role mappings.
4. If no match → assigns the configured **default role**.
5. If there's no default role → user is provisioned with no role.

Matching is case-insensitive, but whitespace and unicode quirks count. Use simple names (`admin`, `member`, `viewer` , `user`).

### Step A — Create role mappings in Stack AI

1. Stack AI → **Settings → SCIM Provisioning** → **Role mappings** card.
2. **Set a default role** in the SCIM config (highly recommended). This is the safety net for users without a matching role.
3. **+ Add mapping** for each label you intend to send from Entra:

<figure><img src="/files/pCEHHWTBJ9q8vsdBhpNn" alt=""><figcaption></figcaption></figure>

### Step B — Define App Roles on the App Registration

App Roles live on the **App Registration**, not the Enterprise application.

<figure><img src="/files/jtrlDQGezkCNF6vsi6wJ" alt=""><figcaption></figcaption></figure>

1. Entra admin → **App registrations** → open the registration that backs your Stack AI enterprise app (same name).
2. **App roles** (under "Manage" on the left-side panel) → **+ Create app role** for each Stack AI role:
   * **Display name**: e.g. `admin` (Must match the `idp_role_value` from Step A.)
   * **Allowed member types**: Users/Groups
   * **Value**: e.g. `admin` (Must match the `idp_role_value` from Step A.)
   * **Description**: anything
   * **Enable this app role?**: Yes
   * Click **Apply**
3. Repeat for editor, viewer, and user.

### Step C — Assign users (or groups) to App Roles

1. Back to the **Enterprise application** → Select your App -> **Manage** -> **Users and groups**.

<figure><img src="/files/R7V2gOIbpdeXlYIyHLrO" alt=""><figcaption></figcaption></figure>

2. **+ Add user/group** → pick a user or group → **Select a role** → choose the app role → **Assign**.

<figure><img src="/files/SRJMnPl1nnRZXGfQdGge" alt=""><figcaption></figcaption></figure>

3. Each user can hold **one** app-role assignment per app. Re-assigning replaces the previous role.
4. If you assign a group, every member inherits the role for SCIM purposes.

### Step D — Map App Roles into the SCIM `roles` attribute

Provisioning blade → **Mappings** → **Provision Microsoft Entra ID Users**:

1. Scroll to the bottom and tick **Show advanced options**.

2. Click **Edit attribute list for customappsso**.

3. Ensure `roles` exists with **Type = String** and **Multi-Valued = True**. Add it if missing. Save.

4. Back in the user mappings list, **+ Add New Mapping**:

   * **Mapping type**: Expression
   * **Expression**: `SingleAppRoleAssignment([appRoleAssignments])`
   * **Target attribute**: `roles[primary eq "True"].value`
   * **Match objects using this attribute**: No
   * **Apply this mapping**: Always

   <figure><img src="/files/sy4m8IADJp5SpvS98uQv" alt=""><figcaption></figcaption></figure>

5. Save.

The `SingleAppRoleAssignment` function emits the user's current app-role **Value** (e.g. `"admin"`), placed into `roles[0].value` with `primary=true` — exactly what StackAI's resolver looks for.

### Step E — Verify

1. **Provision on demand** for a user with an assigned app role.
2. Inspect the outbound SCIM payload — `roles` should be present with the right value.
3. In StackAI → **Settings → Members**, the user's role should reflect the mapping.

### Troubleshooting role mapping

* **`roles` missing from payload** → Step D-3 (schema) wasn't saved, or the user has no app role assigned (Step C).
* **`roles` present but Stack AI uses the default** → label mismatch. The `Value` on the App Role and the `idp_role_value` in Stack AI must match (case-insensitive, no extra whitespace).
* **Existing users not updating** → they don't re-resolve until something on their record changes. Either trigger a Provision on demand for each, or wait for the next sync cycle and ensure their app-role assignment was set/changed (which forces an update).
* **Multiple app-role assignments** → not supported by `SingleAppRoleAssignment`. Use only one app role per user.

### Lightweight alternative

If you don't want to manage App Roles, set a single constant role for everyone:

* In **Mappings → Provision Microsoft Entra ID Users**, **+ Add New Mapping**:
  * **Mapping type**: Constant
  * **Constant value**: `member`
  * **Target attribute**: `roles[primary eq "True"].value`
* Add one matching role mapping in Stack AI: `member` → Member.

Everyone gets the same role on provisioning; admins are promoted manually inside Stack AI. Simpler, less Entra to manage, but no role differentiation from the IdP.

***

### Quick reference

<table data-search="false"><thead><tr><th>Question</th><th>Where it's configured</th></tr></thead><tbody><tr><td>Who can SSO into Stack AI?</td><td>Entra app → Users and groups (assignment), SSO blade (provider config)</td></tr><tr><td>Who gets provisioned?</td><td>Entra app → Users and groups (must be assigned <strong>and</strong> in Provisioning scope)</td></tr><tr><td>What attributes flow over?</td><td>Entra app → Provisioning → Mappings</td></tr><tr><td>Where is my SCIM token?</td><td>Stack AI → SCIM Provisioning page (regenerate if lost)</td></tr><tr><td>Why isn't my group syncing?</td><td>(1) <code>Sync groups</code> toggle in Stack AI, (2) group itself assigned to the app, (3) Group mapping enabled in Entra</td></tr><tr><td>Why is everyone getting the wrong role?</td><td>Role mapping label mismatch, or no <code>roles</code> claim in SCIM payload</td></tr><tr><td>How fast does a change propagate?</td><td>Every ~40 minutes, or instantly via <strong>Provision on demand</strong></td></tr></tbody></table>

[^1]: blade = page


---

# 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.stackai.com/welcome-to-stackai/security-and-governance/security-in-stackai/system-for-cross-domain-identity-management-scim/scim-through-entra.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.
