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_tokenlist_scim_provider_connectionsget_scim_provider_connectiondelete_scim_provider_connectioncreate_scim_userlist_scim_usersget_scim_userupdate_scim_userpatch_scim_userdelete_scim_userget_scim_service_provider_configget_scim_schemasget_scim_schemaget_scim_resource_typesget_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.versionreturns the gem SCIM version.BetterAuth::Plugins.scim.clientreturns 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 (
accountsor 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