docs(openrouter-oauth): sync with openrouter-web changes - #160
docs(openrouter-oauth): sync with openrouter-web changes#160devin-ai-integration[bot] wants to merge 2 commits into
Conversation
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Original prompt from Automation
|
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
Perry's Review
Verdict: ✅ LGTM
Risk: 🟢 Low
Review of docs(openrouter-oauth): sync with openrouter-web changes
Auto-review triggered by pull_request.opened. PR is OPEN, not draft, and no prior Perry review exists on head 5c4247b — guards passed.
What changed
Docs-only sync for the openrouter-oauth skill (+25/-5, 2 files). It documents the new headless / no-callback authorization mode added to openrouter-web in OpenRouterTeam/openrouter-web#32412:
SKILL.md: version2.0.0→2.1.0; frontmatterdescriptionandcompatibilitywidened to cover CLI/Node.js; decision-tree row for "Get an API key programmatically (no UI)" now points at the headless flow; a new "Headless / no-callback flow" section with the authorize URL, param table, and constraints.README.md: compatibility line and "What it covers" list updated to match.
Spec verification — claims checked against the implementation
I cloned the cited openrouter-web PR (#32412, merged) and read its auth-page gate and the consent-section UI. Every documented claim matches the merged code:
| Claim in SKILL.md | Implementation (openrouter-web) | ✓ |
|---|---|---|
Omitting callback_url → code rendered on screen |
no-callback branch renders the auth code in a code block for the user to paste | ✓ |
code_challenge required |
missing challenge → redirect to home | ✓ |
code_challenge_method must be S256; plain rejected |
method ≠ S256 → redirect; the code comment confirms plain is rejected because the challenge sits in the URL of the same screen that renders the code |
✓ |
oauth_client_id must not be present |
client-id present → redirect | ✓ |
| Rejected request → OpenRouter home page | both rejection branches redirect('/', replace) |
✓ |
key_label sets app title; defaults to A command-line app |
keyLabelParam ?? 'A command-line app' |
✓ |
| Code single-use, expires 10 min | UI copy "used once and expires after 10 minutes"; the public docs guide matches | ✓ |
POST /api/v1/auth/keys exchange unchanged |
display-code only changes code delivery, not the key-exchange request | ✓ |
No CI checks are configured for this repo (statusCheckRollup empty), so there is no CI gate to report. No prior review threads exist.
Findings
Suggestion (non-blocking) — see inline on SKILL.md line 66: the skill now advertises CLI/Node.js support, but the only copy-pasteable code remains the browser auth module (window/btoa/sessionStorage/crypto.subtle). A minimal Node PKCE snippet (verifier + S256 challenge via node:crypto/crypto.webcrypto, reading the pasted code, and the fetch exchange) would extend the skill's "copy-pasteable, no dependencies" promise to the newly claimed environment and remove the base64url-encoding risk it exists to prevent. Not a blocker — the prose constraints are correct and sufficient for an agent to synthesize the code.
Observation (not inline) — the decision-tree row "Get an API key programmatically (no UI)" was narrowed from "Just implement the PKCE flow — skip the button section" to "Use the headless / no-callback flow below". The "(no UI)" qualifier makes headless the right target, but a web app that wants programmatic keys with a callback no longer has an explicit pointer. Worth a moment of thought, but consistent with the reframe and not misleading.
Risk assessment
Risk: 🟢 Low
Risk assessment:
| Dimension | Severity | Risk | Reasoning |
|---|---|---|---|
| Implementation risk | 🟩 | Low | Docs-only; every behavioral claim was verified line-by-line against the merged openrouter-web gate. |
| Premise risk | 🟩 | Low | The premise (sync the skill with the merged headless-auth capability) is correct and the cited PR is merged. |
| Estimated impact | 🟩 | Low | Worst case is a CLI author writing their own PKCE glue from correct constraints; no production, security, or data impact from the doc itself. |
| Risk Factor | Severity | Risk | Reasoning |
|---|---|---|---|
| Reversibility | 🟩 | Low | Markdown docs; trivially revertable. |
| Detectability | 🟩 | Low | Inaccuracies would surface the next time someone follows the flow against live behavior. |
| Blast radius | 🟩 | Low | Contained to the openrouter-oauth skill; no other skill references these params. |
| Data integrity | None | No persisted state is touched by a docs change. | |
| Financial exposure | None | No billing/payment surface involved. | |
| Security and privacy exposure | 🟩 | Low | Doc correctly mandates S256 for display-code mode and gives sound verifier-handling guidance; no auth-weakening. |
| Propagation | 🟩 | Low | Agents consuming the skill inherit accurate constraints. |
| Availability | None | Cannot affect whether anything serves. | |
| Recovery cost | 🟩 | Low | A doc fix is a one-line PR. |
| Time to correct | 🟩 | Low | Minutes to amend markdown. |
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Summary
OpenRouter now supports a headless authorization mode that the skill did not teach. Source: openrouter-web #32412 (
projects/docs/guides/overview/auth/oauth.mdx,projects/web/app/(oauth)/auth/page.tsx).Omitting
callback_urlfrom the authorize URL puts the page into display-code mode: after approval the authorization code is rendered on screen for the user to paste into a CLI, instead of a redirect.Constraints documented, taken from the gate in
page.tsx:code_challengeis required andcode_challenge_methodmust beS256;plainis rejected because the challenge sits in the URL of the same screen that renders the code.oauth_client_idmust not be present. A rejected request redirects to the OpenRouter home page rather than showing a code.key_labelsets the app title on the approval screen, defaulting toA command-line app.POST /api/v1/auth/keysexchange is unchanged.Also noted that the drop-in browser auth module's
sessionStorage/localStoragehandling does not apply in CLI/Node — the verifier only needs to survive in the process until the user pastes the code back — and widened the frontmattercompatibilityandREADME.mdcompatibility line, which claimed browser-only. Version bumped to2.1.0.Since the skill now claims CLI/Node support, the headless section also carries a dependency-free Node example (verifier and S256 challenge via
node:crypto, authorize URL, pasted code from stdin,fetchexchange), so the "copy-pasteable, no dependencies" promise covers that environment rather than leaving base64url encoding to the reader. It was run as written on Node LTS through to the live exchange, and its base64url output was checked against the browser module's encoding for identical input bytes.Link to Devin session: https://openrouter.devinenterprise.com/sessions/16f2009a79af4b1fb44a86c0810f07ba