Files
platform/src/servers/super-admin.ts
T
pastilhasandClaude Opus 4.8 1d441e3aa1 auth: confine non-owner accounts to the music app (account + origin gates)
Adds a Super Admin ("owner") identity — SUPER_ADMIN_EMAIL, else the
bootstrap/first user (super-admin.ts) — and closes the hole where a music
account could sign into the full platform:

- Rename EXPO_PUBLIC_CLIENT_ORIGIN -> OFFICER_APP_ORIGIN.
- PUBLIC_URL + OFFICER_APP_ORIGIN are owner-only origins; MUSIC_APP_ORIGIN
  stays path-scoped to /api/auth + /api/music.
- Account backstop (origin-independent): a valid non-owner token may reach
  only /api/auth + /api/music regardless of Origin — airtight even if the
  header is omitted/forged.
- signin rejects a non-owner logging in from an owner-only origin.

Owner keeps full access (verified); music users are confined to the music app.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 18:38:29 +00:00

39 lines
1.4 KiB
TypeScript

import { getUsers, getUserByEmail } from 'officerdb';
// Identifies the platform owner ("Super Admin"). Two mechanisms, both supported:
// (b) explicit — SUPER_ADMIN_EMAIL in .env designates the owner by email.
// (a) automatic — if that's unset, the bootstrap/first account (lowest user id) is the owner.
// Resolved once and cached: the owner never changes at runtime (bootstrap is closed after user #1).
const { SUPER_ADMIN_EMAIL } = process.env;
let cachedId: number | null = null;
let resolved = false;
async function resolveSuperAdminId(): Promise<number | null> {
if (resolved) return cachedId;
if (SUPER_ADMIN_EMAIL) {
const user = await getUserByEmail(SUPER_ADMIN_EMAIL);
cachedId = user?.id ?? null;
} else {
const users = await getUsers();
cachedId = users.length ? users.reduce((min, u) => (u.id < min ? u.id : min), users[0]!.id) : null;
}
resolved = true;
return cachedId;
}
export async function getSuperAdminId(): Promise<number | null> {
try {
return await resolveSuperAdminId();
} catch {
// Transient DB error before the id is cached: stay unresolved (deny) and retry next call.
return null;
}
}
export async function isSuperAdmin(payload: { id?: number } | null | undefined): Promise<boolean> {
if (!payload?.id) return false;
const adminId = await getSuperAdminId();
return adminId !== null && payload.id === adminId;
}