Skip to content

Show the device code in the sign-in dialog - #39

Merged
Cam8863 merged 1 commit into
linuxfrom
fix/device-code-not-shown-in-dialog
Aug 28, 2026
Merged

Cam8863 merged 1 commit into
linuxfrom
fix/device-code-not-shown-in-dialog

Conversation

@guys-inc-ops

@guys-inc-ops guys-inc-ops Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Sign-in is broken in 3.5.0 for anyone signing in from inside the app. Fixes the report from dampmeme64.

What happens

The browser opens to github.com/login/device, which asks for a code, and the application never shows one. From the user's log:

23:59:18  [SignInStore] initializing OAuth device flow
23:59:18  [main] opening in browser: https://github.com/login/device
          (nothing further)

The request was always fine — the browser only opens because it succeeded. The code was fetched, put into state, and never rendered.

Root cause: two sign-in surfaces, one updated

  • Welcome / first-run renders through ui/lib/sign-in.tsx → AuthenticationForm. feat(auth): sign in with the OAuth device flow, stop shipping a client secret #26 taught this one to display the code. It works.

  • The dialog — ui/sign-in/sign-in.tsx, reached by re-authenticating, which is what happens when a token is invalidated — was missed. renderAuthenticationStep ignored state.deviceFlow entirely and printed the pre-device-flow copy:

    Your browser will redirect you back to GitHub Desktop once you've signed in.

    That is now false. Nothing redirects back, and the code you need is never shown.

This is why the pre-release GUI sign-in test passed: it exercised first-run, and the user hit re-authentication.

Visible in the published 3.5.0 bundle — device-flow-user-code and Enter this code at each appear once (the welcome flow), alongside the dialog's redirect sentence.

The fix

renderAuthenticationStep now renders the code when state.deviceFlow is set, using the same markup as the welcome form.

The stylesheet needed changing too: _device-flow.scss was scoped to .sign-in-form, the welcome form's class, so even once the dialog rendered the code it would have been unstyled. Now unscoped, with a comment saying why — it is shown from two places.

The standing copy is corrected where it still described the redirect flow, which also affects the existing-account warning step that reuses it.

Verified by building, not by inference

I got this wrong twice before landing it — first blaming CORS, then a renderer/main-process split — because I reasoned from curl and specs instead of running things. So this one is checked against the compiled output:

3.5.0 this branch
device-flow-user-code 1 2
Enter this code at 1 2
redirect copy 1 0

tsc, eslint, prettier clean. yarn compile:prod exits 0.

Follow-up worth doing separately

Nothing in CI renders a component or launches the app, which is why 1032 passing tests, CodeQL, and the build-secret scan all cleared a build no one could sign into. The secret scan even greps for login/device/code and found it — proving the string compiled, not that a user could reach it. A component test asserting "a sign-in state carrying a device code renders that code", run against both surfaces, would have caught this in milliseconds.

Sign-in is broken in 3.5.0 for anyone who signs in from inside the app. The
browser opens to github.com/login/device, which asks for a code, and the
application never shows one.

There are two sign-in surfaces. The welcome flow renders through
ui/lib/sign-in.tsx and AuthenticationForm, which #26 taught to display the
code. The dialog - the one reached by re-authenticating, which is what
happens when a token is invalidated - renders through ui/sign-in/sign-in.tsx
and was missed. It ignored state.deviceFlow entirely and printed the old
copy promising a redirect back to GitHub Desktop, which no longer happens.

A user's log shows it exactly: "initializing OAuth device flow", then
"opening in browser: https://github.com/login/device", then nothing until
the abandoned poll dies minutes later. The request was always fine. The code
was fetched, stored in state, and never rendered.

Visible in the published bundle: "device-flow-user-code" and "Enter this
code at" each appear once, for the welcome flow, and the dialog's misleading
redirect sentence appears alongside them. After this change they appear
twice and the redirect sentence is gone, confirmed by building.

The styles were scoped to .sign-in-form, the welcome form's class, so even
once the dialog rendered the code it would have been unstyled. They are
now unscoped, because the code is shown from two places.

The standing copy is also corrected where it still described the redirect
flow, including on the existing-account warning step that reuses it.
@guys-inc-ops
guys-inc-ops Bot requested a review from Cam8863 as a code owner August 28, 2026 00:04
@Cam8863
Cam8863 merged commit 4c7b8dd into linux Aug 28, 2026
6 checks passed
@Cam8863
Cam8863 deleted the fix/device-code-not-shown-in-dialog branch August 28, 2026 00:17
@guys-inc-ops guys-inc-ops Bot mentioned this pull request Aug 28, 2026
guys-inc-ops Bot added a commit that referenced this pull request Aug 28, 2026
Ships the sign-in fix from #39. 3.5.0 could not be signed into from inside
the application: the device code was fetched and never rendered by the
re-authentication dialog, so the browser opened asking for a code that was
nowhere on screen.

Also stops the notes generator publishing "TODO". Empty sections rendered a
placeholder that existed as a prompt for whoever edited the draft release by
hand; we publish without that pass, so a patch release fixing one thing
would have shipped a literal "## Improved / TODO" to users. Empty sections
are now omitted. Verified that 3.4.9-linux1 still renders its ten upstream
issue links with no placeholders.

Co-authored-by: guys-inc-ops[bot] <321481384+guys-inc-ops[bot]@users.noreply.github.com>
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.

1 participant