[GH-ISSUE #7657] Cross-Origin Auth Broken in 1.4.x with Hono + Cloudflare Workers + Next.js Proxy #19497

Open
opened 2026-04-15 18:40:32 -05:00 by GiteaMirror · 4 comments
Owner

Originally created by @samsullivandelgobbo on GitHub (Jan 28, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/7657

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

Adopted as tracking issue: 2026-04-14

Summary

Origin, CORS, and trustedOrigins handling regressed across the 1.3.29 to 1.4.9 range when origin checks were split from CSRF checks. The same request-validation path fails under several adapters, including Next.js server components, Hono on Cloudflare Workers, Electron file://, TanStack Start, and multi-domain deployments. The path also rejects requests that do not send an Origin header and requests that send Origin: null.

Root cause

trustedOrigins(request) does not always receive the Request object under every adapter. The origin-check middleware can reject Origin: null and file:// even when they are trusted, and it can reject server-to-server or API-client requests that have no Origin header. Proxied worker deployments also fail because request-context propagation is incomplete. Earlier point fixes closed specific regressions in 1.4.7, 1.4.8, and 1.4.9, but the adapter-wide request propagation problem remains.

Scope

In: origin-check middleware, trustedOrigins resolution with request context, CORS handling across framework adapters, non-http origins (null, file://), expo-origin header injection on Expo (#7014).

Out: baseURL resolution (tracking: baseURL / env resolution, #4151). OAuth state cookie writes (OAuth state_mismatch cluster).

Resolution criteria

  • trustedOrigins(request) receives the Request under every first-party adapter.
  • Clients with no Origin header (Postman, server-to-server) succeed when configured.
  • file:// and null origins pass the check when trusted.
  • Cross-domain cookies interoperate with trustedOrigins on multi-domain setups.
  • Hono on Cloudflare Workers and TanStack Start work without adapter-specific workarounds.
  • Prior regressions: #6798 (1.4.7), #6944 (1.4.8), #7078 (1.4.9).
  • Cross-reference: tracking: baseURL / env resolution (#4151) for cases where baseURL vs trustedOrigins semantics overlap (#7502 docs ambiguity).
  • Adjacent: oauth-provider CORS family (#8503, #7041 closed).
Originally created by @samsullivandelgobbo on GitHub (Jan 28, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/7657 ### 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_ --- ## Adopted as tracking issue: 2026-04-14 ## Summary Origin, CORS, and `trustedOrigins` handling regressed across the 1.3.29 to 1.4.9 range when origin checks were split from CSRF checks. The same request-validation path fails under several adapters, including Next.js server components, Hono on Cloudflare Workers, Electron `file://`, TanStack Start, and multi-domain deployments. The path also rejects requests that do not send an `Origin` header and requests that send `Origin: null`. ## Root cause `trustedOrigins(request)` does not always receive the `Request` object under every adapter. The origin-check middleware can reject `Origin: null` and `file://` even when they are trusted, and it can reject server-to-server or API-client requests that have no `Origin` header. Proxied worker deployments also fail because request-context propagation is incomplete. Earlier point fixes closed specific regressions in 1.4.7, 1.4.8, and 1.4.9, but the adapter-wide request propagation problem remains. ## Scope **In:** origin-check middleware, `trustedOrigins` resolution with request context, CORS handling across framework adapters, non-http origins (`null`, `file://`), expo-origin header injection on Expo (#7014). **Out:** baseURL resolution (`tracking: baseURL / env resolution`, #4151). OAuth state cookie writes (OAuth state_mismatch cluster). ## Resolution criteria - `trustedOrigins(request)` receives the `Request` under every first-party adapter. - Clients with no `Origin` header (Postman, server-to-server) succeed when configured. - `file://` and `null` origins pass the check when trusted. - Cross-domain cookies interoperate with `trustedOrigins` on multi-domain setups. - Hono on Cloudflare Workers and TanStack Start work without adapter-specific workarounds. ## Related - Prior regressions: #6798 (1.4.7), #6944 (1.4.8), #7078 (1.4.9). - Cross-reference: `tracking: baseURL / env resolution` (#4151) for cases where `baseURL` vs `trustedOrigins` semantics overlap (#7502 docs ambiguity). - Adjacent: oauth-provider CORS family (#8503, #7041 closed).
GiteaMirror added the trackingplatformcorebug labels 2026-04-15 18:40:32 -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

<!-- gh-comment-id:3808761021 --> @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?

<!-- gh-comment-id:3979464766 --> @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

<!-- gh-comment-id:3985781157 --> @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?

<!-- gh-comment-id:4015633081 --> @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?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#19497