Sitelet https://github.com/googleworkspace/cli/issues/886
Skip to content

Credentials file is silently deleted when decryption fails — user is logged out with no error explaining why #886

Open

Description

@grahamtyler-gw

Summary

When gws cannot decrypt credentials.enc, it deletes the file rather than reporting the problem. The error surfaced to the user is unrelated to authentication, so there is no indication that the saved login was just destroyed. The only recovery is gws auth login.

I hit this with real credentials, then reproduced it deliberately in an isolated config directory.

Environment

  • gws 0.22.5
  • macOS 26.5.2 (Darwin 25.5.0), Apple Silicon (arm64)
  • zsh, Node.js v26.5.0
  • Keyring backend: keyring (macOS Keychain), storage: encrypted

Reproduction

export D=/tmp/gws-repro
mkdir -p $D

# minimal client config (values can be fake)
cat > $D/client_secret.json <<'EOF'
{"installed":{"client_id":"000000000000-fake.apps.googleusercontent.com",
"project_id":"fake-project-123","auth_uri":"https://accounts.google.com/o/oauth2/auth",
"token_uri":"https://oauth2.googleapis.com/token","client_secret":"FAKE"}}
EOF

# stand in for a credentials file that cannot be decrypted
echo "UNDECRYPTABLE" > $D/credentials.enc

ls $D          # -> client_secret.json  credentials.enc
GOOGLE_WORKSPACE_CLI_CONFIG_DIR=$D gws drive files list --params '{"pageSize":1}'
ls $D          # -> client_secret.json  (credentials.enc is gone)

credentials.enc is deleted. Nothing in the command output mentions credentials, decryption, or logout.

I ran this both with the Keychain reachable and in a stripped environment where it was not. The file is deleted in both cases, so the trigger is decryption failure generally, not Keychain availability specifically.

How this happened with real credentials

I had a working, fully authenticated setup. I then switched install methods — from npx @googleworkspace/cli to npm install -g @googleworkspace/cli — and ran a command.

macOS Keychain access control is per-binary-path, so the newly installed binary at a different path could not retrieve the encryption key that the previous one stored. Decryption failed, and credentials.enc plus token_cache.json were removed. gws auth status then reported auth_method: none, as though I had never signed in.

If that is the mechanism, any change to how gws is installed can silently destroy a user's saved login, which is a surprising outcome for what looks like a routine upgrade.

Expected behavior

Decryption failure should not delete user data. Preferably:

  1. Report the actual cause, e.g. Failed to decrypt credentials (encryption key not available from keyring). Run 'gws auth login' to re-authenticate.
  2. Leave credentials.enc in place, or move it aside (credentials.enc.bak) rather than removing it.
  3. If deletion is intentional, say so explicitly in the output so the user knows they have been logged out.

The current behavior is hard to diagnose because the visible error points somewhere else entirely — in my case at the GCP project, which was not the problem.

Related

