mapProfileToUser not working #1232

Closed
opened 2026-03-13 08:29:23 -05:00 by GiteaMirror · 8 comments
Owner

Originally created by @b-bot on GitHub (May 19, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Implement Sign in with GitHub
  2. Add additional field to users (github) on prisma schema etc.
  3. Try map the username which is logged as profile.login in the call.
  4. Does not save to DB

Current vs. Expected behavior

Currently it does not sync the username to a field in my DB. I think this is the expected behaviour. This has come up a few times on the Discord and Reddit so I believe it is likely a bug perhaps with edge cases as not everyone is getting it, might be provider dependant.

What version of Better Auth are you using?

1.2.8

Provide environment information

macOS (Latest)
Microsoft Edge

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

Backend

Auth config (if applicable)

user: {
    additionalFields: {
      githubUsername: {
        type: 'string',
        input: true,
      },
    },
  },
  socialProviders: {
    github: {
      prompt: 'select_account',
      clientId: serverEnv.GITHUB_CLIENT_ID,
      clientSecret: serverEnv.GITHUB_CLIENT_SECRET,
      mapProfileToUser: (profile) => {
        return {
          githubUsername: profile.login,
        };
      },
    },
  },

Additional context

The docs were also unclear about what is supposed to happen here IMO and maybe some info is missing like not only having to add the additional fields but adding the field in your DB. I thought maybe it only adds to session but thats not happening either.

Originally created by @b-bot on GitHub (May 19, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Implement Sign in with GitHub 2. Add additional field to users (`github`) on prisma schema etc. 3. Try map the username which is logged as `profile.login` in the call. 4. Does not save to DB ### Current vs. Expected behavior Currently it does not sync the username to a field in my DB. I think this is the expected behaviour. This has come up a few times on the Discord and Reddit so I believe it is likely a bug perhaps with edge cases as not everyone is getting it, might be provider dependant. ### What version of Better Auth are you using? 1.2.8 ### Provide environment information ```bash macOS (Latest) Microsoft Edge ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript user: { additionalFields: { githubUsername: { type: 'string', input: true, }, }, }, socialProviders: { github: { prompt: 'select_account', clientId: serverEnv.GITHUB_CLIENT_ID, clientSecret: serverEnv.GITHUB_CLIENT_SECRET, mapProfileToUser: (profile) => { return { githubUsername: profile.login, }; }, }, }, ``` ### Additional context The docs were also unclear about what is supposed to happen here IMO and maybe some info is missing like not only having to add the additional fields but adding the field in your DB. I thought maybe it only adds to session but thats not happening either.
Author
Owner

@jacobsamo commented on GitHub (May 20, 2025):

I encountered this issue too, you may have tried it already but have you made sure that, that column exists in the db? and that it has the correct spelling?

@jacobsamo commented on GitHub (May 20, 2025): I encountered this issue too, you may have tried it already but have you made sure that, that column exists in the db? and that it has the correct spelling?
Author
Owner

@b-bot commented on GitHub (May 20, 2025):

@jacobsamo yep, is yours working now?

@b-bot commented on GitHub (May 20, 2025): @jacobsamo yep, is yours working now?
Author
Owner

@jacobsamo commented on GitHub (May 20, 2025):

Yep i got mine all working correctly example of me mapping of values:

  user: {
    modelName: "users",
    additionalFields: {
      first_name: {
        type: "string",
        fieldName: "first_name",
        required: false,
        input: false,
      },
      last_name: {
        type: "string",
        fieldName: "last_name",
        required: false,
        input: false,
      },
      username: {
        type: "string",
        fieldName: "username",
        required: false,
        input: false,
      },
      bio: {
        type: "string",
        fieldName: "bio",
        required: false,
        input: false,
      },
    },
    fields: {
      // name: "full_name",
      // image: "profile_picture",
      createdAt: "created_at",
      updatedAt: "updated_at",
    },
  },
@jacobsamo commented on GitHub (May 20, 2025): Yep i got mine all working correctly example of me mapping of values: ```ts user: { modelName: "users", additionalFields: { first_name: { type: "string", fieldName: "first_name", required: false, input: false, }, last_name: { type: "string", fieldName: "last_name", required: false, input: false, }, username: { type: "string", fieldName: "username", required: false, input: false, }, bio: { type: "string", fieldName: "bio", required: false, input: false, }, }, fields: { // name: "full_name", // image: "profile_picture", createdAt: "created_at", updatedAt: "updated_at", }, }, ```
Author
Owner

@b-bot commented on GitHub (May 20, 2025):

The only thing I can think of why it might not be working is this - my User model is named singular ie User but I want it named users in my DB. When I call it via Prisma I still use prisma.user this way.

I do this without adding modelName: "users" on auth config and it works (as model is still singular naming despite table name being plural) and it works with all other fields I do this with for example email_verified is still functional.

It might be an edge case with this kind of setup.

Redacted schema and auth config below

model User {
  id                  String    @id @default(cuid(2))
  name                String?
  email               String?   @unique
  emailVerified       Boolean?  @map("email_verified")
  image               String?
  github              String?   @map("github")
  createdAt           DateTime  @default(now()) @map("created_at")
  updatedAt           DateTime  @updatedAt @map("updated_at")

  accounts           Account[]
  sessions           Session[]

  @@map("users")
}
  user: {
    additionalFields: {
      github: {
        fieldName: 'github',
        type: 'string',
        required: false,
        input: false,
      },
    },
  },
  socialProviders: {
    github: {
      prompt: 'select_account',
      clientId: serverEnv.GITHUB_CLIENT_ID,
      clientSecret: serverEnv.GITHUB_CLIENT_SECRET,
      mapProfileToUser: (profile) => {
        return {
          github: profile.login,
        };
      },
    },
},
@b-bot commented on GitHub (May 20, 2025): The only thing I can think of why it might not be working is this - my User model is named singular ie `User` but I want it named `users` in my DB. When I call it via Prisma I still use `prisma.user` this way. I do this without adding `modelName: "users"` on auth config and it works (as model is still singular naming despite table name being plural) and it works with all other fields I do this with for example `email_verified` is still functional. It might be an edge case with this kind of setup. Redacted schema and auth config below ``` model User { id String @id @default(cuid(2)) name String? email String? @unique emailVerified Boolean? @map("email_verified") image String? github String? @map("github") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") accounts Account[] sessions Session[] @@map("users") } ``` ``` user: { additionalFields: { github: { fieldName: 'github', type: 'string', required: false, input: false, }, }, }, socialProviders: { github: { prompt: 'select_account', clientId: serverEnv.GITHUB_CLIENT_ID, clientSecret: serverEnv.GITHUB_CLIENT_SECRET, mapProfileToUser: (profile) => { return { github: profile.login, }; }, }, }, ```
Author
Owner

@jacobsamo commented on GitHub (May 20, 2025):

that is quite possible too in your auth schema just alter the modelName to be users rather than user just like this:

user: {
    modelName: "users", // alter this to be whatever you want
    additionalFields: {
     // ... additional fields
    },
    fields: {
     // ... fields
    },
  }

docs: https://www.better-auth.com/docs/concepts/database#custom-table-names

but if users are getting created in the db I am unsure of the issue

@jacobsamo commented on GitHub (May 20, 2025): that is quite possible too in your auth schema just alter the `modelName` to be `users` rather than user just like this: ```ts user: { modelName: "users", // alter this to be whatever you want additionalFields: { // ... additional fields }, fields: { // ... fields }, } ``` docs: https://www.better-auth.com/docs/concepts/database#custom-table-names but if users are getting created in the db I am unsure of the issue
Author
Owner

@b-bot commented on GitHub (May 21, 2025):

@jacobsamo thanks for the suggestions. Yeah I did do that and strangely it breaks unless Prisma model name matches eg. model Users { } despite the @@map('users')

@b-bot commented on GitHub (May 21, 2025): @jacobsamo thanks for the suggestions. Yeah I did do that and strangely it breaks unless Prisma model name matches eg. `model Users { }` despite the `@@map('users')`
Author
Owner

@b-bot commented on GitHub (May 21, 2025):

In case anyone comes across this I have implemented a workaround.

It seems the mapProfileToUser is only run on creation of the user. This means functions like linkSocial will not populate this field. While this is problematic as if a user changes their details it will not sync, it is likely as intended.

I ended up using database hooks and instead of saving to the user I saved to the Account itself which is actually way cleaner. There is an extra call to the provider API unfortunately so if anyone figures out how to get this info inside the hook let me know.

  databaseHooks: {
    account: {
      update: {
        after: async (account) => {
          const username = await getUsernameForAccount(
            account.providerId,
            account?.accessToken,
          );
          if (username) {
            await db.account.update({
              where: {
                id: account.id,
              },
              data: {
                username,
              },
            });
          }
        },
      },
    },
  },
@b-bot commented on GitHub (May 21, 2025): In case anyone comes across this I have implemented a workaround. It seems the `mapProfileToUser` is only run on creation of the user. This means functions like `linkSocial` will not populate this field. While this is problematic as if a user changes their details it will not sync, it is likely as intended. I ended up using database hooks and instead of saving to the user I saved to the Account itself which is actually way cleaner. There is an extra call to the provider API unfortunately so if anyone figures out how to get this info inside the hook let me know. ``` databaseHooks: { account: { update: { after: async (account) => { const username = await getUsernameForAccount( account.providerId, account?.accessToken, ); if (username) { await db.account.update({ where: { id: account.id, }, data: { username, }, }); } }, }, }, }, ```
Author
Owner

@tkrebs2 commented on GitHub (May 21, 2025):

In case anyone comes across this I have implemented a workaround.

It seems the mapProfileToUser is only run on creation of the user. This means functions like linkSocial will not populate this field. While this is problematic as if a user changes their details it will not sync, it is likely as intended.

I ended up using database hooks and instead of saving to the user I saved to the Account itself which is actually way cleaner. There is an extra call to the provider API unfortunately so if anyone figures out how to get this info inside the hook let me know.

  databaseHooks: {
    account: {
      update: {
        after: async (account) => {
          const username = await getUsernameForAccount(
            account.providerId,
            account?.accessToken,
          );
          if (username) {
            await db.account.update({
              where: {
                id: account.id,
              },
              data: {
                username,
              },
            });
          }
        },
      },
    },
  },

getUsernameForAccount() is just a function that calls the github api and returns the username? And how did you setup the config for extending the Account table?

@tkrebs2 commented on GitHub (May 21, 2025): > In case anyone comes across this I have implemented a workaround. > > It seems the `mapProfileToUser` is only run on creation of the user. This means functions like `linkSocial` will not populate this field. While this is problematic as if a user changes their details it will not sync, it is likely as intended. > > I ended up using database hooks and instead of saving to the user I saved to the Account itself which is actually way cleaner. There is an extra call to the provider API unfortunately so if anyone figures out how to get this info inside the hook let me know. > > ``` > databaseHooks: { > account: { > update: { > after: async (account) => { > const username = await getUsernameForAccount( > account.providerId, > account?.accessToken, > ); > if (username) { > await db.account.update({ > where: { > id: account.id, > }, > data: { > username, > }, > }); > } > }, > }, > }, > }, > ``` `getUsernameForAccount()` is just a function that calls the github api and returns the username? And how did you setup the config for extending the Account table?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1232