docs: tune item 7 cookie quick after 9-10

This commit is contained in:
Jakub Zych
2026-10-22 23:14:09 +02:00
parent e9ebf93d36
commit ec69f547b8
2 changed files with 13 additions and 8 deletions

View File

@@ -43,12 +43,12 @@ Status values: `open` · `done` · `declined`. Shipped: local commit now; replac
/msd-quick --validate User-plugin /login throttle must use surf.ClientIP (trusted proxies), not RemoteAddr. Leave exported RemoteIP as the RemoteAddr helper. Remaining tokens_valid_after writers (reset-password, admin password set) must Truncate(time.Second) like RevokeUserAccess; leave change-password and bouncer's comparison alone. 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. Discuss: a cookie-carried /refresh must rotate that cookie (G3 left body-only); keep SameSite=Lax, no new CSRF token. 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 --discuss --validate Opt-in HttpOnly session cookie on user-plugin POST /login: golem15.user.jwt.token_cookie (default off), attributes from auth.cookie via bouncer.LoadCookieConfig, omit body token when the cookie is set (sociallogin writeSession). Cookie-carried POST /refresh must rotate that cookie (G3 left body-only); bearer-only refresh stays body-only. SameSite=Lax, no CSRF. Base a484d0e. 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.
Suggested order if you care about risk: G4 (serve blacklist), then G3, G2, 9+10, then the two features. Bugs are done locally; remaining are item 7 (cookie) then item 8 (password-routes 404). Do not overlap on the same plugin files.
## Verdicts (kept for the sessions)
@@ -58,7 +58,7 @@ Suggested order if you care about risk: G4 (serve blacklist), then G3, G2, 9+10,
- **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. `--discuss` must decide cookie rotation on `/refresh` (G3 left the body-only rotate) and keep SameSite=Lax (no new CSRF token).
- **7.** Sociallogin already has `token_cookie` for OAuth. Password `/login` does not set a cookie. Next quick: `golem15.user.jwt.token_cookie` twin (default off); when on, omit body `token` like sociallogin `writeSession`; cookie-carried `/refresh` must rotate (G3 leftover); SameSite=Lax, no CSRF.
- **8.** Plugin config; no framework wrap.
- **9.** Done locally (`b6f8055`). /login keys the throttle on surf.ClientIP with http.trusted_proxies (the /register expression); RemoteIP stays the RemoteAddr helper; without trusted proxies X-Forwarded-For is ignored.
- **10.** Done locally (`a484d0e`). /reset-password and the admin password set truncate the cutoff to whole seconds like RevokeUserAccess; change-password and bouncer unchanged.

View File

@@ -24,18 +24,23 @@ 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).
Opt-in config on the user plugin. Cookie attributes stay on `auth.cookie.*` via `bouncer.LoadCookieConfig` (logout already forgets that cookie). The set-on-login flag is a sociallogin twin, not a second attributes block.
`--discuss` also locks: (1) a cookie-carried `/refresh` must rewrite the session cookie (G3 left body-only rotate, which is unusable for an HttpOnly cookie the browser cannot store); follow `bouncer.CookieRefreshHandler`. (2) keep SameSite=Lax and do not add a CSRF token — that is the existing `jwt.auth` POST story (`/tokens`, `/logout`). Do not switch the cookie to SameSite=None.
`--discuss` should confirm these defaults, not reopen them unless a test or the PHP contract forbids them:
After the commit, fill item 7 in `.planning/notes/2026-10-22-external-user-plugin-escalations.md`.
1. Key: `golem15.user.jwt.token_cookie` (default `false`). `auth.cookie.*` is name/path/domain/SameSite/Secure only.
2. When on, `/login` writes `LoadCookieConfig.TokenCookie` (Max-Age = refresh TTL) and **omits `token` from the body**, same as sociallogin `writeSession`. `user` stays in the body. When off, today's `{token, user}` and no `SetCookie`.
3. A cookie-carried `POST /refresh` **must** rewrite that cookie (`bouncer.CookieRefreshHandler` / `TokenCookie`). G3 (`8e4b863`) left body-only rotate; an HttpOnly cookie the browser cannot store would keep the blacklisted jti. Bearer-only refresh stays body-only.
4. Keep SameSite=Lax. No CSRF token. Do not switch to SameSite=None.
Base is sm-user-plugin `a484d0e` (items 9+10). Hold push/tag for SummerCMS v0.5.0. After the commit, fill item 7 in `.planning/notes/2026-10-22-external-user-plugin-escalations.md` like G2–G4 and 9–10 (`done (unreleased)`, ships in v0.5.0, name the config key). Leave the todo in pending with an Outcome.
## Note from G3 (2026-10-22)
Since sm-user-plugin `8e4b863`, `POST /refresh` accepts the `token` and `auth_token` cookies, answers the rotated token in the body only and writes no cookie. When `/login` can set the HttpOnly cookie, decide in `--discuss` whether a cookie-carried `/refresh` rotates that cookie, as `bouncer.CookieRefreshHandler` does. Otherwise the blacklisted old token stays in an HttpOnly cookie the browser cannot rewrite.
Since sm-user-plugin `8e4b863`, `POST /refresh` accepts the `token` and `auth_token` cookies, answers the rotated token in the body only and writes no cookie. Cookie rotation is no longer optional: when the request arrived on a session cookie, rewrite it.
## Invoke
```
/msd-quick --discuss --validate Opt-in HttpOnly session cookie on user-plugin POST /login, following sociallogin token_cookie / bouncer.LoadCookieConfig. Default off. Discuss: a cookie-carried /refresh must rotate that cookie (G3 left body-only); keep SameSite=Lax, no new CSRF token. 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 --discuss --validate Opt-in HttpOnly session cookie on user-plugin POST /login: golem15.user.jwt.token_cookie (default off), attributes from auth.cookie via bouncer.LoadCookieConfig, omit body token when the cookie is set (sociallogin writeSession). Cookie-carried POST /refresh must rotate that cookie (G3 left body-only); bearer-only refresh stays body-only. SameSite=Lax, no CSRF. Base a484d0e. 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
```