[OAuth] Allow sign up with extra fields #343

Closed
opened 2026-03-13 07:42:27 -05:00 by GiteaMirror · 15 comments
Owner

Originally created by @chris-verclytte on GitHub (Dec 5, 2024).

Is your feature request related to a problem? Please describe.
I currently have a use case that involve creating a user with a user type.
When using the login/password flow, everything goes well, I can create a user with any field I'd like.
However, it's not the same when dealing with OAuth flow, I can't pass the user type along the flow.
My database does not allow for userType to be populated with a NULL value so create a user without type and editing afterwards is not a solution I want to consider.

Describe the solution you'd like
The ideal solution would be to provide the extra field in the OAuth signin :

// Inside a React form for instance
await authClient.signIn.social({
    provider: provider.id,
    data: {
    	userType: 'client'
    }
    callbackURL: `/dashboard`,
})

and then get this data provided to mapProfileToUser:

google: {
    mapProfileToUser: (profile: GoogleProfile, data: UserExtraData) => ({
        email: profile.email,
        name: profile.name
        image: profile.picture
        type: data.userType,
    })
},

I think the extra data could be temporarily stored along with callbackURL, codeVerifier and expiresAt inside the verification table.

Describe alternatives you've considered
I've considered the following alternatives:

  1. Pass data as query params inside the callback and "catch it" before redirecting
  2. Add a temporary cookie before saving and use it when saving the data to database (some users are currently doing it this way with next-auth/auth.js)
  3. Add the data inside the state of the OAuth provider URL and then catch it inside callback (this currently breaks the flow as the state is verified when receiving the callback)
Originally created by @chris-verclytte on GitHub (Dec 5, 2024). **Is your feature request related to a problem? Please describe.** I currently have a use case that involve creating a user with a user type. When using the login/password flow, everything goes well, I can create a user with any field I'd like. However, it's not the same when dealing with OAuth flow, I can't pass the user type along the flow. My database does not allow for userType to be populated with a NULL value so create a user without type and editing afterwards is not a solution I want to consider. **Describe the solution you'd like** The ideal solution would be to provide the extra field in the OAuth signin : ```js // Inside a React form for instance await authClient.signIn.social({ provider: provider.id, data: { userType: 'client' } callbackURL: `/dashboard`, }) ``` and then get this data provided to mapProfileToUser: ```js google: { mapProfileToUser: (profile: GoogleProfile, data: UserExtraData) => ({ email: profile.email, name: profile.name image: profile.picture type: data.userType, }) }, ``` I think the extra data could be temporarily stored along with callbackURL, codeVerifier and expiresAt inside the verification table. **Describe alternatives you've considered** I've considered the following alternatives: 1) Pass data as query params inside the callback and "catch it" before redirecting 2) Add a temporary cookie before saving and use it when saving the data to database (some users are currently doing it this way with next-auth/auth.js) 3) Add the data inside the state of the OAuth provider URL and then catch it inside callback (this currently breaks the flow as the state is verified when receiving the callback)
Author
Owner

@chris-verclytte commented on GitHub (Dec 14, 2024):

Here's an idea of how it could look like: https://github.com/chris-verclytte/better-auth/pull/1.

And an example of implementation passing a user type to be used for OAuth user creation :

// action.ts
export async function signInWrapper(
  providerId: 'google',
  userType: UserType,
) {
  // Validation to prevent user from adding any value in userType 

  return auth.api.signInSocial({
    body: {
      newUserCallbackUrl: '/dashboard/welcome',
      callbackURL: '/dashboard',
      provider: providerId,
      data: {
        userType,
      },
    },
  })
}
// auth.config.ts

// ...

export const auth = betterAuth({
 // ... other config params
  databaseHooks: {
    user: {
      create: {
        before: async ({ userType, ...user }) => {
          // Merge standard user info with extra data
          return { ...user, type: userType }
        },
      },
    },
  },

  socialProviders: {
   // ... other providers
    google: {
      clientId: '<CLIENT_ID>',
      clientSecret: '<CLIENT_SECRET>',
      mapProfileToUser: (
        profile: GoogleProfile,
        data: Record<string, unknown>,
      ) => {
        // Forward extra data along with standard fields
        return {
          email: profile.email,
          first_name: profile.given_name,
          last_name: profile.family_name,
          emailVerified: profile.email_verified === true,
          picture_url: profile.picture,
          ...data,
        }
      },
    },
  },
})
@chris-verclytte commented on GitHub (Dec 14, 2024): Here's an idea of how it could look like: https://github.com/chris-verclytte/better-auth/pull/1. And an example of implementation passing a user type to be used for OAuth user creation : ```ts // action.ts export async function signInWrapper( providerId: 'google', userType: UserType, ) { // Validation to prevent user from adding any value in userType return auth.api.signInSocial({ body: { newUserCallbackUrl: '/dashboard/welcome', callbackURL: '/dashboard', provider: providerId, data: { userType, }, }, }) } ``` ```ts // auth.config.ts // ... export const auth = betterAuth({ // ... other config params databaseHooks: { user: { create: { before: async ({ userType, ...user }) => { // Merge standard user info with extra data return { ...user, type: userType } }, }, }, }, socialProviders: { // ... other providers google: { clientId: '<CLIENT_ID>', clientSecret: '<CLIENT_SECRET>', mapProfileToUser: ( profile: GoogleProfile, data: Record<string, unknown>, ) => { // Forward extra data along with standard fields return { email: profile.email, first_name: profile.given_name, last_name: profile.family_name, emailVerified: profile.email_verified === true, picture_url: profile.picture, ...data, } }, }, }, }) ```
Author
Owner

