Repository navigation
feat(cli): sign in with xping login in the browser, and add logout and auth status (P3) - #247
Merged
Merged
Conversation
…fig key source, flow test seams Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The managed HttpListener of macOS and Linux rejects bracketed IPv6 prefixes, so the §4.3 fallback to [::1] can only bind under http.sys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d auth status (P3) xping login runs the loopback PKCE flow (cli-auth-cli-spec §4): fresh discovery, a one-shot listener on 127.0.0.1, the link printed once, the browser opened unless the machine is headless, a 5-minute wait, the code exchange, and the record stored in the file store. It refuses to run without a terminal or with CI set, and every failure has its exit code and, under --json, its JSON document. xping logout revokes the refresh token and removes the sign-in and the caches for the Cloud URL, locally even when revocation fails (§12). xping auth status reports the credential in use without a request (§3.4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…configured Cloud URL (#247 review) - One AuthCommandRunner for login, logout and auth status: a bad XPING_CLOUDURL or settings file now writes the failure document (error "configuration", exit 2), the document names the configured Cloud URL, and every failure message is scrubbed on both streams. - Local listener failures report "local_listener", not "oauth_error". - logout tells "refused" from "could not reach" when revocation fails. - An invalid_request without a description reads cleanly. - Browser launchers run with /dev/null streams through a constant sh script, so a browser they start cannot write into --json output or die on a closed pipe. The launcher's duplicate headless check is gone. - Spec §3.2 and §5.1 amended; AGENTS.md names the --json output as the exception to the IXpingSerializer rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Phase 3 of
docs/internals/implementation-specs/cli-auth-cli-spec.md(§19). Merges into thefeat/cli-authintegration branch, notmain.Implements contract §3.1 (loopback flow), §3.4 (logout), §6.1 (claims for display), §10.1/§10.2 (loopback security) and §10.4 (storage at login), and spec §3.2–§3.4, §4, §5 and §12.
Spec amendments (committed before the code)
ConsoleIOnow has one terminal flag per stream.credentialSourcegains"config"for anXping:ApiKeyfrom appsettings.CI=true.[::1]fallback binds on Windows only. The managedHttpListeneron macOS and Linux rejects bracketed IPv6 prefixes ("Invalid port in prefix"). The attempt is kept so the retry order is the same on every OS.What changes
Pkce: 32-byte verifier and state, S256 challenge, constant-time state compare, bytes zeroed on dispose.LoopbackListener+LoopbackPages:127.0.0.1up to 3 times, then[::1].error_description.IHeadlessDetector/HeadlessDetector(§5.2) andIBrowserLauncher/BrowserLauncher(§5.1): per-OS commands, the URL passed as one argument, a 2 s cap, never throws.LoopbackFlow:TimeProvider.CredentialRecord: expiry is receipt time +expires_in; claims come fromAccessTokenClaims.LoginCommand:CI(exit 13).CredentialStores.--json, writes a document for every outcome, includingcancelled.LogoutCommand(§12): revokes with a 15 s cap, then removes the sign-in from every store, plus the project and discovery caches. Online givessigned-out, offlinesigned-out-locallywith a warning, and no sign-in givesnot-signed-in.AuthStatusCommand(§3.4): text and JSON for every credential state, exit 0 / 10 / 17, and no request ever.--verboselists the~/.xpingpaths.AuthJson(its own options, nulls written; the precedent isReportJsonOptions),AuthText(stderr rendering),CliConfigurationLoader,ProcessEnvironment(environment seam).Tests
Program.Runin-process againstFakeCloud, which gains/connect/authorizewith Approve / Deny / WrongState / ServerError / NoRedirect scripts. AFakeBrowserfollows the redirect to the real listener.Login_Loopback_Success,_Denied,_Timeout,_StateMismatch_ThenGenuine,_StateMismatch_OnlyLogin_Discovery_IssuerMismatch,_ContractVersion,_MinCliVersionLogin_NoTty,Login_CiSet,Login_WithApiKeySet_ProceedsLogout_Revokes_ThenDeletes,Logout_Offline_DeletesAndWarns,Logout_NotSignedIn,Logout_ApiKeyOnlyAuthStatus_*, each asserting that no request was madePkceTests(RFC 7636 appendix B vector),LoopbackListenerTests(real listener; bind retry against a held port),HeadlessDetectorTests(every §5.2 row),BrowserLauncherTests,AccessTokenClaimsTests,AuthJsonTests.Xping.Cli.Tests: 1937 passed. Release build has no warnings;dotnet format --verify-no-changesis clean.Manual verification against production (exit criterion)
On macOS with the file store:
xping login: the browser opened, the link was printed once, and the success block showed the workspace, the session suffix, andStored in ~/.xping/credentials.json.xping auth status: stored login, access token "expires in 14 minutes".~/.xping/credentials.jsonis-rw-------.xping logout: "Signed out of https://app.xping.io (…)". A secondlogoutsays "You are not signed in".Verified: Windows, non-elevated. The loopback listener uses
HttpListener(http.sys) onhttp://127.0.0.1:{port}/. The review asked whether http.sys lets a standard, non-admin process register that prefix without a URL reservation. If it doesn't, every Windows login fails with "Could not open a local port", and the spec (§4.3) forbids alocalhostfallback. Checked on a Windows laptop from a regular, non-elevatedcmd: http.sys accepts the127.0.0.1prefix without a URL reservation, and everything behaved as on macOS.dotnet test tests/Xping.Cli.Tests -c Release --filter "FullyQualifiedName~LoopbackListenerTests"passesxping loginsucceeds against production;auth statusshows the login;logoutrevokesReview fixes (f293044)
AuthCommandRunneris shared by the three commands. A badXPING_CLOUDURLor settings file now writes the failure document under--json(error: configuration, exit 2). The document names the configured Cloud URL, and every failure message is scrubbed on both streams.local_listener, notoauth_error.logoutnow says the server refused, rather than that it could not be reached, when revocation fails.invalid_requestwithout a description no longer prints a stray ": ."./dev/nullstreams through a constantshscript on Unix, with the URL passed as an argument. A browser they start can neither write into--jsonoutput nor die on a closed pipe.--jsonoutput as the exception to theIXpingSerializerrule.Not in this phase:
--device,--no-browserand--workspace(P4); keychains and the fallback warning (P5); refresh and the bearer pipeline (P6);docs/cli/command-reference.md(P8).🤖 Generated with Claude Code