fix(auth): skip OAuth2 refresh in helper for PAT hosts

Description

After re-running glab auth login for the v1.111.0 keyring migration and choosing Token, every registry pull broke with this error:

$ docker pull registry.gitlab.com/gitlab-org/.../gitlab-gdk-in-a-box:main
error getting credentials - err: exit status 1, out: ``

$ echo registry.gitlab.com | docker-credential-glab get   # the real error
Failed to get OAuth2 token source to potentially refresh token: parsing time "" as
"02 Jan 06 15:04 -0700": cannot parse "" as "02"

But glab auth status reported the host as healthy throughout. I asked Claude to investigate, turns out the docker credential helper refreshed the token unconditionally: unmarshal parses oauth2_expiry_date before it reads the token, and a PAT host has no such key, so getContainerRegistryToken failed on the empty string before ever reaching the user and token reads.

That function is documented as reading "the stored user/token for the associated host directly, without minting anything new" – the unconditional refresh contradicts its own contract.

How to reproduce

  • Run glab auth login --hostname gitlab.com and choose Token (not Web or Device).
  • Run glab auth configure-docker.
  • Run docker pull on any image from registry.gitlab.com — it fails with the error above.
  • Workaround: log in again with Web or Device, which writes the oauth2_* keys the helper was assuming.

Fix

To fix this, we gate the refresh on is_oauth2, as api.NewClientFromConfig already does for every API call. Only an OAuth2 access token expires, a PAT has nothing to refresh. We use GetWithSource(..., false) so GLAB_IS_OAUTH2 cannot force the OAuth2 path onto a PAT host.

The new guard covers the h.cfg.Reload() call as well as the refresh. That reload exists only to pick up a token ts.Token() just rotated, and a PAT host has none. On the other hand, cfg stays declared above the guard, because the user and token below still use it – a PAT host falls through with the original h.cfg.

Not a regression from keyring migration, the unconditional refresh dates to the helper's introduction. The keyring migration only made it visible.

N/A

How has this been tested?

I added a new table case in helper_test.go for a PAT-only host.

It fails on main with the exact error above and passes with the fix.

Also checked with GLAB_IS_OAUTH2=true set.

Run tests to confirm:

go test ./internal/commands/auth/docker/... \
        ./internal/oauth2/... \
        ./internal/config/...
Edited by Ahmed Hemdan

Merge request reports

Loading
Loading