Skip to content

Connect enterprise identity

Purpose

Connect Microsoft Entra ID, Okta or Keycloak for OIDC sign-in and use SCIM 2.0 to provision people and groups. Map directory groups to Mission Control's existing roles and scopes. Removing directory access takes effect on subsequent authorization checks; keys do not retain old permissions.

One provider and one existing organization are configured per Console deployment. OIDC authenticates a provisioned identity; SCIM is the authoritative feed for directory people and group membership. Unprovisioned OIDC users cannot sign themselves up. While enterprise sign-in is configured, people the directory provisioned sign in only through it: magic links and GitHub sign-in are unavailable to their addresses, so the provider's MFA and session policy always apply. The upgrade that introduced this rule ends the Console sessions directory-provisioned people held, so each signs in through the provider once more. Local owners keep magic-link sign-in and remain available for recovery.

Prerequisites

  • A current deployment with the directory database migration applied.
  • An existing organization and a local organization owner who can recover access.
  • Administrator access to the identity provider and permission to restart the Console with server-only settings.
  • people.manage to inspect directory administration and the applicable canonical role/Team-membership management permission at the target scope to manage role mappings.
  • HTTPS Console and identity-provider URLs. Client and SCIM secrets must be delivered through the deployment's secret injection, never through the browser or organization YAML.

Procedure

  1. Register a confidential OIDC web application in the chosen provider. Set its callback URL exactly:
Provider SSO_PROVIDER Callback path appended to APP_URL
Microsoft Entra ID entra /api/auth/callback/microsoft-entra-id
Okta okta /api/auth/callback/okta
Keycloak keycloak /api/auth/callback/keycloak

Configure token-endpoint client authentication as client_secret_post for the registered application.

  1. Configure the Console environment. From the deployment directory, the following fictional example illustrates an Okta configuration; supply real secrets through your deployment mechanism:
export SSO_PROVIDER="okta"
export SSO_ORGANIZATION_SLUG="acme"
export SSO_ISSUER="https://example.okta.com/oauth2/default"
export SSO_CLIENT_ID="example-client-id"
export SSO_CLIENT_SECRET="example-not-a-real-client-secret"
export SCIM_BEARER_TOKEN="example-not-a-real-scim-token-replace-before-use"

All six settings must be present together. For Entra, also set SSO_TENANT_ID to the exact tenant GUID and SSO_ISSUER to https://login.microsoftonline.com/<tenant-guid>/v2.0; common and organizations issuers are rejected. Keycloak uses the exact realm issuer, for example https://identity.example.test/realms/acme.

  1. Restart the Console and verify its startup configuration. The scopes are openid profile email; SSO_EXTRA_SCOPES optionally adds explicit space- or comma-separated scopes. Entra does not need an invented groups scope. Group membership comes through SCIM, not arbitrary OIDC claims.
  2. Configure the provider's SCIM client or an administrator-controlled provisioning bridge:

  3. Base URL: APP_URL plus /api/scim/v2.

  4. Authentication: the SCIM_BEARER_TOKEN as a Bearer token.
  5. Push assigned users and selected groups, including group membership changes.
  6. Map userName to a stable normalized email and preserve provider externalId values. For Entra, map externalId to the immutable user object ID (oid in its ID token). For Okta/Keycloak, configure verified email claims.

Entra/Okta provisioning can push users/groups where supported by the configured application. Keycloak deployments need a compatible SCIM provisioning extension or bridge; OIDC alone does not push users or groups.

  1. Provision a small test group and a synthetic test user. Open People & access → Identity, confirm their directory state, and map the group to an existing role at a valid scope. For example, map an engineering group to team.developer on its Team. Directory mappings cannot grant organization.owner.
  2. On Sign in, choose Continue with Microsoft Entra ID, Continue with Okta, or Continue with Keycloak, as configured. Complete provider authentication and verify the user's intended Team access.
  3. Remove the test user's group membership or deactivate the user in the directory. Confirm the next protected request loses the corresponding access. Retain the local owner for recovery before enrolling additional people.

The group membership comes from SCIM; its explicit mapping grants only the selected Team role. Verify the actual mapping and source before applying it broadly.

SCIM contract

Discovery resources are /ServiceProviderConfig, /Schemas, and /ResourceTypes. User and group resources support collection GET/POST and item GET/PUT/PATCH/DELETE. Responses use application/scim+json; errors contain the SCIM error schema, a status and a safe detail.

Supported equality filters include userName eq "person@example.test" and externalId eq "directory-id" for Users, and displayName/externalId equality for Groups. Pagination uses one-based startIndex and bounded count. Unsupported filters and mutation paths return explicit errors rather than being ignored.

For a synthetic provisioning check, run from any administrator terminal:

export CONSOLE_URL="https://console.example.test"
export SCIM_TEST_TOKEN="example-not-a-real-scim-token"
curl --fail-with-body "$CONSOLE_URL/api/scim/v2/Users" \
  --header "Authorization: Bearer $SCIM_TEST_TOKEN" \
  --header "Content-Type: application/scim+json" \
  --data '{"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],"externalId":"employee-example-42","userName":"person@example.test","displayName":"Example Person","active":true}'

