Nexo P1 fix — match API hostname to the page so the session cookie travels

The session cookie is set with `SameSite=Lax` (see
`servers/dating/http.mjs:sessionCookie`). Browsers treat
`http://localhost` and `http://127.0.0.1` as DIFFERENT sites, so
when SvelteKit dev runs on `localhost:5173` and the API client
hard-codes `127.0.0.1:8787`, the cookie set by `/api/auth/login` is
dropped on every subsequent fetch. End result the user reported:
login appears to "do nothing" — the request really did succeed, but
the next `/api/session` probe arrives without the cookie and the
client thinks the user is anonymous again.

Fix: derive the API base from `window.location.hostname` at runtime
(falling back to `127.0.0.1` on SSR / vitest where `window` doesn't
exist). Now the page on `localhost:5173` talks to `localhost:8787`
and the page on `127.0.0.1:5173` talks to `127.0.0.1:8787` — both
same-site pairs, so `SameSite=Lax` keeps the cookie attached on
fetch.

`DATING_API_DEFAULT_BASE` stays exported as a deprecated literal so
external callers that imported it don't break; new code should use
`resolveDatingApiBase()` or pass `options.base` explicitly. The
server's CORS allowlist already covers both `localhost:5173` and
`127.0.0.1:5173`, so no server change is needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
master
dev 5 months ago
parent c6392c317f
commit 27198b53ea

@ -45,7 +45,34 @@ import {
type DatingUser
} from './types.ts';
export const DATING_API_DEFAULT_BASE = 'http://127.0.0.1:8787';
/** Default port the standalone dating server listens on. */
export const DATING_API_DEFAULT_PORT = 8787;
/**
* Resolve the API base URL.
*
* Browsers treat `localhost` and `127.0.0.1` as different sites, so
* the session cookie's `SameSite=Lax` policy drops it on cross-origin
* fetches between them — the user logs in, the cookie is set on the
* 127.0.0.1 origin, and the next `/api/session` probe from
* localhost:5173 lands without it. Result: login looks like it
* silently does nothing.
*
* To stay same-site without touching the server, reuse whatever
* hostname the page is already on (`localhost` ↔ `localhost`,
* `127.0.0.1` ↔ `127.0.0.1`). On non-browser runtimes (SSR, vitest)
* fall back to the explicit `127.0.0.1` since `window` doesn't exist.
*/
export function resolveDatingApiBase(port: number = DATING_API_DEFAULT_PORT): string {
if (typeof window !== 'undefined' && window.location?.hostname) {
const protocol = window.location.protocol === 'https:' ? 'https:' : 'http:';
return `${protocol}//${window.location.hostname}:${port}`;
}
return `http://127.0.0.1:${port}`;
}
/** @deprecated Use `resolveDatingApiBase()`. Kept for callers that import the literal. */
export const DATING_API_DEFAULT_BASE = `http://127.0.0.1:${DATING_API_DEFAULT_PORT}`;
export interface DatingApiClientOptions {
/** Base URL of the standalone dating server. Defaults to `DATING_API_DEFAULT_BASE`. */
@ -160,7 +187,7 @@ export interface DatingApiClient {
export function createDatingApiClient(
options: DatingApiClientOptions = {}
): DatingApiClient {
const base = options.base ?? DATING_API_DEFAULT_BASE;
const base = options.base ?? resolveDatingApiBase();
const doFetch = options.fetch ?? globalThis.fetch.bind(globalThis);
function safeJson(text: string): unknown {

Loading…
Cancel
Save

Powered by TurnKey Linux.