fix(auth): fail clearly on a missing keyring token
Description
When a host has use_keyring: "true" but its keyring entry is gone, glab sent requests without a token. Every command ended in a bare 401 Unauthorized, with no hint about the missing credential. Now glab stops before the request, with the same kind of error OAuth hosts already get:
the token for "gitlab.example.com" is missing from the operating system keyring; run `glab auth login --hostname gitlab.example.com` to re-authenticateDecisions, by their effect on users:
- A token from
GITLAB_TOKEN, the plaintext config, or a CI job token still wins. The error only shows when there is no token at all. - Only
use_keyringin the host's own config entry counts. A top-leveluse_keyring(fromglab config set use_keyring true --global) andGLAB_USE_KEYRINGdo not, the same as when glab reads the token. Otherwise, they would make every tokenless host fail. glab auth login --weband--devicesaveuse_keyringbefore any token exists, so the first login would hit the new error. That login step now does not read or send a token, so a locked keyring does not block it either. It keeps the host's proxy, CA, client certificate,skip_tls_verifyandapi_hostsettings.- This also fixes a re-login loop. If an OAuth host's keyring entries are gone,
glab auth login --deviceonmainfails and tells you to runglab auth login. On this branch, the login starts.
Behavior changes on a host with use_keyring: "true" and no keyring entry:
- Anonymous reads (for example on public projects) now fail early with the error above.
glab auth docker-helperstill works for registries on other hosts, because it does not need the default host's token. A registry on this host still fails, and for an Artifact Registry the error is the one above.
Related Issues
Related to #8552
I didn't use Closes. I fixed the bug the maintainer described in the issue:
when a host is configured with
use_keyring: trueand the keyring entry is missing, glab treats the token as unset and builds an unauthenticated client, so the result is a bare 401 with nothing pointing back at the credential. The OAuth2 path already fails with an actionable message in that situation; the personal access token path does not.
The reporter's own 401 may come from how glab api and glab auth status pick the host. That question is still open, so I left the issue open.
How has this been tested?
I built glab from main and from this branch. I used a temporary config with use_keyring: "true" for a local stub server, and no keyring entry.
$ glab api user # built from main
{"message": "401 Unauthorized"}glab: 401 Unauthorized (HTTP 401)
$ glab api user # built from this branch
The token for "127.0.0.1:18552" is missing from the operating system keyring; run `glab auth login --hostname
127.0.0.1:18552` to re-authenticate.Then I removed use_keyring from the host entry and ran glab config set use_keyring true --global. The previous commit of this branch stopped with the keyring error here. Now the request goes out:
$ glab api user # built from this branch
{"message": "401 Unauthorized"}glab: 401 Unauthorized (HTTP 401)
$ GITLAB_TOKEN=glpat-stub glab api user # built from this branch
{"id": 1, "username": "stub-user", "name": "Stub"}For decision 3, I ran glab auth login --device for a new host. Both builds reached the device endpoint. A branch build with the old login code stopped with the new error instead. For decision 4, main failed with Oauth2 authentication is configured ... run glab auth login, and the branch reached the device endpoint.
I added unit tests for the new error, an environment token, a job token, GLAB_USE_KEYRING, and the login step keeping TLS and header settings. After the review, I added tests for --device with no stored token, a locked keyring when the login client skips the token, the Docker helper with a tokenless default host, and a tokenless host under a top-level use_keyring. Each of these fails on the code before the fix.
$ go test -count=1 ./internal/api/ ./internal/config/ ./internal/commands/auth/docker/ ./internal/commands/auth/login/ ./internal/commands/auth/status/
ok gitlab.com/gitlab-org/cli/internal/api 0.419s
ok gitlab.com/gitlab-org/cli/internal/config 2.242s
ok gitlab.com/gitlab-org/cli/internal/commands/auth/docker 0.261s
ok gitlab.com/gitlab-org/cli/internal/commands/auth/login 38.528s
ok gitlab.com/gitlab-org/cli/internal/commands/auth/status 0.030sgofmt -s -l, gofumpt -l and go vet printed nothing. golangci-lint run --new-from-rev on the changed packages reported 0 issues. The rest runs in the pipeline.
Screenshots (if appropriate):
Not applicable, the change is in CLI output. The new error text is above.
Implemented with AI assistance; I reviewed and tested the changes.