fix: QA cluster — id_token 502, first-login 500, .checkbox theme, vote-read authz - #191
Merged
Conversation
…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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
id_token 502 (P0) — the refresh path re-added the Keycloak
id_tokento the JWT Auth.js encrypts into the session cookie, pushing it past the 3936-byte chunk threshold into two ~4 kBSet-Cookieheaders 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. StripsidTokenon every return path so pre-fix sessions recover without re-login. Ports main's tested fix;auth.callback.test.tsupdated (7/7 pass, incl. two new legacy-strip cases).First-login 500 — auto-
RegisteronNOT_FOUNDhad noUNAVAILABLErescue (WhoAmI already did), and the dashboard loader dereferencedplatformUser!.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..checkboxtheme style —CapabilitiesPanel/PageForm/PhaseFormall useclass="checkbox"with no rule defined, so every switch was a browser-default box ignoring the theme. Ports main's definition.Vote-read authz (HIGH) —
ListVoteCategories/GetVoteCategory/GetVoteusedRequireSubject(admits anonymous). Category entries embed jury emails → a private event's jury roster leaked to anyone who could name its id;GetVotereturned any ballot by id (ballot secrecy). Now require a real user +hackathon:readon the category reads andhackathon:writeonGetVote(matching siblingListVotes).Verification (in devcontainer, off the devenv profile)
go build ./...clean;go test -tags "test unittest" ./internal/service/... ./internal/middleware/...OKvitest run src/auth.callback.test.ts— 7/7 passsvelte-check— 0 errors (5 pre-existing warnings, unrelated files)gofmt -lempty;prettier --checkcleanKnown follow-ups (not in this PR)
vote_service_test.goexists) — worth a Go spec / mutation-manifest entry..checkboxshipped without a computed-style spec, matching main.