Repository: cli/cli

Issue:
Login with a web browser on headless machine
### Describe the feature or problem you’d like to solve

If I have no browser installed I should still be able to run `gh auth login` and select `Login with a web browser` as I can open the URL on another machine.

### Proposed solution

```
$ gh auth login
? What account do you want to log into? GitHub.com
- Logging into github.com
? How would you like to authenticate? Login with a web browser

! First copy your one-time code: XXXX-XXXX
No browser detected, please visit following URL:
  https://github.com/cli-auth
```

How will it benefit CLI and its users?

A lot of people use headless machines to do their work. This would make the login process simplier for those.

### Additional context

The current status looks like this:
```
$ gh auth login
? What account do you want to log into? GitHub.com
- Logging into github.com
? How would you like to authenticate? Login with a web browser

! First copy your one-time code: 36A4-6DE3
- Press Enter to open github.com in your browser... 
/usr/bin/xdg-open: line 871: www-browser: command not found
/usr/bin/xdg-open: line 871: links2: command not found
/usr/bin/xdg-open: line 871: elinks: command not found
/usr/bin/xdg-open: line 871: links: command not found
/usr/bin/xdg-open: line 871: lynx: command not found
```

Discussion:
@mislav: Hi, if `xdg-open` returns a nonzero exit status upon failure (as it should), then that will trigger this error condition in gh, and it will print the URL so you can open it on any computer with a web browser: https://github.com/cli/cli/blob/eb08a9bd29ef9e8b07815a38a168069caf66f240/internal/authflow/flow.go#L95-L99

Did that not happen to you? Can you check the exit status of `xdg-open` and see if you're running an up-to-date GitHub CLI?

You can also try setting `BROWSER=false gh auth login` to make it known that this is a headless environment.@pdostal: The `BROWSER=false` parameter works for me but it's not very straightforward. The output looks like:
```
$ BROWSER=false gh auth login
? What account do you want to log into? GitHub.com
- Logging into github.com
? How would you like to authenticate? Login with a web browser

! First copy your one-time code: C22A-D9BC
- Press Enter to open github.com in your browser... 
! Failed opening a web browser at https://github.com/login/device
  exit status 1
  Please try entering the URL in your browser manually
```

The `xdg-open 'http://github.com/'` command will open the page in `w3m` web browser for me.

I use this machine mainly in headless mode via SSH but it has basic graphical environment installed as well.
What about modifying the dialog by adding third option? So it would look like:
```
$ gh auth login
? What account do you want to log into? GitHub.com
- Logging into github.com
? How would you like to authenticate?  [Use arrows to move, type to filter]
> Login with a web browser
  Login with an external web browser
  Paste an authentication token
```@rpadaki: Alternatively, could we always print the target URL `https://github.com/login/device` when opening in a web browser? So maybe a less-clunky version of the below:

```txt
! First copy your one-time code: C22A-D9BC
- Press Enter to open github.com in your browser,
  or manually go to https://github.com/login/device@mislav: @pdostal I like your proposal. I would reword the options to be more clear about where the browser in question runs:

```
? How would you like to authenticate?  [Use arrows to move, type to filter]
> Login with a web browser on this machine
  Login with a web browser on a different machine
  Paste an authentication token
```

@rpadaki That's also an interesting idea, but I'm gravitating to the above flow because it requires more explicit intention from the user.@ssbarnea: As long the full url is displayed we should be fine. IMHO, even `Press Enter to open https://github.com/login/device in your browser` would suffice.

No need to be too smart (detect) about it as long the message is smart-enough to allow the user to go manual.@callumfrederiksen: I think that this is something that GitHub themselves may have to address since they are the ones hosting the site.

**However**, doing something like this may work using an api to an external web browser accessible on any device. This could potentially be done through the use of HTTP[S] GET/POST requests if executed in the right manner with security issues in mind if directly linked to an account.

Or you could do a command line login and not JUST an auth token?@ulfjack: This is also broken for me. My default browser is Chrome on Ubuntu, and I have multiple Chrome profiles. It pops open a profile selector, but the URL doesn't actually load after selecting a profile.@mislav: > It pops open a profile selector, but the URL doesn't actually load after selecting a profile.