Activity

  1. KlaasKing commented on Aug 21, 2026

    @KlaasKing

    Same deletion, different trigger — and one that I suspect is common: several MCP clients sharing one config dir while disagreeing about the keyring backend.

    Setup

    Four gws profiles under ~/.config/gws/<profile>, each its own Google account. Five MCP clients use those same four profiles via gws-mcp-server: Claude Code (CLI), Claude Desktop, Codex CLI, Gemini CLI and Antigravity IDE. Each client config passes GOOGLE_WORKSPACE_CLI_CONFIG_DIR and --gws-path.

    • gws 0.22.5, gws-mcp-server 0.4.0
    • macOS 26.6 (Darwin 25.6.0), arm64, Node 22.22.1

    What happened

    One client (Claude Code) was switched to GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file, because it also runs from a launchd job where the macOS Keychain is not reliably reachable. The other four clients kept the default keyring backend — their configs simply never mentioned the variable.

    From that day on, the profiles broke roughly daily, seemingly at random. The pattern was always the same:

    1. Client A (file backend) writes credentials.enc encrypted with .encryption_key.
    2. Client B (keyring backend) runs any tool call on the same profile, cannot decrypt, and gws deletes the file — exactly the behaviour in this issue.
    3. The user logs in again. Whichever client touches the profile next re-breaks it.

    Which profile went down depended only on which client had touched it last, which is why it looked random for days. Re-authenticating "fixed" it for anywhere between minutes and a few hours. The visible errors pointed elsewhere — invalid_grant: reauth related error (invalid_rapt) — so the decryption mismatch stayed invisible until I ran a command by hand and saw:

    Warning: removing undecryptable credentials file (…/credentials.enc): Decryption failed.
    Credentials may have been created on a different machine.
    

    "Created on a different machine" is a reasonable guess for the single-user case, but here both writers were the same machine, the same binary and the same config dir — only the backend differed.

    Why I think this will hit others

    Nothing in the MCP setup path surfaces the backend choice. A client config specifies GOOGLE_WORKSPACE_CLI_CONFIG_DIR and that reads as sufficient — the config dir looks like the unit of identity. The backend is an environment property, so two clients pointed at the same directory can silently disagree, and the failure only shows up later as an unrelated auth error. Anyone running gws from more than one MCP client, or from both a GUI client and a background job, can end up here.

    What would have helped, in order of usefulness

    1. Make the backend a property of the config dir, not of the environment. If the choice were recorded next to the credentials (or simply inferred from the presence of .encryption_key), no client could disagree with the directory it was pointed at. This alone would have prevented the whole thing.
    2. Don't delete on decryption failure. Rename to credentials.enc.unreadable and say what happened. Already the substance of this issue, but worth restating: deletion turns a recoverable mismatch into a forced re-login for every client at once.
    3. Report the mismatch in gws auth status. When .encryption_key exists in the config dir but the active backend is keyring (or vice versa), that is detectable and worth a warning.
    4. Mention the backend in the MCP setup docs, so multi-client setups pin it explicitly from the start.

    Workaround, offered as data rather than as a solution

    I have pinned GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file across all five client configs, and put a small wrapper in front of gws via --gws-path that (a) serialises calls per config dir with flock, and (b) keeps a copy of credentials.enc + .encryption_key after each successful call and restores it if gws has deleted it, giving up after three restores in fifteen minutes so a genuinely expired token still surfaces.

    To be clear: this has been running for a few hours, not weeks. I cannot yet say it holds. I am reporting it mainly because the diagnosis took several days and the deletion is what made it so hard to see — the evidence kept destroying itself.

  2. mattlampson commented on Oct 19, 2026

    @mattlampson

    Another trigger for the same deletion, with a valid credential and no install change — just a process that is denied keychain interaction.

    Environment: gws 0.22.5 (Homebrew, /opt/homebrew/bin/gws, unchanged binary), macOS 27.0.1 (Apple Silicon), keyring backend keyring, storage encrypted.

    What happened: the same credentials.enc works from Terminal/SSH sessions and from a launchd user agent that runs gws script scripts run every 5 minutes. Running any read-only command (e.g. gws sheets spreadsheets values get …) from a process launched by a desktop app whose children macOS refuses interactive login-keychain access printed:

    Using keyring backend: keyring
    Warning: removing undecryptable credentials file (~/.config/gws/credentials.enc): OS keyring failed: Platform secure storage failure: User interaction is not allowed.
    

    …and deleted the file. The scheduled agent immediately lost auth (401) until a full gws auth login. It happened three times in one day, once right after restoring the file from a Time Machine snapshot.

    Why this matters beyond #886's case: here the ciphertext was never corrupt and the key still existed — the keychain simply declined that caller (errSecInteractionNotAllowed). A transient, per-process access refusal is being treated as proof the credential is garbage, so one restricted process destructively logs out every other consumer sharing the config dir (similar shape to the multi-client comment above).

    Suggested fix (complements #891): distinguish keyring access/interaction failures from authenticated-decryption failures. For the former, exit non-zero with an actionable message and leave credentials.enc byte-identical; never delete on a keyring error. If deletion is ever kept for genuine corruption, rename to credentials.enc.bak instead.

    Workaround we use meanwhile: a wrapper that snapshots credentials.enc before each run and restores it only if the file existed at start and vanished during that run, plus routing restricted callers through a launchd agent that does have keychain access.

  3. added a commit that references this issue on Oct 31, 2026
    2063544
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions