--- phase: 07-user-plugin-and-authentication plan: 05 subsystem: auth tags: [fonoteka, parity, user-api, php] requires: - phase: 07-user-plugin-and-authentication provides: user session, account, token, and locale handlers provides: - 15 recorded /_user/api/v1 routes in the parity manifest - db_capture helper for reset and activation codes - nuxt-auth and locked-user client flows affects: [07-06] tech-stack: added: [] patterns: [user-api auth group beside the 154 fonoteka routes, code capture through artisan tinker] key-files: created: - ../fonoteka.go/parity/db_capture.go - ../fonoteka.go/parity/fixtures/nuxt/nuxt-auth.yaml - ../fonoteka.go/parity/fixtures/nuxt/nuxt-auth-lock.yaml modified: - ../fonoteka.go/parity/manifest.yaml - ../fonoteka.go/parity/capture-rules.yaml - tide/variables.go key-decisions: - "The 15 user routes stay pending until a Go replay matches the recorded PHP body" - "A sixth failed login is the same 401 as the first; this login path does not suspend" - "Register disabled and register throttled both record as an opaque 500" - "Authenticated activate with a wrong code records an HTML 500, and fetch after logout still returns 200" patterns-established: - "Pattern: user-api routes are extra manifest IDs; the fonoteka routes.php digest stays the 154-route lock" - "Pattern: reset and activation codes are read by db_capture and stored as {id}!{code}" requirements-completed: [] duration: 45min completed: 2026-09-22 --- # Phase 7 Plan 05: User API parity capture Summary **The 15 `/_user/api/v1` routes are recorded from the isolated PHP app. They stay pending because several PHP bodies differ from the current Go handlers.** ## Performance - **Duration:** 45 min - **Started:** 2026-09-22T16:11:00Z - **Completed:** 2026-09-22T16:45:00Z - **Tasks:** 3 - **Files modified:** 45 ## Accomplishments - Recorded login, logout, fetch, refresh, register, forgot-password, reset-password, activate, activate-by-code, update, change-password, avatar, avatar/remove, marketing-consent, and oauth-providers, including validation and auth failures. - `db_capture` reads `reset_password_code` and `activation_code` through the isolated artisan tinker and stores `{id}!{code}`. - `nuxt-auth.yaml` walks register, fetch, update, change-password, refresh, logout, and a following fetch. `nuxt-auth-lock.yaml` records 423 on genres, 200 on me/locale, then a successful change-password. - The corpus audit prints `recorded 169/169`. Ported routes stay at 7. The new routes are pending. ## Task Commits 1. **Task 1–3: Record and verify the user API corpus** — `30984b6` in `fonoteka.go` 2. **Scrubber allow-list for the second test password** — `7d5d865` in `summercms.go` ## Files Created/Modified - `parity/manifest.yaml` — 15 `auth_group: user-api` entries - `parity/capture-rules.yaml` — register, refresh, and activate-by-code token capture - `parity/db_capture.go` — PHP tinker and Postgres code readers - `parity/fixtures/nuxt/nuxt-auth.yaml` — D-14 session flow - `parity/fixtures/nuxt/nuxt-auth-lock.yaml` — locked-user 423 then me/locale - `tide/variables.go` — `parity-alice-next` is an allow-listed test password ## Decisions Made A2 is settled from the recording. Six rapid failed logins for `a2@parity.test` are all 401 `{"error":true,"message":"Nieprawidłowy email lub hasło"}`. The 6th body equals the 1st. This login path does not suspend the account. Register with `allow_registration=false` and register after the per-IP limit both record `{"error":"Internal server error"}` at status 500 under `APP_DEBUG=false`. Every recorded login, fetch, register, and update success user payload contains `feedback_widget_hidden: false`. ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 1 - Bug] Authenticated activate with a wrong code is an HTML 500** - **Found during:** Task 2 - **Issue:** The plan expected 200 `{"user":...}` with `is_activated` unchanged. PHP `attemptActivation` throws outside the JSON catch, and the isolated app returns the generic HTML page titled "Błąd strony" at status 500. `POST activate-by-code` with `1!nope` does the same. - **Fix:** The fixtures keep the recorded HTML. The Go handler's 200 is a gap for a later closure, not a fixture edit. - **Files modified:** `parity/fixtures/routes/POST___user_api_v1_activate_user-api.yaml`, `parity/fixtures/routes/POST___user_api_v1_activate-by-code_user-api__invalid.yaml` - **Committed in:** `30984b6` **2. [Rule 1 - Bug] Fetch after logout is still 200** - **Found during:** Task 2 - **Issue:** The plan expected the logged-out bearer to be refused. PHP logout returns `{"message":"Logged out"}` and a following `GET /fetch` with that bearer is still 200. The Go handler blacklists the token. - **Fix:** `GET___user_api_v1_fetch_user-api__reused.yaml` and the last step of `nuxt-auth.yaml` record the 200. The routes stay `pending`. - **Files modified:** `parity/fixtures/routes/GET___user_api_v1_fetch_user-api__reused.yaml`, `parity/fixtures/nuxt/nuxt-auth.yaml` - **Committed in:** `30984b6` --- **Total deviations:** 2 recorded, not patched in Go **Impact on plan:** The corpus matches PHP. Closing the activate and logout gaps is follow-up work, not a silent fixture rewrite. ## Issues Encountered None ## User Setup Required None - no external service configuration required. ## Next Phase Readiness Ready for 07-06 unit coverage. AUTH-01, AUTH-02, AUTH-03, AUTH-04, and I18N-02 stay unchecked until phase sign-off. The user-api routes stay pending until Go matches the recorded bodies. ## Self-Check: PASSED - Secret grep over the new fixtures found no live JWT or `inv_` token. - `/tmp/summercms-parity/vars.yaml` is mode 0600 and outside the repo. - `go run ./parity/check_corpus.go --require-recorded --require-clients --check-secrets` printed `recorded 169/169`. - `go test ./parity/ -run 'TestParityCorpus|TestDBCapture'` passed with 7 ported and 162 pending. - Commits `30984b6` and `7d5d865` are on master.