Configuration Reference
All options are set under config :ex_scim.
Core
| Key | Default | Description |
|---|---|---|
:base_url | "http://localhost:4000" | Base URL for SCIM endpoints. Falls back to SCIM_BASE_URL env var. |
:storage_strategy | ExScim.Storage.EtsStorage | Module implementing ExScim.Storage.Adapter |
:auth_provider_adapter | required | Module implementing ExScim.Auth.AuthProvider.Adapter |
Resource Mapping
| Key | Default | Description |
|---|---|---|
:user_resource_mapper | ExScim.Users.Mapper.DefaultMapper | Module implementing ExScim.Users.Mapper.Adapter |
:group_resource_mapper | ExScim.Groups.Mapper.DefaultMapper | Module implementing ExScim.Groups.Mapper.Adapter |
Ecto Storage (ex_scim_ecto)
| Key | Default | Description |
|---|---|---|
:storage_repo | required | Your Ecto Repo module |
:user_model | required | Ecto schema module or {Schema, opts} tuple |
:group_model | required | Same format as :user_model |
When using the {Schema, opts} tuple form, available sub-options are:
:preload- list of associations to preload (default[]):lookup_key- primary key field (default:id):filter_mapping- map of SCIM attribute paths to DB columns (default%{}):tenant_key- column used for multi-tenant scoping (defaultnil):field_mapping- map of domain fields to{db_field, to_storage_fn, from_storage_fn}tuples for value transformation (default%{})
config :ex_scim,
storage_strategy: ExScimEcto.StorageAdapter,
storage_repo: MyApp.Repo,
user_model:
{MyApp.Accounts.User,
preload: [:roles],
lookup_key: :uuid,
filter_mapping: %{"emails.value" => :email},
tenant_key: :organization_id,
field_mapping: %{
active: {:status,
fn true -> "active"; false -> "inactive" end,
fn "active" -> true; _ -> false end}
}},
group_model:
{MyApp.Groups.Group,
preload: [:members],
tenant_key: :organization_id}Capabilities
Reported in the ServiceProviderConfig discovery endpoint.
| Key | Default |
|---|---|
:patch_supported | false |
:bulk_supported | true |
:bulk_max_operations | 1000 |
:bulk_max_payload_size | 1_048_576 |
:filter_supported | false |
:filter_max_results | 200 |
:sort_supported | false |
:change_password_supported | false |
:etag_supported | false |
Discovery
| Key | Default | Description |
|---|---|---|
:documentation_uri | nil | URI included in ServiceProviderConfig |
:authentication_schemes | [] | List of scheme maps per RFC 7643 |
:resource_types | User + Group | List of resource type maps |
:schema_modules | Built-in User, EnterpriseUser, Group | Schema definition modules |
Lifecycle & Tenancy
| Key | Default | Description |
|---|---|---|
:lifecycle_adapter | nil | Module implementing ExScim.Lifecycle.Adapter |
:tenant_resolver | nil | Module implementing ExScim.Tenant.Resolver |
Authorization Scopes
Scopes are strings in the ExScim.Scope struct's :scopes list, populated by your AuthProvider.Adapter when validating a token or credentials.
Standard scopes
| Scope | Endpoints | Actions |
|---|---|---|
scim:read | /Users, /Groups, /Schemas, /ResourceTypes, /ServiceProviderConfig | GET (list, show, search) |
scim:create | /Users, /Groups, /Bulk (POST operations) | POST |
scim:update | /Users, /Groups, /Bulk (PUT/PATCH operations) | PUT, PATCH |
scim:delete | /Users, /Groups, /Bulk (DELETE operations) | DELETE |
/Me scopes
| Scope | Action |
|---|---|
scim:me:read | GET /Me |
scim:me:create | POST /Me |
scim:me:update | PUT /Me, PATCH /Me |
scim:me:delete | DELETE /Me |
Bulk operations
For /Bulk, scope is enforced per individual operation rather than on the request as a whole. A caller with only scim:create may submit a bulk request containing POST operations; any PUT, PATCH, or DELETE operations in the same payload will return a 403 operation result (and count toward failOnErrors).
Example scope lists
# Read-only client
scopes: ["scim:read"]
# Provisioning client - can create and update users, but not delete them
scopes: ["scim:read", "scim:create", "scim:update"]
# Full-access admin client
scopes: ["scim:read", "scim:create", "scim:update", "scim:delete"]
# Self-service user (Me endpoint only)
scopes: ["scim:me:read", "scim:me:update"]Multi-Tenancy
Multi-tenancy is opt-in. When no tenant_resolver is configured (or scope.tenant_id is nil), the system operates in single-tenant mode with no isolation applied.
To enable it, wire up three pieces:
- Tenant resolver - implement
ExScim.Tenant.Resolverto extract a tenant identifier from the request (header, subdomain, path, etc.). - Phoenix plug - add
ExScimPhoenix.Plugs.ScimTenantto your SCIM pipeline afterScimAuth. - Ecto tenant key - set
:tenant_keyon your model tuples so queries are scoped and creates inject the tenant ID.
# 1. Resolver
defmodule MyApp.TenantResolver do
@behaviour ExScim.Tenant.Resolver
@impl true
def resolve_tenant(conn, _scope) do
case Plug.Conn.get_req_header(conn, "x-tenant-id") do
[tenant_id] -> {:ok, tenant_id}
_ -> {:error, :missing_tenant}
end
end
end
# 2. Config
config :ex_scim,
tenant_resolver: MyApp.TenantResolver,
user_model: {MyApp.Accounts.User, tenant_key: :organization_id},
group_model: {MyApp.Groups.Group, tenant_key: :organization_id}
# 3. Pipeline
# Add: plug ExScimPhoenix.Plugs.ScimTenantThe resolver can optionally implement tenant_scim_base_url/1 to generate tenant-specific resource location URLs (e.g., https://acme.example.com/scim/v2).
Custom Adapters
Storage Adapter
Implement the ExScim.Storage.Adapter behaviour to use a custom data store:
defmodule MyApp.CustomStorage do
@behaviour ExScim.Storage.Adapter
def get_user(id), do: # your implementation
def create_user(user_data), do: # your implementation
# ... other callbacks
endResource Mapper
Implement ExScim.Users.Mapper.Adapter or ExScim.Groups.Mapper.Adapter to control how domain structs map to SCIM JSON:
defmodule MyApp.UserMapper do
@behaviour ExScim.Users.Mapper.Adapter
def from_scim(scim_data) do
%MyApp.User{
username: scim_data["userName"],
email: get_primary_email(scim_data["emails"])
}
end
def to_scim(%MyApp.User{} = user, _opts) do
%{
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id" => user.id,
"userName" => user.username,
"emails" => format_emails(user.email)
}
end
endEndpoints
All endpoints are served under the scope you configure (typically /scim/v2).
Users
| Method | Path | Description | RFC |
|---|---|---|---|
GET | /Users | List with filtering, sorting, pagination | §3.4.2 |
POST | /Users | Create | §3.3 |
GET | /Users/{id} | Fetch by ID | §3.4.1 |
PUT | /Users/{id} | Replace | §3.5.1 |
PATCH | /Users/{id} | Partial update (JSON Patch) | §3.5.2 |
DELETE | /Users/{id} | Delete | §3.6 |
Groups and Me follow the same pattern. See RFC 7644 §3.11 for Me endpoint details.
Other
| Method | Path | Description | RFC |
|---|---|---|---|
POST | /.search | Cross-resource search | §3.4.3 |
POST | /Bulk | Bulk operations | §3.7 |
GET | /ServiceProviderConfig | Server capabilities | §4 |
GET | /ResourceTypes | Supported resource types | §4 |
GET | /Schemas | Schema definitions | §4 |