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>
This commit is contained in:
@@ -3,6 +3,8 @@ import { getUserByEmail, getPasskeysByUserIdAndOrigin } from 'officerdb';
|
||||
import { sign } from '@@/jwt';
|
||||
import argon2 from 'argon2';
|
||||
import * as errors from '@@/custom-errors';
|
||||
import { isSuperAdminOnlyOrigin } from '@@/_middlewares';
|
||||
import { isSuperAdmin } from '@@/super-admin';
|
||||
import { isLockdown, noteBlocked } from './panic';
|
||||
|
||||
const TEST_USERS: number[] = [];
|
||||
@@ -28,6 +30,12 @@ export const signinHandler: Handler = async function (ctx) {
|
||||
const isValidPassword = TEST_USERS.includes(dbUser.id) || (await argon2.verify(dbUser.password, password));
|
||||
if (!isValidPassword) throw errors.UNAUTHORIZED();
|
||||
|
||||
// A non-owner account may authenticate only through an app origin (e.g. the music app), never the
|
||||
// Super-Admin-only web/mobile origins — so its credentials can't open the full platform in a browser.
|
||||
if (isSuperAdminOnlyOrigin(origin) && !(await isSuperAdmin({ id: dbUser.id }))) {
|
||||
throw errors.FORBIDDEN('This account can only sign in through its app.');
|
||||
}
|
||||
|
||||
const { id, name, username } = dbUser;
|
||||
|
||||
const tokenUser = { id, email, name, username, passkeys: passkeys.length };
|
||||
|
||||
Reference in New Issue
Block a user