#create a user

40 messages · Page 1 of 1 (latest)

pure smeltBOT
#

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

#

To create a user with Better Auth in Convex, you need to implement the required onCreateUser hook in your Convex app. Better Auth manages its own user table, but you must also create your own users table for your application and handle user creation in this hook.

Here’s a step-by-step outline:

  1. Define your users table in your Convex schema (fields are up to you):
import { defineSchema, defineTable } from "convex/server";
export default defineSchema({
  users: defineTable({
    // Add your custom fields here
  }),
});

Users table guide

  1. Implement the onCreateUser hook in your Convex auth setup. This hook is called by Better Auth when a new user is created. You must insert a user into your users table and return the new user’s ID:
import { betterAuthComponent } from "./auth";
import { Id } from "./_generated/dataModel";

export const { createUser, deleteUser, updateUser, createSession } =
  betterAuthComponent.createAuthFunctions({
    // Must create a user and return the user id
    onCreateUser: async (ctx, user) => {
      const userId = await ctx.db.insert("users", {
        // Add any fields you want to store
        email: user.email,
      });
      return userId;
    },
    // Optional: handle updates and deletions
    onUpdateUser: async (ctx, user) => {
      await ctx.db.patch(user.userId as Id<"users">, {
        email: user.email,
      });
    },
    onDeleteUser: async (ctx, userId) => {
      await ctx.db.delete(userId as Id<"users">);
    },
  });

Users table and onCreateUser hook

  1. Better Auth will call this hook when a user signs up or logs in for the first time, ensuring your users table stays in sync with Better Auth’s internal user table.
#

Summary:
You do not create users directly; instead, you implement the onCreateUser hook, and Better Auth will invoke it as part of its authentication flow. You can customize what data you store in your users table as needed.

For more details and examples, see the Better Auth Convex documentation.

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

#

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

verbal mango
#

@pure smelt but my user schema in my db is different from the one in better auth component

pure smeltBOT
#

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

#

Yes, that's expected and supported! With Better Auth for Convex, the user table managed by the Better Auth component is separate from your application's own users table. You are responsible for defining your own users table schema, which can have any fields you want—these do not need to match the schema used internally by Better Auth.

When a user is created, Better Auth will call your onCreateUser hook, and you can insert a user document into your own users table with whatever fields and structure you require. The only requirement is that you return the ID of the user you create, so Better Auth can link its internal user record to your application’s user record.

Example:

export const { createUser } = betterAuthComponent.createAuthFunctions({
  onCreateUser: async (ctx, user) => {
    // Your schema can be different from Better Auth's internal schema
    const userId = await ctx.db.insert("users", {
      email: user.email,
      customField: "myValue", // any custom fields you want
    });
    return userId;
  },
});

This way, your users table can have any structure you need for your app, and you can copy over only the fields you care about from the Better Auth user object. The two tables are kept in sync via the hooks you define, but their schemas are independent Users table guide.

Key point:
You do not need to match your users table schema to Better Auth’s internal schema. Just ensure you handle the mapping in your hooks.

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

#

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

verbal mango
#

@pure smelt yeah but how do i pass the values to it because the variable user in onCreateUser is of better auth. for exemple in my db the user has a fileInformationUrl that dont exist in better auth but when i do this

