Skip to content

fix(oauth): use verification_uri_complete to eliminate manual code entry in device flow#35

Merged
i8ramin merged 3 commits intomainfrom
copilot/fix-oauth-device-flow
Feb 23, 2026
Merged

fix(oauth): use verification_uri_complete to eliminate manual code entry in device flow#35
i8ramin merged 3 commits intomainfrom
copilot/fix-oauth-device-flow

Conversation

Copy link
Contributor

Copilot AI commented Feb 23, 2026

The device flow was instructing agents to tell humans to visit the bare /device URL and manually type the code — despite verification_uri_complete (with the code embedded) already being returned in the response.

Changes

  • oauth-agent.ts: message now uses verification_uri_complete directly:

    "message": "Approve me: https://botcha.ai/device?code=BOTCHA-XXXXXX"
    
  • /device page JS: When ?code= is present in the URL, triggers a 3-second countdown auto-submit with a visible Cancel button — human taps link, agent info loads, approval fires without any typing.

  • Onboarding instructions (step 11): Updated to instruct agents to share verification_uri_complete as the one-click link rather than bare URL + code separately.

The /device page already handled server-side pre-fill and auto-lookup from ?code=; the only missing pieces were the countdown auto-submit and fixing the message copy agents were presenting to humans.

Original prompt

This section details on the original issue you should resolve

<issue_title>bug(oauth): device flow should auto-fill code via verification_uri_complete</issue_title>
<issue_description>## Problem

The OAuth device flow response already returns verification_uri_complete with the code embedded in the URL:

"verification_uri_complete": "https://botcha.ai/device?code=BOTCHA-XXXXXX"

But agent instructions currently tell humans to:

  1. Visit the bare verification_url
  2. Manually type/paste the code

This is unnecessary friction — the complete URL is already in the response.

Expected behavior

  • Agents always present verification_uri_complete as the one-click link
  • The /device frontend reads ?code= from query params and auto-populates (and optionally auto-submits) the field
  • Human experience: tap link → approval screen appears immediately, no typing

Fix

API message field: Change copy to use the complete URL:

"message": "Approve me: https://botcha.ai/device?code=BOTCHA-XXXXXX"

Frontend (/device page) — ~3 lines of JS:

const code = new URLSearchParams(window.location.search).get('code');
if (code) {
  document.getElementById('code-input').value = code;
  // optionally auto-submit to skip manual confirmation step
}

Impact

Every agent using the device flow is currently asking humans to copy-paste codes. The fix is trivial — just use the URL that's already being returned.</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

…i_complete

Co-authored-by: i8ramin <49030+i8ramin@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix OAuth device flow to use verification_uri_complete fix(oauth): use verification_uri_complete to eliminate manual code entry in device flow Feb 23, 2026
Copilot AI requested a review from i8ramin February 23, 2026 12:52
@chocothebot
Copy link
Collaborator

Review from @chocothebot 🐢

Copilot's approach is solid — pushed one improvement on top:

✅ What Copilot got right

  • message field now uses verification_uri_complete (the pre-filled URL) ✓
  • Agent instructions (step 11) updated to use the one-click link ✓
  • 3-second countdown auto-submit with Cancel button is good UX ✓

🐛 Bug I fixed (commit 0ae7a29)

Problem: The countdown timer started immediately after 600ms, regardless of whether the code lookup succeeded. If the code was expired or invalid, it would still fire approve() and give the user a confusing error.

Fix: The countdown now only starts after lookupCode() confirms resolvedCode === urlCode. If the code lookup fails (expired, not found), the countdown is cancelled and a clear error is shown instead.

Also increased the delay from 600ms → 1200ms to give the async lookup time to complete before the countdown check fires.

Also fixed a stale comment in oauth-agent.ts that still said "Visit <verification_url> and enter <user_code>" — updated to match the new one-click link behavior.

⚠️ Note on issue #34

The /device route still has requireDashboardAuth middleware — so users still need to log in before seeing the approval screen. That's tracked separately in #34 and intentionally out of scope for this PR.

LGTM once CI passes. 🟢

@github-actions
Copy link

🚀 Preview Deployed — PR #35

Branch: copilot/fix-oauth-device-flow
Commit: 0ae7a29
URL: https://botcha-pr-35.carrot-cart.workers.dev

Quick smoke tests

BASE="https://botcha-pr-35.carrot-cart.workers.dev"

# Health check
curl "$BASE/health"

# Challenge flow
APP_ID=app_c4e8aade83ce32f0
curl "$BASE/v1/challenge?app_id=$APP_ID"

# New endpoints on this PR (check EPIC.md for specifics)
curl "$BASE/v1/" | jq .

⚠️ Preview uses production KV — test data is real. Clean up test agents/apps when done.


Auto-deployed by preview.yml · View logs

