> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getbifrost.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# User Provisioning (SCIM)

> Authenticate users, sync teams, and provision roles and business units from your identity provider using SCIM-backed OAuth 2.0 / OIDC flows.

## Overview

Bifrost Enterprise uses **SCIM-backed identity provisioning** to connect your organization's identity provider to Bifrost. A single configuration gives you:

* **Single sign-on (SSO)** via OAuth 2.0 / OIDC with JWKS-based JWT validation
* **Automatic role assignment** using custom claims, app roles, or group-to-role mappings
* **Team synchronization** from IdP groups into Bifrost teams
* **Business unit mapping** from IdP attributes to Bifrost business units
* **Bulk user provisioning** with filter-preview before import
* **Silent token refresh** using server-stored refresh tokens

Once configured, users sign in to Bifrost with their corporate credentials and inherit the right [role and permissions](./rbac) immediately - no manual account creation.

<Frame>
  <img src="https://mintcdn.com/bifrost/DI7XIVV33XNLaXJJ/media/user-provisioning/scim-overview.png?fit=max&auto=format&n=DI7XIVV33XNLaXJJ&q=85&s=6a963832fd90a5c7c860f26beee95b46" alt="User Provisioning overview in Bifrost dashboard" width="3476" height="2270" data-path="media/user-provisioning/scim-overview.png" />
</Frame>

***

## Supported Identity Providers

Pick your IdP to follow a step-by-step setup guide. All providers share the same Bifrost configuration surface - the only difference is how the OAuth client and role/group claims are created on the provider side.

<CardGroup cols={3}>
  <Card title="Okta" icon="o" href="/enterprise/setting-up-okta">
    OIDC with Org or Custom Authorization Servers, plus group-to-role mapping and API tokens for bulk user sync.
  </Card>

  <Card title="Microsoft Entra" icon="microsoft" href="/enterprise/setting-up-entra">
    Entra ID (Azure AD) with app roles, group claims, and v1.0 / v2.0 token support.
  </Card>

  <Card title="Zitadel" icon="cloud" href="/enterprise/setting-up-zitadel">
    Cloud or self-hosted Zitadel with project-scoped role claims and service-account-based provisioning.
  </Card>

  <Card title="Google Workspace" icon="google" href="/enterprise/setting-up-google-workspace">
    Google Workspace domains with OAuth login plus optional Directory API sync via a service account.
  </Card>
</CardGroup>

***

## How it works

<Frame>
  <img src="https://mintcdn.com/bifrost/DI7XIVV33XNLaXJJ/media/user-provisioning/scim-flow.png?fit=max&auto=format&n=DI7XIVV33XNLaXJJ&q=85&s=58127e875614995b10cb57297ece2ef9" alt="SCIM authentication and provisioning flow" width="1986" height="1782" data-path="media/user-provisioning/scim-flow.png" />
</Frame>

1. **Login** - Bifrost redirects unauthenticated users to the provider's authorization endpoint (Authorization Code flow).
2. **Token exchange** - on callback, Bifrost exchanges the code for an access token and refresh token, stores them in an `HttpOnly` cookie / server session, and validates the JWT against the provider's JWKS.
3. **Identity extraction** - configurable JWT claims (`userIdField`, `rolesField`, `teamIdsField`) are mapped to a Bifrost user, role, and teams. Provider-specific app roles or custom attributes override claim lookup.
4. **Attribute mapping** - optional `attributeRoleMappings`, `attributeTeamMappings`, and `attributeBusinessUnitMappings` translate arbitrary claim values (e.g., a department string or Okta group name) into Bifrost roles, teams, or business units.
5. **Bulk import** - admins can preview users matching a filter and bulk-import them via the dashboard, which calls the provider's user directory API.
6. **Silent refresh** - when the access token expires, Bifrost uses the stored refresh token to mint a new one without requiring re-login.

***

## Capabilities

| Capability                | Description                                                                                                                          |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| **OAuth 2.0 / OIDC SSO**  | Authorization Code + PKCE with configurable scopes (`openid profile email offline_access`).                                          |
| **JWKS validation**       | JWTs are validated against the provider's published JWKS keys; configuration is cached and auto-refreshed.                           |
| **Role mapping**          | Map from a claim value (string or array) to Admin / Developer / Viewer or a custom role. Highest-privilege wins when multiple match. |
| **Team mapping**          | Map multiple claim values to Bifrost teams in a single pass (a user can belong to many teams).                                       |
| **Business unit mapping** | Same as team mapping but scoped to business units.                                                                                   |
| **Provisioning preview**  | Preview up to 50 users matching filters (groups, roles, departments) before importing.                                               |
| **Bulk import**           | Import matched users into Bifrost with role + team + BU assignments applied.                                                         |
| **Team sync**             | Sync IdP groups as Bifrost teams with a single action.                                                                               |
| **Business unit sync**    | Sync IdP organizational units as Bifrost business units.                                                                             |
| **Deprovisioning**        | Re-running import reconciles removed users and updates role / team assignments.                                                      |
| **API key pass-through**  | Requests using Bifrost API keys (`bfst-*`) bypass SCIM middleware so inference traffic is not affected.                              |

***

## Configuration reference

All providers share the same outer config shape in `config.json`:

```json theme={null}
{
  "scim_config": {
    "enabled": true,
    "provider": "okta | entra | zitadel | keycloak | google | sailpoint",
    "config": {
      "...": "provider-specific fields - see each IdP guide"
    }
  }
}
```

Shared fields across providers:

| Field                           | Required | Description                                                                               |
| ------------------------------- | -------- | ----------------------------------------------------------------------------------------- |
| `clientId`                      | Yes      | OAuth client ID from the identity provider.                                               |
| `clientSecret`                  | Usually  | Client secret. Required for confidential clients and (where applicable) token revocation. |
| `audience`                      | Optional | JWT audience to validate against. Defaults vary per provider.                             |
| `attributeRoleMappings`         | Optional | Ordered list of `{ attribute, value, role }` rules evaluated top-to-bottom.               |
| `attributeTeamMappings`         | Optional | List of `{ attribute, value, team }` rules (all matches apply).                           |
| `attributeBusinessUnitMappings` | Optional | List of `{ attribute, value, businessUnit }` rules (all matches apply).                   |

Provider-specific fields (domain, tenant ID, server URL, service-account credentials) are documented in each IdP's setup guide.

<Note>
  Changing `scim_config` at runtime through the UI is applied after saving. For file-based configuration, restart the Bifrost server to pick up changes.
</Note>

***

## Environment variable support

Fields marked **env.\* supported** accept `"env.VAR_NAME"` in addition to a literal value - Bifrost resolves the variable from the process environment at startup. Attribute mapping arrays are always plain JSON (they cannot reference env vars).

### Okta

| Field                  | JSON key                        | Required | env.\* supported | Notes                                                             |
| ---------------------- | ------------------------------- | -------- | ---------------- | ----------------------------------------------------------------- |
| Issuer URL             | `issuerUrl`                     | Yes      | Yes              | Org server: `https://domain.okta.com`; Custom: `…/oauth2/default` |
| Client ID              | `clientId`                      | Yes      | Yes              | Application Client ID                                             |
| Client Secret          | `clientSecret`                  | No       | Yes              | Required for token revocation                                     |
| API Token              | `apiToken`                      | No       | Yes              | Required for bulk user / team sync                                |
| Audience               | `audience`                      | No       | Yes              | Only applies to Custom Authorization Server                       |
| Team IDs field         | `teamIdsField`                  | No       | Yes              | JWT claim for group IDs (default: `"groups"`)                     |
| Role mappings          | `attributeRoleMappings`         | No       | **Plain only**   | Array of `{ attribute, value, role }` objects                     |
| Team mappings          | `attributeTeamMappings`         | No       | **Plain only**   | Array of `{ attribute, value, team }` objects                     |
| Business unit mappings | `attributeBusinessUnitMappings` | No       | **Plain only**   | Array of `{ attribute, value, businessUnit }` objects             |

### Microsoft Entra ID

| Field                  | JSON key                        | Required | env.\* supported | Notes                                                 |
| ---------------------- | ------------------------------- | -------- | ---------------- | ----------------------------------------------------- |
| Tenant ID              | `tenantId`                      | Yes      | Yes              | Azure tenant ID or `"common"` for multi-tenant        |
| Client ID              | `clientId`                      | Yes      | Yes              | Application (client) ID                               |
| Client Secret          | `clientSecret`                  | Yes      | Yes              | Client secret for OAuth authentication                |
| Cloud                  | `cloud`                         | No       | Yes              | `"commercial"` (default) \| `"gcc-high"` \| `"dod"`   |
| Audience               | `audience`                      | No       | Yes              | JWT audience override (default: `clientId`)           |
| App ID URI             | `appIdUri`                      | No       | Yes              | App ID URI for v1.0 tokens (e.g. `api://{clientId}`)  |
| Team IDs field         | `teamIdsField`                  | No       | Yes              | JWT claim for group IDs (default: `"groups"`)         |
| Role mappings          | `attributeRoleMappings`         | No       | **Plain only**   | Array of `{ attribute, value, role }` objects         |
| Team mappings          | `attributeTeamMappings`         | No       | **Plain only**   | Array of `{ attribute, value, team }` objects         |
| Business unit mappings | `attributeBusinessUnitMappings` | No       | **Plain only**   | Array of `{ attribute, value, businessUnit }` objects |

### Keycloak

| Field                  | JSON key                        | Required | env.\* supported | Notes                                                          |
| ---------------------- | ------------------------------- | -------- | ---------------- | -------------------------------------------------------------- |
| Server URL             | `serverUrl`                     | Yes      | Yes              | Base URL, e.g. `https://keycloak.company.com` (no `/realms/…`) |
| Realm                  | `realm`                         | Yes      | Yes              | e.g. `"master"` or `"my-app"`                                  |
| Client ID              | `clientId`                      | Yes      | Yes              | Application client ID                                          |
| Client Secret          | `clientSecret`                  | No       | Yes              | For confidential clients                                       |
| Audience               | `audience`                      | No       | Yes              | JWT audience for token validation                              |
| Team IDs field         | `teamIdsField`                  | No       | Yes              | JWT claim for group IDs (default: `"groups"`)                  |
| Role mappings          | `attributeRoleMappings`         | No       | **Plain only**   | Array of `{ attribute, value, role }` objects                  |
| Team mappings          | `attributeTeamMappings`         | No       | **Plain only**   | Array of `{ attribute, value, team }` objects                  |
| Business unit mappings | `attributeBusinessUnitMappings` | No       | **Plain only**   | Array of `{ attribute, value, businessUnit }` objects          |

### Zitadel

| Field                         | JSON key                        | Required | env.\* supported | Notes                                                      |
| ----------------------------- | ------------------------------- | -------- | ---------------- | ---------------------------------------------------------- |
| Domain                        | `domain`                        | Yes      | Yes              | e.g. `"my-instance.zitadel.cloud"` or `"auth.company.com"` |
| Client ID                     | `clientId`                      | Yes      | Yes              | Application client ID                                      |
| Client Secret                 | `clientSecret`                  | No       | Yes              | For confidential clients                                   |
| Project ID                    | `projectId`                     | No       | Yes              | For project-scoped role claims                             |
| Audience                      | `audience`                      | No       | Yes              | Access-token audience override                             |
| Service account client ID     | `serviceAccountClientId`        | No       | Yes              | Service account for provisioning API access                |
| Service account client secret | `serviceAccountClientSecret`    | No       | Yes              | Service account secret                                     |
| Team IDs field                | `teamIdsField`                  | No       | Yes              | JWT claim for group IDs                                    |
| Role mappings                 | `attributeRoleMappings`         | No       | **Plain only**   | Array of `{ attribute, value, role }` objects              |
| Team mappings                 | `attributeTeamMappings`         | No       | **Plain only**   | Array of `{ attribute, value, team }` objects              |
| Business unit mappings        | `attributeBusinessUnitMappings` | No       | **Plain only**   | Array of `{ attribute, value, businessUnit }` objects      |

