This project provides a SCIM 2.0-compliant extension for Keycloak, enabling SCIM-based user and group provisioning. It supports:
- Realm-level SCIM APIs:
/realms/{realm}/scim/v2 - Organization-level SCIM APIs (Keycloak 26+ with Organizations):
/realms/{realm}/scim/v2/organizations/{organizationId}
- Prerequisites
- Installation
- Configuration
- Authentication
- Vendor Configuration Guides
- Identity Provider Linking
- SCIM-Managed Users
- License
- Keycloak: This extension is developed for Keycloak 26.3.5. It may work with other versions, but compatibility is not guaranteed.
- Java: Java 21 is required to build the project.
You can reference the JAR file from a GitHub Release directly in your init container or Dockerfile.
For example, using a Helm values.yaml:
extraInitContainers: |
- name: download-scim-plugin
image: alpine:latest
command:
- sh
- -c
- >
apk add --no-cache curl &&
curl -L -o /extensions/keycloak-scim-server-<version>.jar https://github.com/Metatavu/keycloak-scim-server/releases/download/v<version>/keycloak-scim-server-<version>.jar
volumeMounts:
- name: extensions
mountPath: /extensions
extraVolumeMounts: |
- name: extensions
mountPath: /opt/keycloak/providers
extraVolumes: |
- name: extensions
emptyDir: {}Download the JAR file from GitHub packages.
- Download the latest JAR from: GitHub Packages
- Copy it to your Keycloak instance:
cp keycloak-scim-server-*.jar $KEYCLOAK_HOME/providers/- Restart Keycloak.
- Build the extension:
./gradlew build- Copy the built JAR file from
build/libs/keycloak-scim-server-<version>.jarto the Keycloak providers directory:
cp build/libs/keycloak-scim-server-*.jar $KEYCLOAK_HOME/providers/All settings can be applied at three levels. Settings at a more specific level override broader ones (organization > realm > instance).
Configuration on instance level is done by defining environment variables on the Keycloak server.
| Setting | Description |
|---|---|
SCIM_AUTHENTICATION_MODE |
Authentication mode for SCIM API. Possible values are KEYCLOAK and EXTERNAL. If not set the server will respond unauthorized for all requests. |
SCIM_EXTERNAL_ISSUER |
Issuer for external JWT authentication. Used to validate the JWT token issuer claim. |
SCIM_EXTERNAL_AUDIENCE |
Audience for external JWT authentication. Used to validate the JWT token audience claim. |
SCIM_EXTERNAL_JWKS_URI |
JWKS URI for external JWT authentication. Used to fetch public keys for JWT token signature validation. |
SCIM_EXTERNAL_SHARED_SECRET |
Shared secret in PHC String Format used for bearer token authentication/validation. |
SCIM_BASIC_AUTH_USERNAME |
Username for HTTP Basic authentication. |
SCIM_BASIC_AUTH_PASSWORD |
Password hash in PHC String Format for HTTP Basic authentication. |
SCIM_LINK_IDP |
Enables support for linking realm identity provider with user. |
SCIM_IDENTITY_PROVIDER_ALIAS |
Alias of Identity Provider to be linked to the user. |
The following REST call can be made through the Keycloak Admin API to store settings as realm attributes. Realm-level settings override instance-level settings.
PUT /admin/realms/{realm}
{
"attributes": {
"scim.authentication.mode": "EXTERNAL|KEYCLOAK",
"scim.external.issuer": "string",
"scim.external.jwks.uri": "string",
"scim.external.audience": "string",
"scim.external.shared.secret": "string",
"scim.external.shared.secret.hash.algorithm": "string",
"scim.basic.auth.username": "string",
"scim.basic.auth.password": "string",
"scim.link.idp": "true|false",
"scim.identity.provider.alias": "string"
}
}Configuration on organization level is done by defining organization attributes in the Keycloak server. Only EXTERNAL authentication mode is supported at the organization level.
| Setting | Description |
|---|---|
SCIM_AUTHENTICATION_MODE |
Must be EXTERNAL. Only external authentication is supported at the organization level. |
SCIM_EXTERNAL_ISSUER |
Issuer for external JWT authentication. |
SCIM_EXTERNAL_AUDIENCE |
Audience for external JWT authentication. |
SCIM_EXTERNAL_JWKS_URI |
JWKS URI for external JWT authentication. |
SCIM_EXTERNAL_SHARED_SECRET |
Shared secret in PHC String Format for bearer token authentication. |
SCIM_BASIC_AUTH_USERNAME |
Username for HTTP Basic authentication. |
SCIM_BASIC_AUTH_PASSWORD |
Password hash in PHC String Format for HTTP Basic authentication. |
SCIM_LINK_IDP |
Enables support for linking organization identity provider with user. |
SCIM_EMAIL_AS_USERNAME |
Forces server to use email as username instead of actual username. When enabled, username will be unaffected by update operations. Organization-level only. |
The SCIM server supports four authentication methods. For EXTERNAL mode, the method is determined automatically based on the authorization header and configuration.
Uses Keycloak's built-in service account authentication. The SCIM client authenticates using an OAuth2 client credentials flow against Keycloak itself, and the resulting access token is validated natively.
Required settings:
| Setting | Value |
|---|---|
SCIM_AUTHENTICATION_MODE |
KEYCLOAK |
Requirements:
- A Keycloak client with Service Accounts Enabled
- The client's service account must have the
scim-accessrealm role
Example: The SCIM client obtains a token via the Keycloak token endpoint and sends it as a bearer token:
Authorization: Bearer <keycloak-access-token>
Validates a JWT bearer token issued by an external identity provider. The token signature is verified against public keys fetched from the configured JWKS endpoint, and the issuer and audience claims are validated.
Required settings:
| Setting | Value |
|---|---|
SCIM_AUTHENTICATION_MODE |
EXTERNAL |
SCIM_EXTERNAL_ISSUER |
Expected iss claim (e.g., https://sts.windows.net/<tenant-id>/) |
SCIM_EXTERNAL_AUDIENCE |
Expected aud claim |
SCIM_EXTERNAL_JWKS_URI |
URL to the JWKS endpoint for public keys |
Example request:
Authorization: Bearer <jwt-token>
Validates a static bearer token against a pre-configured hash. The client sends the raw secret as a bearer token, and the server verifies it against the stored hash using Keycloak's password hashing infrastructure.
Required settings:
| Setting | Value |
|---|---|
SCIM_AUTHENTICATION_MODE |
EXTERNAL |
SCIM_EXTERNAL_SHARED_SECRET |
Hashed token in PHC String Format (e.g., Argon2id) |
The hash must be in PHC String Format using a Keycloak-supported hash algorithm (e.g., argon2id, pbkdf2-sha512).
Example: If the shared secret is my-secret-token, hash it using Argon2id and configure the resulting PHC string:
SCIM_EXTERNAL_SHARED_SECRET=$argon2id$v=19$m=16,t=2,p=1$<salt>$<hash>
The client then sends the raw secret:
Authorization: Bearer my-secret-token
Validates credentials sent via HTTP Basic Authentication. The client sends a Base64-encoded username:password pair, and the server verifies the username against the configured value and the password against a stored hash.
Required settings:
| Setting | Value |
|---|---|
SCIM_AUTHENTICATION_MODE |
EXTERNAL |
SCIM_BASIC_AUTH_USERNAME |
Expected username |
SCIM_BASIC_AUTH_PASSWORD |
Password hash in PHC String Format (e.g., Argon2id) |
The password hash must be in PHC String Format using a Keycloak-supported hash algorithm.
Example: If the username is scim-admin and password is my-password, hash the password using Argon2id and configure:
SCIM_BASIC_AUTH_USERNAME=scim-admin
SCIM_BASIC_AUTH_PASSWORD=$argon2id$v=19$m=16,t=2,p=1$<salt>$<hash>
The client then sends:
Authorization: Basic <base64("scim-admin:my-password")>
Entra ID supports two authentication options when provisioning to this SCIM server:
- SCIM_AUTHENTICATION_MODE enables external authentication support for the SCIM server. In this case the external authentication source will be the Microsoft Entra ID.
- SCIM_EXTERNAL_ISSUER ensures the JWT token was issued by your tenant.
- SCIM_EXTERNAL_AUDIENCE must be exactly 8adf8e6e-67b2-4cf2-a259-e3dc5476c621 — this is the default audience used by Entra ID for non-gallery applications.
- SCIM_EXTERNAL_JWKS_URI allows Keycloak to fetch public keys for token validation.
Entra ID sends a bearer token signed by Microsoft's identity platform. The SCIM server validates it using Microsoft's JWKS endpoint.
Keycloak settings:
| Setting | Value |
|---|---|
SCIM_AUTHENTICATION_MODE |
EXTERNAL |
SCIM_EXTERNAL_ISSUER |
https://sts.windows.net/<your-tenant-id>/ |
SCIM_EXTERNAL_AUDIENCE |
8adf8e6e-67b2-4cf2-a259-e3dc5476c621 |
SCIM_EXTERNAL_JWKS_URI |
https://login.microsoftonline.com/<your-tenant-id>/discovery/v2.0/keys |
Replace <your-tenant-id> with your Azure tenant ID. The audience 8adf8e6e-67b2-4cf2-a259-e3dc5476c621 is the default used by Entra ID for non-gallery applications.
Entra ID sends a static bearer token that you generate and configure on both sides.
Keycloak settings:
| Setting | Value |
|---|---|
SCIM_AUTHENTICATION_MODE |
EXTERNAL |
SCIM_EXTERNAL_SHARED_SECRET |
<token_hashed_value> |
Replace <token_hashed_value> with the PHC String Format hash of your token.
- Sign in to the Azure portal.
- Go to Identity > Applications > Enterprise applications.
- Click + New application > + Create your own application.
- Enter a name for your application (e.g., "My Keycloak SCIM").
- Choose Integrate any other application you don't find in the gallery.
- Click Create.
- In the application's left-hand menu, select Provisioning.
- Click + New configuration.
- Fill in the following:
- Tenant URL (realm):
https://mykeycloak.example.com/realms/my-realm/scim/v2 - Tenant URL (organization):
https://mykeycloak.example.com/realms/my-realm/scim/v2/organizations/{organizationId} - Secret Token: Leave empty for JWT authentication (Option A), or enter the raw shared secret (Option B).
- Tenant URL (realm):
- Click Test Connection to verify the SCIM endpoint.
- Click Create.
- Navigate to Attribute Mapping (Preview).
- Open Provision Microsoft Entra ID Groups.
- Set Enabled to No.
- Click Save.
- Go back → open Provision Microsoft Entra ID Users.
- Open Provision Microsoft Entra ID Users.
- Define mappings, following are required for Keycloak extension:
- userName
- active
- emails[type eq "work"].value
- name.givenName
- name.familyName
- Click Save.
- Go back to Provisioning.
- Set Provisioning Status to On.
- Click Save.
- Reload the page to ensure the configuration was saved.
- Navigate to Manage > Users and groups > + Add user/group.
- Select the user you want to provision and click Assign.
- Navigate to Provision on demand.
- Find the user you just assigned.
- Click on the user and select Provision.
- Verify that the provisioning completes successfully.
For more information, refer to the following documents:
https://learn.microsoft.com/en-us/entra/identity/saas-apps/tutorial-list
- Go back and open Provision Microsoft Entra ID Users.
- Define mappings. The following are required:
userNameactiveemails[type eq "work"].valuename.givenNamename.familyName
- Click Save.
- Go back to Provisioning.
- Set Provisioning Status to On.
- Click Save.
- Reload the page to ensure the configuration was saved.
- Navigate to Manage > Users and groups > + Add user/group.
- Select the user you want to provision and click Assign.
- Navigate to Provision on demand.
- Find the user you just assigned.
- Click on the user and select Provision.
- Verify that the provisioning completes successfully.
For more information, refer to the Microsoft Entra ID SCIM provisioning documentation.
Okta supports three authentication methods when provisioning to a SCIM server. All three are supported by this extension.
For general Okta SCIM setup, refer to the Okta SCIM provisioning documentation.
Okta sends a static bearer token in the Authorization header. This uses the shared secret authentication method.
Keycloak settings:
| Setting | Value |
|---|---|
SCIM_AUTHENTICATION_MODE |
EXTERNAL |
SCIM_EXTERNAL_SHARED_SECRET |
PHC String Format hash of your API token |
Okta setup:
- In the Okta Admin Console, go to Applications > Applications.
- Select your SCIM application.
- Go to the Provisioning tab and click Configure API Integration.
- Select HTTP Header as the authentication mode.
- In the Authorization field, paste the raw API token (the unhashed value).
- Set the SCIM connector base URL to:
https://mykeycloak.example.com/realms/my-realm/scim/v2 - Click Test API Credentials to verify.
- Click Save.
Okta sends credentials via HTTP Basic Authentication (Base64-encoded username:password).
Keycloak settings:
| Setting | Value |
|---|---|
SCIM_AUTHENTICATION_MODE |
EXTERNAL |
SCIM_BASIC_AUTH_USERNAME |
The username you want Okta to authenticate with |
SCIM_BASIC_AUTH_PASSWORD |
PHC String Format hash of the password |
Okta setup:
- In the Okta Admin Console, go to Applications > Applications.
- Select your SCIM application.
- Go to the Provisioning tab and click Configure API Integration.
- Select Basic Auth as the authentication mode.
- Enter the Username and Password (the raw, unhashed values).
- Set the SCIM connector base URL to:
https://mykeycloak.example.com/realms/my-realm/scim/v2 - Click Test API Credentials to verify.
- Click Save.
Okta obtains an access token from an OAuth2 token endpoint, then sends it as a bearer token. Since Keycloak is itself an OAuth2 provider, you can point Okta at Keycloak's token endpoint. The resulting JWT is then validated by the SCIM server using the JWKS authentication method.
Keycloak prerequisites:
- Create a Keycloak client with Client Authentication enabled (confidential client).
- Enable Service Accounts Enabled on the client.
- Note the client ID and client secret.
Keycloak settings:
| Setting | Value |
|---|---|
SCIM_AUTHENTICATION_MODE |
EXTERNAL |
SCIM_EXTERNAL_ISSUER |
https://mykeycloak.example.com/realms/my-realm |
SCIM_EXTERNAL_AUDIENCE |
The client ID of the Keycloak client, or account |
SCIM_EXTERNAL_JWKS_URI |
https://mykeycloak.example.com/realms/my-realm/protocol/openid-connect/certs |
Okta setup:
- In the Okta Admin Console, go to Applications > Applications.
- Select your SCIM application.
- Go to the Provisioning tab and click Configure API Integration.
- Select OAuth2 as the authentication mode.
- Configure the following:
- Access Token Endpoint:
https://mykeycloak.example.com/realms/my-realm/protocol/openid-connect/token - Client ID: The Keycloak client ID
- Client Secret: The Keycloak client secret
- Access Token Endpoint:
- Set the SCIM connector base URL to:
https://mykeycloak.example.com/realms/my-realm/scim/v2 - Click Test API Credentials to verify.
- Click Save.
Identity Provider linking with Entra ID requires a few additional configuration steps on both the Entra and Keycloak sides.
Step 1: Add externalId
In the Keycloak admin console, ensure that you have an externalId attribute defined in your Realm Settings > User Profile. This attribute is used to store the user's external ID in Keycloak and without it the Identity Provider linking will fail.
Step 2: Map externalId in SCIM provisioning
In Entra ID, make sure that the objectId from Entra ID is mapped into the SCIM externalId field:
- Navigate to your Enterprise Application > Provisioning > Attribute Mapping (Preview) > Provision Microsoft Entra ID Users.
- Click Add New Mapping.
- Set:
- Source attribute:
objectId - Target attribute:
externalId
- Source attribute:
- Click Save.
This ensures that during SCIM provisioning, the Entra objectId is stored in Keycloak as the user's externalId, which will later be used for identity linking.
Step 3: Configure Keycloak Identity Provider to Use Object ID
Configure your Entra ID Identity Provider in Keycloak to use the oid claim from the login token instead of the default sub claim (which is app-specific).
- Navigate to Identity Providers > select your Entra ID provider.
- Go to the Mappers tab.
- Click Add Mapper.
- Fill in the mapper details:
- Name:
map_oid_as_brokerid(or any descriptive name) - Sync Mode: Force
- Mapper Type: Username Template Importer
- Template:
${CLAIM.oid} - Target: BROKER_ID
- Name:
- Click Save.
This mapper tells Keycloak to use the Entra oid claim as the Broker ID, ensuring that the login user is matched correctly with the SCIM-provisioned user.
Step 4: Enable Identity Provider Linking in SCIM
Add the following settings to your SCIM configuration:
SCIM_LINK_IDP=true
If you want to link users to a realm-level identity provider, also add:
SCIM_IDENTITY_PROVIDER_ALIAS=<your-idp-alias>
This ensures that when a user is provisioned via SCIM, a corresponding Identity Provider link is created automatically based on the externalId / oid.
By default, the SCIM server only exposes users who are explicitly assigned the scim-managed role within the realm. This ensures that only users intended to be managed through SCIM are returned or modifiable via SCIM API operations.
This prevents accidental exposure or modification of users that were:
- created manually via the Keycloak admin UI
- imported from external identity providers
- or otherwise not intended to be managed through SCIM
If you want to expose all users (i.e., bypass filtering), you can simply assign the scim-managed role to every user. This effectively disables the filter, making the SCIM behavior equivalent to an unfiltered list.
This role-based filtering applies to all SCIM operations, including:
GET /UsersPATCH /Users/{id}DELETE /Users/{id}
Users without the scim-managed role will be invisible to SCIM clients -- they won't be listed, updated, or removed through SCIM.
This filtering mechanism is designed to improve safety, especially in complex deployments involving federated users, legacy accounts, or overlapping identity sources (such as Entra ID + local users).
This design does mean that provisioning a user through SCIM who previously existed without the role may cause conflicts or provisioning failures if role assignment isn't handled correctly. However, this is a deliberate design choice to provide fine-grained control over which users are SCIM-visible.
This section explains how to provision custom user attributes (e.g., job, department, employeeId) from an external
identity provider (such as Microsoft Entra ID) into Keycloak via SCIM.
By default, the SCIM server only exposes built-in user attributes (userName, email, name.givenName,
name.familyName, active). To provision additional custom attributes, you need to configure Keycloak to accept
unmanaged attributes and define identity provider mappers that tell the SCIM server which attributes to expose.
Before custom attributes can be provisioned, ensure the following conditions are met:
-
Unmanaged Attribute Policy: The realm's User Profile must have
UnmanagedAttributePolicyset toENABLED. This allows Keycloak to store attributes that are not explicitly defined in the User Profile schema. -
Identity Provider Alias: The
SCIM_IDENTITY_PROVIDER_ALIASenvironment variable (or realm/organization attribute) must be configured with the alias of your identity provider. -
Identity Provider Mappers: User attribute mappers must be configured on the identity provider to define which attributes should be provisioned.
When a SCIM provisioning request is received, the SCIM server:
- Checks if
UnmanagedAttributePolicyis set toENABLEDin the realm - Looks up the identity provider specified by
SCIM_IDENTITY_PROVIDER_ALIAS - Reads all user attribute mappers configured on that identity provider
- Exposes those mapped attributes as valid SCIM attributes that can be provisioned
This means the identity provider mappers serve as the source of truth for which custom attributes are available via SCIM.
- Navigate to Realm Settings > User Profile
- Click JSON Editor
- Add or update the
unmanagedAttributePolicyfield:
{
"unmanagedAttributePolicy": "ENABLED",
"attributes": [
]
}- Click Save
Add the following environment variable to your Keycloak server:
SCIM_IDENTITY_PROVIDER_ALIAS=<your-idp-alias>Or set it as a realm attribute via the Admin API:
{
"attributes": {
"scim.identity.provider.alias": "<your-idp-alias>"
}
}The alias must match the alias of your configured identity provider (e.g., entra-id, keycloak-oidc).
For each custom attribute you want to provision via SCIM:
- Navigate to Identity Providers > select your provider (e.g., Entra ID)
- Go to the Mappers tab
- Click Add Mapper
- Configure the mapper:
| Field | Value |
|---|---|
| Name | A descriptive name (e.g., map-job-attribute) |
| Sync Mode | INHERIT or FORCE |
| Mapper Type | Attribute Importer |
| Claim (OIDC) | The claim name from the external IdP (e.g., jobTitle) |
| User Attribute | The Keycloak attribute name (e.g., job) |
- Click Save
- Repeat for each attribute you want to provision (e.g.,
department,employeeId)
In your SCIM client (e.g., Microsoft Entra ID Enterprise Application):
- Navigate to Provisioning > Attribute Mapping (Preview) > Provision Microsoft Entra ID Users
- Click Add New Mapping
- Map the source attribute to the custom SCIM attribute:
| Source Attribute | Target Attribute |
|---|---|
jobTitle |
job |
department |
department |
- Click Save
The target attribute name must match the User Attribute value configured in the Keycloak identity provider mapper.
This example shows how to provision the jobTitle attribute from Microsoft Entra ID to Keycloak as a job attribute.
Keycloak Configuration:
- Enable
UnmanagedAttributePolicyin the realm's User Profile - Set
SCIM_IDENTITY_PROVIDER_ALIAS=entra-id - Create an identity provider mapper:
- Mapper Type: Attribute Importer
- Claim:
jobTitle - User Attribute:
job
Microsoft Entra Configuration:
- In the Enterprise Application provisioning settings, add a mapping:
- Source attribute:
jobTitle - Target attribute:
job
- Source attribute:
When Entra ID provisions a user, the job attribute will be stored in Keycloak and available on the user's attributes.