Files
summercms/.planning/phases/07-user-plugin-and-authentication/07-02-SUMMARY.md
2026-09-22 15:15:16 +02:00

5.6 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
07-user-plugin-and-authentication 02 auth
user
jwt
throttle
register
festival
phase provides
07-user-plugin-and-authentication bouncer Mint, Refresh, BlacklistStore, bcrypt, lagoon email/confirmed
golem15.user login, logout, fetch, refresh, register, oauth-providers
user_throttle and jwt_blacklist migrations
GetApiArrayEvent collected by golem15.fonoteka
07-03
07-04
07-05
added patterns
Bearer-only session handlers
cookie fallback only on the registry jwt guard
pre-password throttle gate
created modified
../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
../fonoteka.go/plugins/golem15/user/plugin.go
../fonoteka.go/plugins/golem15/fonoteka/plugin.go
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
Pattern: /_user/api/v1 group middleware is only throttle:user-api; handlers authenticate Bearer-only
Pattern: mail template names go through MailTemplate and ResolveMailLocale
83min 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.