[GH-ISSUE #7355] OAuth/OIDC token endpoints return wrapped JSON (response field) instead of spec-pure payload #10791

Closed
opened 2026-04-13 07:08:43 -05:00 by GiteaMirror · 11 comments
Owner

Originally created by @gustavovalverde on GitHub (Jan 14, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/7355

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Run Better Auth on canary (or latest) with default config.
  2. Call any OAuth/OIDC token-style endpoint over HTTP (e.g. /oauth2/token, OIDC4VCI /credential).
  3. Inspect the JSON response body.

Current vs. Expected behavior

Current: JSON is wrapped as { "response": { ...actual_oauth_fields... } }. OAuth/OIDC clients expect access_token, token_type, c_nonce, etc. at the top level, so they fail (e.g., sending Bearer undefined or raising invalid_token).

Expected: HTTP responses for OAuth/OIDC endpoints should be spec-pure JSON with top-level fields, e.g. { "access_token": "...", "token_type": "bearer", ... }.

What version of Better Auth are you using?

Canary branch tip (also reproducible on 1.5.0-beta.6)

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:56 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6041",
    "release": "25.2.0",
    "cpuCount": 14,
    "cpuModel": "Apple M4 Pro",
    "totalMemory": "48.00 GB",
    "freeMemory": "0.20 GB"
  },
  "node": {
    "version": "v25.2.1",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "11.6.2"
  },
  "frameworks": null,
  "databases": null,
  "betterAuth": {
    "version": "Unknown",
    "config": null
  }
}

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

  • Backend
  • Package
  • Other

Additional context

Root cause appears to be the endpoint pipeline returning { response, headers, status } for HTTP requests, which then gets serialized directly by the router. This is fine for internal API calls but breaks spec requirements for OAuth/OIDC endpoints that must return top-level JSON fields.

Proposed fix

At the outermost endpoint wrapper (packages/better-auth/src/api/to-auth-endpoints.ts), detect when the call is handling a real HTTP Request and return a Response there (after hooks/cookies), so the JSON body is the raw payload rather than { response: ... }.

Pseudo-diff:

const hasRequest = typeof context?.request?.url === "string";
const wantsResponse = Boolean(context?.asResponse) || hasRequest;

// keep internal execution as non-Response so hooks run
internalContext.asResponse = false;
internalContext.returnHeaders = true;
internalContext.returnStatus = true;

// ... run endpoint, apply hooks ...

if (isAPIError(result.response) && !wantsResponse) throw result.response;

return wantsResponse
  ? toResponse(result.response, { headers: result.headers, status: result.status })
  : context?.returnHeaders
    ? { headers: result.headers, response: result.response, status: result.status }
    : result.response;

This preserves existing internal API behavior while ensuring HTTP responses are spec-pure for OAuth/OIDC (and any other standards-based JSON endpoints).