chocothebot added a commit that referenced this pull request Feb 23, 2026
@i8ramin i8ramin requested a review from Copilot February 23, 2026 13:16
@i8ramin i8ramin marked this pull request as ready for review February 23, 2026 13:16
@i8ramin i8ramin merged commit 2067907 into main Feb 23, 2026
3 checks passed
@github-actions
Copy link

🧹 Preview worker botcha-pr-35 deleted (PR merged).

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves the OAuth device authorization flow UX by switching agents to share the one-click verification_uri_complete link and enhancing the /device page to auto-submit approvals when opened with a ?code= param.

Changes:

  • Update OAuth device response copy to direct users to the one-click verification_uri_complete link.
  • Add client-side countdown + cancel UX to auto-approve on /device when ?code= is present.
  • Update onboarding instruction copy to reference verification_uri_complete instead of manual code entry.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 4 comments.

File Description
packages/cloudflare-workers/src/oauth-agent.ts Device flow guidance + response message updated to use the one-click URL.
packages/cloudflare-workers/src/index.tsx Onboarding step copy updated; /device page adds countdown auto-submit behavior for ?code= links.
package-lock.json Version/lockfile updates aligned with the workspace version bump.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

tick();
}
// Wait for lookup to complete (lookup has 400ms debounce + fetch), then start countdown
setTimeout(startCountdown, 1200);
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The auto-approval countdown relies on a fixed 1200ms delay, then immediately fails if resolvedCode !== urlCode. On slower networks (or if the 400ms debounce + fetch takes longer), this can incorrectly show “Code not found or expired” and never auto-approve. Consider starting the countdown when the lookup actually succeeds (e.g., trigger from lookupCode when d.success and val === urlCode), or poll/wait up to a timeout instead of using a single fixed delay.

Suggested change
setTimeout(startCountdown, 1200);
(function waitForLookupAndStart() {
var maxWaitMs = 10000; // maximum time to wait for code lookup
var pollIntervalMs = 200;
var waitedMs = 0;
function poll() {
if (autoCancelled) return;
if (resolvedCode === urlCode) {
startCountdown();
return;
}
waitedMs += pollIntervalMs;
if (waitedMs >= maxWaitMs) {
autoStatus.textContent = 'Code not found or expired. Please check with your agent.';
return;
}
setTimeout(poll, pollIntervalMs);
}
poll();
})();

Copilot uses AI. Check for mistakes.
Comment on lines +2942 to +2954
// Auto-submit if code came from URL params (verification_uri_complete)
var urlCode = new URLSearchParams(window.location.search).get('code');
if (urlCode && urlCode.length === 13) {
var autoTimer = null;
var autoCancelled = false;
var autoStatus = document.getElementById('status');
function startCountdown() {
if (autoCancelled) return;
// Only proceed if the lookup confirmed the code is valid
if (resolvedCode !== urlCode) {
autoStatus.textContent = 'Code not found or expired. Please check with your agent.';
return;
}
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

urlCode is read directly from the query string and compared to resolvedCode, but it’s never normalized (uppercased) and the countdown doesn’t verify the current input value still matches urlCode. Normalizing urlCode (and/or explicitly setting the input value from it) would make the auto-approval path more robust and avoid false mismatches or approving if the user edits the field before the countdown completes.

Copilot uses AI. Check for mistakes.
@@ -10,7 +10,7 @@
* 1. Agent: POST /v1/oauth/device { agent_id, app_id }
* ← { device_code, user_code: "BOTCHA-XXXX", verification_url, expires_in: 600, interval: 5 }
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The flow doc comment references sending <verification_uri_complete>, but the step-1 response example above doesn’t list verification_uri_complete (or message). Updating the example response fields would keep the comment accurate for readers.

Suggested change
* { device_code, user_code: "BOTCHA-XXXX", verification_url, expires_in: 600, interval: 5 }
* { device_code, user_code: "BOTCHA-XXXX", verification_url, verification_uri_complete, expires_in: 600, interval: 5, message }

Copilot uses AI. Check for mistakes.
Comment on lines 72 to 81
return c.json({
device_code,
user_code,
verification_url: `${base_url}/device`,
verification_uri: `${base_url}/device`, // RFC 8628 canonical name
verification_uri_complete: `${base_url}/device?code=${user_code}`,
expires_in: DEVICE_TTL_SEC,
interval: 5,
message: `Tell your human: visit ${base_url}/device and enter ${user_code}`,
message: `Approve me: ${base_url}/device?code=${user_code}`,
});
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are unit tests covering other Cloudflare Worker modules under tests/unit, but this change alters the OAuth device response contract (message now contains the one-click URL). Adding a small unit test for handleOAuthDevice to assert verification_uri_complete is returned and message uses the same URL would help prevent regressions.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(oauth): device flow should auto-fill code via verification_uri_complete

4 participants