fix(auth): fix GitHub device flow polling and add --wait flag for WSL…#46698
fix(auth): fix GitHub device flow polling and add --wait flag for WSL…#46698chadmoore wants to merge 6 commits intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR fixes two genuine bugs in the GitHub device-flow polling loop and adds a Key changes:
Minor suggestions:
Confidence Score: 4/5
|
src/providers/github-copilot-auth.ts
Outdated
|
|
||
| while (Date.now() < params.expiresAt) { | ||
| // Always wait before polling — avoids immediate `authorization_pending` | ||
| // on the first request which triggers GitHub's slow_down backoff. | ||
| await new Promise((r) => setTimeout(r, intervalMs)); | ||
|
|
||
| if (Date.now() >= params.expiresAt) break; |
There was a problem hiding this comment.
Initial delay still applies after --wait confirmation
When the user passes --wait and confirms "Have you authorized the code in your browser?", they've already indicated the token should be ready. However, pollForAccessToken unconditionally sleeps intervalMs (minimum 1 s, typically 5 s from GitHub) before the first request — regardless of whether the caller told the user to go authorize or the user just confirmed they already did.
This means after the user types "yes", the CLI silently waits up to 5 seconds before making any attempt. It won't cause a functional problem, but it's a small UX cliff that could be avoided by accepting an optional skipFirstDelay parameter and only sleeping when the user hasn't pre-confirmed.
async function pollForAccessToken(params: {
deviceCode: string;
intervalMs: number;
expiresAt: number;
skipFirstDelay?: boolean; // skip the initial sleep when user already confirmed
}): Promise<string> {
// ...
let intervalMs = params.intervalMs;
let first = true;
while (Date.now() < params.expiresAt) {
if (!first || !params.skipFirstDelay) {
await new Promise((r) => setTimeout(r, intervalMs));
if (Date.now() >= params.expiresAt) break;
}
first = false;
// ... rest of loop unchanged
}
}Then pass skipFirstDelay: true from githubCopilotLoginCommand when opts.wait is true and the user confirmed.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/providers/github-copilot-auth.ts
Line: 80-86
Comment:
**Initial delay still applies after `--wait` confirmation**
When the user passes `--wait` and confirms "Have you authorized the code in your browser?", they've already indicated the token should be ready. However, `pollForAccessToken` unconditionally sleeps `intervalMs` (minimum 1 s, typically 5 s from GitHub) before the first request — regardless of whether the caller told the user to go authorize or the user just confirmed they already did.
This means after the user types "yes", the CLI silently waits up to 5 seconds before making any attempt. It won't cause a functional problem, but it's a small UX cliff that could be avoided by accepting an optional `skipFirstDelay` parameter and only sleeping when the user hasn't pre-confirmed.
```typescript
async function pollForAccessToken(params: {
deviceCode: string;
intervalMs: number;
expiresAt: number;
skipFirstDelay?: boolean; // skip the initial sleep when user already confirmed
}): Promise<string> {
// ...
let intervalMs = params.intervalMs;
let first = true;
while (Date.now() < params.expiresAt) {
if (!first || !params.skipFirstDelay) {
await new Promise((r) => setTimeout(r, intervalMs));
if (Date.now() >= params.expiresAt) break;
}
first = false;
// ... rest of loop unchanged
}
}
```
Then pass `skipFirstDelay: true` from `githubCopilotLoginCommand` when `opts.wait` is `true` and the user confirmed.
How can I resolve this? If you propose a fix, please make it concise.85ffe67 to
e127ff5
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e127ff5956
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
src/providers/github-copilot-auth.ts
Outdated
| let first = true; | ||
|
|
||
| while (Date.now() < params.expiresAt) { | ||
| if (!first || !params.skipFirstDelay) { |
There was a problem hiding this comment.
Always honor initial device-flow polling interval
When --wait is used, skipFirstDelay makes the first token poll fire immediately, so users who confirm quickly (before GitHub’s initial interval elapses) still hit the token endpoint too early and can receive authorization_pending/slow_down again. This reintroduces the backoff behavior the change is trying to prevent for the new --wait flow; the first poll should still respect the provider’s minimum interval (or only skip the already-elapsed portion).
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 81222a0e43
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…/remote terminals The device flow polling loop had two bugs: 1. When GitHub returns slow_down, the response includes the new minimum polling interval in seconds. The old code ignored this and only added a fixed 2000ms to the original interval, causing continued rate limiting. Now intervalMs is updated to the server-specified value (seconds * 1000). 2. The loop polled immediately on the first iteration before the user had a chance to authorize, triggering authorization_pending which in turn caused slow_down cascades. Moving the sleep to the top of the loop fixes this. Adds a --wait flag to login-github-copilot that prompts the user to confirm they've authorized before polling begins. Also prints the URL and code as plain text so they're easy to copy in terminals (e.g. WSL, SSH) where the clack box rendering prevents ctrl+click link detection. Usage: openclaw models auth login-github-copilot --wait Co-authored-by: Copilot <[email protected]>
- Skip initial poll delay when --wait is used and user has confirmed authorization, avoiding a silent 1-5s pause after the user types 'yes' - Show note() box only when --wait is not set; when --wait is set show plain-text URL/code only, preventing the info from appearing twice Co-authored-by: Copilot <[email protected]>
Replace skipFirstDelay with firstPollAfter timestamp so the first poll respects GitHub's interval relative to when the device code was issued. If the user takes longer than the interval to confirm, we poll immediately; otherwise we wait only the remaining portion — no backoff risk either way. Co-authored-by: Copilot <[email protected]>
…quest Capturing the timestamp before awaiting requestDeviceCode meant network latency counted as elapsed polling time, potentially making firstPollAfter already past on slow links and triggering an immediate poll. Co-authored-by: Copilot <[email protected]>
de579cd to
db0e97d
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c3e99c60ac
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| provider: "github-copilot", | ||
| method: "device", | ||
| yes: Boolean(opts.yes), | ||
| wait: Boolean(opts.wait), |
There was a problem hiding this comment.
Wire the new --wait flag into the GitHub login path
--wait is advertised here, but it never reaches githubCopilotLoginCommand. modelsAuthLoginCommand only accepts provider/method/setDefault/yes (src/commands/models/auth.ts:494-499), runProviderAuthMethod does not forward arbitrary CLI flags, and the GitHub plugin still invokes githubCopilotLoginCommand({ yes: true, profileId: "github-copilot:github" }, ...) (extensions/github-copilot/index.ts:75-90). In practice, openclaw models auth login-github-copilot --wait behaves exactly like before, so the WSL/SSH users this change targets still won't get the plain-text URL plus confirmation flow.
Useful? React with 👍 / 👎.
…/remote terminals
The device flow polling loop had two bugs:
When GitHub returns slow_down, the response includes the new minimum polling interval in seconds. The old code ignored this and only added a fixed 2000ms to the original interval, causing continued rate limiting. Now intervalMs is updated to the server-specified value (seconds * 1000).
The loop polled immediately on the first iteration before the user had a chance to authorize, triggering authorization_pending which in turn caused slow_down cascades. Moving the sleep to the top of the loop fixes this.
Adds a --wait flag to login-github-copilot that prompts the user to confirm they've authorized before polling begins. Also prints the URL and code as plain text so they're easy to copy in terminals (e.g. WSL, SSH) where the clack box rendering prevents ctrl+click link detection.
Usage:
openclaw models auth login-github-copilot --wait
Summary
response that triggered GitHub's slow_down backoff, making logins unnecessarily slow.
to the URL — and the backoff made the flow even more painful.
returned interval value; new --wait flag prints plain-text URL+code and waits for user confirmation before polling
begins.
Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
polled immediately).
Intended for WSL/remote terminals where the browser doesn't auto-open.
Security Impact (required)
Repro + Verification
Environment
Steps
Expected
Actual
Evidence
Human Verification (required)
confirmed polling does not begin until user confirms.
Review Conversations
Compatibility / Migration
Failure Recovery (if this breaks)
to prior state.
Risks and Mitigations
authorizes immediately.
- Mitigation: The delay equals the server-specified interval (minimum 1s), which is GitHub's own recommended
polling cadence — no change to the happy path timing in practice.