docs(07-02): complete the user session plan
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -281,7 +281,7 @@ Plans:
|
||||
|
||||
**Wave 2** *(blocked on 07-01)*
|
||||
|
||||
- [ ] 07-02-PLAN.md — User/Throttle schema and the core session loop: login/logout/fetch/refresh/register
|
||||
- [x] 07-02-PLAN.md — User/Throttle schema and the core session loop: login/logout/fetch/refresh/register
|
||||
|
||||
**Wave 3** *(blocked on 07-02)*
|
||||
|
||||
@@ -448,7 +448,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 →
|
||||
| 4. CLI scaffolding, i18n and mail | 4/4 | Complete | 2026-09-18 |
|
||||
| 5. Data layer full fidelity | 6/6 | Complete | 2026-09-18 |
|
||||
| 6. HTTP routing, auth groups and rate limiting | 14/14 | Complete | 2026-09-21 |
|
||||
| 7. User plugin and authentication | 1/6 | In Progress| |
|
||||
| 7. User plugin and authentication | 2/6 | In Progress| |
|
||||
| 8. OAuth2.1 authorization server | 0/TBD | Not started | - |
|
||||
| 9. Backend admin authentication and schema pipeline | 0/TBD | Not started | - |
|
||||
| 10. Admin Vue SPA | 0/TBD | Not started | - |
|
||||
|
||||
@@ -4,13 +4,13 @@ milestone: v1.0
|
||||
milestone_name: milestone
|
||||
status: executing
|
||||
stopped_at: Completed 07-01-PLAN.md
|
||||
last_updated: "2026-09-22T11:43:03.651Z"
|
||||
last_updated: "2026-09-22T13:14:46.649Z"
|
||||
last_activity: 2026-09-22
|
||||
progress:
|
||||
total_phases: 15
|
||||
completed_phases: 6
|
||||
total_plans: 43
|
||||
completed_plans: 38
|
||||
completed_plans: 39
|
||||
percent: 40
|
||||
---
|
||||
|
||||
@@ -26,11 +26,11 @@ See: .planning/PROJECT.md (updated 2026-09-16)
|
||||
## Current Position
|
||||
|
||||
Phase: 07 (user-plugin-and-authentication) — EXECUTING
|
||||
Plan: 2 of 6
|
||||
Plan: 3 of 6
|
||||
Status: Ready to execute
|
||||
Last activity: 2026-09-22
|
||||
|
||||
Progress: [█████████░] 88%
|
||||
Progress: [█████████░] 91%
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
@@ -82,6 +82,7 @@ Progress: [█████████░] 88%
|
||||
| Phase 06 P10 | 3h 15m | 1 tasks | 2 files |
|
||||
| Phase 06 P11 | 12h 30m | 1 tasks | 1 files |
|
||||
| Phase 07 P01 | 12 min | 3 tasks | 20 files |
|
||||
| Phase 07 P02 | 83m | 3 tasks | 22 files |
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
|
||||
@@ -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