[GH-ISSUE #5480] mapProfileToUser updates the db only once #18895

Open
opened 2026-04-15 17:35:08 -05:00 by GiteaMirror · 8 comments
Owner

Originally created by @FrancescoSaverioZuppichini on GitHub (Oct 22, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/5480

Originally assigned to: @gustavovalverde on GitHub.

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

I am using

I need to include when we login additional fields to the user (roles and role). I am using microsoft social provider

 mapProfileToUser: (profile) => {
        console.log("mapProfileToUser", profile)
        const msProfile = profile as unknown as MicrosoftEntraIDProfile

        const user = {
          name: msProfile.name,
          firstName: msProfile.given_name,
          lastName: msProfile.family_name,
          email: msProfile.preferred_username,
          emailVerified: true, // Microsoft accounts are pre-verified
          image: msProfile.picture,
          roles: msProfile.roles ?? [],
          role: msProfile.roles.includes("voiceiq:admin:read") ? "admin" : "user",
        }
        console.log("mapProfileToUser", user)
        return user
      },

The first time, roles and role are stored into the db but what I want is to include roles and role in the session without storing them. Moreover, if we change role to

 role: msProfile.roles.includes("voiceiq:admin:read") ? "FOOOOO" : "user",

I can see that the document in the db is never updated and it is still admin. What is the proper way to just pass these fields from the profile returned by ms to the session object so everytime a user login I have fresh and correct data?

Current vs. Expected behavior

Cannot update the custom fields in the session

What version of Better Auth are you using?

^1.3.28

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 24.6.0: Mon Aug 11 21:16:52 PDT 2025; root:xnu-11417.140.69.701.11~1/RELEASE_ARM64_T8112",
    "release": "24.6.0",
    "cpuCount": 8,
    "cpuModel": "Apple M2",
    "totalMemory": "16.00 GB",
    "freeMemory": "0.31 GB"
  },
  "node": {
    "version": "v22.13.0",
    "env": "development"
  },
  "packageManager": {
    "name": "bun",
    "version": "1.2.0"
  },
  "frameworks": [
    {
      "name": "next",
      "version": "15.5.4"
    },
    {
      "name": "react",
      "version": "19.1.1"
    }
  ],
  "databases": [
    {
      "name": "mongodb",
      "version": "^6.9.0"
    }
  ],
  "betterAuth": {
    "version": "^1.3.28",
    "config": {
      "session": {
        "expiresIn": 28800,
        "updateAge": 3600
      },
      "socialProviders": {
        "microsoft": {
          "clientId": "[REDACTED]",
          "clientSecret": "[REDACTED]",
          "tenantId": "473672ba-cd07-4371-a2ae-788b4c61840e",
          "authority": "https://login.microsoftonline.com",
          "disableDefaultScope": true,
          "scope": [
            "openid",
            "profile",
            "email",
            "offline_access",
            "api://voiceiq-dev/user_impersonation"
          ]
        }
      },
      "logger": {
        "level": "debug"
      },
      "user": {
        "additionalFields": {
          "roles": {
            "type": "string[]",
            "input": false
          },
          "role": {
            "type": "string",
            "input": false
          }
        }
      }
    }
  }
}

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

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  emailAndPassword: {  
    enabled: true
  },
});

Additional context

No response

