Repository navigation
fix(auth): retry transient device-flow failures, check the device-code answer, tighten search token scoping - #82
Merged
Merged
Conversation
…ng and refresh pollDeviceToken and refreshToken each posted a form to the dex token endpoint and decoded the reply; postTokenForm now does both. refreshToken takes the stored token and resolves the auth host from its issuer itself, so its two callers no longer call rememberAuthServerFromToken first. The refresh request now has the same 30 s timeout as polling. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A poll that got no answer, or a gateway error page instead of JSON, ended the login even though the device code was still valid. RFC 8628 section 3.5 asks clients to reduce their polling frequency and retry on connection timeouts. Such polls now double the interval (capped at a minute) and are retried until the device code expires; the expiry error then wraps the last failure. A non-JSON answer below 500 still fails at once, with its HTTP status in the message. Polling is now always bounded: without expires_in it stops after 15 minutes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…derr - requestDeviceCode checks the HTTP status and that a device_code came back. An error answer (an OAuth error, or a gateway page) used to be decoded into an empty response, which printed "Go to and authorize this device" and then failed on the first poll. - verification_uri_complete is optional in RFC 8628. Without it the prompt shows verification_uri and the user code. - The approval prompt and the missing-refresh-token warning go to stderr. jh git-credential get runs the device flow when the stored login is for another host, and git reads the helper's stdout as credential lines, so the prompt never reached the user and git rejected the extra lines. - The device-code request uses the 30 s timeout client like polling. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… on them Both sat above a declaration added just before the function, so godoc attached them to splitRegistryNames and errRegistryFetch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iven loginMatchesServer normalized both sides, so an explicit URL whose host has no .com/.dev suffix was expanded before the comparison: --server https://example.org matched a login stored for example.org.juliahub.com and got its token. Every caller already passes the host the request goes to, so only the stored value is normalized now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the registry list cannot be fetched, package search keeps the --registries names for the REST listing but has no registry IDs. If the REST listing then failed too, the GraphQL fallback ran without a registry filter and returned packages from every registry. It now reports the REST error instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
Author
pankgeorg
marked this pull request as ready for review
November 5, 2026 20:29
lamdor
approved these changes
Nov 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is a follow-up to #64. The device-flow loop that #64 introduced already follows RFC 8628 for
interval,slow_down,expires_inand the terminal errors. This PR shares one token request between polling and refresh, and fixes what a post-merge review found around that loop and in the search token scoping. Each commit stands alone.Simplification
postTokenFormnow does the POST, read and decode thatpollDeviceTokenandrefreshTokeneach did on their own.refreshTokentakes the stored token and resolves the dex host from its issuer itself, so its two callers no longer callrememberAuthServerFromTokenfirst.Fixes
expires_in, polling now stops after 15 minutes. Before, it had no bound.Go to and authorize this deviceand failed on the first poll withinvalid_request.requestDeviceCodenow checks the status and requires adevice_code.verification_uri_completeis missing (it is optional in the RFC), the prompt showsverification_uriand the user code.jh git-credential getruns the device flow when the stored login is for another host. git reads the helper's stdout as credential lines, so the approval URL never reached the user, and git rejected the extra lines.loginMatchesServerused to normalize the destination too. So--server https://example.orgmatched a login stored forexample.org.juliahub.comand was sent its token. Every caller already passes the real destination host, so now only the stored value is normalized.jh package search --registries Xkeeps the names for REST but has no registry IDs. If REST also failed, the GraphQL fallback used to run with no registry filter. It now reports the REST error instead.Also
runPackageSearch,resolveRegistries) had ended up above neighbouring declarations. They are back on their functions.Testing
New tests:
TestPollDeviceTokenRetriesTransientFailures(502 → backoff → success)TestPollDeviceTokenBacksOffUntilExpiry(unreachable endpoint; the backoff sequence and the cap)TestPollDeviceTokenNonJSONClientErrorIsFatalTestPollDeviceTokenDefaultsLifetimeTestRequestDeviceCodeTestDeviceCodePromptTestSearchPackagesKeepsRegistryFilterOnFallbackTestLoginMatchesServerLocally I ran
go vetand the targeted-runsets for these tests and for the existing poll, discovery and token-choice tests. I left the full suite to CI. A live login was not exercised.Not in this PR
These were found in the same review. They are older than #64 and need separate decisions:
jh push/fetch/pullpass anhttp.extraHeaderthat is not URL-scoped.-snames.auth.<server>answered first.🤖 Generated with Claude Code