docs(07-02): complete the user session plan
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
---
|
||||
phase: 07-user-plugin-and-authentication
|
||||
plan: 02
|
||||
subsystem: auth
|
||||
tags: [user, jwt, throttle, register, festival]
|
||||
|
||||
requires:
|
||||
- phase: 07-user-plugin-and-authentication
|
||||
provides: bouncer Mint, Refresh, BlacklistStore, bcrypt, lagoon email/confirmed
|
||||
provides:
|
||||
- golem15.user login, logout, fetch, refresh, register, oauth-providers
|
||||
- user_throttle and jwt_blacklist migrations
|
||||
- GetApiArrayEvent collected by golem15.fonoteka
|
||||
affects: [07-03, 07-04, 07-05]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [Bearer-only session handlers, cookie fallback only on the registry jwt guard, pre-password throttle gate]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- ../fonoteka.go/plugins/golem15/user/controllers/api_controller.go
|
||||
- ../fonoteka.go/plugins/golem15/user/routes.go
|
||||
- ../fonoteka.go/plugins/golem15/user/classes/events.go
|
||||
- ../fonoteka.go/plugins/golem15/user/classes/throttle.go
|
||||
modified:
|
||||
- ../fonoteka.go/plugins/golem15/user/plugin.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/plugin.go
|
||||
|
||||
key-decisions:
|
||||
- "Login gates with RejectIfThrottled before the password check and records the attempt once afterward"
|
||||
- "Disabled and throttled registration return the production SafeExceptionResponse body unless app.debug is true"
|
||||
- "Fonoteka listens for GetApiArrayEvent; the user plugin does not import fonoteka"
|
||||
|
||||
patterns-established:
|
||||
- "Pattern: /_user/api/v1 group middleware is only throttle:user-api; handlers authenticate Bearer-only"
|
||||
- "Pattern: mail template names go through MailTemplate and ResolveMailLocale"
|
||||
|
||||
requirements-completed: []
|
||||
|
||||
duration: 83min
|
||||
completed: 2026-09-22
|
||||
---
|
||||
|
||||
# Phase 7 Plan 02: User session loop Summary
|
||||
|
||||
**Login, logout, fetch, refresh, and register on `/_user/api/v1`, with a per-user login throttle, a Postgres JWT blacklist, and fonoteka's organisation fields merged through `GetApiArrayEvent`.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 83 min
|
||||
- **Started:** 2026-09-22T11:48:00Z
|
||||
- **Completed:** 2026-09-22T13:11:00Z
|
||||
- **Tasks:** 3
|
||||
- **Files modified:** 22
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Email and password login returns a JWT whose `sub` is the user id, `prv` is the hardcoded User hash, and `iss` is the request URL. Logout forever-blacklists that jti so the next fetch is 401.
|
||||
- Wrong password, an unknown email, and a suspended account share the body `{"error":true,"message":"Invalid email or password"}`.
|
||||
- Register covers auto (token), user (activation mail), and admin (`{}`). Production hides disabled and throttled causes behind `{"error":"Internal server error"}`.
|
||||
- `golem15.fonoteka` adds `organisation_id`, `organisation_role`, `must_change_password`, and `preferred_locale` on `GetApiArrayEvent`.
|
||||
|
||||
## Task Commits
|
||||
|
||||
1. **Task 1: User session schema and config** — `7046213` (test), `4cc7433` (feat) in `fonoteka.go`
|
||||
2. **Task 2: Throttle, mail locale, login/logout/fetch/refresh** — `1076db9`, `1dd4aba` in `fonoteka.go`
|
||||
3. **Task 3: Register and GetApiArrayEvent** — `bac71fe` in `fonoteka.go`
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `plugins/golem15/user/controllers/api_controller.go` — session and register handlers
|
||||
- `plugins/golem15/user/routes.go` — `/_user/api/v1` group with `throttle:user-api`
|
||||
- `plugins/golem15/user/plugin.go` — Postgres blacklist, cookie-capable jwt guard, user-api bucket, sweep
|
||||
- `plugins/golem15/user/classes/throttle.go` — failed-login counter and the pre-password gate
|
||||
- `plugins/golem15/user/classes/events.go` — `GetApiArrayEvent` and `RegisterEvent`
|
||||
- `plugins/golem15/fonoteka/plugin.go` — payload listener
|
||||
|
||||
## Decisions Made
|
||||
|
||||
The login gate does not increment the attempt counter. `CheckAndRecordLogin(..., false)` both checks a ban and records a failure, so calling it before and after the password check would suspend on the third HTTP failure. `RejectIfThrottled` only reports an existing ban or suspension. The outcome is recorded once.
|
||||
|
||||
Registration's disabled and throttled branches use `{"error":"..."}` at 500. That is distinct from `wire.WriteOpaque500`, which is `{"error":true,"message":"Internal server error"}`.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] The pre-password throttle call must not increment attempts**
|
||||
- **Found during:** Task 2
|
||||
- **Issue:** The plan called `CheckAndRecordLogin(..., false)` before the password check and again on failure. That function increments on `ok=false`, so each failed login counted twice and the sixth HTTP login was not the one rejected before the password comparison.
|
||||
- **Fix:** `RejectIfThrottled` enforces ban and suspension without writing. `CheckAndRecordLogin` runs once with the outcome.
|
||||
- **Files modified:** `classes/throttle.go`, `controllers/api_controller.go`
|
||||
- **Verification:** `TestLoginSixthAttempt` — five failures suspend, the sixth correct password returns the generic 401, and `attempts` stays 5.
|
||||
- **Committed in:** `1dd4aba`
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 1 auto-fixed (Rule 1)
|
||||
**Impact on plan:** Keeps the 5-attempt / 15-minute suspend aligned with one failed HTTP login. No new route.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None - no external service configuration required.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
Ready for 07-03 (account management) and 07-04 (personal tokens). AUTH-01 stays open: password reset and email verification are still 07-03. AUTH-02's payload seam is in place; the requirement checkbox stays pending until the phase requirement is signed off.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- Session and register handlers exist under `plugins/golem15/user/controllers/api_controller.go`.
|
||||
- `fonoteka.go` commits `7046213`, `4cc7433`, `1076db9`, `1dd4aba`, and `bac71fe` are on master.
|
||||
- `go test` for the session, throttle, register, and `TestGetApiArray` filters passed. `go vet` on the user and fonoteka plugins passed.
|
||||
Reference in New Issue
Block a user