That sounds like a problem with your browser, and not something that our tool should necessarily solve?

You could always try passing additional Chrome parameters in GH_BROWSER:
```
GH_BROWSER='google-chrome-stable --profile-directory="Profile 1"' gh auth login
```
To find out your profile name, I think you can determine that by opening the `chrome://version` URL.@ulfjack: You could always try to make your tool with as many configurations as possible. In any case, I'd be happy if the tool printed the URL, and I sent you a PR for that (it's literally a one-line change). That would also help with some of the use cases above.@ElanHasson: > Did that not happen to you? Can you check the exit status of `xdg-open` and see if you're running an up-to-date GitHub CLI?

This doesn't work for everyone. I  use the desktop environment when I'm at the machine, and ssh when I am not there.

I therefore never hit the failure scenario.

Why can't we just output the link ahead of time as suggested here: https://github.com/cli/cli/issues/5500#issuecomment-1113899039

Also, even when I do goto the URL manually and enter the code, `gh` hangs and doesn't ever acknowledge the code was entered. There is a bug here where a separate goroutine should be waiting for the code and cancel the other one that failed to launch the browser.

I love a great developer experience, however this endless bike shedding isn't fun.

```shell
> gh auth login
? What account do you want to log into? GitHub.com
? What is your preferred protocol for Git operations? HTTPS
? Authenticate Git with your GitHub credentials? Yes
? How would you like to authenticate GitHub CLI? Login with a web browser

! First copy your one-time code: ABC-123F
Press Enter to open github.com in your browser... 
[20013:20013:0612/204112.489039:ERROR:ozone_platform_x11.cc(239)] Missing X server or $DISPLAY
[20013:20013:0612/204112.489167:ERROR:env.cc(255)] The platform failed to initialize.  Exiting.

```@hilbix: Come on, its over a year now for such a simple thing!

This problem here hits me so often, that I already add a mandatory bookmark to <https://github.com/login/device> in all of my browsers which I use to login to GH.  I nearly dare to request Firefox Sync to be taken out of the disallowed browser applications at my side due to this only issue with GH to get the bookmarks synced, because it is extremely annoying that the URL is not printed at all and I always have to type that in or add the bookmark manually.

If the URL would be printed into the terminal, it is just a single click to open the real local correct browser (`terminology` anyone?), because the local terminal always knows the right browser, but the deeply buried `bash` on the remote ssh session does not.  Also it does not help if `xdg-open` opens a browser on the remote display (greetings from `dbus`), which then isn't logged into GH (if you happen to see the remote display at all, not all systems have `x11vnc` installed).

----------------

And if you are on it, please explain how to revoke the apropriate CLI again via web.  Without access to the CLI.

AFAICS the inverse is <https://github.com/settings/applications> and remove the entire GitHub Cli.

But this is clumsy, as this revokes access for all CLIs alltogether!
Is there a way to individually remove a single CLI?

Note that `gh auth logout` does not log out other CLIs, only the local one.  What if you want to remove all others but the current one?

