@@ -13,7 +13,7 @@ import {
1313 oneTimeToken ,
1414 organization ,
1515} from 'better-auth/plugins'
16- import { eq } from 'drizzle-orm'
16+ import { and , eq } from 'drizzle-orm'
1717import { headers } from 'next/headers'
1818import Stripe from 'stripe'
1919import {
@@ -100,6 +100,44 @@ export const auth = betterAuth({
100100 } ,
101101 account : {
102102 create : {
103+ before : async ( account ) => {
104+ const existing = await db . query . account . findFirst ( {
105+ where : and (
106+ eq ( schema . account . userId , account . userId ) ,
107+ eq ( schema . account . providerId , account . providerId ) ,
108+ eq ( schema . account . accountId , account . accountId )
109+ ) ,
110+ } )
111+
112+ if ( existing ) {
113+ logger . warn (
114+ '[databaseHooks.account.create.before] Duplicate account detected, updating existing' ,
115+ {
116+ existingId : existing . id ,
117+ userId : account . userId ,
118+ providerId : account . providerId ,
119+ accountId : account . accountId ,
120+ }
121+ )
122+
123+ await db
124+ . update ( schema . account )
125+ . set ( {
126+ accessToken : account . accessToken ,
127+ refreshToken : account . refreshToken ,
128+ idToken : account . idToken ,
129+ accessTokenExpiresAt : account . accessTokenExpiresAt ,
130+ refreshTokenExpiresAt : account . refreshTokenExpiresAt ,
131+ scope : account . scope ,
132+ updatedAt : new Date ( ) ,
133+ } )
134+ . where ( eq ( schema . account . id , existing . id ) )
135+
136+ return false
137+ }
138+
139+ return { data : account }
140+ } ,
103141 after : async ( account ) => {
104142 // Salesforce doesn't return expires_in in its token response (unlike other OAuth providers).
105143 // We set a default 2-hour expiration so token refresh logic works correctly.
0 commit comments