#Convex Auth

1 messages · Page 1 of 1 (latest)

hallow scaffold
#

When a user registers an account I want to create a profile for them in the profile table in which they have row level access to update this, how can I do this?

This is my auth.ts file:

import { convexAuth } from "@convex-dev/auth/server";
import { Password } from "@convex-dev/auth/providers/Password";

export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
providers: [Password],
});

azure cargoBOT
#

Hi @hallow scaffold. 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 profile for each user upon registration and ensure they have row-level access to update it, you can use Convex Auth's authentication callbacks and Convex's row-level security (RLS) helpers.

1. Create the Profile on Registration

You should use the createOrUpdateUser or afterUserCreatedOrUpdated callback in your convexAuth config. In this callback, you can insert a new profile document into your profile table, associating it with the user's ID. For example:

import { convexAuth } from "@convex-dev/auth/server";
import { Password } from "@convex-dev/auth/providers/Password";

export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
  providers: [Password],
  callbacks: {
    async afterUserCreatedOrUpdated(ctx, { userId }) {
      // Insert a profile if it doesn't exist
      const existing = await ctx.db
        .query("profile")
        .withIndex("userId", (q) => q.eq("userId", userId))
        .first();
      if (!existing) {
        await ctx.db.insert("profile", {
          userId,
          // Add any default profile fields here
        });
      }
    },
  },
});

Use afterUserCreatedOrUpdated if you are not using createOrUpdateUser already. If you need more control, use createOrUpdateUser instead, as it allows you to perform additional writes at user creation time.
Writing additional data during authentication

2. Enforce Row-Level Security

To ensure only the user can update their own profile, use the convex-helpers package's row-level security wrappers. Define RLS rules for your profile table, for example:

#
import { customMutation } from "convex-helpers/server/customFunctions";
import { wrapDatabaseWriter, Rules } from "convex-helpers/server/rowLevelSecurity";
import { mutation } from "./_generated/server";

async function rlsRules(ctx) {
  const identity = await ctx.auth.getUserIdentity();
  return {
    profile: {
      modify: async (_, profile) => {
        // Only allow the user to modify their own profile
        return identity && profile.userId === identity.subject;
      },
      read: async (_, profile) => {
        // Only allow the user to read their own profile
        return identity && profile.userId === identity.subject;
      },
    },
  } satisfies Rules;
}

const mutationWithRLS = customMutation(
  mutation,
  async (ctx) => ({
    db: wrapDatabaseWriter(ctx, ctx.db, await rlsRules(ctx)),
  })
);

// Example: updateProfile mutation
export const updateProfile = mutationWithRLS({
  args: {/* ... */},
  handler: async (ctx, args) => {
    // ctx.db is now protected by RLS
    // ...
  },
});

CRUD with Row Level Security
Authorization via row-level security


Summary:

  • Use an auth callback to create a profile document when a user registers.
  • Use row-level security helpers to ensure only the user can update/read their own profile.

If you need more detailed code for your specific schema or have further questions, let me know!

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

#

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

hallow scaffold
#

I have my user's fill out a username as well in the frontend how can I access this within afterUserCreatedOrUpdated @azure cargo

azure cargoBOT
#

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

#

To access additional fields like a username from your frontend during user registration, you need to ensure that these fields are passed to your authentication provider and made available in the user profile object.

With Convex Auth and the Password provider, you can customize the profile method to include extra fields (such as username) from the registration form. These fields are then available in the profile parameter of the callback (such as afterUserCreatedOrUpdated).

How to do it:

  1. Frontend:
    When calling signIn("password", formData), ensure your FormData includes the username field.

  2. Provider Profile Callback:
    In your auth.ts, customize the Password provider to extract and return the username:

    import { Password } from "@convex-dev/auth/providers/Password";
    import { DataModel } from "./_generated/dataModel";
    
    export default Password<DataModel>({
      profile(params) {
        return {
          email: params.email as string,
          username: params.username as string,
          // ...other fields
        };
      },
    });
    

    Customize user information

  3. Access in afterUserCreatedOrUpdated:
    In the afterUserCreatedOrUpdated callback, the args.profile object will contain the username (and any other custom fields you returned from the profile method):

    callbacks: {
      async afterUserCreatedOrUpdated(ctx, { userId, profile }) {
        const username = profile.username;
        // You can now use `username` to create a profile or perform other actions
      },
    }
    

    Convex Auth: How to add custom data to signIn() (or up)

