[GH-ISSUE #6197] Passkey Authentication Fails with "Content-Type is required" Error After v1.4.0 #10440

Closed
opened 2026-04-13 06:34:45 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @mburumaxwell on GitHub (Nov 22, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/6197

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Install better-auth v1.4.0 or later with the passkey plugin
  2. Configure passkey authentication on both server and client
  3. Attempt to sign in using passkey by calling authClient.signIn.passkey()
  4. The request to /passkey/generate-authenticate-options fails with:
    {
      "code": "UNSUPPORTED_MEDIA_TYPE",
      "message": "Content-Type is required. Allowed types: application/json"
    }
    

Alternatively,

  1. Visit https://demo.better-auth.com/sign-in
  2. Open developer tools and the network tab
  3. Click on "Sign in with passkey"
  4. See the failure (response code 415)

Current vs. Expected behavior

Current Behavior

When attempting to sign in with a passkey in v1.4.0+, the /passkey/generate-authenticate-options endpoint returns a 415 error:

{
    "code": "UNSUPPORTED_MEDIA_TYPE",
    "message": "Content-Type is required. Allowed types: application/json"
}

This occurs because:

  1. The endpoint is defined as method: "POST"
  2. The client makes a POST request without a request body or Content-Type header
  3. After upgrading from better-call v1.0.19 (used in v1.3.34) to v1.1.0 (used in v1.4.0), POST requests now require a Content-Type header by default

Expected Behavior

The passkey sign-in flow should work without requiring a Content-Type header, as it worked in v1.3.34 and earlier versions.

Root Cause

The issue stems from a change in the better-call dependency between v1.0.19 and v1.1.0. In better-call@1.1.0, the getBody() function now enforces Content-Type validation for POST requests.

The /passkey/generate-authenticate-options endpoint:

  • Is defined as POST but accepts no request body
  • Performs an idempotent operation (generating authentication options)
  • Is functionally identical to /passkey/generate-register-options which correctly uses GET

What version of Better Auth are you using?

1.4.0

System info

# Affected versions use:
better-call: 1.1.0  # Introduced Content-Type validation

# Working versions use:
better-call: 1.0.19 # No Content-Type validation for POST without body

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

Backend, Package, Client

Auth config (if applicable)

import { betterAuth } from "better-auth"
import { passkey } from "@better-auth/passkey"

export const auth = betterAuth({
  emailAndPassword: {  
    enabled: true
  },
  plugins: [
    passkey()
  ]
});

Additional context

I could create a PR to fix this in one of two options:

Option 1: Change Method from POST to GET (Recommended)

Change /passkey/generate-authenticate-options from POST to GET in packages/passkey/src/index.ts:

Option 2: Add disableBody: true to Endpoint Options

Keep POST but explicitly disable body parsing:

generatePasskeyAuthenticationOptions: createAuthEndpoint(
    "/passkey/generate-authenticate-options",
    {
        method: "POST",
        disableBody: true,  // Add this line
        metadata: {
            // ... existing metadata
        }
    },
    async (ctx) => {
        // ... existing handler
    }
)
Originally created by @mburumaxwell on GitHub (Nov 22, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/6197 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Install better-auth v1.4.0 or later with the passkey plugin 2. Configure passkey authentication on both server and client 3. Attempt to sign in using passkey by calling `authClient.signIn.passkey()` 4. The request to `/passkey/generate-authenticate-options` fails with: ```json { "code": "UNSUPPORTED_MEDIA_TYPE", "message": "Content-Type is required. Allowed types: application/json" } ``` Alternatively, 1. Visit https://demo.better-auth.com/sign-in 2. Open developer tools and the network tab 3. Click on "Sign in with passkey" 4. See the failure (response code 415) ### Current vs. Expected behavior ### Current Behavior When attempting to sign in with a passkey in v1.4.0+, the `/passkey/generate-authenticate-options` endpoint returns a 415 error: ```json { "code": "UNSUPPORTED_MEDIA_TYPE", "message": "Content-Type is required. Allowed types: application/json" } ``` This occurs because: 1. The endpoint is defined as `method: "POST"` 2. The client makes a POST request without a request body or Content-Type header 3. After upgrading from `better-call` v1.0.19 (used in v1.3.34) to v1.1.0 (used in v1.4.0), POST requests now require a Content-Type header by default ### Expected Behavior The passkey sign-in flow should work without requiring a Content-Type header, as it worked in v1.3.34 and earlier versions. ### Root Cause The issue stems from a change in the `better-call` dependency between v1.0.19 and v1.1.0. In `better-call@1.1.0`, the `getBody()` function now enforces Content-Type validation for POST requests. The `/passkey/generate-authenticate-options` endpoint: - Is defined as POST but accepts no request body - Performs an idempotent operation (generating authentication options) - Is functionally identical to `/passkey/generate-register-options` which correctly uses GET ### What version of Better Auth are you using? 1.4.0 ### System info ```bash # Affected versions use: better-call: 1.1.0 # Introduced Content-Type validation # Working versions use: better-call: 1.0.19 # No Content-Type validation for POST without body ``` ### Which area(s) are affected? (Select all that apply) Backend, Package, Client ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" import { passkey } from "@better-auth/passkey" export const auth = betterAuth({ emailAndPassword: { enabled: true }, plugins: [ passkey() ] }); ``` ### Additional context I could create a PR to fix this in one of two options: ### Option 1: Change Method from POST to GET (Recommended) Change `/passkey/generate-authenticate-options` from POST to GET in `packages/passkey/src/index.ts`: ### Option 2: Add `disableBody: true` to Endpoint Options Keep POST but explicitly disable body parsing: ```typescript generatePasskeyAuthenticationOptions: createAuthEndpoint( "/passkey/generate-authenticate-options", { method: "POST", disableBody: true, // Add this line metadata: { // ... existing metadata } }, async (ctx) => { // ... existing handler } ) ```
GiteaMirror added the lockedbug labels 2026-04-13 06:34:45 -05:00
Author
Owner

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

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

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3566649225 --> @better-auth-agent[bot] commented on GitHub (Nov 22, 2025): (tag @better-auth-agent if you would like an answer from the Agent) <!-- 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

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

Hi @mburumaxwell! I'm Dosu and I’m helping the better-auth team.

The 415 "Content-Type is required" error for /passkey/generate-authenticate-options after upgrading to better-auth v1.4.0 (with better-call v1.1.0+) is caused by stricter content-type validation: POST requests now require a Content-Type header, even if no body is sent. This endpoint is defined as POST but doesn't expect a body, so the request fails without the header see code.

There are two main solutions:

  1. Change the endpoint method from POST to GET. This is the cleanest fix, since the operation is idempotent and functionally identical to /passkey/generate-register-options, which already uses GET. This avoids content-type validation issues and aligns with REST conventions.
  2. Alternatively, you can keep POST but add disableBody: true to the endpoint config. This disables body parsing and content-type checks for this endpoint, allowing POST requests without a Content-Type header.

Both approaches are valid, but switching to GET is recommended for consistency and clarity see code. There are no existing issues or docs about this problem, but recent changes in better-call confirm the stricter validation PR #6090.

Let me know if you want code snippets or more details on either fix.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3566652928 --> @dosubot[bot] commented on GitHub (Nov 22, 2025): <!-- Greeting --> Hi @mburumaxwell! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> The 415 "Content-Type is required" error for `/passkey/generate-authenticate-options` after upgrading to better-auth v1.4.0 (with better-call v1.1.0+) is caused by stricter content-type validation: POST requests now require a Content-Type header, even if no body is sent. This endpoint is defined as POST but doesn't expect a body, so the request fails without the header [see code](https://github.com/better-auth/better-auth/blob/44308bf20d431bd943a92a2ed4204a6b7d5a4182/packages/passkey/src/index.ts#L48-L404). There are two main solutions: 1. Change the endpoint method from POST to GET. This is the cleanest fix, since the operation is idempotent and functionally identical to `/passkey/generate-register-options`, which already uses GET. This avoids content-type validation issues and aligns with REST conventions. 2. Alternatively, you can keep POST but add `disableBody: true` to the endpoint config. This disables body parsing and content-type checks for this endpoint, allowing POST requests without a Content-Type header. Both approaches are valid, but switching to GET is recommended for consistency and clarity [see code](https://github.com/better-auth/better-auth/blob/44308bf20d431bd943a92a2ed4204a6b7d5a4182/packages/passkey/src/index.ts#L48-L404). There are no existing issues or docs about this problem, but recent changes in better-call confirm the stricter validation [PR #6090](https://github.com/better-auth/better-auth/pull/6090). Let me know if you want code snippets or more details on either fix. <!-- 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/aab88840-fe22-40c8-9857-7144ca8c59c3?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/aab88840-fe22-40c8-9857-7144ca8c59c3?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/aab88840-fe22-40c8-9857-7144ca8c59c3?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/aab88840-fe22-40c8-9857-7144ca8c59c3?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/aab88840-fe22-40c8-9857-7144ca8c59c3?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/aab88840-fe22-40c8-9857-7144ca8c59c3?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/aab88840-fe22-40c8-9857-7144ca8c59c3?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=The%20415%20%22Content-Type%20is%20required%22%20error%20for%20%60/passkey/generate-authenticate-options%60%20after%20upgrading%20to%20better-auth%20v1.4.0%20%28with%20better-call%20v1.1.0%2B%29%20is%20caused%20by%20stricter%20content-type%20validation%3A%20POST%20requests%20now%20require%20a%20Content-Type%20header%2C%20even%20if%20no%20body%20is%20sent.%20This%20endpoint%20is%20defined%20as%20POST%20but%20doesn%27t%20expect%20a%20body%2C%20so%20the%20request%20fails%20without%20the%20header%C2%A0%5Bsee%20code%5D%28https%3A//github.com/better-auth/better-auth/blob/44308bf20d431bd943a92a2ed4204a6b7d5a4182/packages/passkey/src/index.ts%23L48-L404%29.%0A%0AThere%20are%20two%20main%20solutions%3A%0A%0A1.%20Change%20the%20endpoint%20method%20from%20POST%20to%20GET.%20This%20is%20the%20cleanest%20fix%2C%20since%20the%20operation%20is%20idempotent%20and%20functionally%20identical%20to%20%60/passkey/generate-register-options%60%2C%20which%20already%20uses%20GET.%20This%20avoids%20content-type%20validation%20issues%20and%20aligns%20with%20REST%20conventions.%0A2.%20Alternatively%2C%20you%20can%20keep%20POST%20but%20add%20%60disableBody%3A%20true%60%20to%20the%20endpoint%20config.%20This%20disables%20body%20parsing%20and%20content-type%20checks%20for%20this%20endpoint%2C%20allowing%20POST%20requests%20without%20a%20Content-Type%20header.%0A%0ABoth%20approaches%20are%20valid%2C%20but%20switching%20to%20GET%20is%20recommended%20for%20consistency%20and%20clarity%C2%A0%5Bsee%20code%5D%28https%3A//github.com/better-auth/better-auth/blob/44308bf20d431bd943a92a2ed4204a6b7d5a4182/packages/passkey/src/index.ts%23L48-L404%29.%20There%20are%20no%20existing%20issues%20or%20docs%20about%20this%20problem%2C%20but%20recent%20changes%20in%20better-call%20confirm%20the%20stricter%20validation%C2%A0%5BPR%20%236090%5D%28https%3A//github.com/better-auth/better-auth/pull/6090%29.%0A%0ALet%20me%20know%20if%20you%20want%20code%20snippets%20or%20more%20details%20on%20either%20fix.)&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/6197)
Author
Owner

@Gonzalo-Bruna commented on GitHub (Dec 11, 2025):

Hi! The same is happening when trying to use the sign-out endpoint:

Image

The backend is using better-auth 1.4.6.

<!-- gh-comment-id:3639703586 --> @Gonzalo-Bruna commented on GitHub (Dec 11, 2025): Hi! The same is happening when trying to use the sign-out endpoint: <img width="1008" height="389" alt="Image" src="https://github.com/user-attachments/assets/a589fa63-0744-4c51-8ecc-9950de32c35b" /> The backend is using better-auth 1.4.6.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#10440