FM-037 — OAuth Authorize 404 — Three-Layer Routing Mismatch
/oauth/authorize 404 from Forge IDE — path mismatch + dropped returnTo + PublicOnlyRoute race.
| Field | Value |
|---|---|
| Severity | SEV-2 (Forge IDE OAuth flow broken) |
| Category | Auth / Routing |
| Date | 2026-04-04 |
| Status | Archived |
Symptom
Section titled “Symptom”Navigating to https://cmmd.ai/oauth/authorize?client_id=cmmd-forge&... showed a 404 instead of the OAuth authorization flow.
Root cause
Section titled “Root cause”Three compounding bugs:
- Path mismatch: OAuth API endpoints live at
/api/oauth/authorizebut Forge IDE opens/oauth/authorize. SPA catch-all served the React app, which had no route for/oauth/*→ 404. returnTodropped: The/auth→/loginredirect used wouter’s<Redirect>which dropped query params.PublicOnlyRouterace: After login,PublicOnlyRouteunmountsAuthPageand redirects to/homebeforeAuthPage’sreturnTohandler fires. Additionally, wouter’s<Redirect>does client-sidepushStatewhich doesn’t hit Express for/api/*routes.
- Added
OAuthForwardroute — redirects/oauth/*→/api/oauth/*viawindow.location.replace - Replaced
/auth→/loginredirect withAuthRedirectPreserveParams - Added
resolveReturnTo()toPublicOnlyRoute - Added
FullPageRedirectcomponent for server routes (useswindow.location.replace) - Changed server OAuth redirect from
/auth?returnTo=...to/login?returnTo=...
Prevention
Section titled “Prevention”- When adding API endpoints for external clients (IDEs, mobile apps), test with the user-facing URL without
/api/prefix - When redirecting between SPA routes and server routes, use
window.location, never wouter’s<Redirect> - When redirecting between public routes, preserve query params