Cross-Origin Auth Broken in 1.4.x with Hono + Cloudflare Workers + Next.js Proxy #2779

Open
opened 2026-03-13 10:19:48 -05:00 by GiteaMirror · 5 comments
Owner

Originally created by @samsullivandelgobbo on GitHub (Jan 28, 2026).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Cross-Origin Auth Broken in 1.4.x with Hono + Cloudflare Workers + Next.js Proxy

Environment

  • better-auth version: 1.4.17 (broken), 1.3.13 (works)
  • Framework: Hono on Cloudflare Workers
  • Client: Next.js 15 on Vercel
  • Setup: API at api.example.com, Web app at app.example.com
  • Auth Proxy: Next.js rewrites /api/auth/*api.example.com/api/auth/*

Description

After upgrading from 1.3.13 to 1.4.17, OAuth social login (Google) completely broke on our cross-origin setup. The same configuration worked perfectly on 1.3.x.

Configuration

// Hono CORS config
cors({
  origin: ["https://app.example.com", "http://localhost:3000"],
  allowHeaders: ["Content-Type", "Authorization", "x-api-key"],
  allowMethods: ["POST", "GET", "OPTIONS", "PUT", "DELETE", "PATCH"],
  credentials: true,
})

// better-auth config
export const authOptions = {
  advanced: {
    defaultCookieAttributes: {
      sameSite: "none",
      secure: true,
    },
  },
  trustedOrigins: [
    "https://app.example.com",
    "http://localhost:3000",
  ],
  // ... plugins, social providers, etc.
}

Issues Encountered

1. Undocumented User-Agent Header Requirement

Related: #6358

1.4.x adds a default User-Agent header to requests. This causes CORS preflight failures because most CORS configs don't explicitly allow User-Agent in allowHeaders.

Symptom: CORS errors in browser, especially Safari.

Workaround: Add "User-Agent" to CORS allowHeaders.

2. Stricter Origin/CSRF Validation Breaking Proxy Setups

Related: #4052, #4720, #6944

The changelog mentions "Separate CSRF and origin checks" - this appears to have made validation stricter. Requests coming through a Next.js rewrite proxy fail origin validation even when the origin is in trustedOrigins.

Symptom: Requests return errors or unexpected responses when proxied, but work when hitting the API directly.

3. baseURL Inference Issues

Related: #6944

Without explicit baseURL, the library doesn't correctly infer the origin from proxied requests. Setting baseURL explicitly to the API domain causes a different problem (Issue 4 below).

The 1.4.8 release notes mention a fix for "Respect trustedOrigins when baseURL is inferred" but this doesn't appear to work correctly with proxy setups.

4. OAuth Callback URL Bypasses Proxy

When baseURL is set to the API domain (or inferred as such), the OAuth callback URL sent to Google points directly to the API:

redirect_uri=https://api.example.com/api/auth/callback/google

This means:

  1. User authenticates with Google
  2. Google redirects to api.example.com/callback (bypasses Next.js proxy)
  3. Cookie is set on api.example.com domain
  4. User is redirected to app.example.com
  5. Cookie is on wrong domain - user not authenticated

This worked in 1.3.x because the baseURL inference correctly used the request's origin (which came through the proxy).

Reproduction Steps

  1. Set up Hono API on one domain with better-auth
  2. Set up Next.js app on different domain with rewrite proxy to API
  3. Configure trustedOrigins to include the web app domain
  4. Upgrade to 1.4.x
  5. Try to sign in with Google OAuth
  6. Observe: either CORS errors, origin validation errors, or cookies set on wrong domain

Workaround

Pin to 1.3.13:

{
  "dependencies": {
    "better-auth": "1.3.13"
  }
}
  • #6358 - User-Agent header CORS issue (undocumented breaking change)
  • #4052 - CORS violation even after defining trustedOrigins
  • #4720 - Next.js App Router OPTIONS Preflight Requests Not Honoring trustedOrigins
  • #6944 - Better-Auth 1.4.8 broken due to trusted origins
  • #6613 - Upgrade from 1.3 to 1.4: Error runWithRequestState on Cloudflare Workers
  • #6545 - Error on Cloudflare switching from 1.3.40 to 1.4.5

Current vs. Expected behavior

Expected Behavior

Cross-origin setups with proxy rewrites should work the same as in 1.3.x:

  • Origin should be inferred from the incoming request (respecting the proxy)
  • trustedOrigins should be respected for proxied requests
  • OAuth callback URLs should use the origin from the request, not a hardcoded baseURL

What version of Better Auth are you using?

1.4

System info

{
    "better-auth": "1.3.13 (pinned, 1.4.17 broken)",
    "framework": "hono",
    "runtime": "cloudflare-workers",
    "client": "next.js 15 (vercel)"
  }

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

Backend, Client, Package

Auth config (if applicable)

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

Additional context

No response

Originally created by @samsullivandelgobbo on GitHub (Jan 28, 2026). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce # Cross-Origin Auth Broken in 1.4.x with Hono + Cloudflare Workers + Next.js Proxy ## Environment - **better-auth version**: 1.4.17 (broken), 1.3.13 (works) - **Framework**: Hono on Cloudflare Workers - **Client**: Next.js 15 on Vercel - **Setup**: API at `api.example.com`, Web app at `app.example.com` - **Auth Proxy**: Next.js rewrites `/api/auth/*` → `api.example.com/api/auth/*` ## Description After upgrading from `1.3.13` to `1.4.17`, OAuth social login (Google) completely broke on our cross-origin setup. The same configuration worked perfectly on 1.3.x. ## Configuration ```typescript // Hono CORS config cors({ origin: ["https://app.example.com", "http://localhost:3000"], allowHeaders: ["Content-Type", "Authorization", "x-api-key"], allowMethods: ["POST", "GET", "OPTIONS", "PUT", "DELETE", "PATCH"], credentials: true, }) // better-auth config export const authOptions = { advanced: { defaultCookieAttributes: { sameSite: "none", secure: true, }, }, trustedOrigins: [ "https://app.example.com", "http://localhost:3000", ], // ... plugins, social providers, etc. } ``` ## Issues Encountered ### 1. Undocumented User-Agent Header Requirement Related: #6358 1.4.x adds a default `User-Agent` header to requests. This causes CORS preflight failures because most CORS configs don't explicitly allow `User-Agent` in `allowHeaders`. **Symptom**: CORS errors in browser, especially Safari. **Workaround**: Add `"User-Agent"` to CORS `allowHeaders`. ### 2. Stricter Origin/CSRF Validation Breaking Proxy Setups Related: #4052, #4720, #6944 The changelog mentions "Separate CSRF and origin checks" - this appears to have made validation stricter. Requests coming through a Next.js rewrite proxy fail origin validation even when the origin is in `trustedOrigins`. **Symptom**: Requests return errors or unexpected responses when proxied, but work when hitting the API directly. ### 3. baseURL Inference Issues Related: #6944 Without explicit `baseURL`, the library doesn't correctly infer the origin from proxied requests. Setting `baseURL` explicitly to the API domain causes a different problem (Issue 4 below). The 1.4.8 release notes mention a fix for "Respect trustedOrigins when baseURL is inferred" but this doesn't appear to work correctly with proxy setups. ### 4. OAuth Callback URL Bypasses Proxy When `baseURL` is set to the API domain (or inferred as such), the OAuth callback URL sent to Google points directly to the API: ``` redirect_uri=https://api.example.com/api/auth/callback/google ``` This means: 1. User authenticates with Google 2. Google redirects to `api.example.com/callback` (bypasses Next.js proxy) 3. Cookie is set on `api.example.com` domain 4. User is redirected to `app.example.com` 5. **Cookie is on wrong domain - user not authenticated** This worked in 1.3.x because the baseURL inference correctly used the request's origin (which came through the proxy). ## Reproduction Steps 1. Set up Hono API on one domain with better-auth 2. Set up Next.js app on different domain with rewrite proxy to API 3. Configure `trustedOrigins` to include the web app domain 4. Upgrade to 1.4.x 5. Try to sign in with Google OAuth 6. Observe: either CORS errors, origin validation errors, or cookies set on wrong domain ## Workaround Pin to `1.3.13`: ```json { "dependencies": { "better-auth": "1.3.13" } } ``` ## Related Issues - #6358 - User-Agent header CORS issue (undocumented breaking change) - #4052 - CORS violation even after defining trustedOrigins - #4720 - Next.js App Router OPTIONS Preflight Requests Not Honoring trustedOrigins - #6944 - Better-Auth 1.4.8 broken due to trusted origins - #6613 - Upgrade from 1.3 to 1.4: Error runWithRequestState on Cloudflare Workers - #6545 - Error on Cloudflare switching from 1.3.40 to 1.4.5 ### Current vs. Expected behavior ## Expected Behavior Cross-origin setups with proxy rewrites should work the same as in 1.3.x: - Origin should be inferred from the incoming request (respecting the proxy) - `trustedOrigins` should be respected for proxied requests - OAuth callback URLs should use the origin from the request, not a hardcoded baseURL ### What version of Better Auth are you using? 1.4 ### System info ```bash { "better-auth": "1.3.13 (pinned, 1.4.17 broken)", "framework": "hono", "runtime": "cloudflare-workers", "client": "next.js 15 (vercel)" } ``` ### Which area(s) are affected? (Select all that apply) Backend, Client, Package ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ emailAndPassword: { enabled: true }, }); ``` ### Additional context _No response_
GiteaMirror added the nextjshonobug labels 2026-03-13 10:19:48 -05:00
Author
Owner

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

It looks like you're experiencing issues with cross-origin authentication using Better Auth version 1.4.x with Hono, Cloudflare Workers, and a Next.js proxy setup. Based on the reported behavior and configuration details, here are some troubleshooting steps and potential solutions to address the various issues:

  1. CORS Configuration: Ensure that the User-Agent header is included in your CORS configuration. It appears that the introduction of the User-Agent header in version 1.4.x is causing CORS failures, especially on browsers like Safari. You can add "User-Agent" to your allowHeaders in the CORS setup to resolve this issue.

    cors({
        origin: ["https://app.example.com", "http://localhost:3000"],
        allowHeaders: ["Content-Type", "Authorization", "x-api-key", "User-Agent"],
        allowMethods: ["POST", "GET", "OPTIONS", "PUT", "DELETE", "PATCH"],
        credentials: true,
    });
    
  2. Trusted Origins and CSRF Validation: The stricter validations for CSRF and origin checks introduced in 1.4.x may cause issues with requests routed through the Next.js proxy. Ensure that your trustedOrigins are configured correctly and include both web app and API domains.

  3. OAuth Callback URL: The OAuth callback URL needs to respect the original request's headers or proxy settings. Setting the baseURL explicitly to the API domain might lead to issues where the callback bypasses the proxy. Consider reviewing how the callback URL is constructed in your authentication flow.

  4. Reverting to a Stable Version: If these issues persist and you need a quick fix, it might be worthwhile to revert back to version 1.3.13, which is working for your setup.

{
  "dependencies": {
    "better-auth": "1.3.13"
  }
}

Given these suggestions, how are you currently configuring your CORS settings? Feel free to share your current setup, and I can provide more tailored advice!

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

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Jan 28, 2026): It looks like you're experiencing issues with cross-origin authentication using Better Auth version 1.4.x with Hono, Cloudflare Workers, and a Next.js proxy setup. Based on the reported behavior and configuration details, here are some troubleshooting steps and potential solutions to address the various issues: 1. **CORS Configuration**: Ensure that the `User-Agent` header is included in your CORS configuration. It appears that the introduction of the User-Agent header in version 1.4.x is causing CORS failures, especially on browsers like Safari. You can add `"User-Agent"` to your `allowHeaders` in the CORS setup to resolve this issue. ```typescript cors({ origin: ["https://app.example.com", "http://localhost:3000"], allowHeaders: ["Content-Type", "Authorization", "x-api-key", "User-Agent"], allowMethods: ["POST", "GET", "OPTIONS", "PUT", "DELETE", "PATCH"], credentials: true, }); ``` 2. **Trusted Origins and CSRF Validation**: The stricter validations for CSRF and origin checks introduced in 1.4.x may cause issues with requests routed through the Next.js proxy. Ensure that your `trustedOrigins` are configured correctly and include both web app and API domains. 3. **OAuth Callback URL**: The OAuth callback URL needs to respect the original request's headers or proxy settings. Setting the `baseURL` explicitly to the API domain might lead to issues where the callback bypasses the proxy. Consider reviewing how the callback URL is constructed in your authentication flow. 4. **Reverting to a Stable Version**: If these issues persist and you need a quick fix, it might be worthwhile to revert back to version 1.3.13, which is working for your setup. ```json { "dependencies": { "better-auth": "1.3.13" } } ``` Given these suggestions, how are you currently configuring your CORS settings? Feel free to share your current setup, and I can provide more tailored advice! _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

@VesperQuartz commented on GitHub (Mar 1, 2026):

has this been fixed?

@VesperQuartz commented on GitHub (Mar 1, 2026): has this been fixed?
Author
Owner

@samsullivandelgobbo commented on GitHub (Mar 2, 2026):

@VesperQuartz No - have been pinned to 1.3.x for who knows how long now. The auth proxy is through a nextjs route to external api which resolves the CORS stuff enough for prod

@samsullivandelgobbo commented on GitHub (Mar 2, 2026): @VesperQuartz No - have been pinned to 1.3.x for who knows how long now. The auth proxy is through a nextjs route to external api which resolves the CORS stuff enough for prod
Author
Owner

@hafizjavaid commented on GitHub (Mar 7, 2026):

I'm getting the same error, but within the Nuxt application. "better-auth": "^1.4.7", has anyone found the solution for this?

@hafizjavaid commented on GitHub (Mar 7, 2026): I'm getting the same error, but within the Nuxt application. "better-auth": "^1.4.7", has anyone found the solution for this?
Author
Owner

@drer17 commented on GitHub (Mar 9, 2026):

I found success with this.

app.use('*', async (c, next) => {
  const handler = cors({
    origin: TRUSTED_ORIGINS(c.env.MODE),
    allowHeaders: ['Content-Type', 'Authorization'],
    allowMethods: ['POST', 'GET', 'OPTIONS'],
    credentials: true,
    maxAge: 600,
  })

  // no next() for OPTIONS, short-circuit preflight
  if (c.req.method === 'OPTIONS') return await handler(c, async () => {})

  return next()
})
@drer17 commented on GitHub (Mar 9, 2026): I found success with this. ``` app.use('*', async (c, next) => { const handler = cors({ origin: TRUSTED_ORIGINS(c.env.MODE), allowHeaders: ['Content-Type', 'Authorization'], allowMethods: ['POST', 'GET', 'OPTIONS'], credentials: true, maxAge: 600, }) // no next() for OPTIONS, short-circuit preflight if (c.req.method === 'OPTIONS') return await handler(c, async () => {}) return next() }) ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2779