Gate /admin to staff roles; role-aware login redirects for players

Introducing the 'player' role turned 'logged-in' into 'logged-in but possibly
untrusted', but the admin router only gated content routes (dashboard, posts,
wiki, uploads) by isLoggedIn — so a player session could reach editor-tier
endpoints. Fixes:
- Backend: requireRole('admin','editor','moderator') at the admin router base;
  players now 403 on all /admin/* and use /player instead.
- Client: RequireAuth redirects a signed-in player to /account (mirrors
  RequirePlayer).
- Both login pages redirect by role after auth (player -> /account, staff ->
  /admin) so you land in the right shell whichever door you used.

Verified live: player token 403s on /admin/dashboard + /admin/users, 200s on
/player/account; browser click-through confirms a player at /admin and at
/admin/login both land on /account. 134 server tests green; client builds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rao86n5cXpwAyjdBFEshV
This commit is contained in:
2026-07-06 19:57:31 -05:00
parent 387db52510
commit 82807d18d9
4 changed files with 30 additions and 13 deletions

View File

@@ -17,8 +17,13 @@ const validate = require('../../../middleware/validate')
const adminRouter = express.Router()
// Every admin route requires auth and is kept out of search indexes.
adminRouter.use(noindex, isLoggedIn)
// Every admin route requires auth, a STAFF role, and is kept out of search
// indexes. The staff gate matters now that `player` is a logged-in-but-untrusted
// role: without it, the editor-tier routes below (dashboard, posts, wiki,
// uploads) that are only guarded by isLoggedIn would be reachable by players.
// Players get 403 here and use the self-scoped /player group instead.
const staffOnly = requireRole('admin', 'editor', 'moderator')
adminRouter.use(noindex, isLoggedIn, staffOnly)
// Admin-only gate. Editors may manage content (posts/wiki), but user
// management, site mode, and settings are restricted to the admin role.