Carries the raw ID token in the session cookie so logout can hand it back
to Keycloak as id_token_hint, letting it skip its own logout-confirmation
prompt -- otherwise a user could cancel out of it and land back in the app
with a Keycloak SSO session but no geniusrun profile (e.g. right after
deleting their account).
Same class of bug as the OIDC callback fix: handleSessionLogout redirected
Keycloak's end-session flow back to BackendURL+"/", which 404s in a
split-origin deployment (the backend serves no "/" route). Flagged as a
known-deferred question in the callback-redirect design spec; fixing it
now that it's been hit in practice.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Now that GENIUSRUN_FRONTEND_URL exists as a separate config value, keeping
the backend's own origin named "PublicBaseURL" invited exactly the kind of
mixup that caused the OIDC callback 404 in the first place. Renamed
consistently: env var, Config.BackendURL, api.SessionConfig.BackendURL.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
handleSessionCallback's redirects (success and all 4 failure branches)
were relative paths, which resolve against the backend's own origin --
broken in this project's own supported split-origin local dev setup,
since the Go backend serves no "/" route at all. Now uses the new
config.Config.FrontendURL (defaults to PublicBaseURL, so no change for
single-origin production deployments).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
resolveUser attaches the session's provisioned geniusrun user (if any) to
request context without blocking; requireProvisionedUser (wired fully in
Task 13) 403s routes that need one. GET /api/session/me now reports
has_profile/display_name so the frontend can show the setup screen.
Four bugs slipped through per-task review since each task only saw its
own diff:
- Logout was a plain <a href> GET against a POST-only backend route, so
it 405'd and never cleared the session cookie or hit Keycloak's
end-session redirect. Now a <form method="post"> with a submit button
styled to match the old link (still a real full-page navigation, not
a fetch, so the Keycloak redirect chain still works).
- Login/logout used origin-relative paths, unreachable from the Vite
dev server (:5173) against the backend (:8080) with no proxy
configured. Both now build their URL from client.ts's now-exported
BASE_URL.
- handleSessionCallback's four failure paths redirected to
/?auth_error=failed with no logging, making a real OIDC failure
undiagnosable in production. Added log.Printf on each failure site.
- handleSessionLogout passed a bare "/" to EndSessionURL; Keycloak
requires post_logout_redirect_uri to be an absolute, registered URL.
Added SessionConfig.PublicBaseURL, wired from cfg.PublicBaseURL in
main.go, and used to build an absolute redirect.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Router() now wraps every route except /health, /session/login, and
/session/callback in a chi group requiring a valid session cookie
(auth.RequireSession). Adds internal/api/session.go with the four
session HTTP handlers (login/callback/logout/me) and SessionConfig.
NewServer takes an auth.Verifier and SessionConfig. Test infra
(doJSON, newTestServer) now mints/attaches a signed session cookie
automatically so the 36 pre-existing tests keep exercising the
already-logged-in path unchanged, plus 8 new tests cover the gating
and session endpoints themselves.