The "unauthorized_client" error means that OAuth Device Flow is implemented, but either isn't enabled on this GHES instance, or that it isn't enabled for this particular OAuth app. In these cases we fall back to old OAuth app authorization flow.
I want to avoid falling back to the old OAuth flow for just any HTTP
4xx/5xx because other statuses should be allowed to surface a problem
with a request or the server.
Before, we implemented the OAuth app authorization flow which requires a
callback URL. To provide such a URL, we had to spin up a local HTTP
server, which was brittle and did not cover cases where a person might
want to authenticate with a browser that runs on a different machine
than the GitHub CLI process.
This implements the OAuth Device Authorization flow where the user is
given a one-time code and asked to paste it in the browser flow. There
is no callback URL, so we can avoid spinning up a local server, and the
user may open a browser on any of their devices, as long as they provide
the correct one-time code.
If the Device Authorization flow is detected to be unavailable for the
OAuth app (right now, it's specifically enabled for GitHub CLI) or for
an older GitHub Enterprise instance, this falls back to the old app
authentication flow.
Avoids the error when `localhost` was resolved to something else other
than `127.0.0.1`:
listen tcp 192.168.1.*:0: bind: can't assign requested address
Web developers who have previously ran an application on
`http://localhost` that enabled HSTS (HTTP Strict Transport Security)
will find themselves unable to authenticate because their browser
(typically Safari, in practice) will keep redirecting them to
`https://localhost`, which isn't handled by our local server.
This switches the authorization callback to be to `127.0.0.1`, which
should be equivalent to `localhost`, but not subject to HSTS.
Mitigates https://github.com/cli/cli/issues/297
This adds some more information to the output when authentication fails
due to `gh` being unable to open a browser. It communicates the details
of the workaround without the user having to search issues on the `gh`
repo to see how to get around it.
Before, the local server handled any request regardless of path, which
could potentially include requests generated by the browser such as the
one for favicon. This could lead to race conditions around reading the
code to continue to OAuth flow with.
Now, have the OAuth flow redirect to `localhost:PORT/callback` and only
handle `/callback` requests specifically.