### Google Workspace

| Field                       | JSON key                        | Required    | env.\* supported | Notes                                                          |
| --------------------------- | ------------------------------- | ----------- | ---------------- | -------------------------------------------------------------- |
| Domain                      | `domain`                        | Yes         | Yes              | Google Workspace domain (e.g. `"company.com"`)                 |
| Client ID                   | `clientId`                      | Yes         | Yes              | Google OAuth2 client ID                                        |
| Client Secret               | `clientSecret`                  | No          | Yes              | For token revocation                                           |
| Credential mode             | `credentialMode`                | No          | Yes              | `"inherit"` (ADC) \| `"env"` \| `"file"`                       |
| Service account JSON        | `serviceAccountJson`            | No          | Yes              | Raw service account JSON string                                |
| Service account env var     | `serviceAccountEnvVar`          | No          | Yes              | Env var containing the service account JSON                    |
| Service account file        | `serviceAccountFile`            | No          | Yes              | Path to the service account JSON key file                      |
| Admin email                 | `adminEmail`                    | Conditional | Yes              | Required for Directory API / domain-wide delegation            |
| Impersonate service account | `impersonateServiceAccount`     | No          | Yes              | GCP SA email to impersonate when using ADC + Workload Identity |
| Audience                    | `audience`                      | No          | Yes              | Optional JWT audience override                                 |
| Team IDs field              | `teamIdsField`                  | No          | Yes              | Claim field for group IDs (default: `"groups"`)                |
| Role mappings               | `attributeRoleMappings`         | No          | **Plain only**   | Array of `{ attribute, value, role }` objects                  |
| Team mappings               | `attributeTeamMappings`         | No          | **Plain only**   | Array of `{ attribute, value, team }` objects                  |
| Business unit mappings      | `attributeBusinessUnitMappings` | No          | **Plain only**   | Array of `{ attribute, value, businessUnit }` objects          |

### SailPoint

SailPoint fields are **plain values only** - `env.*` references are not supported for any SailPoint config field.

| Field                  | JSON key                        | Required    | Notes                                                               |
| ---------------------- | ------------------------------- | ----------- | ------------------------------------------------------------------- |
| Product                | `product`                       | Yes         | `"isc"` (SailPoint Identity Security Cloud) \| `"iiq"` (IdentityIQ) |
| Tenant                 | `tenant`                        | Conditional | ISC only: tenant name (e.g. `"acme"`)                               |
| Client ID              | `clientId`                      | Conditional | Required for ISC; optional for IIQ OAuth                            |
| Client Secret          | `clientSecret`                  | Conditional | Required for ISC; optional for IIQ OAuth                            |
| Base URL               | `baseUrl`                       | Conditional | IIQ only: SCIM base URL                                             |
| Username               | `username`                      | Conditional | IIQ basic auth username                                             |
| Password               | `password`                      | Conditional | IIQ basic auth password                                             |
| Team IDs field         | `teamIdsField`                  | No          | JWT claim for group IDs                                             |
| Role mappings          | `attributeRoleMappings`         | No          | Array of `{ attribute, value, role }` objects                       |
| Team mappings          | `attributeTeamMappings`         | No          | Array of `{ attribute, value, team }` objects                       |
| Business unit mappings | `attributeBusinessUnitMappings` | No          | Array of `{ attribute, value, businessUnit }` objects               |

***

## Configuring from the dashboard

1. Navigate to **Governance → User Provisioning** in the Bifrost dashboard.
2. Select your identity provider from the **SCIM Provider** dropdown.
3. Fill in the provider-specific fields. Required fields are marked and validated on **Verify**.

<Frame>
  <img src="https://mintcdn.com/bifrost/DI7XIVV33XNLaXJJ/media/user-provisioning/scim-provider-select.png?fit=max&auto=format&n=DI7XIVV33XNLaXJJ&q=85&s=dfa263e53c6c72f48ac297064ea923c3" alt="Selecting a SCIM provider in the Bifrost dashboard" width="3454" height="2250" data-path="media/user-provisioning/scim-provider-select.png" />