@vinihvc commented on GitHub (Feb 2, 2025):

I'm wondering if is this working?

@vinihvc commented on GitHub (Feb 2, 2025): I'm wondering if is this working?
Author
Owner

@chris-verclytte commented on GitHub (Feb 3, 2025):

Hello @vinihvc, I made it work on a personal fork but I was not sure this use case was common enough to justify a PR on this repo. I'm going to open a draft PR asap.

@chris-verclytte commented on GitHub (Feb 3, 2025): Hello @vinihvc, I made it work on a personal fork but I was not sure this use case was common enough to justify a PR on this repo. I'm going to open a draft PR asap.
Author
Owner

@vinihvc commented on GitHub (Feb 3, 2025):

It seems like something interesting to have, is there this possibility for signUp with email:

const res = await auth.api.signUpEmail({
  email: "test@example.com",
  password: "password",
  lang: "fr" // extra field
})

Not for social 🙁, that's exactly my use case, add the current lang the user sign up

@vinihvc commented on GitHub (Feb 3, 2025): It seems like something interesting to have, is there this possibility for signUp with email: ```tsx const res = await auth.api.signUpEmail({ email: "test@example.com", password: "password", lang: "fr" // extra field }) ``` Not for social 🙁, that's exactly my use case, add the current lang the user sign up
Author
Owner

@jeffrey0606 commented on GitHub (Feb 3, 2025):

Hi @vinihvc this is the right way of doing:

await auth.api.signUpEmail({
      body: {
        email: email,
        password: password,
        lang: "fr" // extra field
      },
    });

Then in your auth.ts

export const auth = betterAuth({
  user: {
    additionalFields: {
      lang: {
        type: "string",
        required: true,
      },
    },
  },
});

Here is the link to the docs https://www.better-auth.com/docs/concepts/database#extending-core-schema

