preferred_username was being read from the ID token and thrown away after
serving as a display-name fallback. It is the whole identity story: MAS imports
it as the Matrix localpart, so it is also who the player is in the euro economy.
Keep it in the session, and derive @user:server from it.
The session cookie was host-only, so a sign-in on news never reached games.
Widen it with an opt-in web.auth.cookie_domain — but only the session cookie:
the OAuth round-trip cookie pairs with a redirect back to the host that started
the login and stays where it was set. And because the redirect must return to
that host, the redirect_uri is now derived per-request for hosts inside the
cookie domain, with the configured URL as the fallback for anything else — a
Host header we don't own is never echoed into a redirect.
- Route image-validation, Matrix image-download, and wayback/archive.today
fetches through safehttp so feed-controlled URLs can't reach loopback/
RFC1918/cloud-metadata IPs (incl. via redirects).
- Cap feed body size with safehttp.LimitedBody (16 MiB) to prevent OOM.
- Fail closed if matrix.pickle_key is unset/<16 chars; drop the hardcoded
"pete_pickle_key" default that silently weakened E2EE-at-rest.
- Gate !post behind a matrix.admins allowlist; empty = disabled (channel
rooms are public, so empty must not mean anyone).
- Reject /\host open-redirect bypass in post-login safeNext.
- Allowlist http(s) schemes for the Matrix link href; escape JSON embedded
in inline <script> (prefs/sources blobs).
Signed-in users get their preferences (hidden feeds, weather location,
weather toggle) stored server-side keyed by their OIDC subject and synced
across devices. Anonymous visitors keep using browser localStorage, so the
site stays public. First sign-in migrates existing localStorage prefs up.
- config: [web.auth] section (issuer, client_id/secret, redirect, session_secret)
- storage: user_preferences table + Get/PutUserPrefs
- web/auth: OIDC code flow, HMAC-signed session cookie, CSRF state + nonce
- web/prefs_api: GET/PUT /api/preferences (auth-gated, 64KB cap)
- frontend: prefs.js sync layer seeds localStorage from server, pushes on write
- header: sign-in / account control
OIDC discovery is non-fatal at boot: if Authentik is down, Pete serves
anonymously rather than refusing to start.