SCIM

SCIM 2.0 provisioning endpoints.

This page documents the current Ruby port behavior. Ruby uses snake_case option names and auth.api method names; HTTP paths and JSON keys keep the upstream wire shape where implemented.

Some UI or provider-specific setup from upstream is app-provided in Ruby. The protocol endpoints below are implemented, but you must supply your own pages, callbacks, client objects, or provider metadata where noted by the option names.

Configure

config/auth.rb
require "better_auth"

auth = BetterAuth.auth(
  secret: ENV.fetch("BETTER_AUTH_SECRET"),
  base_url: ENV.fetch("BETTER_AUTH_URL", "http://localhost:3000"),
  plugins: [
    BetterAuth::Plugins.scim
  ]
)

Usage

server.rb
token = auth.api.generate_scim_token(headers: { "cookie" => admin_cookie }, body: { providerId: "okta", organizationId: organization_id })
user = auth.api.create_scim_user(headers: { "authorization" => "Bearer #{token[:scimToken]}" }, body: { userName: "ada@example.com" })

Routes

MethodPathRuby API method
POST/scim/generate-tokenauth.api.generate_scim_token
GET/scim/list-provider-connectionsauth.api.list_scim_provider_connections
GET/scim/get-provider-connectionauth.api.get_scim_provider_connection
POST/scim/delete-provider-connectionauth.api.delete_scim_provider_connection
POST/scim/v2/Usersauth.api.create_scim_user
GET/scim/v2/Usersauth.api.list_scim_users
GET/scim/v2/Users/:userIdauth.api.get_scim_user
PUT/scim/v2/Users/:userIdauth.api.update_scim_user
PATCH/scim/v2/Users/:userIdauth.api.patch_scim_user
DELETE/scim/v2/Users/:userIdauth.api.delete_scim_user
GET/scim/v2/ServiceProviderConfigauth.api.get_scim_service_provider_config
GET/scim/v2/Schemasauth.api.get_scim_schemas
GET/scim/v2/Schemas/:schemaIdauth.api.get_scim_schema
GET/scim/v2/ResourceTypesauth.api.get_scim_resource_types
GET/scim/v2/ResourceTypes/:resourceTypeIdauth.api.get_scim_resource_type

Filters

auth.api.list_scim_users and GET /scim/v2/Users support the upstream-compatible userName eq "value" filter. The filter value is matched against the canonicalized user email.

Other SCIM filter attributes and operators are not supported in the Ruby port. Filters such as externalId eq "id" and operators such as ne, co, sw, ew, and pr return a SCIM invalidFilter error instead of silently widening the result set.

Options

Current Ruby options accepted by BetterAuth::Plugins.scim:

  • store_scim_token
  • default_scim
  • provider_ownership
  • required_role
  • before_scim_token_generated
  • after_scim_token_generated

store_scim_token defaults to "hashed" and accepts "plain", "hashed", "encrypted", { hash: ->(token) { ... } }, or { encrypt: ->(token) { ... }, decrypt: ->(stored) { ... } }.

provider_ownership: { enabled: true } stores the user that generated non-organization provider tokens and restricts provider connection management to that owner.

required_role controls which organization roles can generate, list, view, or delete organization-scoped SCIM providers. When omitted, Ruby follows upstream and allows admin plus the organization creator role.

default_scim accepts provider hashes for tests or static providers:

BetterAuth::Plugins.scim(
  default_scim: [
    { providerId: "okta", scimToken: "secret-token", organizationId: "org-id" }
  ]
)

Static default_scim providers are checked before database-backed providers. If a static provider and a database provider share the same providerId, the static provider takes precedence and a bearer token generated for the database provider is rejected.

Ruby behavior notes

Ruby canonicalizes SCIM email/userName values to lowercase before storing the user email and returning the SCIM user resource. When externalId is omitted, Ruby also stores the lowercase userName as the SCIM account id. Upstream preserves the original userName casing for that account id fallback.

Production databases should enforce both of these uniqueness invariants:

  • scimProvider.providerId is globally unique.
  • SCIM account identity is unique by (providerId, accountId).

Plugin Surface

BetterAuth::Plugins.scim exposes version and client metadata. The client descriptor uses the upstream id scim-client and points back to the server plugin id scim.

When combined with BetterAuth::Plugins.open_api, provider management routes are included in the generated schema, while SCIM protocol routes under /scim/v2/* are hidden to match upstream HIDE_METADATA.

Support Notes

  • The examples above are based on Ruby plugin source and tests in packages/better_auth.
  • If an upstream section is not represented here, treat it as not yet documented or not yet supported by the Ruby port until the matching Ruby implementation exists.

On this page