feat(auth): guide self-managed OAuth setup interactively
What does this MR do and why?
glab auth login --hostname <self-managed> currently fails with a bare
ERROR set 'client_id' first with ... message when no OAuth application
is configured for the host, forcing a context switch to docs, a separate
glab config set client_id command, and a re-run of auth login.
This MR replaces that dead end with a guided interactive prompt. When the
session is a TTY and no client_id is already resolvable (config file or
GITLAB_CLIENT_ID), glab asks how to continue:
No OAuth application ID is configured for self.hosted.gitlab.com.
How do you want to continue?
> I have an Application ID to paste
Show me what an admin needs to create
Cancel- Paste: prompts for the Application ID, then continues straight into
login. The value is persisted exactly the way
glab config set client_id <id> -g --host <hostname>would, but only once the OAuth round trip that used it actually succeeds (see below). - Show me: prints the redirect URI, scopes, and confidential setting an admin needs to register an application, plus a link to the docs, then exits with today's error unchanged.
- Cancel, or Ctrl+C at either step: also exits with today's error unchanged.
Non-interactive sessions (CI, scripts, piped stdin) are unaffected: they
keep today's error message pointing at glab config set client_id, and so
does a client_id already resolvable from config or GITLAB_CLIENT_ID
(no prompt at all in that case).
Why the value isn't persisted on paste
An earlier version of this MR wrote the pasted client_id to config
immediately. Review caught that this makes a bad paste sticky: a typo, an
ID from another instance, or the application Secret pasted in place of the
Application ID (both shown together on the same admin page) would be
persisted right away. Once client_id is non-empty, the guided prompt
never fires again, and StartFlow waits on the OAuth callback with no
timeout, so glab hangs silently instead of failing visibly, the exact
symptom cli#1338 describes.
resolveClientID now returns a persist function alongside the client_id
instead of writing it itself. StartFlow/StartDeviceFlow call it only
after their OAuth round trip actually succeeds, so a pasted value is only
committed once it's been proven to work. An already-configured or
GITLAB_CLIENT_ID-supplied value is unaffected: its persist function is a
no-op.
Where this lives
internal/oauth2/config.go:
oauthClientIDlooks up what's already configured (config file orGITLAB_CLIENT_ID, viacfg.Get), without prompting.resolveClientIDis the only place that decides what to do when nothing is configured yet: prompts ifiois interactive, otherwise returns the standard non-interactive error.promptClientIDChoice/promptClientID/printClientIDRegistrationInstructionsimplement the paste/show/cancel flow above.
StartFlow (oauth2.go, Web flow) and StartDeviceFlow (device.go,
Device flow) both call resolveClientID, then persist the returned
client_id only after their respective OAuth exchange succeeds.
NewConfigTokenSource's background token-refresh path
(token_source.go) calls oauthClientID directly and never prompts.
References
Fixes #8525 (closed)
Related to cli#1338 (closed in 2024 by making client_id itself
configurable via glab config set client_id), but distinct from it: that
issue's fix didn't add a timeout to the OAuth callback wait, so a wrong
client_id (however it got there) still hangs glab silently instead of
failing with an error. This MR's guided prompt makes it easier to end up
with a client_id in the first place, which is exactly why the
deferred-persist fix above matters, it keeps this MR from reintroducing
that same silent-hang symptom through the new prompt.
Screenshots or screen recordings
N/A (terminal-only prompt change)
How to set up and validate locally
- Run
glab auth login --hostname <a self-managed instance with no client_id configured>from an interactive terminal. - Pick "Show me what an admin needs to create": confirm the redirect URI, scopes, confidential setting, and docs link print, then confirm the command exits with the standard
set 'client_id' firsterror and nothing was written (glab config get client_id --host <hostname>stays empty). - Re-run, pick "Cancel": confirm the same standard error, nothing written.
- Re-run, pick "I have an Application ID to paste" and paste a valid Application ID: confirm login continues, and confirm
glab config get client_id --host <hostname>reflects the pasted value only after login succeeds. - Re-run non-interactively (e.g. with stdin redirected from
/dev/null) and confirm the original error message is unchanged. - With
GITLAB_CLIENT_IDset (orclient_idalready configured for the host), confirm no prompt appears at all.