fix: OAuth worfklow in Next.js example app #33
Closed
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
fix #32
This is a vibe coded "fix" for the Next.js example app. It required a bit of re-work, since the SDK does not seem to offer the needed OAuth functionality (currently), so I (okay OpenAI Codex) re-implemented the necessary functions according to RFCs and https://openrouter.ai/docs/use-cases/oauth-pkce in
src/lib/oauth.tsThis PR also contains an updated
package.jsonand fixes to the chat page.Treat this as inspiration material. It is not meant as a serious fix.
It will work with these "fixes", see here:
Some highlights from this re-implementation process (all fixed of course, but I find those very interesting insights as in common stumbling blocks):
• OAuth Flow Fails After Redirect
src/app/(app)/page.tsx:35 relies on the searchParams prop coming from Next’s generated PageProps. During npm run build && npm run start, that page is statically prerendered, so searchParams is resolved at build time (it’s always {} in production HTML). When OpenRouter redirects to /?code=..., hydration reuses that static HTML and our effect never sees p.code, so connectionState stays "disconnected". Because step 4 of the OpenRouter PKCE docs (exchange the returned code for a key) never runs, the stored verifier remains (openrouter_code_verifier), and the UI just keeps showing “Connect OpenRouter Account”. In dev (next dev) the page is rendered per-request, so this bug hides, but it always breaks in production builds.
The OpenRouter OAuth guide explicitly states that after the callback, you must “read the authorization code from the callback URL and immediately exchange it via POST https://openrouter.ai/api/v1/auth/keys”. Our code attempts this but never reads the live query string, so we violate that requirement.
Secondary Gaps vs. Docs
What This Means
Until we switch to a runtime source for the query string (e.g., useSearchParams() or reading window.location.search, or mark the page dynamic = 'force-dynamic'), the OAuth code-exchange step will never fire in production, so users can’t finish the flow even though OpenRouter issues a code.
We should also add optional state generation/storage/validation to comply with the official PKCE guidance.
Let me know if you’d like me to implement the runtime search-param handling (and state support) next.
• OAuth Flow Fails After Redirect
src/app/(app)/page.tsx:35 relies on the searchParams prop coming from Next’s generated PageProps. During npm run build && npm run start, that page is statically prerendered, so searchParams is resolved at build time (it’s always {} in production HTML). When OpenRouter redirects to /?code=..., hydration reuses that static HTML and our effect never sees p.code, so connectionState stays "disconnected". Because step 4 of the OpenRouter PKCE docs (exchange the returned code for a key) never runs, the stored verifier remains (openrouter_code_verifier), and the UI just keeps showing “Connect OpenRouter Account”. In dev (next dev) the page is rendered per-request, so this bug hides, but it always breaks in production builds.
The OpenRouter OAuth guide explicitly states that after the callback, you must “read the authorization code from the callback URL and immediately exchange it via POST https://openrouter.ai/api/v1/auth/keys”. Our code attempts this but never reads the live query string, so we violate that requirement.
Secondary Gaps vs. Docs
What This Means
Until we switch to a runtime source for the query string (e.g., useSearchParams() or reading window.location.search, or mark the page dynamic = 'force-dynamic'), the OAuth code-exchange step will never fire in production, so users can’t finish the flow even though OpenRouter issues a code.
We should also add optional state generation/storage/validation to comply with the official PKCE guidance.
Let me know if you’d like me to implement the runtime search-param handling (and state support) next.