</Frame>

4. Click **Verify** to test credentials end-to-end. Bifrost will reach the provider's JWKS / directory endpoint and report any failures.
5. Configure **Attribute → Role / Team / Business Unit** mappings as needed.
6. Toggle **Enabled** and click **Save Configuration**.

<Warning>
  After enabling a new provider, the next dashboard load redirects to your IdP for login. Test in an incognito window first to avoid being locked out of your current session.
</Warning>

***

## Attribute mappings

Attribute mappings let you translate claim values into Bifrost roles, teams, or business units without forcing your IdP admins to restructure claim names.

<Frame>
  <img src="https://mintcdn.com/bifrost/DI7XIVV33XNLaXJJ/media/user-provisioning/scim-attribute-mapping.png?fit=max&auto=format&n=DI7XIVV33XNLaXJJ&q=85&s=a0c102219fe68fe4ec95575c179e9f89" alt="Preview of users matching an import filter" width="2978" height="1770" data-path="media/user-provisioning/scim-attribute-mapping.png" />
</Frame>

Each mapping is an ordered rule:

```json theme={null}
{
  "attribute": "department",
  "value": "Engineering",
  "role": "developer"
}
```

Rules are evaluated top-to-bottom:

* **Role mappings** - first match wins. Set a fallback with `"attribute": "*"` at the end.
* **Team mappings** and **business unit mappings** - all matching rules apply, so a user with `department=Platform` and `group=sre` can be placed on multiple teams.

Claim values can be strings, arrays, or nested objects - Bifrost resolves dotted paths (e.g., `realm_access.roles`).

***

## Bulk user provisioning

Once SCIM is enabled, import users in bulk from your IdP:

1. Go to **Governance → User Provisioning → Import Users**.
2. Select a filter - groups, roles, departments, or a custom query depending on provider support.
3. Click **Preview** to see up to 50 matching users.
4. Click **Import** to create them in Bifrost with role / team / BU assignments applied.

<Frame>
  <img src="https://mintcdn.com/bifrost/DI7XIVV33XNLaXJJ/media/user-provisioning/scim-import-preview.png?fit=max&auto=format&n=DI7XIVV33XNLaXJJ&q=85&s=2d108b02300630769e46a490775a5b12" alt="Preview of users matching an import filter" width="3472" height="2252" data-path="media/user-provisioning/scim-import-preview.png" />
</Frame>

Re-running an import reconciles existing users - role and team changes in the IdP are reflected on the next import.

***

## Troubleshooting

| Symptom                                                                       | Likely cause                                                                                                                                                                                            |
| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Access denied: no application role or group mapping is assigned to this user. | Make sure you have assigned user to the Bifrost IdP application and they have a valid group/attribute mapping to role in Bifrost                                                                        |
| Redirect loop on login                                                        | Make sure you have restarted pods/Bifrost instance after changing SCIM configuration, or check for a redirect URI mismatch. Exact string match required - check trailing slashes and `http` vs `https`. |
| `invalid audience`                                                            | `audience` field does not match the access token's `aud` claim. Use the same value your IdP issues.                                                                                                     |
| Empty roles / teams                                                           | Claim mapping is off. Verify the JWT at [jwt.io](https://jwt.io) and check `rolesField` / `teamIdsField`.                                                                                               |
| Token refresh failing                                                         | `offline_access` scope missing or refresh token revoked. Re-enable the scope and re-authenticate.                                                                                                       |
| First user gets Admin                                                         | By design - if no matching role mapping applies, the first user is promoted to Admin so they can finish configuration. Subsequent users default to Viewer.                                              |

Provider-specific troubleshooting lives in each IdP's guide.

***

## Related

* [Role-Based Access Control](./rbac) - permissions model and custom roles
* [Advanced Governance](./advanced-governance) - budgets, limits, and compliance
* [Audit Logs](./audit-logs) - track authentication events and role changes
