Part of #589.
CONTEXT
Today iamnim returns the session to a downstream service as ?token= appended to the redirect URI (redirectAfterLogin, internal/web/server.go:445-455), and also accepts ?token= on API calls (docs/AUTHENTICATION_AND_AUTHORIZATION.md:20-29). The session therefore lands in server access logs, Referer headers, and browser history. The new hydraorganization dashboard must not ship on this pattern. This issue adds a one-time code handoff.
SCOPE
- On login with a redirect_uri, redirect to redirect_uri + ?code= instead of ?token=. The code is random, short-lived (60 seconds), single-use, and maps server-side to the session ID. Store codes in the same in-memory session layer; they die with the process, which is acceptable (the user just logs in again).
- Add POST /api/session/exchange: body {"code": "..."}, response {"session": ""}. Consume the code on first use. Return 401 on unknown, expired, or reused codes. Keep the existing redirect-target allowlist (server.go:465-472) unchanged.
- Backward compatibility: hydramancer still uses ?token=. Keep the old behavior behind a per-redirect opt-in so nothing breaks: append ?code= when the login request carries code=1 (or an allowlist entry marks the target as code-capable), else keep ?token=. Document the migration path for hydramancer as a follow-up issue.
- API ?token= acceptance: do not remove it in this issue (hydramancer depends on the current shape), but document in docs/AUTHENTICATION_AND_AUTHORIZATION.md that new consumers must use the cookie/header form only, and file the removal as a follow-up on issues.experiencenet.com.
- Tests: exchange happy path, expiry, reuse rejection, old-flow regression.
ACCEPTANCE CRITERIA
- A downstream service that opts into the code flow never receives a session ID in any URL. Verified by reading the redirect Location and the access log during a test login.
- POST /api/session/exchange returns the session exactly once per code; a second exchange returns 401.
- Codes expire after 60 seconds.
- hydramancer login still works unchanged (regression check).
- docs/AUTHENTICATION_AND_AUTHORIZATION.md describes the code flow and marks ?token= as legacy.
FILES
- /home/claude-user/iamnim/internal/web/server.go (redirectAfterLogin at 445-455, allowlist at 465-472, authenticate choke point)
- /home/claude-user/iamnim/docs/AUTHENTICATION_AND_AUTHORIZATION.md
DEPLOY
Standard iamnim release per docs/runbooks/deploy.md. Tag, push, let CI publish. No manual deploy.