Skip to main content

Configure SCIM Provisioning with Microsoft Entra ID

This guide describes how to configure Microsoft Entra ID to provision users and groups to AtScale Keycloak using Keycloak's native scim-api.

This integration supports the following:

  • OAuth 2.0 client credentials authentication
  • User provisioning, deprovisioning, and profile updates
  • Group provisioning and membership synchronization
  • Import/read operations from Keycloak

Integration overview​

The integration uses the following flow: Microsoft Entra ID > HTTPS > Keycloak SCIM API.

Keycloak exposes the SCIM API at https://<atscale-host>/auth/realms/atscale/scim/v2. The OAuth token endpoint is https://<atscale-host>/auth/realms/atscale/protocol/openid-connect/token.

Entra ID authenticates using the OAuth 2.0 client credentials grant and sends a bearer token when calling the SCIM API.

Prerequisites​

Before configuring SCIM provisioning with Entra ID, ensure you meet the following requirements:

  • You have administrator access for both AtScale and Entra ID.
  • AtScale is running Keycloak 26.7.x or later.
  • The SCIM endpoint is reachable from Entra ID over HTTPS.

Enable and configure SCIM provisioning in Keycloak​

First, you must enable and configure SCIM in AtScale's Keycloak instance.

  1. Enable the SCIM API in Keycloak. To do this, add the scim-api to the Keycloak feature's startup arguments. For example:

    keycloak:
    extraStartupArgs: --import-realm --features=token-exchange,admin-fine-grained-authz:v1,opentelemetry-logs,scim-api --log=console --hostname-backchannel-dynamic true

    Once this has been set, the Keycloak startup log should show:

    Preview features enabled: scim-api:v1
  2. Enable SCIM for the atscale realm in Keycloak:

    1. Log in to Design Center and click Security in the sidebar. Keycloak opens.

    2. In the sidebar, under Configure, click Realm settings.

    3. On the General tab, enable the SCIM API option.

      The resulting SCIM base URL is https://<atscale-host>/auth/realms/atscale/scim/v2. Note that the /auth portion of the URL is required.

  3. Create a Keycloak User Profile attribute for the SCIM externalId field. For example:

    {
    "name": "myExternalId",
    "displayName": "External ID",
    "multivalued": false,
    "permissions": {
    "view": ["admin"],
    "edit": ["admin"]
    },
    "annotations": {
    "kc.scim.schema.attribute": "externalId"
    }
    }
    Important

    The annotation key must be kc.scim.schema.attribute. Using scim.schema.attribute will not work. This mapping is required so that the SCIM externalId value can be stored and subsequently returned by Keycloak.

  4. Create a confidential client for SCIM:

    1. In the sidebar, click Clients.

    2. Click Create client.

    3. On the General settings tab, complete the following fields:

      • Client type: Leave this set to OpenID Connect.
      • Client ID: Enter scim-client.
    4. Click Next.

    5. On the Capability config tab, complete the following fields:

      • Client authentication: Enable this option.
      • Authentication flow: Disable Standard flow (if it is not required for this client), and enable Service accounts roles.
  5. Assign the scim-client the manage-users role. This provides the permissions required for the SCIM user lifecycle.

    1. Open the scim-client you just created and go to the Service account roles tab.
    2. Click Assign role.
    3. In the Assign roles to scim-client window, select the manage-users role.
    4. Click Assign.
  6. Configure the SCIM audience. The Entra ID provisioning configuration does not provide a field for specifying an OAuth audience, so you need to add the SCIM audience to the Keycloak access token. Without this, SCIM requests will be rejected with 401 Unauthorized, even if the OAuth token is valid.

    1. Open the scim-client and go to the Client scopes tab.

    2. Click the scim-client-dedicated scope.

    3. Click Configure a new mapper and select Audience.

    4. Complete the following fields:

      • Name: Enter a name for the mapper.
      • Included Custom Audience: Enter the SCIM base URL, https://<atscale-host>/auth/realms/atscale/scim/v2.
      • Add to access token: Enable this option.
    5. Click Next, then click Save.

  7. Obtain the client secret for the scim-client. You will need this later when configuring Entra ID.

    1. Open the scim-client.

    2. Go to the Credentials tab and copy the Client Secret.

      Important

      Do not regenerate the Client Secret.

Enable and configure SCIM provisioning in Microsoft Entra ID​