#

Note:
If you do not see your custom fields in profile, double-check that your frontend is sending them and that your profile method is returning them. Also, ensure your schema allows for these fields.


Summary:

  • Add the field to your frontend form and include it in the FormData sent to signIn.
  • Return the field from the provider's profile method.
  • Access it via args.profile in afterUserCreatedOrUpdated.

If you follow these steps, you will be able to access the username (and any other custom fields) in your afterUserCreatedOrUpdated callback.

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

#

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

hallow scaffold
#

This is my frontend code, but this is what I am seeing:

6/6/2025, 1:44:05 AM [CONVEX M(auth:store)] [LOG] {
existingUserId: null,
type: 'credentials',
provider: {
id: 'password',
type: 'credentials',
authorize: [Function: authorize],
options: {
id: 'password',
authorize: [Function: authorize],
crypto: {
hashSecret: [Function: hashSecret],
verifySecret: [Function: verifySecret]
},
extraProviders: [ undefined, undefined ]
},
crypto: {
hashSecret: [Function: hashSecret],
verifySecret: [Function: verifySecret]
},
extraProviders: [ undefined, undefined ]
},
profile: {
email: '[email protected]'
},
shouldLinkViaEmail: false,
shouldLinkViaPhone: false
}

#

This is my frontend code, but this is what I am seeing:

6/6/2025, 1:44:05 AM [CONVEX M(auth:store)] [LOG] {
existingUserId: null,
type: 'credentials',
provider: {
id: 'password',
type: 'credentials',
authorize: [Function: authorize],
options: {
id: 'password',
authorize: [Function: authorize],
crypto: {
hashSecret: [Function: hashSecret],
verifySecret: [Function: verifySecret]
},
extraProviders: [ undefined, undefined ]
},
crypto: {
hashSecret: [Function: hashSecret],
verifySecret: [Function: verifySecret]
},
extraProviders: [ undefined, undefined ]
},
profile: {
email: '[email protected]'
},
shouldLinkViaEmail: false,
shouldLinkViaPhone: false
}

@azure cargo

azure cargoBOT
#

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

#

Based on your log output, it appears that only the email field is present in the profile object, and your custom username field is missing. According to the Convex Auth documentation, for custom fields like username to appear in the profile object (and thus be accessible in afterUserCreatedOrUpdated), you must:

  1. Ensure your frontend form includes the username field in the FormData sent to signIn.
  2. Customize the profile method in your Password provider to return the username field.

Here’s what the relevant part of your auth.ts should look like:

import { Password } from "@convex-dev/auth/providers/Password";
import { convexAuth } from "@convex-dev/auth/server";

export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
  providers: [
    Password({
      profile(params) {
        return {
          email: params.email as string,
          username: params.username as string, // <-- Make sure this is included
        };
      },
    }),
  ],
});

Customize user information

Common issues to check:

  • Make sure your frontend is actually sending the username field in the FormData object.
  • Double-check that your profile method is returning the username field.
  • If you are using a custom Password provider file (as shown in the docs), ensure you are importing and using it in your auth.ts instead of the default.
#

If you have done all of the above and still do not see the username in the profile object, the knowledge sources do not provide further troubleshooting steps. In that case, enabling debug logging (AUTH_LOG_LEVEL=DEBUG) as suggested in the Discord thread may help you see what is being passed through the pipeline and where the data might be getting lostConvex Auth: How to add custom data to signIn() (or up).

If you need to see a working example, refer to the Customize user information section in the docs. If the issue persists, it may be a bug or a misconfiguration not covered in the current documentation.

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