|
| 1 | +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. |
| 2 | + |
| 3 | +/** |
| 4 | + * The forwarding facts the client-address trust rule reads |
| 5 | + * (`./client-address.ts`, ADR-0069 D5/D7), and the one way a request this |
| 6 | + * plugin re-dispatches in-process carries them (#24877). |
| 7 | + * |
| 8 | + * INTERNAL. This module is not on the package's `exports` map, and neither the |
| 9 | + * package root (`index.ts`) nor the published `./client-address` entry |
| 10 | + * re-exports it: nothing outside this package reads the list. |
| 11 | + * |
| 12 | + * ⛔ Keep this module free of imports. The published `./client-address` entry |
| 13 | + * — the lean entry the runtime loads without the better-auth family — imports |
| 14 | + * the list from here, so a value import here would become a load-time |
| 15 | + * dependency of every consumer of that entry. |
| 16 | + */ |
| 17 | + |
| 18 | +/** |
| 19 | + * The forwarded headers the rule reads, in the order it believes them, and |
| 20 | + * only when the deployment declares `server.trustProxy`: `X-Forwarded-For` |
| 21 | + * (its leftmost entry), then `X-Real-IP`. Lower-cased, as every adapter |
| 22 | + * delivers them. `resolveClientAddress` reads exactly this list. |
| 23 | + */ |
| 24 | +export const FORWARDED_CLIENT_ADDRESS_HEADERS: readonly string[] = Object.freeze(['x-forwarded-for', 'x-real-ip']); |
| 25 | + |
| 26 | +/** |
| 27 | + * Carry the forwarding facts the rule reads ({@link FORWARDED_CLIENT_ADDRESS_HEADERS}) |
| 28 | + * from the request a bridge serves onto the request it re-dispatches, as the |
| 29 | + * incoming request had them. `AuthManager.handleRequest` judges the |
| 30 | + * re-dispatched request by the rule, so it is judged on the same facts as the |
| 31 | + * request the bridge serves: the forwarded client address behind a declared |
| 32 | + * `server.trustProxy`, the connection peer otherwise, when the rule ignores |
| 33 | + * these headers. The bridges' header builders (`register-sso-provider.ts`, |
| 34 | + * `send-verification-email.ts`, `sys-session-revoke.ts`) call this; none of |
| 35 | + * them names a header itself. |
| 36 | + */ |
| 37 | +export function carryForwardedClientAddress(from: Headers, to: Headers): void { |
| 38 | + for (const name of FORWARDED_CLIENT_ADDRESS_HEADERS) { |
| 39 | + const value = from.get(name); |
| 40 | + if (value !== null) to.set(name, value); |
| 41 | + } |
| 42 | +} |
0 commit comments