Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions convex/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import GitHub from '@auth/core/providers/github'
import { convexAuth } from '@convex-dev/auth/server'
import { Id } from './_generated/dataModel'

export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
providers: [
Expand All @@ -16,4 +17,51 @@ export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
},
}),
],
callbacks: {
/**
* Handle re-authentication of soft-deleted users.
*
* Performance note: This callback runs on every OAuth sign-in, but the
* audit log query ONLY executes when a soft-deleted user attempts to
* sign in (user.deletedAt is set). For normal active users, this is
* just a single `if` check on an already-loaded field - no extra queries.
*/
async createOrUpdateUser(ctx, args) {
// New user - let Convex Auth handle creation with default behavior
if (!args.existingUserId) {
return null
}

const userId = args.existingUserId as Id<'users'>
const user = await ctx.db.get(userId)

// Active user - normal sign-in, no additional processing needed
if (!user?.deletedAt) {
return args.existingUserId
}

// Soft-deleted user attempting to sign in - check if banned or self-deleted
// Uses the by_target index for efficient lookup (not a full table scan)
const banRecord = await ctx.db
.query('auditLogs')
.withIndex('by_target', (q) =>
q.eq('targetType', 'user').eq('targetId', userId)
)
.filter((q) => q.eq(q.field('action'), 'user.ban'))
.first()

if (banRecord) {
// User was banned by a moderator - do NOT restore
throw new Error('This account has been suspended')
}

// User self-deleted their account - restore it
await ctx.db.patch(userId, {
deletedAt: undefined,
updatedAt: Date.now(),
})

return args.existingUserId
},
},
})