#how to block account creation for a specific email ?

25 messages · Page 1 of 1 (latest)

mint archBOT
#

Hi @worthy kettle. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

Blocking Account Creation for a Specific Email

The right place to block account creation is the createOrUpdateUser callback, not beforeSessionCreation or afterUserCreatedOrUpdated. Here's why:

  • beforeSessionCreation runs after the user already exists — it blocks sign-in, not account creation.
  • afterUserCreatedOrUpdated is called after the user is already created/updated, so it's too late to prevent the write.
  • createOrUpdateUser is called before the user is created, giving you full control. Throwing an error here aborts the entire operation. [Controlling user creation]

Example: Block a specific email (or pattern)

// convex/auth.ts
import { convexAuth } from "@convex-dev/auth/server";
import GitHub from "@auth/core/providers/github";

export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
  providers: [GitHub],
  callbacks: {
    async createOrUpdateUser(ctx, args) {
      const email = args.profile.email as string | undefined;

      // Block a specific email or use a regex
      if (email && /blocked@example\.com/.test(email)) {
        throw new Error("This email address is not allowed to register.");
      }

      if (args.existingUserId) {
        return args.existingUserId;
      }

      // Your normal user creation logic
      return ctx.db.insert("users", {
        email,
        // ...other fields
      });
    },
  },
});

Key points

#
  • Throw an error inside createOrUpdateUser to abort the mutation transaction and prevent the user from being created. [Controlling user creation]
  • You can use a regex, a blocklist, or even query your database (e.g., an allowlist table) inside this callback since you have access to ctx.
  • If you want to pass structured error info to the frontend, use ConvexError instead of a plain Error. [Application Errors]
  • Note: when you provide createOrUpdateUser, the library no longer handles user creation automatically — you must implement all creation/update logic yourself for all providers. [Controlling user creation]

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@worthy kettle Kapa.ai is still learning and improving, please let me know how I did by reacting below.

worthy kettle
#

@mint arch also I have #sym:afterUserCreatedOrUpdated.
But it seems it wont be called if I provied a createOrUpdateUser * This callback is only called if createOrUpdateUser
* is not specified.
If createOrUpdateUser is specified,
* you can perform any additional writes in that callback.

mint archBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

You're exactly right. The docs confirm this behavior explicitly:

This callback is only called if createOrUpdateUser is not specified. If createOrUpdateUser is specified, you can perform any additional writes in that callback.

[afterUserCreatedOrUpdated]

Solution: Move your afterUserCreatedOrUpdated logic into createOrUpdateUser

Since you now need createOrUpdateUser for the email blocking, simply consolidate both pieces of logic into that single callback:

// convex/auth.ts
import { convexAuth } from "@convex-dev/auth/server";
import GitHub from "@auth/core/providers/github";

export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
  providers: [GitHub],
  callbacks: {
    async createOrUpdateUser(ctx, args) {
      const email = args.profile.email as string | undefined;

      // 1. Block specific emails
      if (email && /blocked@example\.com/.test(email)) {
        throw new Error("This email address is not allowed to register.");
      }

      // 2. Handle existing user (previously in afterUserCreatedOrUpdated)
      if (args.existingUserId) {
        // Optionally update fields on the existing user
        await ctx.db.patch(args.existingUserId, { /* updated fields */ });
        return args.existingUserId;
      }

      // 3. Create new user + any extra writes you had in afterUserCreatedOrUpdated
      const userId = await ctx.db.insert("users", {
        email,
        // ...other fields
      });

      // Extra writes that were previously in afterUserCreatedOrUpdated
      await ctx.db.insert("someTable", { userId, data: "some data" });

      return userId;
    },
  },
});

Summary

#
Callback Called when createOrUpdateUser is set?
afterUserCreatedOrUpdated ❌ No
createOrUpdateUser ✅ Yes — do everything here

[Advanced Auth]

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@worthy kettle Kapa.ai is still learning and improving, please let me know how I did by reacting below.

worthy kettle
#

@mint arch the solution you provided extended the original createOrUpdateUser function or the original is more complex ?

mint archBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The knowledge sources only show a simplified example of createOrUpdateUser. The full default implementation inside the library is more complex — it handles things like account linking logic, email/phone verification tracking, and updating existing user fields automatically.