Once you have enabled SCIM in Keycloak, you can configure it in Entra ID.

Add an application in Entra ID​

First, create an Enterprise application in Entra ID.

  1. In the Microsoft Entra admin center, go to Enterprise apps and click New application.
  2. Click Create your own application.
  3. Give the app a name.
  4. Select Integrate any other application you don't find in the gallery (Non-gallery).
  5. Click Create.

Configure the SCIM connection​

Next, configure a SCIM connection for the application.

  1. Open the application you created above and go to Provisioning > Connect your application.

  2. Complete the following fields:

    • Authentication method: Set to OAuth2 client credentials grant.
    • Tenant URL: Enter https://<atscale-host>/auth/realms/atscale/scim/v2.
    • OAuth token endpoint: Enter https://<atscale-host>/auth/realms/atscale/protocol/openid-connect/token.
    • Client identifier: Enter scim-client.
    • Client secret: Enter the scim-client secret you copied from Keycloak.
  3. Click Test connection. The connection must succeed before continuing.

Configure user attribute mappings​

Next, you need to configure user attribute mappings in Entra ID. Go to Provisioning > Mappings > Provision Microsoft Entra ID Users and review the default mappings. Be sure to set the following.

SCIM AttributeRecommended Entra ID Source
userNameUser Principal Name
externalIdobjectId1
name.givenNameGiven name
name.familyNameSurname
emails[type eq "work"].valuemail or userPrincipalName2

1 It is recommended that you map the SCIM externalId attribute to objectId in Entra ID. This is because the objectId is a stable value, as opposed to email-based values, which can change. This allows the provisioning system to maintain a stable identity match.

2 Keycloak requires an email value for user creation when the email attribute is configured as required. Some Entra ID users, especially cloud-only test users, may not have a populated mail attribute. In that case, the default mapping of emails[type eq "work"].value to mail can result in the emails attribute being omitted. For environments where mail is not consistently populated, you should map emails[type eq "work"].value to userPrincipalName to ensure the SCIM user always has an email value.

Enable provisioning​

After the connection and user attribute mappings have been configured, you can enable the provisioning job. This job is responsible for synchronizing the complete user/group state.

  1. In Entra ID, go to Manage > Provisioning.
  2. Set Provisioning Status to On.
  3. Click Save.
Note

Be sure to use the Provisioning Status control under Manage > Provisioning, rather than the Start provisioning button shown on the application Overview page.

Assign users and groups​

Use the Enterprise application assignment controls to determine which Entra ID users and groups are provisioned to Keycloak. AtScale recommends the following rollout strategy:

  1. Start with a dedicated test user.
  2. Assign the test user to the Enterprise application.
  3. Run Provision on demand.
  4. Verify the user in Keycloak.
  5. Validate profile updates.
  6. Validate deprovisioning.
  7. Add the required production users/groups.

Use application assignments to control the provisioning scope.

Working with SCIM provisioning​

User provisioning​

Entra ID can provision a user to Keycloak using the SCIM /Users endpoint. A typical SCIM user contains the following attributes:

{
"userName": "user@example.com",
"externalId": "<entra-object-id>",
"active": true,
"name": {
"givenName": "First",
"familyName": "Last"
}
}

After provisioning, the user should be visible in Keycloak.

User profile updates​

Changes to mapped Entra ID attributes are propagated to Keycloak through SCIM. For example, Entra ID Surname > SCIM name.familyName > Keycloak lastName.

Use Provision on demand for an immediate validation, or allow the normal provisioning cycle to process the change.

User deprovisioning​

When an assigned Entra ID user is disabled or removed from the provisioning scope, Entra ID sends the corresponding SCIM deactivation. This results in the user's account being deactivated in Keycloak. Note that the user is not deleted.

Deactivation prevents subsequent authentication, but existing Keycloak sessions/tokens are not necessarily revoked immediately. Existing sessions can remain valid until their normal expiration.

Group provisioning​

Entra ID can provision groups to Keycloak through the SCIM /Groups endpoint. A group creation request contains the following attributes:

{
"displayName": "example-group",
"externalId": "<entra-group-object-id>"
}

Entra ID does not include the complete members array when creating the group. This is compatible with native Keycloak SCIM.

Group membership synchronization​

