Skip to content

fix(auth): auto-recover from stale encrypted credentials after upgrade#435

Merged
jpoehnelt merged 8 commits intomainfrom
fix/stale-credentials.enc
Mar 12, 2026
Merged

fix(auth): auto-recover from stale encrypted credentials after upgrade#435
jpoehnelt merged 8 commits intomainfrom
fix/stale-credentials.enc

Conversation

@jpoehnelt
Copy link
Member

Summary

Fixes #389

After upgrading between versions that changed how the encryption key is stored (keyring migration in #345, native backends in #373), users can end up with a credentials.enc file encrypted with a now-unavailable key. This leaves them stuck in a loop where logout + login doesn't fix the issue.

Changes

src/auth.rs — When credentials.enc fails to decrypt:

  1. Automatically remove the stale encrypted credentials file
  2. Remove stale token caches (token_cache.json, sa_token_cache.json)
  3. Fall through to other credential sources (plaintext credentials.json, ADC) instead of hard-erroring
  4. If no other source exists, the user gets the standard "No credentials found" message directing them to gws auth login

src/credential_store.rs — When the OS keyring returns a valid key but no .encryption_key file backup exists, create one. This prevents future key loss if the keyring becomes unavailable (container restart, daemon change, OS upgrade).

Test plan

  • cargo clippy clean
  • cargo fmt clean
  • All 553 tests pass
  • New test: test_load_credentials_corrupt_encrypted_file_is_removed — verifies stale file is cleaned up and function falls through
  • New test: test_load_credentials_corrupt_encrypted_falls_through_to_plaintext — verifies fall-through to plaintext credentials
  • New test: keyring_backend_creates_file_backup_when_missing — verifies file backup is created when keyring has key but file is missing

When credentials.enc cannot be decrypted (e.g. after an upgrade that
changed the encryption key), automatically remove the stale file and
fall through to other credential sources (plaintext, ADC) instead of
hard-erroring. This breaks the stuck loop where logout+login couldn't
fix the issue.

Also sync .encryption_key file backup when the keyring has a valid key
but the file is missing, preventing future key loss if the keyring
becomes unavailable.

Fixes #389
@changeset-bot
Copy link

changeset-bot bot commented Mar 12, 2026

🦋 Changeset detected

Latest commit: ad04fa0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@googleworkspace/cli Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the robustness of credential management by introducing automatic recovery mechanisms for situations where encrypted credentials become unusable due to encryption key changes, typically after system upgrades. It ensures a smoother user experience by preventing credential-related dead ends and enhancing the resilience of encryption key storage.

Highlights

  • Automated Credential Recovery: Implemented automatic removal of credentials.enc and associated token caches (token_cache.json, sa_token_cache.json) when decryption fails, preventing users from being stuck with stale encrypted credentials after upgrades.
  • Credential Source Fallback: Modified the credential loading process to fall through to other credential sources (e.g., plaintext credentials.json, ADC) if encrypted credentials cannot be decrypted, instead of hard-erroring.
  • Encryption Key Backup Sync: Ensured that if the OS keyring provides a valid encryption key but the .encryption_key file backup is missing, a new backup file is created to prevent future key loss.
  • New Test Coverage: Added new tests to verify the automatic removal of corrupt encrypted credential files, the fallback mechanism to plaintext credentials, and the creation of encryption key file backups.
Changelog
  • .changeset/fix-stale-credentials-recovery.md
    • Added a new changeset file documenting the credential recovery fix.
  • src/auth.rs
    • Modified load_credentials_inner to handle decryption failures for credentials.enc by removing the stale file and associated token caches.
    • Updated credential loading to fall through to other credential sources if encrypted credentials cannot be decrypted.
    • Added test_load_credentials_corrupt_encrypted_file_is_removed to verify stale file cleanup.
    • Added test_load_credentials_corrupt_encrypted_falls_through_to_plaintext to verify fallback behavior.
  • src/credential_store.rs
    • Modified resolve_key to create a .encryption_key file backup if the keyring provides a key but the file is missing.
    • Added keyring_backend_creates_file_backup_when_missing test to verify the key file backup creation.
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Generative AI Prohibited Use Policy, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@googleworkspace-bot googleworkspace-bot added the cla: yes This human has signed the Contributor License Agreement. label Mar 12, 2026
@codecov
Copy link

codecov bot commented Mar 12, 2026

Codecov Report

❌ Patch coverage is 86.59794% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.02%. Comparing base (087066f) to head (ad04fa0).
⚠️ Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
src/auth.rs 86.95% 9 Missing ⚠️
src/credential_store.rs 85.71% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #435      +/-   ##
==========================================
+ Coverage   64.44%   66.02%   +1.57%     
==========================================
  Files          38       38              
  Lines       15601    16407     +806     
==========================================
+ Hits        10054    10832     +778     
- Misses       5547     5575      +28     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a helpful auto-recovery mechanism for stale encrypted credentials and improves the durability of the encryption key by creating a file backup from the keyring. The implementation is logical and includes relevant tests.

My review identifies a recurring pattern in the new error handling logic: file system operation failures are silently ignored. In both auth.rs when cleaning up stale files and in credential_store.rs when creating the key backup, ignoring these errors could prevent the recovery and backup mechanisms from working, leaving the user in a broken state without clear feedback. I've suggested changes to log these errors, which will make the new features more robust and transparent to the user.

@github-actions github-actions bot added the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
Address review feedback: log warnings when file removal or backup
creation fails, so users get clear feedback instead of silent failures.
Token cache cleanup now skips NotFound errors since they may not exist.
@github-actions github-actions bot removed the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
@googleworkspace-bot
Copy link
Collaborator

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a robust recovery mechanism for when encrypted credentials become undecryptable, for example after an upgrade. Instead of erroring, the application now removes the stale files and falls back to other credential sources. The change also proactively creates a file backup of the encryption key when it's found in the OS keyring, improving resilience. The implementation is logical and includes good test coverage. I have one suggestion to improve the consistency of file operations within an async context.

@github-actions github-actions bot added the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
Switch from std::fs::remove_file to tokio::fs::remove_file in the
async load_credentials_inner function to avoid blocking the runtime,
consistent with other file operations in the same function.
@github-actions github-actions bot removed the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
@googleworkspace-bot
Copy link
Collaborator

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a robust recovery mechanism for stale encrypted credentials, which is a significant improvement for user experience after upgrades. The changes in src/auth.rs to handle decryption errors by cleaning up stale files and falling through to other credential sources are well-implemented. The addition in src/credential_store.rs to back up the keyring key to a file is a good defensive measure against key loss. The new tests effectively cover these changes. I have a couple of suggestions to improve code reuse and robustness.

@github-actions github-actions bot added the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
- Reuse parse_credential_file for encrypted creds instead of manual
  JSON parsing. This removes duplication and adds ServiceAccount support.
- Use save_key_file_exclusive (atomic) for key backup creation, with
  AlreadyExists race condition handling.
- Update stale comment about encrypted creds being AuthorizedUser only.
@github-actions github-actions bot removed the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
@googleworkspace-bot
Copy link
Collaborator

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively addresses the issue of stale encrypted credentials after an upgrade by automatically cleaning up undecryptable files and falling back to other credential sources. The added logic to back up the keyring encryption key to a file is a great improvement for robustness. The changes are well-tested. I have one suggestion to improve maintainability by centralizing the hardcoded token cache filenames.

@github-actions github-actions bot added the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
Extract hardcoded 'token_cache.json' and 'sa_token_cache.json' strings
into TOKEN_CACHE_FILENAME and SA_TOKEN_CACHE_FILENAME constants in
auth_commands.rs. Update all 5 reference sites in auth.rs and
auth_commands.rs to use the constants.
@github-actions github-actions bot removed the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
@googleworkspace-bot
Copy link
Collaborator

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a robust auto-recovery mechanism for stale encrypted credentials, which is a great improvement for user experience after upgrades. The changes are well-implemented and include thorough tests. I've also added a suggestion to further improve the resilience of the encryption key backup mechanism to ensure it stays synchronized with the OS keyring, preventing potential issues if the keyring becomes unavailable.

@github-actions github-actions bot added the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
- Unconditionally sync the .encryption_key file with the keyring value
  on every successful read, not just when file is missing. Prevents
  stale file backups causing decryption failures if keyring becomes
  unavailable later. Uses save_key_file (overwrite + fsync) instead
  of save_key_file_exclusive (create-only).

- Use parse_credential_file for plaintext credentials at default path,
  consistent with encrypted/ADC paths. Adds ServiceAccount support
  for credentials.json, not just AuthorizedUser.

- Updated test: keyring_backend_syncs_file_when_keyring_differs
  verifies file content is overwritten to match keyring key.
@github-actions github-actions bot removed the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
@googleworkspace-bot
Copy link
Collaborator

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a robust auto-recovery mechanism for stale encrypted credentials, which can occur after an application upgrade changes key storage. When decryption fails, the stale credentials and token caches are now automatically removed, and the application falls back to other credential sources, preventing users from getting stuck in an unrecoverable state. Additionally, the encryption key from the OS keyring is now proactively backed up to a file to prevent key loss if the keyring becomes unavailable.

The changes are well-implemented and include thorough tests. I have one high-severity concern regarding the atomicity of the key file backup operation, which could lead to data loss under certain failure conditions. The details are in the specific comment.

@github-actions github-actions bot added the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
Move TOKEN_CACHE_FILENAME/SA_TOKEN_CACHE_FILENAME constants and
parse_credential_file for the plaintext credentials path to a
follow-up refactor PR — keep this PR focused on stale credential
auto-recovery.
@github-actions github-actions bot removed the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
@googleworkspace-bot
Copy link
Collaborator

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a robust auto-recovery mechanism for stale encrypted credentials, which is a significant improvement for user experience after upgrades. When decryption fails, the CLI now intelligently removes the corrupt file and associated token caches, then gracefully falls back to other credential sources. Additionally, the change to back up the encryption key from the OS keyring to a file enhances the resilience of the authentication system. The code is well-structured and includes relevant tests. I've identified a minor issue in the new tests related to using synchronous I/O in an asynchronous context, which should be addressed.

@github-actions github-actions bot added the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
Replace std::fs::write with tokio::fs::write().await in async test
functions for consistency with the async runtime.
@github-actions github-actions bot removed the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
@googleworkspace-bot
Copy link
Collaborator

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a robust recovery mechanism for stale encrypted credentials, which is a great improvement for user experience after upgrades. The logic to remove undecryptable files and fall back to other credential sources is well-implemented and tested. Similarly, adding a file backup for the keyring key enhances the resilience of the credential store. My only suggestion is to avoid hardcoding token cache filenames to improve maintainability, as detailed in the specific comment.

@github-actions github-actions bot added the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 12, 2026
@jpoehnelt jpoehnelt merged commit 510024f into main Mar 12, 2026
34 checks passed
@jpoehnelt jpoehnelt deleted the fix/stale-credentials.enc branch March 12, 2026 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: auth cla: yes This human has signed the Contributor License Agreement. gemini: reviewed Gemini Code Assist has reviewed the latest changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auth broken after update

3 participants