mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-13 14:37:26 +00:00
`logicsrc login --device` told users to open https://logicsrc-credentials-production.up.railway.app/cli/device even when they had reached the app on the real domain. /cli/device/code built verification_uri from `config.origin`, which is a single fixed value read from $PUBLIC_ORIGIN, so the response was wrong for every hostname except the one that variable happened to name. Derive the origin from the request instead: whatever host the CLI called is the host it gets sent back to. Express honours X-Forwarded-Proto/Host here because server.mjs sets `trust proxy` behind Railway's TLS terminator. Deliberately scoped to the two device-flow URLs. The WebAuthn expectedOrigin in passkey.mjs stays pinned to config.origin — validating a signature against a host the caller supplied would defeat the check. Note this fixes which URL is *printed*; the host still has to route to this service for the link to load. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
47 lines
1.9 KiB
JavaScript
47 lines
1.9 KiB
JavaScript
// `logicsrc login --device` printed a generated Railway hostname to users on the
|
|
// real domain, because /cli/device/code echoed $PUBLIC_ORIGIN instead of the host
|
|
// the CLI had just called. These pin the replacement behaviour.
|
|
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import { requestOrigin } from "../src/lib/origin.mjs";
|
|
|
|
/** A minimal stand-in for the Express request surface requestOrigin touches. */
|
|
const req = (host, protocol = "https") => ({
|
|
protocol,
|
|
headers: { host },
|
|
get: (h) => (h.toLowerCase() === "host" ? host : undefined),
|
|
});
|
|
|
|
const FALLBACK = "https://logicsrc-credentials-production.up.railway.app";
|
|
|
|
test("uses the host the caller actually reached", () => {
|
|
assert.equal(requestOrigin(req("logicsrc.com"), FALLBACK), "https://logicsrc.com");
|
|
// The same deployment answering on its Railway hostname still self-describes
|
|
// correctly — this is not a hardcode swap, it follows the request.
|
|
assert.equal(
|
|
requestOrigin(req("logicsrc-credentials-production.up.railway.app"), FALLBACK),
|
|
FALLBACK,
|
|
);
|
|
});
|
|
|
|
test("keeps the forwarded protocol and any explicit port", () => {
|
|
assert.equal(requestOrigin(req("localhost:8080", "http"), FALLBACK), "http://localhost:8080");
|
|
});
|
|
|
|
test("falls back to the configured origin when there is no Host header", () => {
|
|
assert.equal(requestOrigin({ protocol: "https" }, FALLBACK), FALLBACK);
|
|
assert.equal(requestOrigin({}, `${FALLBACK}/`), FALLBACK, "trailing slash is trimmed");
|
|
});
|
|
|
|
test("reads the header directly when req.get is unavailable", () => {
|
|
// Some middleware stacks (and our own tests) pass a bare object.
|
|
assert.equal(
|
|
requestOrigin({ protocol: "https", headers: { host: "logicsrc.com" } }, FALLBACK),
|
|
"https://logicsrc.com",
|
|
);
|
|
});
|
|
|
|
test("defaults to https when the request carries no protocol", () => {
|
|
assert.equal(requestOrigin({ headers: { host: "logicsrc.com" } }, FALLBACK), "https://logicsrc.com");
|
|
});
|