Replace client-controlled redirectUri with server-side redirect intents.
The portal server component validates the ?rd= hostname against
isForwardAuthDomain, stores the URI in a new forward_auth_redirect_intents
table, and passes only an opaque rid (128-bit random, SHA-256 hashed) to
the client. Login endpoints consume the intent atomically (one-time use,
10-minute TTL) and retrieve the stored URI — the client never sends the
redirect URL to any API endpoint.
Security properties:
- Redirect URI is never client-controlled in API requests
- rid is 128-bit random, stored as SHA-256 hash (not reversible from DB)
- Atomic one-time consumption prevents replay
- 10-minute TTL limits attack window for OAuth round-trip
- Immediate deletion after consumption
- Expired intents cleaned up opportunistically
- Hostname validated against registered forward-auth domains before storage
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>