docs: capture six user-plugin escalation quicks
This commit is contained in:
@@ -955,22 +955,31 @@ Recent decisions affecting current work:
|
||||
- [unknown] [sm-user-plugin / cabana] Verified link between a frontend user and a backend user — [todo file](.planning/todos/pending/2026-10-17-frontend-backend-account-link.md)
|
||||
- [unknown] [sm-user-plugin / pact] Plugins register their frontend permission codes in Go — [todo file](.planning/todos/pending/2026-10-17-plugin-registered-frontend-permissions.md)
|
||||
- [2026-10-19] [sm-oauthserver-plugin] Record PHP OIDC parity against WavePath before the OIDC cutover — [todo file](.planning/todos/pending/2026-10-19-record-php-oidc-parity-against-wavepath.md)
|
||||
- [2026-10-20] [consumers] Move grzybyfunkcjonalne to NUXT_CACHE_PURGE_TOKEN and the new layers before the NUXT_SITE_CACHE_* names are dropped — [todo file](.planning/todos/pending/2026-10-20-rename-site-cache-purge-env-in-consumers.md)
|
||||
- [2026-10-21] [framework] Quiet plugin boot logs on console commands like --help — [todo file](.planning/todos/pending/2026-10-21-quiet-plugin-boot-logs-on-console-commands.md) — Needs TBD. Options, possibly combined:.
|
||||
- [2026-10-22] [framework] Console review info findings (IN-01..IN-13) and the delegate signal exit code — [todo file](.planning/todos/pending/2026-10-22-console-review-info-findings.md)
|
||||
- [2026-10-22] [sm-user-plugin] User login ignores is_activated — [todo file](.planning/todos/pending/2026-10-22-user-login-ignores-is-activated.md)
|
||||
- [2026-10-22] [sm-user-plugin] User login lockout and same-second cutoff — [todo file](.planning/todos/pending/2026-10-22-user-login-lockout-and-same-second-cutoff.md)
|
||||
- [2026-10-22] [sm-user-plugin] User login opt-in session cookie — [todo file](.planning/todos/pending/2026-10-22-user-login-opt-in-session-cookie.md)
|
||||
- [2026-10-22] [sm-user-plugin] User logout blacklist missing under serve — [todo file](.planning/todos/pending/2026-10-22-user-logout-blacklist-missing-under-serve.md)
|
||||
- [2026-10-22] [user-plugin] User plugin logout leaves the session cookie valid for up to 60 minutes — [todo file](.planning/todos/pending/2026-10-22-user-logout-cookie-stays-valid.md)
|
||||
- [2026-10-22] [sm-user-plugin] User password routes 404 switch — [todo file](.planning/todos/pending/2026-10-22-user-password-routes-404-switch.md)
|
||||
- [2026-10-22] [sm-user-plugin] User session routes ignore the cookie — [todo file](.planning/todos/pending/2026-10-22-user-session-routes-ignore-cookie.md)
|
||||
- [unknown] [cabana / admin] Audit-log admin action runs with the admin id (toolbar actions) — [todo file](.planning/todos/pending/admin-action-audit-log.md)
|
||||
- [unknown] [summercms.go bonfire] Reject duplicate console command names in bonfire.NewRoot — [todo file](.planning/todos/pending/bonfire-duplicate-command-names.md)
|
||||
- [unknown] [sm-faq-plugin] Port the FAQ plugin as sm-faq-plugin so FAQs become editable — [todo file](.planning/todos/pending/faq-plugin-port.md)
|
||||
- [2026-10-09] [plugins] Expose author subtitle, description and avatar on the Journal author API — [todo file](.planning/todos/pending/journal-author-profile-fields.md)
|
||||
- [unknown] [summercms.go lagoon] lagoon README registers an after-commit GORM callback that can sort after the flush — [todo file](.planning/todos/pending/lagoon-readme-after-commit-callback-order.md)
|
||||
- [unknown] [sm-user-plugin mail] Port MailBlocker and the block-mail checkbox with the mailing work — [todo file](.planning/todos/pending/mailblocker-with-mailing.md)
|
||||
- [unknown] [sm-menu-plugin] Port the menu plugin as sm-menu-plugin so blog menus become editable — [todo file](.planning/todos/pending/menu-plugin-port.md)
|
||||
- [2026-10-21] [sm-user-plugin] Let password-less social accounts set a first password through change-password — [todo file](.planning/todos/pending/oauth-password-bootstrap-change-password.md)
|
||||
- [unknown] [summercms.go/modules/*/README.md, internal/docsite] Convert module README Go code blocks to compiled src= references — [todo file](.planning/todos/pending/readme-go-fences-src.md)
|
||||
- [unknown] [../fonoteka.go/README.md] Refresh fonoteka.go README — short status plus migrate/serve/login — [todo file](.planning/todos/pending/refresh-fonoteka-readme.md)
|
||||
- [unknown] [cabana] Show the administrators holding a role on the Roles screen — [todo file](.planning/todos/pending/roles-screen-holders-list.md)
|
||||
- [unknown] [summercms.go internal/build] make:admin-controller output does not boot and names the model after the controller — [todo file](.planning/todos/pending/scaffold-admin-controller-incomplete.md)
|
||||
- [unknown] [summercms.go internal/build] Scaffolded files carry a DO NOT EDIT header, and make:command cannot reach the application — [todo file](.planning/todos/pending/scaffold-generated-header-and-command-deps.md)
|
||||
- [unknown] [summercms.go internal/build] make:model and make:migration in the same second produce migrations that run out of order — [todo file](.planning/todos/pending/scaffold-same-second-migration-order.md)
|
||||
- [2026-10-12] [framework] Purge the Nuxt site cache on menu and FAQ admin saves (framework-level purge service) — [todo file](.planning/todos/pending/site-cache-purge-for-menu-and-faq.md)
|
||||
- [unknown] [sm-sitemap-plugin] Port the sitemap plugin 1:1 as sm-sitemap-plugin for the next blog project — [todo file](.planning/todos/pending/sitemap-plugin-port.md)
|
||||
- [unknown] [sm-sitemap-plugin] Sitemap - replace the browser XSLT view before Chromium drops XSLT — [todo file](.planning/todos/pending/sitemap-xslt-browser-deprecation.md)
|
||||
- [unknown] [sm-user-plugin] Let the sm-user-plugin console commands open their own database — [todo file](.planning/todos/pending/user-plugin-commands-open-database.md)
|
||||
|
||||
### Blockers/Concerns
|
||||
|
||||
|
||||
107
.planning/notes/2026-10-22-external-user-plugin-escalations.md
Normal file
107
.planning/notes/2026-10-22-external-user-plugin-escalations.md
Normal file
@@ -0,0 +1,107 @@
|
||||
---
|
||||
title: External user-plugin escalations
|
||||
date: 2026-10-22
|
||||
context: /msd-explore of an external app's "escalate to summercms devs" list. Six /msd-quick tasks; send this reply when they are all done.
|
||||
g15office: bhoyg8z4
|
||||
---
|
||||
|
||||
# External user-plugin escalations
|
||||
|
||||
Reviewed 2026-10-22. The list is `golem15.user` (sm-user-plugin), not cabana admin auth. Phase 19.6 (2FA) is not mid-flight.
|
||||
|
||||
**How to use this file.** Each quick fills its row in the tracker and the matching paragraph under [Reply to send](#reply-to-send). When all six are done, send that section as-is. Do not send until then.
|
||||
|
||||
**Do not run the six quicks in parallel on one working tree.** They share `sm-user-plugin` (`api_controller.go`, `plugin.go`, `routes.go`, README). Sequential fresh sessions.
|
||||
|
||||
## Tracker
|
||||
|
||||
| # | Their label | Verdict | Quick / todo | g15office | Status | Shipped |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 1 | G2 login ignores `is_activated` | Fix | [2026-10-22-user-login-ignores-is-activated.md](../todos/pending/2026-10-22-user-login-ignores-is-activated.md) | 3r4or1ex | open | |
|
||||
| 2 | G3 cookie-only 401 | Fix remaining four; logout already done | [2026-10-22-user-session-routes-ignore-cookie.md](../todos/pending/2026-10-22-user-session-routes-ignore-cookie.md) | kkknstxl | open | |
|
||||
| 3 | G4 logout blacklist under `serve` | Fix | [2026-10-22-user-logout-blacklist-missing-under-serve.md](../todos/pending/2026-10-22-user-logout-blacklist-missing-under-serve.md) | ud0gficu | open | |
|
||||
| 4 | G6 `/update` email without proof | Decline | — | — | declined | n/a |
|
||||
| 5 | G7 Users form has no role field | Decline | — | — | declined | n/a |
|
||||
| 6 | G8 API token guard ignores `tokens_valid_after` | Decline | — | — | declined | n/a |
|
||||
| 7 | Cookie option on `/login` | Feature | [2026-10-22-user-login-opt-in-session-cookie.md](../todos/pending/2026-10-22-user-login-opt-in-session-cookie.md) | strnsccy | open | |
|
||||
| 8 | Password-routes 404 switch | Feature | [2026-10-22-user-password-routes-404-switch.md](../todos/pending/2026-10-22-user-password-routes-404-switch.md) | h7yrafgd | open | |
|
||||
| 9 | Lockout keyed on `RemoteAddr` | Fix | with 10: [2026-10-22-user-login-lockout-and-same-second-cutoff.md](../todos/pending/2026-10-22-user-login-lockout-and-same-second-cutoff.md) | sniewiif | open | |
|
||||
| 10 | Same-second token refusal | Fix | with 9, same todo | sniewiif | open | |
|
||||
|
||||
Status values: `open` · `done` · `declined`. Shipped: plugin version (and framework tag if needed).
|
||||
|
||||
## Six invocations (fresh session each, sequential)
|
||||
|
||||
```
|
||||
/msd-quick --validate Password /login must refuse is_activated=false when golem15.user.activation.require_activation is true (default), same opaque invalid-login body as a bad password. Do not change deleted_at restore-on-login. See .planning/todos/pending/2026-10-22-user-login-ignores-is-activated.md and .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
|
||||
/msd-quick --validate /fetch /refresh /update /change-password must accept the token/auth_token cookies the same way jwt.auth and EndSession already do. Logout is already fixed. See .planning/todos/pending/2026-10-22-user-session-routes-ignore-cookie.md and .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
|
||||
/msd-quick --discuss --validate Under serve, user-plugin Boot runs before lagoon.Publish so the jwt blacklist is never wired and logout does not revoke. Wire BlacklistStore and RegisterHooks through lagoon.OnDatabase; test Activate-then-Publish. See .planning/todos/pending/2026-10-22-user-logout-blacklist-missing-under-serve.md and .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
|
||||
/msd-quick --validate User-plugin /login throttle must use surf.ClientIP (trusted proxies), not RemoteAddr. Remaining tokens_valid_after writers (reset-password, admin password set) must Truncate(time.Second) like RevokeUserAccess so a same-second JWT iat is not refused. See .planning/todos/pending/2026-10-22-user-login-lockout-and-same-second-cutoff.md and .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
|
||||
/msd-quick --discuss --validate Opt-in HttpOnly session cookie on user-plugin POST /login, following sociallogin token_cookie / bouncer.LoadCookieConfig. Default off so existing body-token clients stay unchanged. See .planning/todos/pending/2026-10-22-user-login-opt-in-session-cookie.md and .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
|
||||
/msd-quick --validate User-plugin config switch that does not register POST /login /forgot-password /reset-password so they 404. Default keeps today’s routes. No framework route-wrap seam. See .planning/todos/pending/2026-10-22-user-password-routes-404-switch.md and .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
```
|
||||
|
||||
Suggested order if you care about risk: G4 (serve blacklist), then G3, G2, 9+10, then the two features. Any order is fine if you do not overlap on the same plugin files.
|
||||
|
||||
## Verdicts (kept for the sessions)
|
||||
|
||||
- **G2.** `require_activation` is documented and `/register` honors it; `/login` does not. Deactivation (`deleted_at`) stays reversible.
|
||||
- **G3.** Logout already reads cookies (`358ee5d`). `/fetch`, `/refresh`, `/update`, `/change-password` still use `bearerToken()` / a cookie-less guard.
|
||||
- **G4.** `Boot` before `lagoon.Publish`. Tests publish first and hide it. Seam is `lagoon.OnDatabase`. `RegisterHooks` has the same hole.
|
||||
- **G6.** D-27: email change clears `email_verified_at` and does not write a new proof. A confirm-the-new-inbox flow would be a later feature.
|
||||
- **G7.** Stock form omission was a 12.1 decision. Stale `organisation_role` is already cleared (`ClearOnChange`, v0.1.8). Apps extend the form.
|
||||
- **G8.** Personal tokens die via `revoked_at`. `RevokeUserAccess` already sets that on deactivate/ban. Do not teach the API-token guard a JWT cutoff. Password reset killing API tokens would be a separate, explicit revoke.
|
||||
- **7.** Sociallogin already has `token_cookie` for OAuth. Password `/login` does not set a cookie.
|
||||
- **8.** Plugin config; no framework wrap.
|
||||
- **9.** `/login` uses `RemoteAddr`; `/register` already uses `surf.ClientIP`.
|
||||
- **10.** `RevokeUserAccess` already truncates. Reset-password and the admin password set still stamp `time.Now()`.
|
||||
|
||||
## Reply to send
|
||||
|
||||
Copy from here when the tracker is complete. Replace every `TODO` as a quick lands. Declined items are already written.
|
||||
|
||||
```
|
||||
Thanks for the escalation list. We reviewed the current golem15.user plugin (and the framework boot order). Here is what we will ship, what is already true, and what we are not changing.
|
||||
|
||||
G2 — login ignores is_activated
|
||||
Agreed. require_activation defaults to true and /register already withholds a token in admin mode; /login did not check is_activated. That is not the same as deactivation (deleted_at), which stays self-service: a later password login still restores the account.
|
||||
TODO: shipped in sm-user-plugin vX.Y.Z — inactive accounts get the same opaque invalid-login response as a bad password when require_activation is true.
|
||||
|
||||
G3 — some routes ignore the cookie
|
||||
Partly already fixed, rest agreed. POST /logout already accepts the token/auth_token cookies (and blacklists every JWT the request carries). GET /fetch, POST /refresh, POST /update and POST /change-password still required the Authorization header.
|
||||
TODO: shipped in sm-user-plugin vX.Y.Z — those four accept the same cookies as jwt.auth.
|
||||
|
||||
G4 — logout does not blacklist under serve
|
||||
Agreed. Plugin Boot runs before serve publishes the database, so the in-process blacklist store was never wired in production (the jwt_blacklist table exists; nothing wrote to it). Tests hid this because they publish the DB before Boot.
|
||||
TODO: shipped in sm-user-plugin vX.Y.Z — logout under serve blacklists the jti; the next jwt.auth request with that token is 401.
|
||||
|
||||
G6 — /update changes email without proof
|
||||
As designed, not a bug. A new address clears email_verified_at and stays unproven until the owner completes an activation code or a reset-password. We are not adding a “confirm the new inbox before the change sticks” flow in this round. Say if you need that as a later feature.
|
||||
|
||||
G7 — stock Users form has no role field
|
||||
As designed. organisation_role is intentionally off the stock form (an app extends the form). v0.1.8 already clears organisation_role when the organisation changes (members manager and the Users form). No further stock-form change.
|
||||
|
||||
G8 — API token guard ignores tokens_valid_after
|
||||
As designed. Personal API tokens are revoked through revoked_at, not the JWT cutoff. Deactivate and ban already revoke those rows (RevokeUserAccess). A password reset is not supposed to kill API tokens; if you want that, say so and we will revoke the rows, not mix the two mechanisms.
|
||||
|
||||
Cookie on /login
|
||||
Agreed as an opt-in. Sociallogin already has token_cookie for OAuth complete; password /login will grow the same kind of flag (default off, so body-token clients stay unchanged).
|
||||
TODO: shipped in sm-user-plugin vX.Y.Z — config key <name>, HttpOnly cookie via bouncer.LoadCookieConfig.
|
||||
|
||||
Password-routes switch
|
||||
Agreed, in the user plugin, not as a framework “wrap another plugin’s routes” seam. A config key will stop registering /login, /forgot-password and /reset-password so they 404.
|
||||
TODO: shipped in sm-user-plugin vX.Y.Z — config key <name>.
|
||||
|
||||
Login lockout keyed on RemoteAddr
|
||||
Agreed. /login used RemoteAddr; /register already uses the trusted-proxy client address. Behind a reverse proxy five failures locked everyone.
|
||||
TODO: shipped in sm-user-plugin vX.Y.Z — /login throttle uses surf.ClientIP.
|
||||
|
||||
Same-second token refusal
|
||||
Agreed. JWT iat is whole seconds; some tokens_valid_after writers stamped sub-second time. RevokeUserAccess already truncates. Reset-password and the admin password set will too.
|
||||
TODO: shipped in sm-user-plugin vX.Y.Z — a token minted in the same second as a reset is accepted.
|
||||
```
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
created: 2026-10-22T20:59:59+02:00
|
||||
title: User login ignores is_activated
|
||||
area: sm-user-plugin
|
||||
severity: major
|
||||
g15office: 3r4or1ex
|
||||
parent: bhoyg8z4
|
||||
invoke: /msd-quick --validate
|
||||
files:
|
||||
- ../sm-demo-app/plugins/golem15/user/controllers/api_controller.go
|
||||
- ../sm-demo-app/plugins/golem15/user/controllers/registration.go
|
||||
- ../sm-demo-app/plugins/golem15/user/session_test.go
|
||||
- ../sm-demo-app/plugins/golem15/user/README.md
|
||||
- .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
External app G2: password login ignores `is_activated`. An account set to not activated still gets a token.
|
||||
|
||||
Confirmed 2026-10-22 (`/msd-explore`). `Login` (`controllers/api_controller.go`) checks throttle, password, and restores `deleted_at` (deactivation is self-service). It never reads `IsActivated`. `golem15.user.activation.require_activation` defaults to true and is documented as “must be activated before login”. `/register` already withholds the token in `admin` mode when `require` is true. Admin-created users start inactive (`FormBeforeCreate`) and can still log in if they have a password.
|
||||
|
||||
This is not deactivation (`deleted_at`). Do not change the restore-on-login behaviour.
|
||||
|
||||
## Solution
|
||||
|
||||
When `require_activation` is true, `/login` refuses an inactive account with the same opaque invalid-login body as a bad password. When it is false, login still works. Pin with a test: inactive + require true → 401, no token; inactive + require false → 200; activated → 200; deactivated (`deleted_at`) still restores. README: activation gates login; deactivation does not.
|
||||
|
||||
After the commit, fill G2 in `.planning/notes/2026-10-22-external-user-plugin-escalations.md` (version + one-line reply).
|
||||
|
||||
## Invoke
|
||||
|
||||
```
|
||||
/msd-quick --validate Password /login must refuse is_activated=false when golem15.user.activation.require_activation is true (default), same opaque invalid-login body as a bad password. Do not change deleted_at restore-on-login. See .planning/todos/pending/2026-10-22-user-login-ignores-is-activated.md and .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
created: 2026-10-22T20:59:59+02:00
|
||||
title: User login lockout and same-second cutoff
|
||||
area: sm-user-plugin
|
||||
severity: major
|
||||
g15office: sniewiif
|
||||
parent: bhoyg8z4
|
||||
invoke: /msd-quick --validate
|
||||
files:
|
||||
- ../sm-demo-app/plugins/golem15/user/controllers/api_controller.go
|
||||
- ../sm-demo-app/plugins/golem15/user/controllers/users_admin_controller.go
|
||||
- ../sm-demo-app/plugins/golem15/user/classes/admin_actions.go
|
||||
- modules/bouncer/jwt.go
|
||||
- modules/bouncer/mint.go
|
||||
- .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
Two convention holes, one quick (external items 9 and 10).
|
||||
|
||||
**Lockout.** `/login` throttle keys on the host of `r.RemoteAddr` (`remoteIP`). `/register` and the `user-api` bucket already use `surf.ClientIP` with trusted proxies. Behind nginx/Caddy every visitor shares one address, so five wrong passwords lock the account for everyone for 15 minutes.
|
||||
|
||||
**Same-second cutoff.** JWT `iat` is whole seconds (`jwt.NewNumericDate` in `bouncer.Mint`). `issuedBeforeCutoff` is `iat.Before(cutoff)`. `RevokeUserAccess` already stamps `time.Now().Truncate(time.Second)`. `/reset-password` (`tokens_valid_after: now`) and the admin Users password set (`time.Now()` into `TokensValidAfter`) do not, so a token minted in that same second is refused intermittently.
|
||||
|
||||
## Solution
|
||||
|
||||
`/login` throttle IP = `surf.ClientIP(r, surf.TrustedProxies(app.Config))`, same as `/register`. Every `tokens_valid_after` writer uses whole seconds (truncate down, matching `RevokeUserAccess`; do not switch the comparison to a new rule in bouncer unless a writer cannot be found). Tests: X-Forwarded-For behind a trusted proxy is the throttle key; a reset then an immediate login in the same second succeeds.
|
||||
|
||||
After the commit, fill items 9 and 10 in `.planning/notes/2026-10-22-external-user-plugin-escalations.md`.
|
||||
|
||||
## Invoke
|
||||
|
||||
```
|
||||
/msd-quick --validate User-plugin /login throttle must use surf.ClientIP (trusted proxies), not RemoteAddr. Remaining tokens_valid_after writers (reset-password, admin password set) must Truncate(time.Second) like RevokeUserAccess so a same-second JWT iat is not refused. See .planning/todos/pending/2026-10-22-user-login-lockout-and-same-second-cutoff.md and .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
```
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
created: 2026-10-22T20:59:59+02:00
|
||||
title: User login opt-in session cookie
|
||||
area: sm-user-plugin
|
||||
severity: minor
|
||||
g15office: strnsccy
|
||||
parent: bhoyg8z4
|
||||
invoke: /msd-quick --discuss --validate
|
||||
files:
|
||||
- ../sm-demo-app/plugins/golem15/user/controllers/api_controller.go
|
||||
- ../sm-demo-app/plugins/golem15/user/config/config.yaml
|
||||
- ../sm-demo-app/plugins/golem15/user/README.md
|
||||
- modules/bouncer/cookie.go
|
||||
- .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
External app item 7: `/login` cannot set the session cookie. It returns the token only in the response body. An option to set the HttpOnly cookie would remove their Nuxt bridge.
|
||||
|
||||
Sociallogin already has `golem15.sociallogin.token_cookie` for OAuth complete (19.5 D-09): `bouncer.LoadCookieConfig` + `TokenCookie`, and the body may omit `token`. Password `/login` never calls `SetCookie`. Logout already forgets that cookie.
|
||||
|
||||
Phase 19.6 (2FA) is not mid-flight; `Login()` is free to change.
|
||||
|
||||
## Solution
|
||||
|
||||
Opt-in config on the user plugin (decide the key in `--discuss`: reuse a shared `auth.cookie` / bouncer cookie config, or a `golem15.user.jwt.token_cookie` twin of sociallogin). Default off, body still carries `token` unless the discuss decision matches sociallogin’s “cookie set, token omitted”. Cookie attributes from `bouncer.LoadCookieConfig`. No cookie when the flag is off (current contract).
|
||||
|
||||
After the commit, fill item 7 in `.planning/notes/2026-10-22-external-user-plugin-escalations.md`.
|
||||
|
||||
## Invoke
|
||||
|
||||
```
|
||||
/msd-quick --discuss --validate Opt-in HttpOnly session cookie on user-plugin POST /login, following sociallogin token_cookie / bouncer.LoadCookieConfig. Default off so existing body-token clients stay unchanged. See .planning/todos/pending/2026-10-22-user-login-opt-in-session-cookie.md and .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
```
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
created: 2026-10-22T20:59:59+02:00
|
||||
title: User logout blacklist missing under serve
|
||||
area: sm-user-plugin
|
||||
severity: major
|
||||
g15office: ud0gficu
|
||||
parent: bhoyg8z4
|
||||
invoke: /msd-quick --discuss --validate
|
||||
files:
|
||||
- ../sm-demo-app/plugins/golem15/user/plugin.go
|
||||
- ../sm-demo-app/plugins/golem15/user/classes/session_user.go
|
||||
- ../sm-demo-app/plugins/golem15/user/logout_test.go
|
||||
- modules/lagoon/ondatabase.go
|
||||
- .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
External app G4: logout does not blacklist under `serve`. The plugin's `Boot` runs before the database is published, so the blacklist table stays empty and the token keeps working after logout.
|
||||
|
||||
Confirmed. `party.Activate` (and every plugin `Boot`) runs in generated `main` before `surf.ServeCommand` calls `lagoon.Publish`. User plugin `Boot` looks up `*sql.DB` / `*gorm.DB`, finds none, registers the jwt guard with `bl=nil`, and never publishes `bouncer.BlacklistStore`. `EndSession` looks the store up at request time and writes nothing. Cabana avoids this by building the store in `cabana.Activate` during `BuildRouter`, after publish. The `jwt_blacklist` table exists (migrations); nothing is wired to write it. Tests hide the bug because they publish the DB before `Boot`. `RegisterHooks` has the same Boot-before-DB hole.
|
||||
|
||||
## Solution
|
||||
|
||||
Use `lagoon.OnDatabase` from `Boot` (the documented seam: lighthouse and beachcomber already do). On publish: install `RegisterHooks`, publish `bouncer.NewPostgresBlacklist(sqlDB, "jwt_blacklist")`, and give the jwt guard that store (re-register, or a lazy store that `Lookup`s at request time — decide in `--discuss`). A test must boot like `serve`: Activate/Boot, then Publish, then login + logout + jwt.auth 401 and a blacklist row. A test that publishes first is not enough.
|
||||
|
||||
After the commit, fill G4 in `.planning/notes/2026-10-22-external-user-plugin-escalations.md`.
|
||||
|
||||
## Invoke
|
||||
|
||||
```
|
||||
/msd-quick --discuss --validate Under serve, user-plugin Boot runs before lagoon.Publish so the jwt blacklist is never wired and logout does not revoke. Wire BlacklistStore and RegisterHooks through lagoon.OnDatabase; test Activate-then-Publish. See .planning/todos/pending/2026-10-22-user-logout-blacklist-missing-under-serve.md and .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
```
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
created: 2026-10-22T20:59:59+02:00
|
||||
title: User password routes 404 switch
|
||||
area: sm-user-plugin
|
||||
severity: minor
|
||||
g15office: h7yrafgd
|
||||
parent: bhoyg8z4
|
||||
invoke: /msd-quick --validate
|
||||
files:
|
||||
- ../sm-demo-app/plugins/golem15/user/routes.go
|
||||
- ../sm-demo-app/plugins/golem15/user/config/config.yaml
|
||||
- ../sm-demo-app/plugins/golem15/user/README.md
|
||||
- .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
External app item 8: they want a config switch that makes `/login`, `/forgot-password` and `/reset-password` answer 404. They said SummerCMS has no seam for one plugin to wrap another plugin’s routes.
|
||||
|
||||
They do not need a framework wrap. The user plugin owns those routes and can skip registering them.
|
||||
|
||||
## Solution
|
||||
|
||||
A user-plugin config key (name it in the plan; default keeps today’s routes). When on, do not register `POST /login`, `/forgot-password` and `/reset-password` so they 404 like any unknown path. `/register`, `/activate*`, `/fetch` and the rest stay. Document the key. A test boots both values and checks the route table (or a 404 vs 401/200 probe). No change in `pact` / `surf`.
|
||||
|
||||
After the commit, fill item 8 in `.planning/notes/2026-10-22-external-user-plugin-escalations.md`.
|
||||
|
||||
## Invoke
|
||||
|
||||
```
|
||||
/msd-quick --validate User-plugin config switch that does not register POST /login /forgot-password /reset-password so they 404. Default keeps today’s routes. No framework route-wrap seam. See .planning/todos/pending/2026-10-22-user-password-routes-404-switch.md and .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
```
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
created: 2026-10-22T20:59:59+02:00
|
||||
title: User session routes ignore the cookie
|
||||
area: sm-user-plugin
|
||||
severity: major
|
||||
g15office: kkknstxl
|
||||
parent: bhoyg8z4
|
||||
invoke: /msd-quick --validate
|
||||
files:
|
||||
- ../sm-demo-app/plugins/golem15/user/controllers/api_controller.go
|
||||
- ../sm-demo-app/plugins/golem15/user/classes/session_user.go
|
||||
- ../sm-demo-app/plugins/golem15/user/logout_test.go
|
||||
- ../sm-demo-app/plugins/golem15/user/README.md
|
||||
- .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
External app G3: `/fetch`, `/refresh`, `/logout`, `/update` and `/change-password` answer 401 to a caller with only the cookie, while `jwt.auth` routes accept it.
|
||||
|
||||
Partly stale. `Logout` / `EndSession` already read bearer then `token` / `auth_token` cookies (sm-user-plugin `358ee5d`, todo `2026-10-22-user-logout-cookie-stays-valid.md`). The remaining four still go through `bearerToken()` and/or `NewJWTGuard(...)` with no cookie names. `authenticate` even fails closed on an empty bearer before it builds the guard.
|
||||
|
||||
## Solution
|
||||
|
||||
`/fetch`, `/refresh`, `/update` and `/change-password` extract the session JWT the same way the jwt guard and `EndSession` do (bearer, then `token`, then `auth_token`). Reuse `classes.sessionToken` / `sessionTokens` rather than a third extractor. A cookie-only caller that `jwt.auth` accepts must get 200 on those four. Logout stays as it is. README JWT-sessions bullet names the four.
|
||||
|
||||
After the commit, fill G3 in `.planning/notes/2026-10-22-external-user-plugin-escalations.md`.
|
||||
|
||||
## Invoke
|
||||
|
||||
```
|
||||
/msd-quick --validate /fetch /refresh /update /change-password must accept the token/auth_token cookies the same way jwt.auth and EndSession already do. Logout is already fixed. See .planning/todos/pending/2026-10-22-user-session-routes-ignore-cookie.md and .planning/notes/2026-10-22-external-user-plugin-escalations.md
|
||||
```
|
||||
Reference in New Issue
Block a user