No, this is NOT a request to add this feature (as this would be a new issue) but, **if you are on it, please add the URL to the reverse, too!**.  This could go into the same line ..@FloHu: I have not been able to resolve this. I work on a remote machine and can log in via remote desktop but this is extremely annoying. Why can I not authenticate on the CLI with my token? Reading the above comments I managed to get a browser open with w3m but after entering the one-time code I have to authorize GitHub to access data from my organisation (I don't know why I am asked every time) and then it fails because apparently JavaScript needs to work for that. 
Not everyone has full administrative control over their computers. I am in a corporate environment and have now spent several hours already fiddling around with authentication on remote servers. Also VSCode with ssh connection is not able to authenticate GitHub copilot properly. @daviewales: Note: I encounter this issue in WSL if I have curses-based browsers installed such as `links` or `lynx`. If I uninstall these, then `update-alternatives` sets the `www-browser` to `wslview`, which automatically opens URLs in the default Windows browser.

So, on WSL at least, you should be able to work around this problem with the following:

```
sudo update-alternatives --set www-browser /usr/bin/wslview
```@evilhamsterman: > Note: I encounter this issue in WSL if I have curses-based browsers installed such as `links` or `lynx`. If I uninstall these, then `update-alternatives` sets the `www-browser` to `wslview`, which automatically opens URLs in the default Windows browser.
> 
> So, on WSL at least, you should be able to work around this problem with the following:
> 
> ```
> sudo update-alternatives --set www-browser /usr/bin/wslview
> ```

You have to ensure the `wslu` package is installed to get the wslview app@Karinza38: https://github.com/cli/cli/issues/5500#issuecomment-1117504829@corneliusroemer: I've turned this into a StackOverflow Q&A for better discoverability and faster consumption: https://stackoverflow.com/questions/78890002/how-to-do-gh-auth-login-when-run-in-headless-mode/78890003#78890003

Solution patch:
diff --git a/internal/authflow/flow.go b/internal/authflow/flow.go
index 370e08784..3fc753b44 100644
--- a/internal/authflow/flow.go
+++ b/internal/authflow/flow.go
@@ -72,7 +72,7 @@ func AuthFlow(oauthHost string, IO *iostreams.IOStreams, notice string, addition
 				return nil
 			}
 
-			fmt.Fprintf(w, "%s to open %s in your browser... ", cs.Bold("Press Enter"), oauthHost)
+			fmt.Fprintf(w, "%s to open %s in your browser... ", cs.Bold("Press Enter"), authURL)
 			_ = waitForEnter(IO.In)
 
 			if err := b.Browse(authURL); err != nil {
diff --git a/pkg/cmd/extension/manager.go b/pkg/cmd/extension/manager.go
index 410d4c224..2431c6b83 100644
--- a/pkg/cmd/extension/manager.go
+++ b/pkg/cmd/extension/manager.go
@@ -29,6 +29,8 @@ import (
 // ErrInitialCommitFailed indicates the initial commit when making a new extension failed.
 var ErrInitialCommitFailed = errors.New("initial commit failed")
 
+const darwinAmd64 = "darwin-amd64"
+
 type Manager struct {
 	dataDir    func() string
 	lookPath   func(string) (string, error)
@@ -266,13 +268,16 @@ func (m *Manager) installBin(repo ghrepo.Interface, target string) error {
 	// if using an ARM-based Mac and an arm64 binary is unavailable, fall back to amd64 if a relevant binary is available and Rosetta 2 is installed
 	if asset == nil && isMacARM {
 		for _, a := range r.Assets {
-			if strings.HasSuffix(a.Name, "darwin-amd64") {
+			if strings.HasSuffix(a.Name, darwinAmd64) {
 				if !hasRosetta() {
 					return fmt.Errorf(
-						"%[1]s unsupported for %[2]s. Install Rosetta with `softwareupdate --install-rosetta` to use the available darwin-amd64 binary, or open an issue: `gh issue create -R %[3]s/%[1]s -t'Support %[2]s'`",
-						repo.RepoName(), platform, repo.RepoOwner())
+						"%[1]s unsupported for %[2]s. Install Rosetta with `softwareupdate --install-rosetta` to use the available %[3]s binary, or open an issue: `gh issue create -R %[4]s/%[1]s -t'Support %[2]s'`",
+						repo.RepoName(), platform, darwinAmd64, repo.RepoOwner())
 				}
 
+				fallbackMessage := fmt.Sprintf("%[1]s not available for %[2]s. Falling back to compatible %[3]s binary", repo.RepoName(), platform, darwinAmd64)
+				fmt.Fprintln(m.io.Out, fallbackMessage)
+
 				asset = &a
 				break
 			}



Test patch:
diff --git a/pkg/cmd/extension/manager_test.go b/pkg/cmd/extension/manager_test.go
index 0ad8e991e..e25bc1496 100644
--- a/pkg/cmd/extension/manager_test.go
+++ b/pkg/cmd/extension/manager_test.go
@@ -1098,7 +1098,7 @@ func TestManager_Install_amd64_when_supported(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "FAKE BINARY", string(fakeBin))
 
-	assert.Equal(t, "", stdout.String())
+	assert.Equal(t, "gh-bin-ext not available for darwin-arm64. Falling back to compatible darwin-amd64 binary\n", stdout.String())
 	assert.Equal(t, "", stderr.String())
 }