@jeffrey0606 commented on GitHub (Feb 3, 2025): Hi @vinihvc this is the right way of doing: ```ts await auth.api.signUpEmail({ body: { email: email, password: password, lang: "fr" // extra field }, }); ``` Then in your `auth.ts` ```ts export const auth = betterAuth({ user: { additionalFields: { lang: { type: "string", required: true, }, }, }, }); ``` Here is the link to the docs [https://www.better-auth.com/docs/concepts/database#extending-core-schema](url)
Author
Owner

@jeffrey0606 commented on GitHub (Feb 3, 2025):

Hi @chris-verclytte , I don't know why we don't have the same behaviour for the the OAuth as well 🤷🏾‍♂️. But it is available for signup with email tho.

@jeffrey0606 commented on GitHub (Feb 3, 2025): Hi @chris-verclytte , I don't know why we don't have the same behaviour for the the OAuth as well 🤷🏾‍♂️. But it is available for signup with email tho.
Author
Owner

@vinihvc commented on GitHub (Feb 4, 2025):

@jeffrey0606 Yes, I can extend extend it, but how can I pass the values through the method:

auth.api.signInSocial

Something like that:

    const { url } = await auth.api.signInSocial({
      body: {
        provider,
        callbackURL: '/',
      },
      fields: {
        languageId: currentUserUrl, // how to add it
        currencyId: currentUserCurrency // how to add it
      },
    })
@vinihvc commented on GitHub (Feb 4, 2025): @jeffrey0606 Yes, I can extend extend it, but how can I pass the values through the method: ```ts auth.api.signInSocial ``` Something like that: ```ts const { url } = await auth.api.signInSocial({ body: { provider, callbackURL: '/', }, fields: { languageId: currentUserUrl, // how to add it currencyId: currentUserCurrency // how to add it }, })
Author
Owner

@chris-verclytte commented on GitHub (Feb 4, 2025):

Hi @chris-verclytte , I don't know why we don't have the same behaviour for the the OAuth as well 🤷🏾‍♂️. But it is available for signup with email tho.

Indeed but both cases are totally different. While sign up with email happens in a "synchronous" flow, OAuth involves a callback mechanism that loses the call context.

The solution I proposed involves storing the extra fields in the verification table (as it is currently done with the code verifier and callback url) so that we can :

  1. Retrieve them in the callback context
  2. Pass them in getUserInfo and mapProfileToUser
  3. Finally receive them in the DB before hook and handle the extra field the way you want there

Note: I also know that the way to go using next-auth for such a case is to rely on a cookie but I find the approach very messy and difficult to maintain.

@chris-verclytte commented on GitHub (Feb 4, 2025): > Hi [<img alt="" width="16" height="16" src="https://avatars.githubusercontent.com/u/1611574?u=b245b89737f975eab803a9868fc6d7ea92a3f493&amp;v=4&amp;size=80">@chris-verclytte](https://github.com/chris-verclytte) , I don't know why we don't have the same behaviour for the the OAuth as well 🤷🏾‍♂️. But it is available for signup with email tho. Indeed but both cases are totally different. While sign up with email happens in a "synchronous" flow, OAuth involves a callback mechanism that loses the call context. The solution I proposed involves storing the extra fields in the `verification` table (as it is currently done with the code verifier and callback url) so that we can : 1. Retrieve them in the callback context 2. Pass them in `getUserInfo` and `mapProfileToUser` 3. Finally receive them in the DB `before` hook and handle the extra field the way you want there Note: I also know that the way to go using next-auth for such a case is to rely on a cookie but I find the approach very messy and difficult to maintain.
Author
Owner

@Kinfe123 commented on GitHub (Apr 11, 2025):

please make sure to use mapProfileToUser to kinda of map specific field that you want and make sure to drill any modification on getUserInfo

@Kinfe123 commented on GitHub (Apr 11, 2025): please make sure to use `mapProfileToUser` to kinda of map specific field that you want and make sure to drill any modification on `getUserInfo`
Author
Owner

@chris-verclytte commented on GitHub (Apr 13, 2025):

Hello @Kinfe123, I am unclear about the solution you mention, mapProfileToUser, as far as I know, intents to provide a hook for mapping Oauth Profile to user data that will be saved in DB.
Here, we are talking about retrieving data that was available on the flow that triggered the OAuth sign up flow (typical use case would be a page differentiating sign up for different user roles). I am unclear how mapProfileToUser could handle that.

@chris-verclytte commented on GitHub (Apr 13, 2025): Hello @Kinfe123, I am unclear about the solution you mention, `mapProfileToUser`, as far as I know, intents to provide a hook for mapping Oauth Profile to user data that will be saved in DB. Here, we are talking about retrieving data that was available on the flow that triggered the OAuth sign up flow (typical use case would be a page differentiating sign up for different user roles). I am unclear how `mapProfileToUser` could handle that.
Author
Owner

@Bekacru commented on GitHub (Apr 13, 2025):

@chris-verclytte sorry, this shouldn’t be closed. I saw #1395 - thanks for the effort! just wanted to take some time to review it, and we’ll have a solution soon

@Bekacru commented on GitHub (Apr 13, 2025): @chris-verclytte sorry, this shouldn’t be closed. I saw #1395 - thanks for the effort! just wanted to take some time to review it, and we’ll have a solution soon
Author
Owner

@chris-verclytte commented on GitHub (Apr 13, 2025):

All right, thanks for your answer @Bekacru ! Don't hesitate to tell me if it needs an update, I wanted to get a confirmation that the approach find you well before investing more time into it but up for continuing iterating on it !

@chris-verclytte commented on GitHub (Apr 13, 2025): All right, thanks for your answer @Bekacru ! Don't hesitate to tell me if it needs an update, I wanted to get a confirmation that the approach find you well before investing more time into it but up for continuing iterating on it !
Author
Owner

@Kinfe123 commented on GitHub (Apr 13, 2025):

@chris-verclytte hey sorry, i should misunderstood you for some reason with the issue that i was discussing with someone here about linking and overriding userdata maps. it was just a coincidence of facing the same issue again. but the proposed solution is valid having the states on the verification table. will be happy to forward you my assumption once again after i have getting the substances.

@Kinfe123 commented on GitHub (Apr 13, 2025): @chris-verclytte hey sorry, i should misunderstood you for some reason with the issue that i was discussing with someone here about linking and overriding userdata maps. it was just a coincidence of facing the same issue again. but the proposed solution is valid having the states on the verification table. will be happy to forward you my assumption once again after i have getting the substances.
Author
Owner

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

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

Issue Summary

  • Feature request to pass extra fields during OAuth sign-up.
  • You shared a personal fork with a potential solution.
  • Discussion on similar needs and technical challenges with other contributors.
  • Awaiting review and potential support for using the verification table for extra fields.

Next Steps

  • Please confirm if this issue is still relevant to the latest version of the better-auth repository by commenting here.
  • If there is no response, the issue will be automatically closed in 7 days.

Thank you for your understanding and contribution!

@dosubot[bot] commented on GitHub (Jul 13, 2025): Hi, @chris-verclytte. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog. I'm marking this issue as stale. **Issue Summary** - Feature request to pass extra fields during OAuth sign-up. - You shared a personal fork with a potential solution. - Discussion on similar needs and technical challenges with other contributors. - Awaiting review and potential support for using the verification table for extra fields. **Next Steps** - Please confirm if this issue is still relevant to the latest version of the better-auth repository by commenting here. - If there is no response, the issue will be automatically closed in 7 days. Thank you for your understanding and contribution!
Author
Owner

@CodeThatBreak commented on GitHub (Aug 22, 2025):

@Bekacru Any plan to provide this support in future?

@CodeThatBreak commented on GitHub (Aug 22, 2025): @Bekacru Any plan to provide this support in future?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#343