docs(website): one self-service surface — /auth/me/account
Companion to RunicGateway/website's collapse of /admin/account (6 routes) and
/player/account (8 routes) onto /auth/me/account, which was already a strict
superset of both.
- BACKEND_DESIGN.md: the two router-tree entries go; the /auth/me prose is
rewritten from "additive, the older routes stay for web back-compat" to the
single surface it now is, recording why /auth/me was the one to keep and
that gating was equivalent. account.controller.js moved to router/v1/auth/.
The /player-group paragraph loses account.router.js from its mount list.
- ENGAGEMENT.md: new Phase 1a records the collapse as built, and §0.6 finding 4
is corrected — it named router/v1/player/account.router.js, which is gone.
Phase 1b's change-and-verify flow now lands on /auth/me/account and nowhere
else, which was the reason to do this first: a self-service field would
otherwise have been written three times, in 1b and again in Phase 3.
- android/PLAN.md §6.4/§8: the "routes stay for web back-compat" note is now
false. The app needed no change — MeApi.kt was already 100% /auth/me/*.
- API_V2_PLAN.md: a forward pointer only. Its router inventories are a record
of the domain split as it landed and are deliberately left as written.
api-route-inventory.json is regenerated wholesale, not partially updated. It is
a generated mirror of server/routes.manifest.json with no CI gate, and it had
drifted on its own: 168 entries against the manifest's 203, missing every Teams
route and still listing the two Gmail connect routes Phase 1 deleted. It is now
byte-identical to the manifest. A gate for it is flagged in ENGAGEMENT.md
Phase 1a as later work — a mirror nothing checks will drift again.
PROJECT_TREE.md is deliberately untouched: it is auto-generated by the
sync-project-tree workflow in the website repo, which regenerates it from
tracked files on main.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -87,7 +87,15 @@ server/
|
||||
/me sub-routers below also match the
|
||||
bare /me and supply its noindex header
|
||||
me.routes.js (23) /auth/me/account*, sessions, trusted
|
||||
devices — router-level requireAuth
|
||||
devices — router-level requireAuth.
|
||||
The ONLY self-service account surface
|
||||
(see below); account.controller.js
|
||||
sits beside it and is reached from
|
||||
nowhere else
|
||||
account.controller.js the self-service handlers: username,
|
||||
password, TOTP, identities, device
|
||||
sessions, trusted devices, recovery
|
||||
codes
|
||||
notifications.routes.js (3) /auth/me/devices*, notifications/*
|
||||
mobile.routes.js + /auth/mobile/* — native bearer login
|
||||
mobileSso.routes.js (5)
|
||||
@@ -121,9 +129,6 @@ server/
|
||||
player/ index.js owns the shared `noindex, requireAuth` gate
|
||||
(authenticated, ANY role — staff are a superset
|
||||
of players) and the mount table
|
||||
account.router.js (8) /player/account — credentials, TOTP,
|
||||
linked identities; handlers shared
|
||||
with /admin/account and /auth/me
|
||||
appeals.router.js (4) /player/appeals
|
||||
appeals.controller.js
|
||||
(/player/shard is module-uo's)
|
||||
@@ -145,7 +150,6 @@ server/
|
||||
own prefixes; owns the shared
|
||||
`noindex, isLoggedIn, staffOnly` gate and
|
||||
declares no routes itself
|
||||
account.router.js (6) /admin/account — self-service, no adminOnly
|
||||
users.router.js (9) /admin/users — adminOnly. The six
|
||||
/users/:id/shard/* routes are a
|
||||
MODULE's, reached through the
|
||||
@@ -786,21 +790,29 @@ their own router level, and `/sso/:provider/link` carries `requireAuth` per rout
|
||||
| GET · PUT | `/me/notifications/subscriptions` | cookie / bearer | `{streams:[id]}` on PUT | get / replace own opted-in streams (unknown ids dropped) |
|
||||
| GET · PUT | `/me/notifications/teams` | cookie / bearer | `{teams:[{teamId,muted,emailMode}]}` on PUT | get / replace own **per-Team** preferences (phase 6, [`TEAMS.md`](TEAMS.md) §6.3). One entry per Team the caller could be notified about — active membership or an active forum grant — plus any Team they already hold a preference for; server-side defaults applied. An entry naming a Team the caller has no access to is **dropped, not refused**: a Team left between loading the screen and saving it is a race, not a client bug. The array is required even when empty (`../android/PLAN.md` §11) |
|
||||
|
||||
**Role-agnostic self-service (`/auth/me/*`).** The canonical "me" surface for **every** authenticated
|
||||
role. It reuses the exact `account.controller` handlers as `/player/account/*` and `/admin/account/*`
|
||||
(no logic duplication) behind `requireAuth` **only** — any active account, never a specific role. This
|
||||
lets a client (the Android app) manage its own account through one surface without ever touching
|
||||
`/admin` (docs/android/PLAN.md §6.4). The older `/player/account/*` + `/admin/account/*` routes stay
|
||||
for web back-compat.
|
||||
**Role-agnostic self-service (`/auth/me/*`).** The **only** self-service account surface, for every
|
||||
authenticated role, behind `requireAuth` **only** — any active account, never a specific role. A
|
||||
client (the Android app) manages its own account through it without ever touching `/admin`
|
||||
(docs/android/PLAN.md §6.4).
|
||||
|
||||
It used to be the third of three URL surfaces onto `account.controller`, beside `/player/account/*`
|
||||
and `/admin/account/*`. **Those 14 routes were deleted.** Both were strictly *smaller* than this one —
|
||||
neither carried recovery codes, and `/admin/account` carried no username or password change — so the
|
||||
web client already reached in here for part of a single screen. Gating was equivalent where it
|
||||
overlapped (`/player` and `/auth/me` are byte-identical `noindex, requireAuth`; `staffOnly` on
|
||||
`/admin/account` was strictly narrower and bought nothing, since every handler is self-scoped to
|
||||
`req.user.id`). The controller moved to `router/v1/auth/account.controller.js` beside its one
|
||||
remaining router. **New self-service fields go here and only here.**
|
||||
|
||||
**The `/player/*` group is self-service, not player-only.** Staff are a **superset** of players — every
|
||||
player ability plus their staff tools on top — so the whole group (`account.router.js`,
|
||||
`appeals.router.js`, mounted by `player/index.js`, plus whatever a module mounts here) sits behind
|
||||
player ability plus their staff tools on top — so the whole group (`appeals.router.js`, mounted by
|
||||
`player/index.js`, plus whatever a module mounts here) sits behind
|
||||
the shared `noindex, requireAuth` gate **only**, never `requireRole('player')`. Every handler is self-scoped to the caller by `req.user.id`, so an admin/editor/
|
||||
moderator using it sees only their **own** linked accounts and characters (with the pre-existing
|
||||
`isAdmin` bypass still letting a genuine admin read *any* character). `module-uo` inherits the rule
|
||||
and relies on it: its `/player/shard/*` handlers are the identical self-scoped ones it also serves
|
||||
under `/admin/shard/*`, so the two are interchangeable. This is why a staff account with linked game characters gets its "My characters" and
|
||||
under `/admin/shard/*`, so the two are interchangeable. (Core no longer does this for account
|
||||
security — see `/auth/me/*` above — but the rule the module depends on is unchanged.) This is why a staff account with linked game characters gets its "My characters" and
|
||||
personal notification streams on the mobile client — the group no longer 403s a non-`player` role.
|
||||
|
||||
`teams.router.js` joins the group in Teams phase 2, and relies on exactly that rule: a moderator is in
|
||||
|
||||
Reference in New Issue
Block a user