SCIM and SSO Integration#
Dokploy Enterprise supports both SCIM 2.0 user provisioning and OIDC/SSO authentication via Better Auth. When both are enabled for the same identity provider, SCIM creates Dokploy users ahead of their first SSO login — and those two identities must be linked at sign-in time. The interaction between them is controlled by a single guard in Better Auth: an existing user's emailVerified flag.
The Root Problem#
The SCIM plugin creates users via internalAdapter.createUser({ email, name }), which stores them with emailVerified: false (the default). When the same user later signs in through SSO, Better Auth's OAuth account-linking guard checks whether the existing local user is verified before it will link the new SSO account to it. Because the SCIM-provisioned user is unverified, the callback returns error=account_not_linked .
A compounding factor: Dokploy configures sso({ trustEmailVerified: true }) — meaning Better Auth will trust an email_verified: true claim from the IdP as a linking signal. However, Microsoft Entra ID's OIDC UserInfo response does not include the email_verified claim , so setting a provider as trusted doesn't resolve the issue for Entra-based tenants.
Two-Part Fix#
PR #4880 (fix(auth): enable email verification for SSO and user creation) laid the groundwork:
- Changed
createOrganizationUserWithCredentialsto insert new users withemailVerified: true, so administrator-created users are immediately trustable by the linking guard. - Set
sso({ trustEmailVerified: true })in the Better Auth config so that SSO providers that do return anemail_verifiedclaim are trusted for account linking .
PR #4974 (fix(auth): link SCIM users through verified SSO domains) addresses the remaining gap for SCIM-provisioned users and IdPs without email_verified:
- Adds a
markSCIMProvisionedUserEmailVerified()function that marks users created via the/scim/v2/Usersendpoint asemailVerified: true. The SCIM provisioning token is issued to organization admins and serves as an authoritative trust signal for managed identities. - Introduces DNS domain verification as an alternative trust path. Admins can prove ownership of a domain (e.g.,
example.com) by placing a DNS TXT record at_dokploy-sso-<providerId>.example.com. Once verified, the SSO provider gains adomainVerified: trueflag in thesso_providertable, and Better Auth will allow account linking against SCIM-provisioned users for that domain — even when the IdP omitsemail_verified. - Adds a new database column via migration:
ALTER TABLE "sso_provider" ADD COLUMN "domain_verified" boolean DEFAULT false NOT NULL;. All existing providers default tofalseto preserve prior behavior.
Security Properties#
The design deliberately avoids weakening the account-linking guard:
- SCIM tokens are org-admin credentials. Trusting them as email-verification signals is equivalent to an admin pre-verifying the user's address.
- DNS domain verification requires actual domain ownership. It cannot be spoofed by a third party, and it must be explicitly initiated by an admin.
- Unverified providers are unchanged. Domain verification is opt-in and additive; providers without it continue to require a valid
email_verifiedclaim from the IdP . - Unrelated unverified identities cannot hijack accounts. The existing Better Auth guard remains in place for all other flows.
Key Source Files#
| File | Purpose |
|---|---|
packages/server/src/lib/auth.ts | Better Auth instance: SCIM + SSO plugin config, accountLinking, databaseHooks |
packages/server/src/services/user.ts | createOrganizationUserWithCredentials — sets emailVerified: true on admin-created users |
packages/server/src/lib/sso-account-linking.ts | New module (PR #4974, not yet merged): markSCIMProvisionedUserEmailVerified, createDokploySSOPlugin, getSSODomainVerificationRecordName |
apps/dokploy/drizzle/0180_groovy_hemingway.sql | Migration adding domain_verified column to sso_provider |
packages/server/src/db/schema/sso.ts | Schema: ssoProvider table including new domainVerified field |
apps/dokploy/server/api/routers/proprietary/sso.ts | New tRPC endpoints: requestDomainVerification, verifyDomain |
Authentication Flow (SCIM + SSO)#
Related Issues & Tracking#
- GitHub Issue: #4973 — SCIM-provisioned users cannot sign in through SSO (account_not_linked)
- Linear:
DOK-611 - PR #4880 (merged July 21, 2026): foundational email-verification fix
- PR #4974 (open as of Aug 6, 2026): domain verification + SCIM email-marking fix