Group membership is synchronized separately from group object creation. The expected flow is as follows:

  1. Entra ID creates the group in Keycloak.
  2. The provisioning cycle runs.
  3. Entra ID synchronizes the group membership.
  4. Keycloak reflects the membership on both the group and user resources.
Important

Provision on demand can create/update the group object, but group membership is synchronized by the scheduled provisioning cycle. To validate group membership:

  1. In Entra ID, go to Manage > Provisioning.
  2. Set Provisioning Status to On.
  3. Click Save.
  4. Allow the provisioning cycle to run.
  5. Verify the membership in Keycloak.

Import/read operations​

When determining whether users already exist, Entra ID reads existing Keycloak users through the SCIM /Users endpoint. This allows Entra ID to read users from Keycloak, match existing users, determine the provisioning scope, and maintain the provisioning relationship.

During the provisioning process, users always move from Entra ID to Keycloak. Keycloak does not independently pull users from Entra ID.

Expected supported lifecycle​

The supported Entra ID to Keycloak lifecycle for users is as follows:

  1. A user is created in Entra ID.
  2. The user is provisioned via SCIM.
  3. The user is created in Keycloak.
  4. The user's profile changes.
  5. The corresponding Keycloak user is updated.
  6. The Entra ID user is disabled or removed from scope.
  7. The user is deactivated via SCIM.
  8. The user is disabled in Keycloak.

For groups, the lifecycle is:

  1. A group is created in Entra ID.
  2. The group is created in SCIM.
  3. The provisioning cycle runs.
  4. Group membership is synchronized.
  5. Keycloak group membership is updated.

For a production deployment:

  • Use the native Keycloak scim-api.
  • Use a dedicated confidential scim-client.
  • Use OAuth 2.0 client credentials.
  • Store the client secret securely.
  • Grant only the permissions required for SCIM provisioning.
  • Configure the SCIM audience mapper.
  • Map the Entra ID objectId attribute to SCIM's externalId.
  • Ensure every provisioned user has a valid email value.
  • Exclude Keycloak/AtScale administrative users from SCIM assignments.
  • Start with a small test assignment before enabling production users.
  • Keep group membership synchronization enabled through the normal provisioning cycle.

Validation checklist​

Use the following checklist when configuring a new environment.

CapabilityExpected Result
OAuth 2.0 client credentialsSupported
Test connectionSuccessful
Read users/matchingSupported
User provisioningSupported
User profile updatesSupported
User deprovisioningSupported
Group creationSupported
Group membership synchronizationSupported
Stable externalIdSupported
Import/read from KeycloakSupported

Known limitations and important behavior​

Email must be available​

Keycloak requires an email value when the email attribute is configured as required. If an Entra ID user has no mail value, map emails[type eq "work"].value to userPrincipalName.

Use a stable externalId​

It is recommended that you map externalId to objectId. Avoid using mutable values such as email addresses as the primary external identity.

Keycloak admin users should not be provisioned through SCIM​

Users holding Keycloak administrative roles can be protected from SCIM modification. Do not include AtScale/Keycloak administrative accounts in the Entra ID SCIM assignment scope. Use dedicated application assignments for regular users.

Active sessions are not immediately revoked​

SCIM deactivation changes the Keycloak user state to disabled. It does not necessarily invalidate already-issued sessions or tokens immediately.

Troubleshooting​

401 Unauthorized error from the SCIM API​

Verify the following:

  • Keycloak scim-api is enabled.
  • Realm SCIM API is enabled.
  • The OAuth client is confidential.
  • The service account has the manage-users role.
  • The client secret is correct.
  • The audience mapper is configured.
  • The audience exactly matches https://<atscale-host>/auth/realms/atscale/scim/v2.

User creation fails with a "Please specify email" error​

Check the Entra ID attribute mapping of emails[type eq "work"].value. If the user's mail attribute is empty, map the source to userPrincipalName.

Group is created but membership is missing​

Check that the provisioning job is enabled:

  1. In Entra ID, go to Manage > Provisioning.
  2. Set Provisioning Status to On.
  3. Click Save.

Group creation and group membership synchronization are separate operations. Membership is handled by the provisioning cycle.

Existing users are not matched​

Check the externalId configuration. AtScale recommends mapping objectId in Entra ID to externalId. Also verify that Keycloak has the User Profile attribute annotated with kc.scim.schema.attribute = externalId.