Skip to content

fix: QA cluster — id_token 502, first-login 500, .checkbox theme, vote-read authz - #191

Merged
caviri merged 4 commits into
developfrom
fix/feedback-20260818
Aug 18, 2026
Merged

fix: QA cluster — id_token 502, first-login 500, .checkbox theme, vote-read authz#191
caviri merged 4 commits into
developfrom
fix/feedback-20260818

Conversation

@caviri

@caviri caviri commented Aug 18, 2026

Copy link
Copy Markdown
Member

Confirmed low-risk cluster from the 2026-08-18 QA feedback (Thibaut, Valerio) cross-referenced with the code audit. Each fix is either ported from main's tested version or matches an established develop pattern.

What's in it

  1. id_token 502 (P0) — the refresh path re-added the Keycloak id_token to the JWT Auth.js encrypts into the session cookie, pushing it past the 3936-byte chunk threshold into two ~4 kB Set-Cookie headers that overflow a proxy's 4 kB buffer. Every response 502'd ~4.5 min into each session (first refresh). Reported twice by Thibaut ("broke a user", "token expiry redirect broken"), reproduced live. Strips idToken on every return path so pre-fix sessions recover without re-login. Ports main's tested fix; auth.callback.test.ts updated (7/7 pass, incl. two new legacy-strip cases).

  2. First-login 500 — auto-Register on NOT_FOUND had no UNAVAILABLE rescue (WhoAmI already did), and the dashboard loader dereferenced platformUser!.id. A backend blip on the first page after login became a bare-TypeError 500. Reported by Thibaut ("register 500, reload fixes it"). Adds the rescue + a 503 that names the symptom. Surgical — does not pull main's broader dashboard rewrite.

  3. .checkbox theme styleCapabilitiesPanel/PageForm/PhaseForm all use class="checkbox" with no rule defined, so every switch was a browser-default box ignoring the theme. Ports main's definition.

  4. Vote-read authz (HIGH)ListVoteCategories/GetVoteCategory/GetVote used RequireSubject (admits anonymous). Category entries embed jury emails → a private event's jury roster leaked to anyone who could name its id; GetVote returned any ballot by id (ballot secrecy). Now require a real user + hackathon:read on the category reads and hackathon:write on GetVote (matching sibling ListVotes).

Verification (in devcontainer, off the devenv profile)

  • go build ./... clean; go test -tags "test unittest" ./internal/service/... ./internal/middleware/... OK
  • vitest run src/auth.callback.test.ts — 7/7 pass
  • svelte-check — 0 errors (5 pre-existing warnings, unrelated files)
  • gofmt -l empty; prettier --check clean

Known follow-ups (not in this PR)

  • D1/D2 have no pinning test yet (no vote_service_test.go exists) — worth a Go spec / mutation-manifest entry.
  • .checkbox shipped without a computed-style spec, matching main.

caviri added 4 commits August 18, 2026 23:59
…king

The refresh path re-added the Keycloak id_token to the JWT that Auth.js
encrypts into the session cookie. Access + refresh tokens alone encrypt to
~3.8 kB; the ~1.2 kB id_token pushed the value past @auth/core's 3936-byte
chunk threshold, splitting it into two ~4 kB Set-Cookie headers that overflow
a reverse proxy's default 4 kB response-header buffer -- so every response
became a 502, roughly 4.5 minutes into each session when the first refresh
fired.

Strip idToken ahead of every return path, including the still-valid branch so
sessions minted before this fix recover without re-login, and stop storing the
refreshed id_token. Nothing reads it. Ports the tested fix from main.
hooks.server.ts leaves locals.platformUser undefined when WhoAmI returns
UNAVAILABLE, but the auto-Register that runs on NOT_FOUND had no such rescue,
and the dashboard loader dereferenced platformUser!.id unconditionally. A
backend that dropped between the two calls, or during the first page after
login, surfaced as a bare TypeError 500 that named nothing.

Wrap the auto-Register in the same UNAVAILABLE rescue WhoAmI already has, and
guard the dashboard loader with a 503 that names the symptom.
CapabilitiesPanel, PageForm and PhaseForm all render class="checkbox", but
nothing defined the rule, so every switch fell back to a browser-default box
that ignored the theme. Adds the appearance:none / accent-fill style from main.
…allers

ListVoteCategories, GetVoteCategory and GetVote used RequireSubject, which
admits the anonymous subject. The vote-category entry mapper embeds jury
members' emails, so anyone who could name a private event's id could read its
jury roster; GetVote returned any voter's ballot by id, breaking ballot
secrecy.

Require a real user and enforce hackathon:read on the category reads
(member-scoped, matching who needs to vote) and hackathon:write on GetVote
(organizer/admin, matching its sibling ListVotes).
@caviri
caviri merged commit ed9b25f into develop Aug 18, 2026
1 check passed
@caviri
caviri deleted the fix/feedback-20260818 branch August 18, 2026 22:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant