@@ -643,14 +643,12 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
643643 . limit ( 1 )
644644
645645 /**
646- * Mirrors Sim's own domain-ownership proof onto Better Auth's `domainVerified`
647- * flag, which is what lets an SSO sign-in auto-link to an existing same-email
648- * account. Must run after every write, not just on create: `registerSSOProvider`
649- * always persists `false`, and `updateSSOProvider` resets it to `false` whenever
650- * the domain changes. Callers re-check ownership immediately before granting it,
651- * so it can never mark an unproven domain as verified. Org-less (personal) SSO is
652- * not domain-gated by Sim and keeps its pre-existing trust here, matching how it
653- * behaved before the flag existed.
646+ * Unconditional write of Better Auth's `domainVerified` flag — the value Sim
647+ * mirrors from its own DNS proof, and what lets an SSO sign-in auto-link to an
648+ * existing same-email account. Only used to *withdraw* trust, and to grant it on
649+ * the org-less (personal) path that Sim never domain-gated. Granting on an
650+ * org-scoped provider goes through {@link grantProviderDomainTrust}, which
651+ * re-tests ownership in the same statement.
654652 */
655653 const setProviderDomainVerified = async ( verified : boolean ) => {
656654 await db . update ( ssoProvider ) . set ( { domainVerified : verified } ) . where ( ownerClause )
@@ -669,12 +667,12 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
669667 * Org-less (personal) SSO is not domain-gated by Sim, so it grants
670668 * unconditionally as it always has.
671669 */
672- const grantProviderDomainTrust = async ( ) => {
670+ const grantProviderDomainTrust = async ( ) : Promise < boolean > => {
673671 if ( ! orgId ) {
674672 await setProviderDomainVerified ( true )
675- return
673+ return true
676674 }
677- await db
675+ const granted = await db
678676 . update ( ssoProvider )
679677 . set ( { domainVerified : true } )
680678 . where (
@@ -694,6 +692,8 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
694692 )
695693 )
696694 )
695+ . returning ( { id : ssoProvider . id } )
696+ return granted . length > 0
697697 }
698698
699699 if ( existingOwnedProvider ) {
@@ -709,10 +709,13 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
709709 } )
710710
711711 // The verified sso_domain row can be deleted while updateSSOProvider is in
712- // flight. There is no newly-created row to roll back here, so on failure
713- // clear the flag: `updateSSOProvider` only resets it when the domain
714- // changes, so a same-domain edit would otherwise leave stale trust standing.
715- if ( orgId && ! ( await isOrgDomainVerified ( ) ) ) {
712+ // flight, in which case the conditional grant matches nothing. There is no
713+ // newly-created row to roll back here, so clear the flag instead:
714+ // `updateSSOProvider` only resets it when the domain changes, so a
715+ // same-domain edit would otherwise leave stale trust standing. Reporting the
716+ // failure keeps the response honest rather than saying "saved" while the
717+ // provider is left unable to sign anyone in.
718+ if ( ! ( await grantProviderDomainTrust ( ) ) ) {
716719 await setProviderDomainVerified ( false )
717720 logger . warn ( 'Revoked SSO domain trust: verification was removed mid-update' , {
718721 domain,
@@ -723,7 +726,6 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
723726 return domainNotVerifiedResponse ( )
724727 }
725728
726- await grantProviderDomainTrust ( )
727729 logger . info ( 'SSO provider updated successfully' , { providerId, providerType, domain } )
728730 return NextResponse . json ( {
729731 success : true ,
@@ -738,22 +740,25 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
738740 headers,
739741 } )
740742
741- // Grant trust in the same statement that re-tests ownership, closing the
742- // window between Better Auth persisting the provider and this write. A failure
743- // means the verified sso_domain row was removed in that window, so roll the
744- // provider back. registerSSOProvider is create-only (it throws if the
743+ // Granting trust re-tests ownership inside the same statement, so a failure
744+ // here means the verified sso_domain row was removed between the pre-write
745+ // check and Better Auth persisting the provider. That would leave a provider
746+ // on a domain the org no longer proves, so roll it back.
747+ // registerSSOProvider is create-only (it throws if the
745748 // providerId already exists), so a successful call always created a brand-new
746749 // row — we roll it back by its primary-key `id` (not the logical providerId,
747750 // which a concurrent delete+recreate could point at a different row). Personal
748- // SSO is not gated, so this only runs for org-scoped registration .
749- if ( orgId && ! ( await isOrgDomainVerified ( ) ) ) {
751+ // SSO is not gated, so grantProviderDomainTrust always succeeds there .
752+ if ( ! ( await grantProviderDomainTrust ( ) ) ) {
750753 // registerSSOProvider spreads the created row's `id` at runtime, but the
751754 // typed return omits it — read it defensively and only delete when it's a
752755 // real id, so a future shape change can't turn the rollback into a silent
753- // no-op that leaves a provider on an unverified domain.
756+ // no-op that leaves a provider on an unverified domain. `orgId` is checked
757+ // only to narrow it: the org-less path grants unconditionally, so a refused
758+ // grant always means an org-scoped registration.
754759 // double-cast-allowed: Better Auth's return type omits the runtime `id`
755760 const createdRowId = ( registration as unknown as { id ?: unknown } ) . id
756- if ( typeof createdRowId === 'string' && createdRowId . length > 0 ) {
761+ if ( orgId && typeof createdRowId === 'string' && createdRowId . length > 0 ) {
757762 await db
758763 . delete ( ssoProvider )
759764 . where ( and ( eq ( ssoProvider . id , createdRowId ) , eq ( ssoProvider . organizationId , orgId ) ) )
@@ -774,8 +779,6 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
774779 return domainNotVerifiedResponse ( )
775780 }
776781
777- await grantProviderDomainTrust ( )
778-
779782 logger . info ( 'SSO provider registered successfully' , {
780783 providerId,
781784 providerType,
0 commit comments