Originally created by @gustavovalverde on GitHub (Jan 14, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/7355 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Run Better Auth on canary (or latest) with default config. 2. Call any OAuth/OIDC token-style endpoint over HTTP (e.g. `/oauth2/token`, OIDC4VCI `/credential`). 3. Inspect the JSON response body. ### Current vs. Expected behavior **Current:** JSON is wrapped as `{ "response": { ...actual_oauth_fields... } }`. OAuth/OIDC clients expect `access_token`, `token_type`, `c_nonce`, etc. at the top level, so they fail (e.g., sending `Bearer undefined` or raising `invalid_token`). **Expected:** HTTP responses for OAuth/OIDC endpoints should be spec-pure JSON with top-level fields, e.g. `{ "access_token": "...", "token_type": "bearer", ... }`. ### What version of Better Auth are you using? Canary branch tip (also reproducible on 1.5.0-beta.6) ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:56 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6041", "release": "25.2.0", "cpuCount": 14, "cpuModel": "Apple M4 Pro", "totalMemory": "48.00 GB", "freeMemory": "0.20 GB" }, "node": { "version": "v25.2.1", "env": "development" }, "packageManager": { "name": "npm", "version": "11.6.2" }, "frameworks": null, "databases": null, "betterAuth": { "version": "Unknown", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) - [x] Backend - [x] Package - [x] Other ### Additional context Root cause appears to be the endpoint pipeline returning `{ response, headers, status }` for HTTP requests, which then gets serialized directly by the router. This is fine for internal API calls but breaks spec requirements for OAuth/OIDC endpoints that must return top-level JSON fields. ### Proposed fix At the outermost endpoint wrapper (`packages/better-auth/src/api/to-auth-endpoints.ts`), detect when the call is handling a real HTTP `Request` and return a `Response` there (after hooks/cookies), so the JSON body is the raw payload rather than `{ response: ... }`. Pseudo-diff: ```ts const hasRequest = typeof context?.request?.url === "string"; const wantsResponse = Boolean(context?.asResponse) || hasRequest; // keep internal execution as non-Response so hooks run internalContext.asResponse = false; internalContext.returnHeaders = true; internalContext.returnStatus = true; // ... run endpoint, apply hooks ... if (isAPIError(result.response) && !wantsResponse) throw result.response; return wantsResponse ? toResponse(result.response, { headers: result.headers, status: result.status }) : context?.returnHeaders ? { headers: result.headers, response: result.response, status: result.status } : result.response; ``` This preserves existing internal API behavior while ensuring HTTP responses are spec-pure for OAuth/OIDC (and any other standards-based JSON endpoints).
GiteaMirror added the lockedbug labels 2026-04-13 07:08:43 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Jan 14, 2026):

It seems that the issue regarding the OAuth/OIDC token endpoints returning wrapped JSON instead of the expected spec-pure payload is recognized within the Better Auth community. Users have observed that the current implementation wraps responses in a { "response": { ...actual_fields... } } structure, while clients expect top-level fields such as access_token, token_type, etc., leading to failures in authentication processes [1].

To resolve this, the proposed fix involves returning the raw response payload when a real HTTP Request context is provided, without the outer wrapping. This adjustment would maintain internal API behavior while adhering to the OAuth/OIDC specifications for external client calls.

If you're looking for next steps on this issue, consider providing feedback or contributing to the discussion on this proposed fix in the relevant GitHub issue. Would you like assistance with anything else related to Better Auth?

