Better Auth SCIM

External SCIM provisioning plugin package for better_auth.

SCIM is not login. It is a provisioning API used by identity platforms to create, update, deactivate, and list users. It can be used alongside SSO, but it does not depend on SSO.

gem "better_auth-scim"
require "better_auth"
require "better_auth/scim"

BetterAuth.auth(
  plugins: [
    BetterAuth::Plugins.scim
  ]
)

Implemented API methods include token generation, provider connection management, SCIM user CRUD, and SCIM metadata endpoints:

  • generate_scim_token
  • list_scim_provider_connections
  • get_scim_provider_connection
  • delete_scim_provider_connection
  • create_scim_user
  • list_scim_users
  • get_scim_user
  • update_scim_user
  • patch_scim_user
  • delete_scim_user
  • get_scim_service_provider_config
  • get_scim_schemas
  • get_scim_schema
  • get_scim_resource_types
  • get_scim_resource_type

Options use Ruby snake_case names: store_scim_token, default_scim, provider_ownership, required_role, before_scim_token_generated, and after_scim_token_generated. store_scim_token defaults to "hashed" so generated SCIM provider tokens are not stored in plaintext. provider_ownership defaults to { enabled: true }, a Ruby-specific security default that scopes personal SCIM provider management to the user who generated the provider token. To preserve legacy shared management of non-organization providers, configure provider_ownership: { enabled: false }; only use that mode when all authenticated users who can access SCIM management routes are trusted to view, rotate, or delete unowned personal providers.

Organization-scoped SCIM bearer tokens must include the organization component generated by generate_scim_token. Removing that component invalidates the token. Deleting a SCIM user unlinks the current provider account first; the underlying Better Auth user is deleted only when no other accounts remain.

The plugin exposes upstream-style surface metadata:

  • BetterAuth::Plugins.scim.version returns the gem SCIM version.
  • BetterAuth::Plugins.scim.client returns the Ruby client-plugin descriptor (scim-client) for integrations that inspect plugin parity metadata.
  • SCIM protocol routes are hidden from generated OpenAPI output, matching upstream HIDE_METADATA; provider management routes remain visible.

Production recommendations

  • In the accounts table (accounts or the configured table name), use a unique composite index on (providerId, accountId) to prevent duplicate SCIM accounts under concurrent provisioning. The gem does not create this constraint automatically because index syntax and migrations depend on your database adapter and application.

Testing

Run the default SCIM package suite from this package directory:

rbenv exec bundle exec rake test

The suite includes SCIM route coverage for memory, custom, secondary-storage, and database-backed rate limiting. It also runs a live SQLite adapter smoke test against a temporary database.

External adapter smoke tests are skip-gated. They run only when the relevant gem and service are available:

  • PostgreSQL: BETTER_AUTH_POSTGRES_URL
  • MySQL: BETTER_AUTH_MYSQL_HOST, BETTER_AUTH_MYSQL_PORT, BETTER_AUTH_MYSQL_USER, BETTER_AUTH_MYSQL_PASSWORD, BETTER_AUTH_MYSQL_DATABASE
  • MSSQL: BETTER_AUTH_MSSQL_URL, BETTER_AUTH_MSSQL_MASTER_URL
  • MongoDB: BETTER_AUTH_MONGODB_URL