Workhorse: Proxy OAuth calls to the IAM Auth Service during gradual rollout
Overview
As part of the gradual rollout plan for OAuth in Protocells (AUTH-011), Workhorse and Rails act as a temporary reverse proxy for OAuth traffic during the migration period. No changes to the HTTP Router are required during the rollout phase — OAuth requests continue to arrive at Rails exactly as today.
Workhorse is responsible for inspecting each incoming OAuth request and deciding whether to handle it locally (Doorkeeper/Rails) or proxy it to the IAM Auth service, based on feature flag state, query parameter presence, and token prefixes.
Routing Logic
Workhorse applies the following routing logic for OAuth endpoints:
| Endpoint | Condition | Action |
|---|---|---|
/.well-known/openid-configuration |
Always | Proxy to IAM Auth service |
/oauth/discovery/keys |
Always | Proxy to IAM Auth service |
/oauth/userinfo |
Always | Handle locally (Rails) |
/oauth/device |
Always | Handle locally (Rails) |
/oauth/authorize, /oauth/authorize_device |
Feature flag enabled for client/percentage or IAM Auth-specific query parameter is present | Proxy to IAM Auth service |
/oauth/authorize, /oauth/authorize_device |
Feature flag not enabled | Handle locally (Doorkeeper) |
/oauth/token, /oauth/revoke, /oauth/introspect |
Token has giat_ prefix |
Proxy to IAM Auth service |
/oauth/token, /oauth/revoke, /oauth/introspect |
No prefix (Doorkeeper token) | Handle locally (Doorkeeper) |
Requirements
- Proxy
/.well-known/openid-configurationunconditionally to the IAM Auth service - Proxy
/oauth/discovery/keysunconditionally to the IAM Auth service - Always handle
/oauth/userinfoand/oauth/devicelocally in Rails - For
/oauth/authorizeand/oauth/authorize_device: proxy to IAM Auth service when the feature flag is enabled for the client/percentage or an IAM Auth-specific query parameter is present; otherwise handle locally via Doorkeeper - For
/oauth/token,/oauth/revoke, and/oauth/introspect: inspect the token/code/refresh_token parameter — proxy to IAM Auth service if it carries thegiat_prefix, otherwise handle locally via Doorkeeper - Use the existing
PreAuthorizeorsend-urlmechanism to call Rails for feature flag checks - Support a new
Authn::OAuthApplicationactor type in Rails feature flags for per-client rollout control - Emit structured log entries for each OAuth request indicating whether it was handled locally or forwarded to the IAM Auth service
- This proxy logic must be cleanly removable after the Phase 5 Cloudflare Origin Rule cutover
Background
The routing decision is non-trivial because OAuth flows are stateful — multiple sequential requests must reach the same service that started the flow. The giat_ prefix on IAM Auth-issued tokens/codes ensures that once a flow starts on IAM Auth, all subsequent requests (token exchanges, revocations) are automatically proxied to IAM Auth without additional feature flag checks.
IAM Auth service issues structured opaque codes with a giat_ prefix:
code=giat_ac_Hn_JMPQas_4diX0q1BruK68BFx0PtxF2x7u6Bb2cUH0.kBMbu366Lq_P8dQVo8uHVMQfWHwwFF45F6_oXmZgdfQDoorkeeper issues opaque codes with no prefix or structure:
code=SplxlOBeZQQYbYS6WxSbIA39dkJm8zChecklist for the MRs to achieve this issue
- MR 1: Workhorse skeleton + structured logging
- MR 2: Token-prefix routing
- MR 3: Rails FF infrastructure
- MR 4: Workhorse FF-gated routing