Originally created by @FrancescoSaverioZuppichini on GitHub (Oct 22, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/5480 Originally assigned to: @gustavovalverde on GitHub. ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce I am using I need to include when we login additional fields to the user (roles and role). I am using `microsoft` social provider ```js mapProfileToUser: (profile) => { console.log("mapProfileToUser", profile) const msProfile = profile as unknown as MicrosoftEntraIDProfile const user = { name: msProfile.name, firstName: msProfile.given_name, lastName: msProfile.family_name, email: msProfile.preferred_username, emailVerified: true, // Microsoft accounts are pre-verified image: msProfile.picture, roles: msProfile.roles ?? [], role: msProfile.roles.includes("voiceiq:admin:read") ? "admin" : "user", } console.log("mapProfileToUser", user) return user }, ``` The first time, roles and role are stored into the db but what I want is to include roles and role in the session without storing them. Moreover, if we change role to ```js role: msProfile.roles.includes("voiceiq:admin:read") ? "FOOOOO" : "user", ``` I can see that the document in the db is never updated and it is still `admin`. What is the proper way to just pass these fields from the profile returned by ms to the session object so everytime a user login I have fresh and correct data? ### Current vs. Expected behavior Cannot update the custom fields in the session ### What version of Better Auth are you using? ^1.3.28 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 24.6.0: Mon Aug 11 21:16:52 PDT 2025; root:xnu-11417.140.69.701.11~1/RELEASE_ARM64_T8112", "release": "24.6.0", "cpuCount": 8, "cpuModel": "Apple M2", "totalMemory": "16.00 GB", "freeMemory": "0.31 GB" }, "node": { "version": "v22.13.0", "env": "development" }, "packageManager": { "name": "bun", "version": "1.2.0" }, "frameworks": [ { "name": "next", "version": "15.5.4" }, { "name": "react", "version": "19.1.1" } ], "databases": [ { "name": "mongodb", "version": "^6.9.0" } ], "betterAuth": { "version": "^1.3.28", "config": { "session": { "expiresIn": 28800, "updateAge": 3600 }, "socialProviders": { "microsoft": { "clientId": "[REDACTED]", "clientSecret": "[REDACTED]", "tenantId": "473672ba-cd07-4371-a2ae-788b4c61840e", "authority": "https://login.microsoftonline.com", "disableDefaultScope": true, "scope": [ "openid", "profile", "email", "offline_access", "api://voiceiq-dev/user_impersonation" ] } }, "logger": { "level": "debug" }, "user": { "additionalFields": { "roles": { "type": "string[]", "input": false }, "role": { "type": "string", "input": false } } } } } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ emailAndPassword: { enabled: true }, }); ``` ### Additional context _No response_
GiteaMirror added the databaseoauthbug labels 2026-04-15 17:35:09 -05:00
Author
Owner

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

This is a common limitation in Better Auth: mapProfileToUser only runs on initial user creation and does not update user fields in the database or session on subsequent logins. Custom fields like roles and role are only included in the session if they are stored in the database and returned by your adapter. There is no built-in way to inject fresh provider profile data into the session object on every login without persisting it to the database (source, source).

To include dynamic fields like roles and role in the session without storing them, you need to use a custom session plugin or endpoint to postprocess the session object before returning it to the client. This lets you fetch the latest profile data from the provider and merge it into the session at runtime, but it requires custom logic (source).

The returned property in additionalFields only affects TypeScript types and validation, not what is actually included in the session at runtime. If you want these fields to always be present in the session, you must either persist them in the database or manually inject them into the session using a custom session handler (source).

This is a known gap in Better Auth and has been discussed in several issues and discussions. If you need an example of a custom session plugin or want to discuss possible workarounds, let me know!

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:3432098802 --> @dosubot[bot] commented on GitHub (Oct 22, 2025): <!-- Answer --> This is a common limitation in Better Auth: mapProfileToUser only runs on initial user creation and does not update user fields in the database or session on subsequent logins. Custom fields like roles and role are only included in the session if they are stored in the database and returned by your adapter. There is no built-in way to inject fresh provider profile data into the session object on every login without persisting it to the database ([source](https://github.com/better-auth/better-auth/issues/3888), [source](https://github.com/better-auth/better-auth/issues/3290)). To include dynamic fields like roles and role in the session without storing them, you need to use a custom session plugin or endpoint to postprocess the session object before returning it to the client. This lets you fetch the latest profile data from the provider and merge it into the session at runtime, but it requires custom logic ([source](https://github.com/better-auth/better-auth/issues/3888)). The returned property in additionalFields only affects TypeScript types and validation, not what is actually included in the session at runtime. If you want these fields to always be present in the session, you must either persist them in the database or manually inject them into the session using a custom session handler ([source](https://github.com/better-auth/better-auth/issues/3888)). This is a known gap in Better Auth and has been discussed in several issues and discussions. If you need an example of a custom session plugin or want to discuss possible workarounds, let me know! <!-- 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/9c20a540-a664-488a-b2bd-3cc47845f9e6?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/9c20a540-a664-488a-b2bd-3cc47845f9e6?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/9c20a540-a664-488a-b2bd-3cc47845f9e6?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/9c20a540-a664-488a-b2bd-3cc47845f9e6?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/9c20a540-a664-488a-b2bd-3cc47845f9e6?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/9c20a540-a664-488a-b2bd-3cc47845f9e6?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/9c20a540-a664-488a-b2bd-3cc47845f9e6?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/5480)
Author
Owner

@FrancescoSaverioZuppichini commented on GitHub (Oct 22, 2025):

damn so it looks like I cannot migrate from auth.js, this seems like a big limitation

<!-- gh-comment-id:3432118150 --> @FrancescoSaverioZuppichini commented on GitHub (Oct 22, 2025): damn so it looks like I cannot migrate from auth.js, this seems like a big limitation
Author
Owner

@ataylorme commented on GitHub (Oct 27, 2025):

@dosu show an example of a custom session plugin where the database is updated every time with custom items such as role in mapProfileToUser

<!-- gh-comment-id:3453500228 --> @ataylorme commented on GitHub (Oct 27, 2025): @dosu show an example of a custom session plugin where the database is updated every time with custom items such as `role` in `mapProfileToUser`
Author
Owner

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

Here's how you can update the database with custom items like role on every login using Better Auth:

Better Auth doesn't update user fields like role on every login by default—mapProfileToUser only runs on user creation. To keep fields like role in sync with your provider, you need to use a database hook or plugin that runs after each login and updates the user record with fresh data from the provider profile.

A common approach is to use a database hook on the account or session, for example:

databaseHooks: {
  account: {
    update: {
      after: async (account) => {
        // Fetch the latest profile from your provider using the access token
        const msProfile = await fetchMicrosoftProfile(account.accessToken);

        // Compute the new role
        const newRole = msProfile.roles.includes("voiceiq:admin:read") ? "admin" : "user";

        // Update the user in your database (example for Prisma)
        await db.user.update({
          where: { id: account.userId },
          data: {
            roles: msProfile.roles,
            role: newRole,
          },
        });
      },
    },
  },
}

You can also use the customSession plugin to inject fresh fields into the session response, but this does not persist them to the database—it's just for session shaping.

If you want both: use the database hook to persist the latest roles/role, and the custom session plugin to ensure they're always present in the session object returned to the client.

References:

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:3453510274 --> @dosubot[bot] commented on GitHub (Oct 27, 2025): <!-- Answer --> Here's how you can update the database with custom items like `role` on every login using Better Auth: Better Auth doesn't update user fields like `role` on every login by default—`mapProfileToUser` only runs on user creation. To keep fields like `role` in sync with your provider, you need to use a database hook or plugin that runs after each login and updates the user record with fresh data from the provider profile. A common approach is to use a database hook on the account or session, for example: ```ts databaseHooks: { account: { update: { after: async (account) => { // Fetch the latest profile from your provider using the access token const msProfile = await fetchMicrosoftProfile(account.accessToken); // Compute the new role const newRole = msProfile.roles.includes("voiceiq:admin:read") ? "admin" : "user"; // Update the user in your database (example for Prisma) await db.user.update({ where: { id: account.userId }, data: { roles: msProfile.roles, role: newRole, }, }); }, }, }, } ``` You can also use the [`customSession` plugin](https://www.better-auth.com/docs/concepts/session-management#customizing-session-response) to inject fresh fields into the session response, but this does not persist them to the database—it's just for session shaping. If you want both: use the database hook to persist the latest roles/role, and the custom session plugin to ensure they're always present in the session object returned to the client. References: - [Database hook example](https://github.com/better-auth/better-auth/discussions/3290) - [Custom session plugin docs](https://github.com/better-auth/better-auth/blob/5b491010505ef5041b6ec9c83149029deabd894a/docs/content/docs/concepts/session-management.mdx) <!-- 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/1cb9670a-47fd-40a9-bdcd-b3a4895a04b0?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/1cb9670a-47fd-40a9-bdcd-b3a4895a04b0?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/1cb9670a-47fd-40a9-bdcd-b3a4895a04b0?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/1cb9670a-47fd-40a9-bdcd-b3a4895a04b0?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/1cb9670a-47fd-40a9-bdcd-b3a4895a04b0?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/1cb9670a-47fd-40a9-bdcd-b3a4895a04b0?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/1cb9670a-47fd-40a9-bdcd-b3a4895a04b0?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/5480)
Author
Owner

@Bekacru commented on GitHub (Oct 28, 2025):

You can enable overrideUserInfoOnSignIn

	github: {
			clientId: process.env.GITHUB_CLIENT_ID || "",
			clientSecret: process.env.GITHUB_CLIENT_SECRET || "",
			overrideUserInfoOnSignIn: true
		},
<!-- gh-comment-id:3454326010 --> @Bekacru commented on GitHub (Oct 28, 2025): You can enable `overrideUserInfoOnSignIn` ``` github: { clientId: process.env.GITHUB_CLIENT_ID || "", clientSecret: process.env.GITHUB_CLIENT_SECRET || "", overrideUserInfoOnSignIn: true }, ```
Author
Owner

@snowfoxsh commented on GitHub (Dec 3, 2025):

You can enable overrideUserInfoOnSignIn

that option does not re run the mapProfileToUser method on login. the mapProfileToUser method is only run once during account creation. (at least for the Microsoft provider)

is this an issue that is planned to be fixed or is even reasonable to fix? would this project accept a pr attempting a fix for this issue?

<!-- gh-comment-id:3608803657 --> @snowfoxsh commented on GitHub (Dec 3, 2025): > You can enable `overrideUserInfoOnSignIn` that option does not re run the `mapProfileToUser` method on login. the `mapProfileToUser` method is only run once during account creation. (at least for the Microsoft provider) is this an issue that is planned to be fixed or is even reasonable to fix? would this project accept a pr attempting a fix for this issue?
Author
Owner

@dylanwe commented on GitHub (Feb 24, 2026):

I'm also having problems with this.

  1. Users login.
  2. I assign roles to them after account creation through Azure.
  3. The session expires.
  4. They login again but they don't have the new roles.
<!-- gh-comment-id:3952052817 --> @dylanwe commented on GitHub (Feb 24, 2026): I'm also having problems with this. 1. Users login. 2. I assign roles to them after account creation through Azure. 3. The session expires. 4. They login again but they don't have the new roles.
Author
Owner

@dylanwe commented on GitHub (Feb 25, 2026):

Dosu's suggestion to use database hooks works for my use case

<!-- gh-comment-id:3960057960 --> @dylanwe commented on GitHub (Feb 25, 2026): Dosu's suggestion to use database hooks works for my use case
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#18895