[GH-ISSUE #3656] user.additionalFields is not recognized in onCustomerCreate callback (TypeScript / Stripe error) #26998

Closed
opened 2026-04-17 17:46:32 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @KinanLak on GitHub (Jul 27, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/3656

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

In your auth.ts setup:

export const auth = betterAuth({
  plugins: [
    organization(),
    stripe({
      stripeClient,
      stripeWebhookSecret: env.STRIPE_WEBHOOK_SECRET,
      createCustomerOnSignUp: true,
      onCustomerCreate: async ({ customer, stripeCustomer, user }, request) => {
        if (stripeCustomer) {
          await stripeClient.customers.update(stripeCustomer.id, {

            name: `${user.name} ${user.first_name}`,  //!  <--- HERE

          });
        }
      },
      subscription: { enabled: true, plans: [ /* ... */ ] },
    }),
  ],
  user: {
    additionalFields: {
      first_name: { type: "string", required: true },
    },
  },
});
  1. User signs up → Stripe customer is created.

  2. In onCustomerCreate, you attempt to reference user.first_name.

  3. TypeScript reports:

    Property 'first_name' does not exist on type ...
    

    despite having defined it in additionalFields.

Current vs. Expected behavior

Current: In onCustomerCreate, user.first_name is not recognized by TypeScript (user type lacks that field).

Expected: The type system should include first_name (from user.additionalFields) when used inside plugin hooks like onCustomerCreate.

What version of Better Auth are you using?

1.3.4

Provide environment information

- macOS
- Firefox
- VS Code
- Stripe

Which area(s) are affected? (Select all that apply)

Types, Package, Backend

Auth config (if applicable)

export const auth = betterAuth({
  // ...

  plugins: [
    stripe({
      stripeClient,
      stripeWebhookSecret: env.STRIPE_WEBHOOK_SECRET!,
      createCustomerOnSignUp: true,
      onCustomerCreate: async ({ customer, stripeCustomer, user }, request) => {
        if (stripeCustomer) {
          await stripeClient.customers.update(stripeCustomer.id, {
            name: `${user.name} ${user.first_name}`,
          });
        }
      },
      subscription: {
        enabled: true,
        plans: [
          /* ... */
        ],
      },
    }),
  ],

  user: {
    additionalFields: {
      first_name: { type: "string", required: true },
    },
  },

  // ...
});

Additional context

Happens with strict TypeScript checking.

Originally created by @KinanLak on GitHub (Jul 27, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/3656 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce In your `auth.ts` setup: ```ts export const auth = betterAuth({ plugins: [ organization(), stripe({ stripeClient, stripeWebhookSecret: env.STRIPE_WEBHOOK_SECRET, createCustomerOnSignUp: true, onCustomerCreate: async ({ customer, stripeCustomer, user }, request) => { if (stripeCustomer) { await stripeClient.customers.update(stripeCustomer.id, { name: `${user.name} ${user.first_name}`, //! <--- HERE }); } }, subscription: { enabled: true, plans: [ /* ... */ ] }, }), ], user: { additionalFields: { first_name: { type: "string", required: true }, }, }, }); ``` 1. User signs up → Stripe customer is created. 2. In `onCustomerCreate`, you attempt to reference `user.first_name`. 3. TypeScript reports: ``` Property 'first_name' does not exist on type ... ``` despite having defined it in `additionalFields`. ### Current vs. Expected behavior **Current**: In `onCustomerCreate`, `user.first_name` is not recognized by TypeScript (`user` type lacks that field). **Expected**: The type system should include `first_name` (from `user.additionalFields`) when used inside plugin hooks like `onCustomerCreate`. ### What version of Better Auth are you using? 1.3.4 ### Provide environment information ```bash - macOS - Firefox - VS Code - Stripe ``` ### Which area(s) are affected? (Select all that apply) Types, Package, Backend ### Auth config (if applicable) ```typescript export const auth = betterAuth({ // ... plugins: [ stripe({ stripeClient, stripeWebhookSecret: env.STRIPE_WEBHOOK_SECRET!, createCustomerOnSignUp: true, onCustomerCreate: async ({ customer, stripeCustomer, user }, request) => { if (stripeCustomer) { await stripeClient.customers.update(stripeCustomer.id, { name: `${user.name} ${user.first_name}`, }); } }, subscription: { enabled: true, plans: [ /* ... */ ], }, }), ], user: { additionalFields: { first_name: { type: "string", required: true }, }, }, // ... }); ``` ### Additional context Happens with strict TypeScript checking.
GiteaMirror added the locked label 2026-04-17 17:46:32 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Jul 27, 2025):

This is a known limitation in Better Auth: TypeScript does not automatically infer custom user fields (like first_name from additionalFields) in server-side plugin hooks such as onCustomerCreate. The user object you receive in that callback does not include those extra fields in its type, even though they're present at runtime. This is due to how type inference is currently handled in the framework and is a common pain point for users adding custom fields via additionalFields [source].

As of v1.3.4, there isn't a built-in, type-safe way to propagate additionalFields to these plugin hooks. The recommended workaround is to manually cast the user object in your callback, for example:

onCustomerCreate: async ({ user, ...rest }, request) => {
  const typedUser = user as typeof user & { first_name: string };
  // Now you can use typedUser.first_name
}

Recent PRs have started to improve typings for custom fields in some plugin hooks, but this isn't yet universal for all hooks, including onCustomerCreate in the Stripe plugin [source].

You can track progress or add feedback on related issues like #3328 and #2944. For now, manual casting is the most reliable approach.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

<!-- gh-comment-id:3124765899 --> @dosubot[bot] commented on GitHub (Jul 27, 2025): <!-- Answer --> This is a known limitation in Better Auth: TypeScript does not automatically infer custom user fields (like first_name from additionalFields) in server-side plugin hooks such as onCustomerCreate. The user object you receive in that callback does not include those extra fields in its type, even though they're present at runtime. This is due to how type inference is currently handled in the framework and is a common pain point for users adding custom fields via additionalFields [[source](https://github.com/better-auth/better-auth/issues/3328)]. As of v1.3.4, there isn't a built-in, type-safe way to propagate additionalFields to these plugin hooks. The recommended workaround is to manually cast the user object in your callback, for example: ```ts onCustomerCreate: async ({ user, ...rest }, request) => { const typedUser = user as typeof user & { first_name: string }; // Now you can use typedUser.first_name } ``` Recent PRs have started to improve typings for custom fields in some plugin hooks, but this isn't yet universal for all hooks, including onCustomerCreate in the Stripe plugin [[source](https://github.com/better-auth/better-auth/pull/3600)]. You can track progress or add feedback on related issues like [#3328](https://github.com/better-auth/better-auth/issues/3328) and [#2944](https://github.com/better-auth/better-auth/issues/2944). For now, manual casting is the most reliable approach. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/98f62ccf-1f3d-4991-a0cc-0d73cb0353b3?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/98f62ccf-1f3d-4991-a0cc-0d73cb0353b3?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/98f62ccf-1f3d-4991-a0cc-0d73cb0353b3?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/98f62ccf-1f3d-4991-a0cc-0d73cb0353b3?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/98f62ccf-1f3d-4991-a0cc-0d73cb0353b3?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/98f62ccf-1f3d-4991-a0cc-0d73cb0353b3?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/98f62ccf-1f3d-4991-a0cc-0d73cb0353b3?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/3656)
Author
Owner

@KinanLak commented on GitHub (Jul 27, 2025):

Appreciate the explanation clanker, but I believe this might still be a bug, or at least an inconsistency worth addressing.

Both the user.additionalFields definition and the onCustomerCreate hook live in the backend file (auth.ts), and types from additionalFields are available in most other plugin contexts. So the argument about client/server separation or runtime inference limitations seems irrelevant here.

In my case:

  • Reordering the plugin declarations or the additionalFields does not change anything.
  • Manually casting user (as you suggested) does not help, user.first_name is still undefined, because the actual user object in that callback doesn't include the additional field, even at runtime.
  • In contrast, other plugins like email/password or middleware do infer and expose custom fields like first_name properly.

If it's not currently possible to propagate extended fields into onCustomerCreate, then maybe a workaround would be to allow customization of the customer.name value during creation, with a separate hook or config option? That would avoid having to access user data manually in the hook.

Either way, I think this inconsistency is worth tracking as a bug. The type system and runtime behavior are both misleading in this context.

Thanks again.

<!-- gh-comment-id:3124772473 --> @KinanLak commented on GitHub (Jul 27, 2025): Appreciate the explanation clanker, but I believe this might still be a bug, or at least an inconsistency worth addressing. Both the `user.additionalFields` definition and the `onCustomerCreate` hook live in the backend file (`auth.ts`), and types from `additionalFields` are available in most other plugin contexts. So the argument about client/server separation or runtime inference limitations seems irrelevant here. In my case: - Reordering the plugin declarations or the `additionalFields` does **not** change anything. - Manually casting `user` (as you suggested) does not help, `user.first_name` is still `undefined`, because the actual user object in that callback doesn't include the additional field, even at runtime. - In contrast, other plugins like email/password or middleware *do* infer and expose custom fields like `first_name` properly. If it's not currently possible to propagate extended fields into `onCustomerCreate`, then maybe a workaround would be to allow customization of the `customer.name` value during creation, with a separate hook or config option? That would avoid having to access user data manually in the hook. Either way, I think this inconsistency is worth tracking as a bug. The type system and runtime behavior are both misleading in this context. Thanks again.
Author
Owner

@dosubot[bot] commented on GitHub (Oct 26, 2025):

Hi, @KinanLak. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary:

  • You reported that in Better Auth v1.3.4, TypeScript does not recognize custom user fields like first_name in the Stripe plugin's onCustomerCreate callback.
  • Manual casting does not resolve the issue because those fields are missing at runtime as well.
  • This is more than a typing problem; it reflects an inconsistency since other plugins expose these fields properly.
  • You suggested adding a dedicated hook or configuration option to customize customer data during creation.
  • The issue remains unresolved and is considered a bug due to misleading type and runtime behavior.

Next Steps:

  • Please let me know if this issue is still relevant with the latest version of better-auth by commenting here to keep the discussion open.
  • Otherwise, this issue will be automatically closed in 7 days.

Thank you for your understanding and contribution!

<!-- gh-comment-id:3448664283 --> @dosubot[bot] commented on GitHub (Oct 26, 2025): Hi, @KinanLak. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. **Issue Summary:** - You reported that in Better Auth v1.3.4, TypeScript does not recognize custom user fields like `first_name` in the Stripe plugin's `onCustomerCreate` callback. - Manual casting does not resolve the issue because those fields are missing at runtime as well. - This is more than a typing problem; it reflects an inconsistency since other plugins expose these fields properly. - You suggested adding a dedicated hook or configuration option to customize customer data during creation. - The issue remains unresolved and is considered a bug due to misleading type and runtime behavior. **Next Steps:** - Please let me know if this issue is still relevant with the latest version of better-auth by commenting here to keep the discussion open. - Otherwise, this issue will be automatically closed in 7 days. Thank you for your understanding and contribution!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#26998