onCreateUser: async (ctx, user) => {
// Your schema can be different from Better Auth's internal schema
const userId = await ctx.db.insert("users", {
email: user.email,
customField: user.fileInformationUrl
});
return userId;

it doest not work

pure smeltBOT
#

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

#

You are correct: the user object passed to onCreateUser in Better Auth only contains fields that Better Auth itself knows about (such as email, name, etc.), and does not include custom fields like fileInformationUrl that you might collect from your own sign-up form.

How to handle custom fields?

The knowledge sources confirm that the onCreateUser user parameter only contains data from the authentication provider, not arbitrary frontend fields. There is no built-in way in Better Auth to pass extra custom fields (like fileInformationUrl) from your frontend sign-up form directly into the onCreateUser hook. This is a known limitation and is discussed in the Discord support threads:

"I’m trying to add additional fields to my sign up form which will be added to the users table. But I can’t figure out how to pass them onward. Adding them to the FormData object passed to signIn() doesn’t seem to work."
Discord support thread

What can you do?

  • You can only insert fields into your users table in onCreateUser that are present on the user object provided by Better Auth.
  • If you need to store additional custom fields (like fileInformationUrl), you must collect and store them separately, for example by calling a Convex mutation from your frontend after the user is created and authenticated.

Example workaround:

  1. User signs up/logs in via Better Auth.
  2. After authentication, your frontend calls a Convex mutation to update the user document with the custom field:
// In your Convex mutation
export const setFileInformationUrl = mutation({
  args: { userId: v.id("users"), fileInformationUrl: v.string() },
  handler: async (ctx, args) => {
    await ctx.db.patch(args.userId, { fileInformationUrl: args.fileInformationUrl });
  },
});
#

This approach is also discussed in the official documentation and Discord threads, where it is clarified that only fields provided by the auth provider are available in the onCreateUser user object, and custom fields must be handled separately after authentication Discord support thread.

Summary:
You cannot pass arbitrary custom fields from your frontend directly into onCreateUser via Better Auth. You must update your user record with those fields after authentication using a separate mutation.

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

#

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

verbal mango
#

@pure smelt how do i create a user in a convex mutation with better auth

pure smeltBOT
#

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

#

To create a user in a Convex mutation when using Better Auth, you do not create the user directly via a standard mutation. Instead, Better Auth manages user creation and expects you to implement the onCreateUser hook, which it calls automatically when a new user signs up or logs in for the first time.

Inside this hook, you insert a user into your own users table and return the new user's ID. Here’s how you do it:

export const { createUser } = betterAuthComponent.createAuthFunctions({
  onCreateUser: async (ctx, user) => {
    // Insert a user into your application's users table
    const userId = await ctx.db.insert("users", {
      email: user.email, // or any other fields from the Better Auth user object
      // Add your custom fields here if they are available from the auth provider
    });
    return userId;
  },
});

Important:

  • The user object in onCreateUser only contains fields provided by the authentication provider (like email, name, etc.).
  • If you need to add custom fields (not present in the auth provider), you must update the user document after authentication using a separate mutation, as these fields cannot be passed through the onCreateUser hook directly Users table guide.

Summary:
You create a user in your Convex database by implementing the onCreateUser hook for Better Auth, inserting the user in your users table, and returning the ID. For custom fields not provided by the auth provider, update the user record after authentication using a separate mutation.

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

#

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

verbal mango
#

@pure smelt j'ai cette erreur lorsque je log avec better auth Make sure the secret currently in use is the same as the one used to encrypt the private key. If you are using a different secret, either cleanup your jwks or disable private key encryption.] name: 'BetterAuthError', cause: undefined

pure smeltBOT
#

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

#

L’erreur que tu rencontres avec Better Auth —

“Make sure the secret currently in use is the same as the one used to encrypt the private key. If you are using a different secret, either cleanup your jwks or disable private key encryption.”
indique un problème de cohérence entre la clé secrète (BETTER_AUTH_SECRET) et la clé privée utilisée pour chiffrer/déchiffrer le JWKS (JSON Web Key Set).

Explication et solution :

  • Better Auth chiffre la clé privée du JWKS avec la valeur de BETTER_AUTH_SECRET.
  • Si tu changes la valeur de BETTER_AUTH_SECRET sans régénérer le JWKS, ou si tu utilises un JWKS généré avec une autre secret, tu obtiens cette erreur.
  • Il faut donc t’assurer que :
    • La variable d’environnement BETTER_AUTH_SECRET est la même que celle utilisée lors de la génération du JWKS.
    • Si tu changes le secret, il faut régénérer le JWKS avec ce nouveau secret, ou bien supprimer/“nettoyer” l’ancien JWKS pour en générer un nouveau cohérent.

Étapes à suivre :

  1. Vérifie la valeur de BETTER_AUTH_SECRET dans tes variables d’environnement Convex.
  2. Si tu veux changer ce secret, supprime l’ancien JWKS et régénère-le avec le nouveau secret.
  3. Redémarre ton application pour t’assurer que tout est bien synchronisé.

Documentation associée :
Ce comportement est mentionné dans la documentation officielle de Better Auth pour Convex, notamment dans la section sur la génération et la gestion des secrets et des JWKS Better Auth Convex Guide.

#

Si tu continues à avoir des soucis, assure-toi que toutes les instances de ton application (local, production, etc.) utilisent exactement la même valeur pour BETTER_AUTH_SECRET et que le JWKS a bien été généré avec ce secret.

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

#

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

verbal mango
#

@pure smelt comment regenerer le JWKS

pure smeltBOT
#

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

#

Pour régénérer le JWKS (JSON Web Key Set) dans un projet Convex, tu peux utiliser un script Node.js basé sur la librairie jose, comme indiqué dans la documentation officielle. Voici la méthode recommandée :

  1. Crée un fichier generateKeys.mjs avec le contenu suivant :
import { exportJWK, exportPKCS8, generateKeyPair } from "jose";
 
const keys = await generateKeyPair("RS256", {
  extractable: true,
});
const privateKey = await exportPKCS8(keys.privateKey);
const publicKey = await exportJWK(keys.publicKey);
const jwks = JSON.stringify({ keys: [{ use: "sig", ...publicKey }] });
 
process.stdout.write(
  `JWT_PRIVATE_KEY="${privateKey.trimEnd().replace(/\n/g, " ")}"`,
);
process.stdout.write("\n");
process.stdout.write(`JWKS=${jwks}`);
process.stdout.write("\n");
  1. Exécute le script dans ton terminal :
node generateKeys.mjs
  1. Copie les valeurs générées (JWT_PRIVATE_KEY et JWKS) et colle-les dans les variables d’environnement de ton déploiement Convex, via le dashboard ou la CLI.

Ce processus est décrit dans la documentation officielle Convex : Manual Setup

⚠️ Important : Si tu utilises Better Auth, assure-toi que le secret utilisé pour chiffrer la clé privée (BETTER_AUTH_SECRET) est cohérent avec celui utilisé lors de la génération du JWKS.

Si tu utilises une autre méthode spécifique à Better Auth pour générer le JWKS, réfère-toi à la documentation Better Auth, mais la méthode ci-dessus est la méthode standard pour Convex Manual Setup.

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

#

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

verbal mango
#

@pure smelt {"code":"InvalidAuthHeader","message":"Could not parse JWT payload"}

pure smeltBOT
#

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

#

The error message:

{"code":"InvalidAuthHeader","message":"Could not parse JWT payload"}

indicates that Convex is unable to decode or parse the JWT token sent in the Authorization header. This usually means the token is either malformed, not a valid JWT, or not properly encoded.

Possible causes and troubleshooting steps based on the knowledge sources:

  1. Malformed or Invalid JWT:

    • Make sure the token you are sending is a valid JWT. You can check this by pasting the token into jwt.io and verifying that it decodes properly and contains the required fields (aud, iss, sub, etc.) Custom JWT Provider.
  2. Incorrect Token Passing:

    • Ensure your frontend is actually passing the JWT token to Convex. For web clients, inspect the network traffic and confirm the Authorization header is present and contains a valid JWT Debugging Authentication: Step 2.
  3. Token Format:

    • The token must be a string in the format header.payload.signature, with each part base64url-encoded. If any part is missing or corrupted, Convex will not be able to parse it.
  4. Auth Provider Configuration:

    • Double-check that your Convex auth.config.ts matches the JWT fields (especially iss and aud) and that the JWKS endpoint is accessible and valid Debugging Authentication: Step 3.
#
  1. Library/Dependency Issues:
    • If you are using a custom auth flow or a library to generate JWTs, ensure it is generating tokens in the correct format. There have been cases where the wrong export or version of a JWT library caused issues Discord: Auth token is not a valid JWT.

If you have verified all of the above and the issue persists, try regenerating your JWTs and JWKS, and ensure all systems are using the same configuration and secrets.

If you need more specific help, please provide the JWT (with sensitive info redacted), your auth.config.ts, and details about how you are generating and passing the token.


If you need more details or step-by-step debugging, refer to the Convex Debugging Authentication guide.

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

#

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

verbal mango
#

@pure smelt

/21/2025, 5:22:08 PM [CONVEX H(GET /api/auth/convex/token)] [LOG] '\x1B[2m2025-08-21T15:22:08.696Z\x1B[0m \x1B[34mINFO\x1B[0m \x1B[1m[Better Auth]:\x1B[0m [Convex Adapter]' '\x1B[35m#2\x1B[0m \x1B[40m\x1B[33m[3/3]\x1B[0m' '\x1B[1mfindMany\x1B[0m \x1B[2m(Parsed Result)\x1B[0m:' {
model: 'jwks',
data: [
{
publicKey: '{"alg":"EdDSA","kty":"OKP","crv":"Ed25519","x":"9_IxvhYgZK4Rw6xj8CI3mgejbLQsicg1QwbJf0i9B2w"}',
privateKey: '"06c027293c02c310c5b0a53bd8f82a71692f5637ad1229fda79a095bd79e0e954c4f7fd9b15158449ee2b0666f1d33bb5267c1e6e7c1ed18b5c58511ab5905a856e7df61cfaed5cce9bd2e830887c0967e07592f2050af6c520e0f57fa810c04c996aa7b175f2bf96860aec956136264c1b2da2a991b31f151341918c336a69da3ad3ff72fecdee06206f77d2a74dfbedf50887111f05f67d34a962561ab4c84b22875dfaaac254713"',
createdAt: 1754318937700,
id: 'mh7e5eeh75t66tzgh7pqwzdfys7n0zg3'
}
]
}
8/21/2025, 5:22:08 PM [CONVEX H(GET /api/auth/convex/token)] [ERROR] '# SERVER_ERROR: ' { [BetterAuthError: Failed to decrypt private private key. Make sure the secret currently in use is the same as the one used to encrypt the private key. If you are using a different secret, either cleanup your jwks or disable private key encryption.] name: 'BetterAuthError', cause: undefined }
8/21/2025, 5:22:08 PM [CONVEX H(GET /api/auth/convex/token)] [LOG] 'response headers' Headers {}

pure smeltBOT
#

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

#

Your log shows the following error from Better Auth:

Failed to decrypt private private key. Make sure the secret currently in use is the same as the one used to encrypt the private key. If you are using a different secret, either cleanup your jwks or disable private key encryption.

What this means:
Better Auth encrypts your private key using the value of BETTER_AUTH_SECRET. If you change this secret after the JWKS/private key has been generated, Better Auth will not be able to decrypt the existing private key, resulting in this error.

How to resolve:

  1. Ensure the same secret is used:
    Make sure the value of BETTER_AUTH_SECRET in your environment variables is exactly the same as the one used when the JWKS/private key was originally generated.

  2. If the secret has changed:

    • You need to "clean up" (delete) the old JWKS/private key and generate a new one using the current BETTER_AUTH_SECRET.
    • This process is referenced in the error message and is a known requirement in the Better Auth integration for Convex Convex Better Auth Guide.
  3. Steps to regenerate JWKS/private key:

    • Delete the old JWKS/private key from your database or environment.
    • With the correct BETTER_AUTH_SECRET set, generate a new JWKS/private key pair.
    • Update your environment variables and/or database with the new values.
#

Summary:
This error is caused by a mismatch between the secret used to encrypt your private key and the current value of BETTER_AUTH_SECRET. You must ensure they match, or regenerate your JWKS/private key with the current secret. This is a common issue when rotating secrets or moving between environments Convex Better Auth Guide.

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

#

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

verbal mango
#

@pure smelt how to

the secret has changed:
You need to "clean up" (delete) the old JWKS/private key and generate a new one using the current BETTER_AUTH_SECRET.

pure smeltBOT
#

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

#

To resolve the error caused by a changed BETTER_AUTH_SECRET, you need to "clean up" (delete) the old JWKS/private key and generate a new one using the current secret. Here’s how you can do this, based on the official Convex and Better Auth documentation:

  1. Delete the old JWKS/private key:

    • Remove the existing JWKS/private key from your environment variables or from your database, depending on where it is stored. If you are using environment variables in the Convex dashboard, delete the JWKS and any related private key variables.
  2. Generate a new JWKS/private key pair:

    • With the correct BETTER_AUTH_SECRET set, generate a new JWKS/private key. You can use a script like the following (as shown in the Convex manual setup):
    import { exportJWK, exportPKCS8, generateKeyPair } from "jose";
     
    const keys = await generateKeyPair("RS256", {
      extractable: true,
    });
    const privateKey = await exportPKCS8(keys.privateKey);
    const publicKey = await exportJWK(keys.publicKey);
    const jwks = JSON.stringify({ keys: [{ use: "sig", ...publicKey }] });
     
    process.stdout.write(
      `JWT_PRIVATE_KEY="${privateKey.trimEnd().replace(/\n/g, " ")}"`,
    );
    process.stdout.write("\n");
    process.stdout.write(`JWKS=${jwks}`);
    process.stdout.write("\n");
    

    Run this script with Node.js, and it will output new values for JWT_PRIVATE_KEY and JWKS Manual Setup.

  3. Set the new values:

    • Copy the new JWT_PRIVATE_KEY and JWKS values and set them in your Convex deployment’s environment variables (via the dashboard or CLI).
    • Make sure your BETTER_AUTH_SECRET is set and matches the one used for encryption.
  4. Restart your deployment to ensure the new keys and secret are loaded.

#

Note:
This process is necessary because the private key is encrypted with the BETTER_AUTH_SECRET. If the secret changes, the old encrypted key cannot be decrypted, so you must generate a new key pair Convex Better Auth Guide.

If you need more details, refer to the manual setup instructions for Convex Auth.

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