When you provide createOrUpdateUser, the library's default implementation is completely replaced — you own all of it:

"When you provide this callback, the library doesn't create or update users at all. It is up to you to implement all the necessary logic for all providers you use."

[Controlling user creation]

What this means in practice

The example I provided was a minimal starting point, not a drop-in replacement for a complex existing implementation. If your current createOrUpdateUser already has custom logic, you should add the email blocking and the afterUserCreatedOrUpdated logic into your existing callback, rather than replacing it with my simplified example.

So the pattern is:

async createOrUpdateUser(ctx, args) {
  // ADD: email blocking
  if (/* blocked email */) throw new Error("...");

  // KEEP: your existing createOrUpdateUser logic here
  // ...

  // MOVE: logic from your afterUserCreatedOrUpdated here
  // ...
}

I'd recommend reviewing your current createOrUpdateUser implementation carefully and only adding the new pieces to it, rather than replacing it wholesale.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@worthy kettle Kapa.ai is still learning and improving, please let me know how I did by reacting below.

worthy kettle
#

@mint arch can I still call the original one ? inside my custom build ? like is there a way to import it ?

mint archBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The knowledge sources do not contain enough information to confirm whether the library's default createOrUpdateUser implementation is exported or importable from @convex-dev/auth/server.

The knowledge sources only show the public API surface — convexAuth, createAccount, getAuthUserId, etc. — and there is no mention of an exported default user creation function that you could call internally.

What I'd suggest

  1. Check the library's source code directly on GitHub (github.com/get-convex/convex-auth) to see if any internal user creation helper is exported.

  2. Ask in the Convex Discord (convex.dev/community) — this is a specific enough question that the maintainers would give a definitive answer quickly.

These are the most reliable ways to confirm whether you can compose with the default behavior rather than fully replacing it.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@worthy kettle Kapa.ai is still learning and improving, please let me know how I did by reacting below.

worthy kettle
#

@mint arch what's the purpose of authVerifies ?

mint archBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The knowledge sources only briefly mention authVerifiers without much detail:

authVerifiers

PKCE verifiers for OAuth.

[authTables]

What this means

PKCE (Proof Key for Code Exchange) is a security extension to the OAuth 2.0 authorization flow. The authVerifiers table stores temporary PKCE verifier values used during OAuth sign-in to prevent authorization code interception attacks.

However, the knowledge sources don't go into further detail about this table's internals. For a deeper explanation, I'd recommend checking the Convex Auth GitHub source or asking on the Convex Discord.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@worthy kettle Kapa.ai is still learning and improving, please let me know how I did by reacting below.

worthy kettle
#

@mint arch is there a way to quickly delete user and auth information from a user ?

mint archBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The knowledge sources do not contain enough information about a built-in "delete user" helper specifically for Convex Auth (@convex-dev/auth).

However, based on what the knowledge sources do show, here are the relevant approaches:

1. Manual deletion in a mutation

You would need to delete the user and all related auth tables (sessions, accounts, verificationTokens, authVerifiers, etc.) manually:

export const deleteUser = internalMutation({
  args: { userId: v.id("users") },
  handler: async (ctx, { userId }) => {
    // Delete your app user
    await ctx.db.delete(userId);

    // Delete related auth data
    const sessions = await ctx.db
      .query("sessions")
      .withIndex("userId", (q) => q.eq("userId", userId))
      .collect();
    for (const session of sessions) {
      await ctx.db.delete(session._id);
    }

    const accounts = await ctx.db
      .query("accounts")
      .withIndex("userId", (q) => q.eq("userId", userId))
      .collect();
    for (const account of accounts) {
      await ctx.db.delete(account._id);
    }
  },
});

[Writing Data]

2. Using triggers for cascade deletes

If you use the convex-helpers triggers library, you can register a trigger on the users table to automatically cascade deletes to related auth tables when a user is deleted. [Cascade Deletes]

3. For large-scale deletions

If a user has many related documents, a single mutation may hit limits. In that case, use a scheduled mutation or action to spread the deletions out. [Discord thread]


#

For a definitive answer on whether @convex-dev/auth exports a built-in delete helper, I'd recommend checking the Convex Auth GitHub or asking on the Convex Discord.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: