[PR #9134] fix(auth)!: gate dynamic baseURL proxy headers behind trustedProxyHeaders #25360

Open
opened 2026-04-15 22:51:13 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/9134
Author: @gustavovalverde
Created: 4/12/2026
Status: 🔄 Open

Base: mainHead: 2026-04-12/security/dynamic-baseurl-proxy-trust-default


📝 Commits (9)

  • 4fc1eee fix: resolve dynamic baseURL for direct auth.api calls
  • 4fa8201 chore: add changeset
  • 56d2704 fix: apply fallback support and https placeholder for dynamic baseURL
  • c3aeda0 refactor: eliminate fake Request wrapper in dynamic baseURL resolution
  • 71e0ab6 Merge branch 'main' into 2026-04-11/fix/dynamic-baseurl-api-calls
  • f87fd55 refactor: simplify isRequestLike
  • 6cc85af refactor: remove unnecessary type casting
  • 908d431 refactor: rename FromRequest helpers to FromSource
  • 6294885 fix(auth)!: gate dynamic baseURL proxy headers behind trustedProxyHeaders

📊 Changes

9 files changed (+390 additions, -110 deletions)

View changed files

.changeset/lovely-toes-swim.md (+5 -0)
.changeset/proxy-trust-default-dynamic.md (+29 -0)
📝 packages/better-auth/src/api/to-auth-endpoints.test.ts (+93 -0)
📝 packages/better-auth/src/api/to-auth-endpoints.ts (+38 -1)
📝 packages/better-auth/src/auth/base.ts (+11 -40)
📝 packages/better-auth/src/auth/full.test.ts (+7 -0)
📝 packages/better-auth/src/context/helpers.ts (+63 -1)
📝 packages/better-auth/src/utils/url.test.ts (+66 -30)
📝 packages/better-auth/src/utils/url.ts (+78 -38)

📄 Description

Security hardening split out from #9131.

Problem

The static-config baseURL path already required advanced.trustedProxyHeaders: true to honor x-forwarded-host / x-forwarded-proto. The dynamic (allowedHosts) path trusted them implicitly, so deployments that don't strip client-supplied x-forwarded-* let an attacker pivot between any two hosts listed in allowedHosts via header spoofing.

This aligns the two paths: proxy headers are now ignored unless the user explicitly opts in with trustedProxyHeaders: true.

Migration

Dynamic configs deployed behind a reverse proxy that only exposes the public hostname via x-forwarded-host need to add:

betterAuth({
  baseURL: { allowedHosts: [...] },
  advanced: {
    trustedProxyHeaders: true,
  },
});

Setups where the proxy rewrites Host: to the public hostname (nginx default, Vercel, Cloudflare, Netlify) are unaffected.

Scope

Small, focused on the security default only. The DX and correctness fixes from the same bug class (direct auth.api calls, MCP/OAuth-provider metadata forwarding, APIError on unresolvable, etc.) ship in #9131.

Related: #9105


Summary by cubic

Hardened dynamic baseURL resolution in better-auth by ignoring x-forwarded-host and x-forwarded-proto unless advanced.trustedProxyHeaders is true. This aligns allowedHosts with static configs and prevents header-spoofing pivots between allowed hosts.

  • Migration
    • If using baseURL.allowedHosts behind a proxy that exposes the public hostname via x-forwarded-host, set advanced.trustedProxyHeaders: true.
    • No changes needed when the proxy rewrites Host to the public hostname (nginx default, Vercel, Cloudflare, Netlify).

Written for commit 6294885e64. Summary will update on new commits.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/better-auth/better-auth/pull/9134 **Author:** [@gustavovalverde](https://github.com/gustavovalverde) **Created:** 4/12/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `2026-04-12/security/dynamic-baseurl-proxy-trust-default` --- ### 📝 Commits (9) - [`4fc1eee`](https://github.com/better-auth/better-auth/commit/4fc1eeea0c3397d67a780c1e366c701db01544c1) fix: resolve dynamic baseURL for direct auth.api calls - [`4fa8201`](https://github.com/better-auth/better-auth/commit/4fa8201c2c93f52d4e30f6b58e287c4c277a5418) chore: add changeset - [`56d2704`](https://github.com/better-auth/better-auth/commit/56d2704828220e77c94375d64c19260cb6caabb3) fix: apply fallback support and https placeholder for dynamic baseURL - [`c3aeda0`](https://github.com/better-auth/better-auth/commit/c3aeda09f1b6fbfcdd654c5b7f5a1fe85497d106) refactor: eliminate fake Request wrapper in dynamic baseURL resolution - [`71e0ab6`](https://github.com/better-auth/better-auth/commit/71e0ab684fb1a902c2753ec495d4902665eded28) Merge branch 'main' into 2026-04-11/fix/dynamic-baseurl-api-calls - [`f87fd55`](https://github.com/better-auth/better-auth/commit/f87fd55e2a948a0c60c6db8d876f6ff7d6223d3c) refactor: simplify isRequestLike - [`6cc85af`](https://github.com/better-auth/better-auth/commit/6cc85af8097d8e004a69c97ab2708ccbb12b19c7) refactor: remove unnecessary type casting - [`908d431`](https://github.com/better-auth/better-auth/commit/908d4317ce52abf7e34133936089f55296d5f85c) refactor: rename FromRequest helpers to FromSource - [`6294885`](https://github.com/better-auth/better-auth/commit/6294885e64c810c4d7b0eb6bf8c3842c98b5e631) fix(auth)!: gate dynamic baseURL proxy headers behind trustedProxyHeaders ### 📊 Changes **9 files changed** (+390 additions, -110 deletions) <details> <summary>View changed files</summary> ➕ `.changeset/lovely-toes-swim.md` (+5 -0) ➕ `.changeset/proxy-trust-default-dynamic.md` (+29 -0) 📝 `packages/better-auth/src/api/to-auth-endpoints.test.ts` (+93 -0) 📝 `packages/better-auth/src/api/to-auth-endpoints.ts` (+38 -1) 📝 `packages/better-auth/src/auth/base.ts` (+11 -40) 📝 `packages/better-auth/src/auth/full.test.ts` (+7 -0) 📝 `packages/better-auth/src/context/helpers.ts` (+63 -1) 📝 `packages/better-auth/src/utils/url.test.ts` (+66 -30) 📝 `packages/better-auth/src/utils/url.ts` (+78 -38) </details> ### 📄 Description Security hardening split out from #9131. ## Problem The static-config `baseURL` path already required `advanced.trustedProxyHeaders: true` to honor `x-forwarded-host` / `x-forwarded-proto`. The dynamic (`allowedHosts`) path trusted them implicitly, so deployments that don't strip client-supplied `x-forwarded-*` let an attacker pivot between any two hosts listed in `allowedHosts` via header spoofing. This aligns the two paths: proxy headers are now ignored unless the user explicitly opts in with `trustedProxyHeaders: true`. ## Migration Dynamic configs deployed behind a reverse proxy that only exposes the public hostname via `x-forwarded-host` need to add: ```ts betterAuth({ baseURL: { allowedHosts: [...] }, advanced: { trustedProxyHeaders: true, }, }); ``` Setups where the proxy rewrites `Host:` to the public hostname (nginx default, Vercel, Cloudflare, Netlify) are unaffected. ## Scope Small, focused on the security default only. The DX and correctness fixes from the same bug class (direct `auth.api` calls, MCP/OAuth-provider metadata forwarding, `APIError` on unresolvable, etc.) ship in #9131. Related: #9105 <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Hardened dynamic baseURL resolution in `better-auth` by ignoring `x-forwarded-host` and `x-forwarded-proto` unless `advanced.trustedProxyHeaders` is true. This aligns `allowedHosts` with static configs and prevents header-spoofing pivots between allowed hosts. - **Migration** - If using `baseURL.allowedHosts` behind a proxy that exposes the public hostname via `x-forwarded-host`, set `advanced.trustedProxyHeaders: true`. - No changes needed when the proxy rewrites `Host` to the public hostname (nginx default, Vercel, Cloudflare, Netlify). <sup>Written for commit 6294885e64c810c4d7b0eb6bf8c3842c98b5e631. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-15 22:51:13 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#25360