-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Fix auth login and auth refresh to use UNIX socket
#11922
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The assertions should check for header values instead of single string, because when getting a single header value, we get an empty string if the header is not set or is set to an empty string. So, to make sure a header is not set we should check the `Values` slice which is `nil` only if the header is missing. Signed-off-by: Babak K. Shandiz <babakks@github.com>
Signed-off-by: Babak K. Shandiz <babakks@github.com>
Signed-off-by: Babak K. Shandiz <babakks@github.com>
Signed-off-by: Babak K. Shandiz <babakks@github.com>
Signed-off-by: Babak K. Shandiz <babakks@github.com>
Signed-off-by: Babak K. Shandiz <babakks@github.com>
…lient` Signed-off-by: Babak K. Shandiz <babakks@github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes an issue where gh auth login and gh auth refresh commands were not respecting UNIX socket configuration due to using zero HTTP clients instead of the configured ones. The fix refactors HTTP client instantiation to use the factory pattern and adds a new PlainHttpClient that supports UNIX sockets while skipping default headers for authentication flows.
- Introduces a new
PlainHttpClientfactory field that creates HTTP clients without automatic headers for OAuth flows - Updates authentication commands to use the proper HTTP clients from the factory
- Removes duplicate HTTP client creation and logging code from the
authflowpackage
Reviewed Changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/cmdutil/factory.go | Adds PlainHttpClient field to Factory struct for header-free HTTP clients |
| pkg/cmd/factory/default.go | Implements plainHttpClientFunc to create clients with UNIX socket support |
| pkg/cmd/factory/default_test.go | Adds test coverage for the new plain HTTP client functionality |
| pkg/cmd/auth/login/login.go | Updates login command to use factory-provided HTTP clients |
| pkg/cmd/auth/refresh/refresh.go | Updates refresh command to use factory-provided HTTP clients |
| pkg/cmd/auth/shared/login_flow.go | Adds PlainHTTPClient parameter to LoginOptions |
| internal/authflow/flow.go | Simplifies AuthFlow to accept HTTP client parameter and removes duplicate logging |
| api/http_client.go | Adds SkipDefaultHeaders option to HTTPClientOptions |
| api/http_client_test.go | Updates tests to cover the new SkipDefaultHeaders functionality |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
Signed-off-by: Babak K. Shandiz <babakks@github.com>
| assert.Equal(t, 204, res.StatusCode) | ||
| assert.Equal(t, []string{"GitHub CLI v1.2.3"}, receivedHeaders.Values("User-Agent")) | ||
| assert.Nil(t, receivedHeaders.Values("Authorization")) | ||
| assert.Nil(t, receivedHeaders.Values("Content-Type")) | ||
| assert.Nil(t, receivedHeaders.Values("Accept")) | ||
| assert.Nil(t, receivedHeaders.Values("Time-Zone")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice 🔥
| // PlainHttpClient is a special HTTP client that does not automatically set | ||
| // auth and other headers. This is meant to be used in situations where the | ||
| // client needs to specify the headers itself (e.g. during login). | ||
| PlainHttpClient func() (*http.Client, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thx for the comment 🙇
BagToad
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Thanks for the write-up and context; it made it straightforward to follow the reasoning behind the PR and the current implementation's problems.
|
I further tested the changes, and I'm going to merge this as it seems all good. The only practical difference (beside OAuth flow now passing through the UNIX socket, if any) is that the OAuth flow API calls now have the right |
This MR contains the following updates: | Package | Update | Change | |---|---|---| | [cli/cli](https://github.com/cli/cli) | minor | `v2.81.0` -> `v2.82.0` | MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot). **Proposed changes to behavior should be submitted there as MRs.** --- ### Release Notes <details> <summary>cli/cli (cli/cli)</summary> ### [`v2.82.0`](https://github.com/cli/cli/releases/tag/v2.82.0): GitHub CLI 2.82.0 [Compare Source](cli/cli@v2.81.0...v2.82.0) ##### ✨ Features - `gh pr edit`: Only fetch org teams for reviewers when required by [@​BagToad](https://github.com/BagToad) in [#​11835](cli/cli#11835) ##### 🐛 Fixes - fix(cache delete): report correct deleted count for key and key+ref deletions by [@​luxass](https://github.com/luxass) in [#​11838](cli/cli#11838) - `gh agent-task create`: Fix `--follow` not killing the progress indicator by [@​BagToad](https://github.com/BagToad) in [#​11879](cli/cli#11879) - `gh agent-task create`: Fix targetting upstream instead of default repo by [@​BagToad](https://github.com/BagToad) in [#​11896](cli/cli#11896) - Fix `auth login` and `auth refresh` to use UNIX socket by [@​babakks](https://github.com/babakks) in [#​11922](cli/cli#11922) **Full Changelog**: <cli/cli@v2.81.0...v2.82.0> </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this MR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box --- This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNDguNiIsInVwZGF0ZWRJblZlciI6IjQxLjE0OC42IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiXX0=-->
Fixes #11891
This is a fix + refactor around how
ghinstantiates special HTTP clients for authentication purposes.Below is a bit about the changes, and then instructions to verify the PR fixes the UNIX socket support during login.
Rationale
As a bit of context, almost every
ghcommand usesf.HttpClientto get a new HTTP client instance. The client is created bycli/go-ghand is equipped with various layers of custom roundtrip overrides that take care of:Authorization,User-Agent,Accept, orContent-Type)Authorizationheader is a bit tricky as it gets set atghside.However, there were two places where a zero HTTP client (i.e.
http.Client{}) was used instead of the central one; inauth loginand inauth refresh. Both cases needed a plain HTTP client for their job and hence the workaround. The problem with these two outliers were:User-Agentheader was left to Golang's defaultGo-http-client/2.0Changes
To fix 1 and 2, we needed to use the HTTP client created by
cli/go-gh. Thankfully, there is an option namedSkipDefaultHeadersincli/go-gh, that we could just expose and use incli/clito create plain HTTP clients.To resolve 3, we needed to pull up the HTTP client instantiation to the command level where we have the information about the
ghversion (i.e.f.AppVersion). However, if we were going to do that exactly at the command-level, then we had to something similar to what is done in thefactorypackage, inhttpClientFunc(here). The better approach seemed to pull up the changes to thefactorypackage and then provide a plain counterpart toHttpClientin thefactorystruct. This is what's done in this PR; a new field, namedPlainHttpClientis now added tofactory.Problem 4 is now resolved by adding an
http.Clientparameter to theOAuthFlowfunction.Note that in the
auth logincommand, both HTTP client (ordinary and plain) are used. The plain one is only used for the OAuth flow, and the rest of the API calls are made through the ordinary one. Although usages of the ordinary client could be changed to use the plain one (except for one case where a function inssh addcommand package is called), but I decided to not do that in case we break things unknowingly; it'd probably be fine, though.Testing UNIX socket support
Against GHES
It's best to make a testing directory and do these within:
makeand copy the built binary to the testing directory.socat:temp.sockwill be created in the working directory.ghconfig directory once for all.ghto use the UNIX socket:./gh config set http_unix_socket temp.sockauth login, select Other and enter the hostname:socatlogs contain the same calls as inghdebug output (note the responses insocatlogs aregzip-ed so it's fine if request bodies match). FWIW in the issue (gh authignores the http_unix_socket config value #11891), the OAuth flow calls didn't pass through the socket.socatlogs.authcommands and verify they work as expected:Against
github.comTesting against
github.comis a bit tricky, since we use two different domains during the login process;github.comfor OAuth andapi.github.comfor secondary calls (e.g. discovering scopes). It's best to make a testing directory and do these within:makeand copy the built binary to the testing directory.hack.sh, to dynamically handle arbitrary domains:socatit to the script:socat -v unix-listen:temp.sock,fork system:'bash hack.sh'temp.sockwill be created in the working directory.ghconfig directory once for all.ghto use the UNIX socket:./gh config set http_unix_socket temp.sockauth loginand selectGitHub.comfor host:socatlogs contain the same calls as inghdebug output (note the responses insocatlogs aregzip-ed so it's fine if request bodies match). FWIW in the issue (gh authignores the http_unix_socket config value #11891), the OAuth flow calls didn't pass through the socket.socatlogs.authcommands and verify they work as expected: