Repository navigation
GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE ignored when credentials.enc exists (inverts documented precedence) #857
Description
Summary
On 0.22.5, when an encrypted credentials.enc exists in the config dir, setting GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE has no effect: API calls authenticate as the encrypted-credential account, not the account in the file. This inverts the documented precedence.
AGENTS.md and the README both state that CREDENTIALS_FILE takes precedence over encrypted credentials, with encrypted credentials being the fallback only when the env var is unset:
GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE| Path to OAuth credentials JSON (no default; if unset, falls back to encrypted credentials in~/.config/gws/)
(README precedence table: GOOGLE_WORKSPACE_CLI_TOKEN = priority 1, GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE = priority 2, encrypted credentials = priority 3.)
Observed precedence is the reverse for priorities 2 vs 3: the encrypted store wins whenever credentials.enc is present, even with CREDENTIALS_FILE explicitly set.
Reproduction
Controlled A/B in an isolated config dir, varying only the presence of credentials.enc. Two distinct accounts (call them A and B); credentials.enc is account A, the credentials file is account B.
# Case 1: config dir has a credentials.enc (account A), written by `gws auth login`
GOOGLE_WORKSPACE_CLI_CONFIG_DIR=/tmp/dir-with-enc \
GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE=/path/to/accountB-creds.json \
gws drive about get --params '{"fields":"user(emailAddress)"}'
# => returns account A's email (CREDENTIALS_FILE ignored)
# Case 2: identical, but config dir has NO credentials.enc
GOOGLE_WORKSPACE_CLI_CONFIG_DIR=/tmp/dir-without-enc \
GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE=/path/to/accountB-creds.json \
gws drive about get --params '{"fields":"user(emailAddress)"}'
# => returns account B's email (CREDENTIALS_FILE honored, correct)credentials.enc present |
CREDENTIALS_FILE set |
Identity returned |
|---|---|---|
| yes | yes (account B) | account A (wrong) |
| no | yes (account B) | account B (correct) |
The only difference between the two runs is whether credentials.enc exists. The credentials file is well-formed (type: authorized_user with a valid refresh_token); Case 2 proves it loads and authenticates correctly in isolation. This was reproduced from a freshly gws auth login-written credentials.enc in a pristine GOOGLE_WORKSPACE_CLI_CONFIG_DIR, so it is not stale/migration state.
Expected vs actual
- Expected (per docs): with
CREDENTIALS_FILEset, it wins;credentials.encis only the fallback when the var is unset. - Actual:
credentials.encshadowsCREDENTIALS_FILE.
Impact
This is the documented multi-account story: a user with a logged-in default account cannot select an alternate account via CREDENTIALS_FILE, because the default's credentials.enc always wins. The only reliable workaround is an isolated GOOGLE_WORKSPACE_CLI_CONFIG_DIR per account (which has no credentials.enc to shadow the file). Worth noting the failure is silent: it returns another account's data with a 200, rather than erroring, which is a sharp edge for agent use.
Environment
gws 0.22.5(Homebrewgoogleworkspace-cli0.22.5, macOS arm64)keyringbackend (default)
Possible locus
Credential-source selection likely consults the encrypted store before checking the GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE env var (auth.rs / credential_store.rs). The TOKEN (priority 1) path was not tested here.
Filed by an agent (Claude Opus 4.8 via opencode) and verified by a human (the clean-room A/B repro above was run interactively). ✨
Activity
cuppett commentedon Jun 30, 2026
Can confirm this also affects
GOOGLE_WORKSPACE_CLI_TOKEN(documented as priority 1).Setup:
gws 0.22.5on Fedora 44 x86_64,keyringbackend.gws auth loginwas done against GCP project A (cloudability-it). A separate OAuth client on GCP project B (dev) has Gmail/Calendar/Drive/Sheets APIs enabled.Repro for
TOKEN(priority 1):Also tried combining
TOKEN+CONFIG_DIRpointed at an empty dir +KEYRING_BACKEND=file— still got the project A error. Only way to get the env var honored was removingcredentials.encentirely (or in our case, disablinggwsfrom the calling script).So the full observed precedence is:
TOKENcredentials.encCREDENTIALS_FILEcredentials.enccredentials.encTOKENorCREDENTIALS_FILE(only when .enc absent)All three priorities are inverted when
credentials.encexists.