Repository navigation
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use the registered OAuth app without gh, persist credentials with OS-backed encryption, refresh device-flow tokens, guard account changes during active turns, and prevent fallback after sign-out. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Recheck cancellation after SDK shutdown and prevent a cancelled refresh from granting a run lease. Persist refreshed grants without a fallible profile request after rotation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ceiling src/i18n/dict/it/settings.ts sat exactly at the 500-line ceiling, so the Copilot sign-in strings pushed it to 519 and failed the gate. It is the Italian counterpart of src/i18n/dict/en/settings.ts, which the gate already exempts for the same reason: translation dictionary data with no application logic, where "split by responsibility" has no meaning — the file has exactly one responsibility and splitting it would only scatter the locale across modules. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
OAuth app is created in my Github account. Let me know and I'll transfer ownership to @0xsline. |
0xsline
left a comment
There was a problem hiding this comment.
Thanks for the thorough work here. The design held up well in review: you use your own OAuth App client ID and only documented GitHub endpoints. The token reaches Copilot through the SDK's supported path, is encrypted with safeStorage, and never reaches the renderer or the logs. The new routes have loopback/Host/Origin checks on top of the global guard.
Two things need to change before merge:
- OAuth App ownership. The project will register a maintainer-owned OAuth App (with Device Flow enabled) and post its Client ID here, so there's no need to transfer yours. Please swap it in
server/copilot/oauth-api.tsonce it's posted. In the same change, please reword the "this fork's" OAuth App text inREADME.md(around lines 303-306). - CLI lockout. Take a user whose gh/Copilot CLI login already works. If they try in-app sign-in and it fails (denied, expired or org-blocked), the card keeps showing an error. The only visible reset is "Sign out of this app". That saves a signed-out marker, which from then on blocks the CLI fallback, and the only way back is deleting
copilot-auth-v1.encby hand. When the app never stored a login, signing out should just clear the error (or offer a separate "Dismiss").
Optional, if you're in there anyway:
- After a successful refresh, always persist the new token pair. Quitting mid-refresh can currently leave the old, now-revoked pair on disk.
- During the device-code poll, retry transient network/5xx errors until the code expires instead of aborting the whole sign-in.
A user whose gh/Copilot CLI login already works could try in-app sign-in, have it fail (denied, expired, org-blocked), and then find "Sign out of this app" as the only visible reset. That saved a signed-out marker, which from then on blocked the CLI fallback until copilot-auth-v1.enc was deleted by hand. - Sign-out writes the marker only when the app holds a saved login (valid, expired or unreadable). With nothing saved it just clears the error and leaves the CLI fallback intact. - The auth state reports savedLogin. Without one, the card offers "Dismiss" (POST /api/copilot/auth/dismiss, same guards) instead of sign-out. Dismiss never touches stored credentials. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Refreshing rotates the grant on GitHub, so the old pair is revoked from then on. If sign-out or quit arrived mid-refresh, the service either dropped the new pair or wrote the old, now-revoked one back to disk. - Once a refresh succeeds, the new pair is persisted unconditionally. Cancellation only withholds the token (no run lease). A queued sign-out still overwrites the pair afterwards. - The refresh request is no longer aborted once sent, because GitHub may already have rotated the grant. It stays bounded by the 15s request timeout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A single network blip or GitHub 5xx while polling ended the whole sign-in, even though the device code was still valid. - Network failures and HTTP 5xx are marked transient. Polling retries them at the normal interval until the code expires, and the card shows a retrying notice meanwhile. OAuth rejections and 4xx still fail at once. - Device codes are single-use, so once GitHub grants a token a transient profile-lookup failure retries only the lookup. The code is never polled again. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve the CopilotVendorPane conflict by keeping this branch's showLegacyNote gating together with upstream's onOpenPage link on SettingsNoteAction (469a043). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Thanks for the careful review @0xsline. Status on each point:
• Sign-out now writes the signed-out marker only when the app holds a saved login (valid, expired or unreadable). With nothing saved, it just clears the error, so a working gh/Copilot CLI login keeps working. Optional items: both done. • Always persist the refreshed pair (5bd2fb7). A successful refresh is now saved unconditionally, so cancelling only withholds the token and a queued sign-out still overwrites it afterwards. Previously, cancel/quit mid-refresh could drop the new pair or write the old, revoked one back. The refresh request is also no longer aborted once sent, since GitHub may already have rotated the grant; it stays bounded by the 15s request timeout. I checked each regression test against the pre-fix code, and each one fails there. |
|
Main has moved on: #200, #203, #207, #209, #211 and #213 are merged now, plus a fix for the >500-line verify gate that was failing If you want, I can merge main into your branch and push the resolution back here (maintainer edits are enabled) so the diff is reviewable again — just say the word. The remaining review points from the earlier CHANGES_REQUESTED are still open on our side and we would like to see how they are addressed before merging. |
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # package.json
|
Thanks @0xsline. I merged current main into this branch myself (5eb9e23), so the PR is mergeable again. Besides the
On the earlier review: the CLI lockout fix and both optional items are already on the branch (77e3f6d, 5bd2fb7, 2b32424). Details are in my comment above. The one open item is OAuth App ownership. Have you had a chance to register the maintainer-owned OAuth App (Device Flow enabled)? Once you post the Client ID, I'll swap it into |
Adds an in-app GitHub sign-in for the Copilot backend, so using Copilot no longer depends on the user having separately authenticated the Copilot CLI outside OpenChatCut.
Builds on the Copilot backend already merged in #139.
What this adds
server/copilot/oauth-{types,store,service,api}.tsimplement the GitHub device-code flow end to end, with a standalone sign-in state shared throughshared/copilot-auth.ts.~/.openchatcut/copilot-auth-v1.enc; nothing lands in.env.localand no token is ever echoed back to the browser. The store covers restart, file permissions, unavailable storage and corrupted payloads.COPILOT_HOMEthe backend already uses, so in-app auth never disturbs the user's own~/.copilot.CopilotAccountCardin the settings vendor pane shows the pending user code, account state, and sign-out.Correctness details worth reviewing
Verification
New
npm run verify:copilot-authgate (wired intoverify:server-runs):All pass on top of
main;tsc -bis clean.