add a switch that turns origin checking off entirely

The /api/music exemption was not enough on its own: an app has to reach /api/auth to sign
in before it ever calls its own feature, so the login was still 403ing on Invalid origin.

ALLOW_ANY_ORIGIN=true now accepts every Origin everywhere and skips the per-origin path
scoping. Set on this host; ALLOW_ANY_ORIGIN_MUSIC stays as the narrower option.

The account backstop is deliberately NOT disabled — a valid non-owner token is still
confined to /api/auth + /api/music whatever Origin it claims, because that rule is
account-based rather than origin-based, and it is the airtight half of the pair. Every
protected route still requires a valid token.

Reverting is an env edit and a restart. Both flags and their call sites come out when the
tailnet becomes the perimeter.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-30 10:54:16 +00:00
co-authored by Claude Opus 5
parent 418729a470
commit 1e3dff27f5
+24 -9
View File
@@ -85,18 +85,29 @@ for (const { slug, origin } of APP_ORIGIN_LIST) {
// Last, so the web origin wins if an app ever declares the same one.
if (PUBLIC_ORIGIN) ORIGIN_RULES[PUBLIC_ORIGIN] = { superAdminOnly: true };
// ── TEMPORARY: origin exemption for /api/music ──
// Off unless ALLOW_ANY_ORIGIN_MUSIC=true. While the platform is still internet-exposed, this lets a music
// client that cannot present the app's custom-scheme origin reach /api/music anyway. It drops ONE layer:
// a valid token is still required (userMiddleware) and the non-owner account backstop still confines
// music accounts. Origin was never authentication — it is client-chosen and trivially forged outside a
// browser — so this removes defence in depth, not the lock. Delete once the tailnet is the perimeter.
// ── TEMPORARY: origin checking switched off ──
// ALLOW_ANY_ORIGIN=true accepts every Origin, everywhere, and skips the per-origin path scoping.
// ALLOW_ANY_ORIGIN_MUSIC=true is the narrower version, /api/music only — which is not enough on its own,
// because an app has to reach /api/auth to sign in before it ever calls its own feature.
//
// What still holds with these on: every protected route requires a valid token (userMiddleware), and the
// account backstop below still confines a non-owner account to /api/auth + /api/music whatever Origin it
// claims — that one is deliberately NOT disabled, since it is account-based, not origin-based.
//
// What is lost: defence in depth, not the lock. Origin was never authentication here — `officer://<hex>`
// is chosen by the client, forgeable outside a browser, and extractable from a shipped app binary.
// Both flags and their call sites come out once the tailnet is the perimeter.
const ALLOW_ANY_ORIGIN = process.env.ALLOW_ANY_ORIGIN === 'true';
const ALLOW_ANY_ORIGIN_MUSIC = process.env.ALLOW_ANY_ORIGIN_MUSIC === 'true';
export function isMusicOriginExempt(path: string): boolean {
return ALLOW_ANY_ORIGIN_MUSIC && pathAllowed(path, ['/api/music']);
export function isOriginCheckDisabled(path?: string): boolean {
if (ALLOW_ANY_ORIGIN) return true;
return ALLOW_ANY_ORIGIN_MUSIC && !!path && pathAllowed(path, ['/api/music']);
}
/** @deprecated use isOriginCheckDisabled — kept so existing call sites read the same. */
export const isMusicOriginExempt = isOriginCheckDisabled;
// True when an Origin is reserved for the platform owner (used at signin to reject a non-owner login).
export function isSuperAdminOnlyOrigin(origin: string | undefined): boolean {
return !!origin && ORIGIN_RULES[origin]?.superAdminOnly === true;
@@ -104,6 +115,7 @@ export function isSuperAdminOnlyOrigin(origin: string | undefined): boolean {
export function isOriginAllowed(origin: string | undefined, host?: string): boolean {
if (IS_DEV_BUILD) return true;
if (ALLOW_ANY_ORIGIN) return true;
if (origin) {
if (origin.startsWith('chrome-extension://')) {
@@ -178,7 +190,10 @@ export const originScopeMiddleware: MiddlewareHandler = async function (ctx, nex
throw errors.FORBIDDEN('This account is limited to the music app');
}
// 2. Per-origin rules.
// 2. Per-origin rules. Skipped entirely while origin checking is off (the account backstop above is
// account-based, not origin-based, so it deliberately still applies).
if (isOriginCheckDisabled(path)) return next();
const rule = origin ? ORIGIN_RULES[origin] : undefined;
if (rule) {
if (rule.paths && !pathAllowed(path, rule.paths)) {