[GH-ISSUE #6447] generateId: false causes 401 on API endpoints in stateless mode #19151

Closed
opened 2026-04-15 17:57:12 -05:00 by GiteaMirror · 6 comments
Owner

Originally created by @mariusch on GitHub (Dec 1, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/6447

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Configure better-auth with stateless session (no database, JWT cookie cache)
  2. Set generateId: false to use OAuth provider's ID as user ID
  3. Configure genericOAuth with Microsoft Entra ID
  4. Return id: idToken.oid from mapProfileToUser
  5. Log in successfully
  6. Call auth.api.getAccessToken({ headers, body: { providerId: "..." } })
  7. Receive 401 UNAUTHORIZED

Current vs. Expected behavior

Use case: Single OAuth provider (Microsoft Entra ID), no database, stateless sessions. I want to use the Entra ID oid as the user ID instead of a generated UUID.

Expected: Setting generateId: false and returning id: idToken.oid from mapProfileToUser should preserve that ID in the session. API endpoints like getAccessToken should work.

Actual:

  • getSession() returns user data but without the id field
  • getAccessToken() and accountInfo() fail with 401 UNAUTHORIZED

The id is filtered out by parseUserOutput in setCookieCache, so it's missing from the JWT session cache. When getAccessToken checks:

let resolvedUserId = session?.user?.id || userId;
if (!resolvedUserId) throw ctx.error("UNAUTHORIZED");

It throws 401 because session.user.id is undefined.

What version of Better Auth are you using?

1.4.4

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.1.0: Mon Oct 20 19:33:00 PDT 2025; root:xnu-12377.41.6~2/RELEASE_ARM64_T6020",
    "release": "25.1.0",
    "cpuCount": 10,
    "cpuModel": "Apple M2 Pro",
    "totalMemory": "16.00 GB",
    "freeMemory": "2.39 GB"
  },
  "node": {
    "version": "v22.15.0",
    "env": "development"
  },
  "packageManager": {
    "name": "bun",
    "version": "1.3.3"
  },
  "frameworks": null,
  "databases": null,
  "betterAuth": {
    "version": "Unknown",
    "config": null
  }

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

Backend

Auth config (if applicable)


Additional context

Root cause: In setCookieCache (cookies module), the user is filtered through parseUserOutput:

const filteredUser = parseUserOutput(ctx.context.options, session.user);

When generateId: false is set, the id field is not recognized as a configured field and gets filtered out before storing in the JWT cookie.

Suggested fix: Always preserve id in setCookieCache:

const filteredUser = { id: session.user.id, ...parseUserOutput(ctx.context.options, session.user) };

Workaround: Store the external ID in a custom field instead:

// Don't use generateId: false
// Instead, add custom field:
user: {
  additionalFields: {
    entraId: { type: "string", required: false, input: false },
  },
},

// In mapProfileToUser:
return {
  // Let better-auth generate id
  entraId: idToken.oid, // Store Entra ID separately
  ...
};
Originally created by @mariusch on GitHub (Dec 1, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/6447 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Configure better-auth with stateless session (no database, JWT cookie cache) 2. Set `generateId: false` to use OAuth provider's ID as user ID 3. Configure genericOAuth with Microsoft Entra ID 4. Return `id: idToken.oid` from `mapProfileToUser` 5. Log in successfully 6. Call `auth.api.getAccessToken({ headers, body: { providerId: "..." } })` 7. Receive 401 UNAUTHORIZED ### Current vs. Expected behavior **Use case:** Single OAuth provider (Microsoft Entra ID), no database, stateless sessions. I want to use the Entra ID `oid` as the user ID instead of a generated UUID. **Expected:** Setting `generateId: false` and returning `id: idToken.oid` from `mapProfileToUser` should preserve that ID in the session. API endpoints like `getAccessToken` should work. **Actual:** - `getSession()` returns user data but **without the `id` field** - `getAccessToken()` and `accountInfo()` fail with 401 UNAUTHORIZED The `id` is filtered out by `parseUserOutput` in `setCookieCache`, so it's missing from the JWT session cache. When `getAccessToken` checks: ```ts let resolvedUserId = session?.user?.id || userId; if (!resolvedUserId) throw ctx.error("UNAUTHORIZED"); ``` It throws 401 because `session.user.id` is undefined. ### What version of Better Auth are you using? 1.4.4 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.1.0: Mon Oct 20 19:33:00 PDT 2025; root:xnu-12377.41.6~2/RELEASE_ARM64_T6020", "release": "25.1.0", "cpuCount": 10, "cpuModel": "Apple M2 Pro", "totalMemory": "16.00 GB", "freeMemory": "2.39 GB" }, "node": { "version": "v22.15.0", "env": "development" }, "packageManager": { "name": "bun", "version": "1.3.3" }, "frameworks": null, "databases": null, "betterAuth": { "version": "Unknown", "config": null } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript ``` ### Additional context **Root cause:** In `setCookieCache` (cookies module), the user is filtered through `parseUserOutput`: ```ts const filteredUser = parseUserOutput(ctx.context.options, session.user); ``` When `generateId: false` is set, the `id` field is not recognized as a configured field and gets filtered out before storing in the JWT cookie. **Suggested fix:** Always preserve `id` in `setCookieCache`: ```ts const filteredUser = { id: session.user.id, ...parseUserOutput(ctx.context.options, session.user) }; ``` **Workaround:** Store the external ID in a custom field instead: ```ts // Don't use generateId: false // Instead, add custom field: user: { additionalFields: { entraId: { type: "string", required: false, input: false }, }, }, // In mapProfileToUser: return { // Let better-auth generate id entraId: idToken.oid, // Store Entra ID separately ... }; ```
GiteaMirror added the lockedbug labels 2026-04-15 17:57:12 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Dec 1, 2025):

(tag @better-auth-agent if you would like an answer from the Agent)

If you need more help, tag @Skyvern in a comment so I can respond.

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3597681331 --> @better-auth-agent[bot] commented on GitHub (Dec 1, 2025): (tag @better-auth-agent if you would like an answer from the Agent) _If you need more help, tag @Skyvern in a comment so I can respond._ <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) • [Discord](https://discord.gg/fG2XXEuQX3) • [GitHub](https://github.com/Skyvern-AI/Skyvern) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/fG2XXEuQX3) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/Skyvern-AI/Skyvern)
Author
Owner

@GautamBytes commented on GitHub (Dec 1, 2025):

Looking into it!!

<!-- gh-comment-id:3598426075 --> @GautamBytes commented on GitHub (Dec 1, 2025): Looking into it!!
Author
Owner

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

Thanks, @GautamBytes. I checked this again, and it looks like the issue is still present in version 1.4.5.

I believe the test in #6452 passes because it did not include the configuration that causes the issue:

advanced: {
  database: {
    generateId: false,
  },
},
<!-- gh-comment-id:3606661971 --> @mariusch commented on GitHub (Dec 3, 2025): Thanks, @GautamBytes. I checked this again, and it looks like the issue is still present in version 1.4.5. I believe the test in #6452 passes because it did not include the configuration that causes the issue: ``` advanced: { database: { generateId: false, }, }, ```
Author
Owner

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

Thanks, @GautamBytes. I checked this again, and it looks like the issue is still present in version 1.4.5.

I believe the test in #6452 passes because it did not include the configuration that causes the issue:

advanced: {
  database: {
    generateId: false,
  },
},

My bad , just raised the follow up pr. Hope it fixes the issue!

<!-- gh-comment-id:3607392219 --> @GautamBytes commented on GitHub (Dec 3, 2025): > Thanks, [@GautamBytes](https://github.com/GautamBytes). I checked this again, and it looks like the issue is still present in version 1.4.5. > > I believe the test in [#6452](https://github.com/better-auth/better-auth/pull/6452) passes because it did not include the configuration that causes the issue: > > ``` > advanced: { > database: { > generateId: false, > }, > }, > ``` My bad , just raised the follow up pr. Hope it fixes the issue!
Author
Owner

@himself65 commented on GitHub (Jan 24, 2026):

My bad , just raised the follow up pr. Hope it fixes the issue!

Which one?

<!-- gh-comment-id:3793094097 --> @himself65 commented on GitHub (Jan 24, 2026): > My bad , just raised the follow up pr. Hope it fixes the issue! Which one?
Author
Owner

@himself65 commented on GitHub (Jan 24, 2026):

PR: https://github.com/better-auth/better-auth/pull/6452

<!-- gh-comment-id:3793104055 --> @himself65 commented on GitHub (Jan 24, 2026): PR: https://github.com/better-auth/better-auth/pull/6452
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#19151