Merge pull request 'docs: document /auth/me self surface; mark PLAN §8.1 done' (#10) from docs/auth-me-self-surface into main
Reviewed-on: #10 Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
This commit is contained in:
@@ -6,10 +6,10 @@ API changes it depends on can be landed in `website/` and `docs/` first. When we
|
||||
authoritative API reference is the committed OpenAPI spec at
|
||||
`website/server/swagger/swagger-output.json` (regenerated via `npm run swagger`).
|
||||
|
||||
**Prerequisite progress (§8):** ✅ **Password reset** — the "build FIRST, before app work" prerequisite
|
||||
(§8 item 2) is **done** (RunicGateway/website#75 + docs#8, 2026-07-19). The remaining §8 items are still
|
||||
open: role-agnostic `/auth/me/*` self surface (item 1), version/health for first-run (item 4), branding
|
||||
confirmation (item 6); push (item 3) is post-v1.
|
||||
**Prerequisite progress (§8):** ✅ **Password reset** (§8 item 2 — the "build FIRST" prerequisite;
|
||||
website#75 + docs#8) and ✅ **role-agnostic `/auth/me/*` self surface** (§8 item 1) are **done**
|
||||
(2026-07-19). Still open: version/health for first-run (item 4) and branding confirmation (item 6);
|
||||
push (item 3) is post-v1.
|
||||
|
||||
The workspace already holds `website/`, `link/`, `servuo-plugins/`, and `docs/`. `android-app/` is
|
||||
the fifth repo. It is **purely an API client of the website backend** — it never talks to the
|
||||
@@ -290,16 +290,18 @@ web client and the app both consume v1; a second parallel route tree + Swagger s
|
||||
maintenance cost. Reserve v2 for a real breaking re-shape if one ever arises.
|
||||
|
||||
**To build (all additive, v1):**
|
||||
1. **Role-agnostic self-service under `/auth/**` (§6.4, decided).** Mount the existing
|
||||
`account.controller` self handlers behind `requireAuth` (any role), so the app has one self surface
|
||||
and never touches `/admin`. Keep the old `/player/account/*` + `/admin/account/*` routes for web
|
||||
back-compat. New canonical routes:
|
||||
- `GET /auth/me` — current `{ id, username, role }` (already exists; the app's role source).
|
||||
1. **Role-agnostic self-service under `/auth/**` (§6.4, decided).**
|
||||
✅ **DONE (2026-07-19, RunicGateway/website#76 (+ this docs PR)).** A `me.routes.js`
|
||||
sub-router mounts the existing `account.controller` self handlers behind `requireAuth` (any role) at
|
||||
`/auth/me/*`, so the app has one self surface and never touches `/admin`. The old
|
||||
`/player/account/*` + `/admin/account/*` routes stay for web back-compat. Shipped routes:
|
||||
- `GET /auth/me` — current `{ id, username, role }` (already existed; the app's role source).
|
||||
- `GET /auth/me/account` — full self account.
|
||||
- `PATCH /auth/me/account/username`, `PATCH /auth/me/account/password`.
|
||||
- `POST /auth/me/account/totp/setup|enable|disable`.
|
||||
- `GET /auth/me/account/identities`, `DELETE /auth/me/account/identities/:provider`.
|
||||
- Regenerate Swagger; add `#swagger` annotations for each.
|
||||
- Swagger regenerated with `#swagger` annotations; `test/authMe.test.js` guards the auth gate; and
|
||||
an end-to-end smoketest confirmed both a player and an editor (staff) drive the same surface.
|
||||
2. **Password reset — build on backend + web front end FIRST (a prerequisite, not app scope).**
|
||||
✅ **DONE (2026-07-19, RunicGateway/website#75 + docs#8).** Full platform flow shipped in `website/`:
|
||||
request-reset (`POST /auth/password/forgot`, always a generic 200 — no account enumeration) emails a
|
||||
|
||||
@@ -172,6 +172,18 @@ accepts `Authorization: Bearer` for API testing).
|
||||
| POST | `/password/forgot` | — (rate-limited) | `{email}` | email a single-use, ~1h reset link to **every active account** on the address; **always** returns the same generic 200 (no account enumeration). Email is non-unique, so several accounts may each get a link naming their username. Logs `account.password.reset.request`. |
|
||||
| GET | `/password/reset/:token` | — | — | validate a link → `{username}` for the form, else 404 (never distinguishes expired/used/never-existed) |
|
||||
| POST | `/password/reset/:token` | — (rate-limited) | `{password}` | consume the single-use link, rotate the hash, and revoke **all** sessions (web cutoff + mobile refresh tokens). Does **not** sign the user in — they log in fresh (so a 2FA account still passes TOTP). Logs `account.password.reset.complete`. |
|
||||
| GET | `/me/account` | cookie / bearer | — | full self account (`id, username, role, email, status, totp_enabled, has_password`) |
|
||||
| PATCH | `/me/account/username` | cookie / bearer (rate-limited) | `{username}` | change own username; re-issues the caller's session |
|
||||
| PATCH | `/me/account/password` | cookie / bearer (rate-limited) | `{newPassword, currentPassword?}` | change/set own password (current required unless the account has none); revokes other sessions, keeps the caller's |
|
||||
| POST | `/me/account/totp/setup` · `…/enable` · `…/disable` | cookie / bearer | `{code}` on enable/disable | self 2FA enrollment (disable needs a valid current code, not a password) |
|
||||
| GET | `/me/account/identities` · DELETE `…/:provider` | cookie / bearer | — | list / unlink own SSO identities |
|
||||
|
||||
**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.
|
||||
|
||||
**Password reset.** Uses the same audited pattern as `user_invites`: an opaque 32-byte token
|
||||
whose **sha256 hash only** is stored in `password_resets`, single-use and short-lived (~1h). It
|
||||
|
||||
Reference in New Issue
Block a user