The response contains the stable SCIM resource id. Use the returned ID for subsequent item requests; do not substitute a Console member or principal ID. Group member value fields refer to SCIM User IDs.

To deactivate a provisioned test person, replace the fictional item ID with the returned one:

curl --fail-with-body --request PATCH \
  "$CONSOLE_URL/api/scim/v2/Users/scim_example_user_id" \
  --header "Authorization: Bearer $SCIM_TEST_TOKEN" \
  --header "Content-Type: application/scim+json" \
  --data '{"schemas":["urn:ietf:params:scim:api:messages:2.0:PatchOp"],"Operations":[{"op":"replace","path":"active","value":false}]}'

An active: false resource remains a deactivated directory resource. DELETE retains internal history but removes the resource from the SCIM API; repeated deletion is idempotent. A multi-operation group PATCH either applies completely or leaves membership unchanged.

Expected result

The identity provider must return a signed ID token for the exact issuer and client, with a valid expiry and the sign-in nonce. An access token or user-info response alone is insufficient. Okta and Keycloak must provide verified email proof; Entra provisioning must map its immutable object ID to SCIM externalId so the token's tenant-scoped oid proves the first association. The first accepted login binds the provisioned identity to its immutable provider subject. Subsequent subject reassignment, ambiguous email identity and cross-organization account takeover are rejected. Changing the configured issuer/provider does not automatically rebind existing directory identities; restore the original configuration or use a separately reviewed migration. An existing local account is not automatically converted merely because its email matches a SCIM request.

A directory-provisioned account belongs to this organization. Its address comes from the directory rather than from the person proving it, so the account cannot read, accept or reject another organization's invitations on the same deployment; invite a different address for work elsewhere. SCIM cannot change the userName of an account that also belongs to another organization and answers 409; its other attributes still update.

Directory mappings use bounded Organization/Team presets or custom roles, real membership sources and management audit. A mapping cannot grant organization.owner, and the person creating it must hold every permission of the mapped role at that scope. The Team-preset exception for member managers does not apply, because the identity provider can add the person to the group later. See Grants stay within your own authority. Direct editors change direct assignments/sources; inherited authority remains until its mapping or membership ends. A Chat directory-group audience grants app access without Team membership.

Deactivation suspends the person's organization membership: it ends directory-derived authority, deletes their Console sessions, revokes the membership's user virtual keys, their client connections and its management tokens, and deletes the person's own Connector connections and header values. Reactivation restores none of these credentials; the person issues new keys and tokens and connects again. The old membership's keys and API owner grants do not revive on a later join. Ordinary service accounts survive their creator's departure and must be contained separately if needed. Directory reactivation does not override manual administrator suspension. Deleting a group disables its derived access without deleting governance history. Production Chat audience is managed under Live → Change access, with live directory group membership evaluated at invocation.

Changing provider/client/environment settings requires a Console restart. Changing SCIM group membership or a role mapping takes effect without a new Mission Control key or gateway configuration restart. Rotating the SCIM token requires updating the provisioning client and Console setting together; the old token is rejected after restart.

Verification

  1. Use the SCIM discovery endpoints with the configured bearer token; repeat with an invalid token and confirm denial.
  2. Provision a synthetic user and group, then inspect Identity and People.
  3. Map a role and sign in through the actual OIDC browser callback.
  4. Verify permitted and forbidden Team operations.
  5. Remove membership and verify access changes on the next call, including an existing personal key.
  6. Deactivate the person, confirm session/key denial, and confirm their Agents and audit history remain.
  7. Request a magic link for the provisioned address and confirm no message arrives, then confirm the retained local owner still receives one.
  8. Manually suspend a test person and push active: true; verify the manual suspension still applies.

Failure recovery

Symptom Recovery
Console refuses partial SSO configuration Supply the complete required settings or remove the enterprise configuration, then restart.
Callback/issuer failure Correct the exact provider callback, issuer, tenant and client settings. Start a new sign-in attempt.
User authenticates but cannot join Confirm SCIM provisioned the active identity in the configured organization. OIDC sign-up is intentionally disabled.
Existing-account conflict Keep the local account intact and use a separately provisioned test identity. Do not edit database identity links manually.
User has no Team access Push the correct group membership and map its role to the correct Team scope. Authentication does not grant access by itself.
SCIM update returns 400 Use a supported filter/PATCH path and valid SCIM IDs. Failed group PATCH operations leave membership unchanged.
Reactivation still denied Check manual suspension and current role bindings; directory sync cannot remove an administrator's suspension.
Enterprise access is unavailable Sign in with the retained local owner, correct provider/provisioning settings and verify the test journey before wider rollout.
Provisioned person receives no magic link Expected: provisioned people sign in with the configured provider. Keep a local owner for recovery instead of relying on directory accounts.
Provisioned people were signed out after an upgrade Expected once: the upgrade that made provider sign-in mandatory for directory-provisioned accounts ended their earlier sessions. They sign in again with the configured provider.
SCIM userName update returns 409 The account also belongs to another organization on this deployment, so its address cannot change. Keep the address, or provision a new identity.