If you need more help, tag @better-auth-agent in a comment so I can respond.

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3749283853 --> @better-auth-agent[bot] commented on GitHub (Jan 14, 2026): It seems that the issue regarding the OAuth/OIDC token endpoints returning wrapped JSON instead of the expected spec-pure payload is recognized within the Better Auth community. Users have observed that the current implementation wraps responses in a `{ "response": { ...actual_fields... } }` structure, while clients expect top-level fields such as `access_token`, `token_type`, etc., leading to failures in authentication processes [[1]](https://github.com/better-auth/better-auth/issues/3725). To resolve this, the proposed fix involves returning the raw `response` payload when a real HTTP `Request` context is provided, without the outer wrapping. This adjustment would maintain internal API behavior while adhering to the OAuth/OIDC specifications for external client calls. If you're looking for next steps on this issue, consider providing feedback or contributing to the discussion on this proposed fix in the relevant GitHub issue. Would you like assistance with anything else related to Better Auth? _If you need more help, tag @better-auth-agent in a comment so I can respond._ <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

@gustavovalverde commented on GitHub (Jan 14, 2026):

cc: @dvanmali Considering you were recently making changes around OAuth.

<!-- gh-comment-id:3749330986 --> @gustavovalverde commented on GitHub (Jan 14, 2026): cc: @dvanmali Considering you were recently making changes around OAuth.
Author
Owner

@sidd190 commented on GitHub (Jan 15, 2026):

Is this open to contributions? I'd like to take a crack at it :D

<!-- gh-comment-id:3755521094 --> @sidd190 commented on GitHub (Jan 15, 2026): Is this open to contributions? I'd like to take a crack at it :D
Author
Owner

@sidd190 commented on GitHub (Jan 16, 2026):

It seems that the issue regarding the OAuth/OIDC token endpoints returning wrapped JSON instead of the expected spec-pure payload is recognized within the Better Auth community. Users have observed that the current implementation wraps responses in a { "response": { ...actual_fields... } } structure, while clients expect top-level fields such as access_token, token_type, etc., leading to failures in authentication processes [1].

To resolve this, the proposed fix involves returning the raw response payload when a real HTTP Request context is provided, without the outer wrapping. This adjustment would maintain internal API behavior while adhering to the OAuth/OIDC specifications for external client calls.

If you're looking for next steps on this issue, consider providing feedback or contributing to the discussion on this proposed fix in the relevant GitHub issue. Would you like assistance with anything else related to Better Auth?

If you need more help, tag @better-auth-agent in a comment so I can respond.

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent, is there anyone working on this, any related issues or any related open PR? Can I pick this up?

<!-- gh-comment-id:3758662001 --> @sidd190 commented on GitHub (Jan 16, 2026): > It seems that the issue regarding the OAuth/OIDC token endpoints returning wrapped JSON instead of the expected spec-pure payload is recognized within the Better Auth community. Users have observed that the current implementation wraps responses in a `{ "response": { ...actual_fields... } }` structure, while clients expect top-level fields such as `access_token`, `token_type`, etc., leading to failures in authentication processes [[1]](https://github.com/better-auth/better-auth/issues/3725). > > To resolve this, the proposed fix involves returning the raw `response` payload when a real HTTP `Request` context is provided, without the outer wrapping. This adjustment would maintain internal API behavior while adhering to the OAuth/OIDC specifications for external client calls. > > If you're looking for next steps on this issue, consider providing feedback or contributing to the discussion on this proposed fix in the relevant GitHub issue. Would you like assistance with anything else related to Better Auth? > > _If you need more help, tag @better-auth-agent in a comment so I can respond._ > > [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) > > [![Diagram](https://camo.githubusercontent.com/66c06e6468849c4c6af49b6edad983955d87ac1be2d5aac7c0e7b2b4ef684048/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4469616772616d2d3262333133373f7374796c653d666c61742d737175617265)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://camo.githubusercontent.com/786c566c038583b12f1757a3420f88fd59051efdc719b7574438987b0a545b3b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6a6f696e2d3538363546323f6c6f676f3d646973636f7264266c6f676f436f6c6f723d7768697465267374796c653d666c61742d737175617265)](https://discord.gg/better-auth) [![Star](https://camo.githubusercontent.com/45f39e23a829525840ce0c72438ebc5f34db1e9c6614a4c00e6b43506cf033b4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f737461722d3138313731373f6c6f676f3d676974687562266c6f676f436f6c6f723d7768697465267374796c653d666c61742d737175617265)](https://github.com/better-auth/better-auth) @better-auth-agent, is there anyone working on this, any related issues or any related open PR? Can I pick this up?
Author
Owner

@better-auth-agent[bot] commented on GitHub (Jan 16, 2026):

It looks like the issue you're highlighting regarding the OAuth/OIDC token endpoints returning wrapped JSON instead of the expected spec-pure payload is recognized within the Better Auth community. Users have observed that the responses currently wrap data in a { "response": { ...actual_oauth_fields... } } structure, while OAuth/OIDC clients expect top-level fields like access_token, token_type, etc., leading to authentication failures.

The proposed fix aims to adjust the outermost endpoint wrapper to return the raw response payload when handling real HTTP requests, ensuring compliance with OAuth/OIDC specifications. This change is intended to preserve existing internal API behavior while providing the necessary structure for external client calls.

If you're interested in contributing, you can participate in the conversation on this proposed fix in the relevant GitHub issue. Would you like clarification on anything else regarding Better Auth or specific steps on how to get started with contributing?

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3758662754 --> @better-auth-agent[bot] commented on GitHub (Jan 16, 2026): It looks like the issue you're highlighting regarding the OAuth/OIDC token endpoints returning wrapped JSON instead of the expected spec-pure payload is recognized within the Better Auth community. Users have observed that the responses currently wrap data in a `{ "response": { ...actual_oauth_fields... } }` structure, while OAuth/OIDC clients expect top-level fields like `access_token`, `token_type`, etc., leading to authentication failures. The proposed fix aims to adjust the outermost endpoint wrapper to return the raw response payload when handling real HTTP requests, ensuring compliance with OAuth/OIDC specifications. This change is intended to preserve existing internal API behavior while providing the necessary structure for external client calls. If you're interested in contributing, you can participate in the conversation on this proposed fix in the relevant GitHub issue. Would you like clarification on anything else regarding Better Auth or specific steps on how to get started with contributing? <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

@dvanmali commented on GitHub (Jan 16, 2026):

@sidd190 go for it

<!-- gh-comment-id:3760841678 --> @dvanmali commented on GitHub (Jan 16, 2026): @sidd190 go for it
Author
Owner

@sidd190 commented on GitHub (Jan 19, 2026):

@sidd190 go for it

Hey @dvanmali , I tried to create a fix, seems like it should be fine with this and not wrap the http requests in the response wrapper, but quick question, if there are other related http requests that might expect response wrapped around like the one currently, the fix would affect them, so are there any such components, and should I limit it to OAuth/OIDC endpoints only?

Sorry, it took me a while to setup the codebase fully and reproducing the issue locally.

<!-- gh-comment-id:3768979344 --> @sidd190 commented on GitHub (Jan 19, 2026): > [@sidd190](https://github.com/sidd190) go for it Hey @dvanmali , I tried to create a fix, seems like it should be fine with this and not wrap the http requests in the response wrapper, but quick question, if there are other related http requests that might expect response wrapped around like the one currently, the fix would affect them, so are there any such components, and should I limit it to OAuth/OIDC endpoints only? Sorry, it took me a while to setup the codebase fully and reproducing the issue locally.
Author
Owner

@dvanmali commented on GitHub (Jan 20, 2026):

@sidd190 Awesome! Much appreciated! Only the OAuth/OIDC endpoints should not need to be wrapped atm since they must be spec-compliant

<!-- gh-comment-id:3774150346 --> @dvanmali commented on GitHub (Jan 20, 2026): @sidd190 Awesome! Much appreciated! Only the OAuth/OIDC endpoints should not need to be wrapped atm since they must be spec-compliant
Author
Owner

@sidd190 commented on GitHub (Jan 20, 2026):

@sidd190 Awesome! Much appreciated! Only the OAuth/OIDC endpoints should not need to be wrapped atm since they must be spec-compliant

Ohh okay! I'll just do the updates quickly to be these endpoints only!

<!-- gh-comment-id:3774184202 --> @sidd190 commented on GitHub (Jan 20, 2026): > [@sidd190](https://github.com/sidd190) Awesome! Much appreciated! Only the OAuth/OIDC endpoints should not need to be wrapped atm since they must be spec-compliant Ohh okay! I'll just do the updates quickly to be these endpoints only!
Author
Owner

@gustavovalverde commented on GitHub (Jan 21, 2026):

Oh, I just realized you guys were having a conversation here and that @sidd190 had opened a PR already 😅

I had this solution a few days ago from a branch I'm using myself https://github.com/gustavovalverde/better-auth/commit/8401fbea2 but I forgot to open the PR

<!-- gh-comment-id:3777179981 --> @gustavovalverde commented on GitHub (Jan 21, 2026): Oh, I just realized you guys were having a conversation here and that @sidd190 had opened a PR already 😅 I had this solution a few days ago from a branch I'm using myself https://github.com/gustavovalverde/better-auth/commit/8401fbea2 but I forgot to open the PR
Author
Owner

@github-actions[bot] commented on GitHub (Mar 31, 2026):

This issue has been locked as it was closed more than 7 days ago. If you're experiencing a similar problem or you have additional context, please open a new issue and reference this one.

<!-- gh-comment-id:4165913893 --> @github-actions[bot] commented on GitHub (Mar 31, 2026): This issue has been locked as it was closed more than 7 days ago. If you're experiencing a similar problem or you have additional context, please open a new issue and reference this one.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#10791