Every AI coding assistant on a developer laptop holds at least one credential, and several of them can fall back to a plaintext file or read a token from an environment variable that quietly overrides the one you intended. Those tokens reach model APIs, GitHub, cloud accounts and every MCP server the assistant is configured to call, so a stolen laptop profile or a malicious extension can turn them into direct access. This runbook lists where Claude Code, GitHub Copilot CLI, OpenAI Codex, Gemini CLI, VS Code and Cursor store credentials and configuration, how to check each location without exposing the secret, how to rate what you find, and how to move to safer storage with a rollback. It is for endpoint, platform and application security teams auditing developer machines. For the wider strategy, see Developer endpoints as NHI stores; for permission and sandbox settings, see the AI IDE and Coding Agent Security Guide.
This is an independent guide from NHI Mgmt Group, based on each vendor's public documentation as of November 2026. It is not sponsored or reviewed by any vendor. These tools change monthly, so confirm paths and settings against current documentation before scripting them.
Key takeaways
- Know the fallbacks. Claude Code, Copilot CLI and Codex prefer the OS keychain but can write plaintext files:
~/.claude/.credentials.json,~/.copilot/config.jsonand~/.codex/auth.json. - Environment variables win. Copilot CLI checks
COPILOT_GITHUB_TOKEN,GH_TOKENandGITHUB_TOKENbefore its keychain token, and Claude Code ranksANTHROPIC_AUTH_TOKENandANTHROPIC_API_KEYabove its own login. Shell profiles are part of the audit. - Config files carry secrets too. MCP configurations (
~/.claude.json,.mcp.json,.cursor/mcp.json,.vscode/mcp.json) and.gemini/.envfiles often hold literal API keys and bearer headers. - Logout is not revocation. Copilot CLI's
/logoutremoves the local token but does not revoke it on GitHub; revoke at the issuer as well. - Enforce with managed settings. Claude Code's
forceLoginMethod,forceLoginOrgUUIDandallowedProviders, and Codex'sforced_login_methodandcli_auth_credentials_store, turn audit findings into policy.
When you need this
- Before or after rolling out an assistant: a baseline shows which login methods and storage modes are actually in use, compared with the approved one.
- After a malicious extension or package report: campaigns such as the JetBrains Marketplace AI plugin campaign, where fake AI coding assistants stole developers' AI API keys, and the TrapDoor supply chain campaign, which hunted developer credentials, show attackers going after the developer credentials these files hold.
- When a developer leaves or a laptop is lost: you need to know which tokens existed on that machine to revoke them. The Compromised Developer Workstation Playbook covers the incident side.
- For audit evidence: NIST SP 800-53 IA-5(7) asks that unencrypted static authenticators are not embedded in applications or stored in files; plaintext token files are the case it describes.
Where each assistant keeps credentials
| Tool | Preferred store | File fallback or alternative | Environment variables that override |
|---|---|---|---|
| Claude Code | macOS Keychain | ~/.claude/.credentials.json (mode 0600) on Linux, on Windows as %USERPROFILE%\.claude\.credentials.json, and on macOS when the Keychain rejects the write, for example over SSH. CLAUDE_CONFIG_DIR moves it | ANTHROPIC_AUTH_TOKEN, ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, plus apiKeyHelper and Anthropic profiles in ~/.config/anthropic |
| GitHub Copilot CLI | OS keychain, service name copilot-cli | Plaintext ~/.copilot/config.json if no keychain is available and the user accepts | COPILOT_GITHUB_TOKEN, GH_TOKEN, GITHUB_TOKEN; then gh auth token as a fallback |
| OpenAI Codex CLI and IDE extension | OS credential store when cli_auth_credentials_store is keyring or auto | Plaintext auth.json under CODEX_HOME (default ~/.codex) when set to file or when auto falls back | API key sign-in is an alternative to ChatGPT sign-in |
| Gemini CLI | Cached Google sign-in | .gemini/.env found by searching up from the working directory, then ~/.gemini/.env; only the first file found is loaded | GEMINI_API_KEY, GOOGLE_API_KEY, GOOGLE_APPLICATION_CREDENTIALS (path to a service account key) |
| VS Code (Copilot MCP) | VS Code secret storage for input variables and confidential OAuth client secrets | .vscode/mcp.json, profile mcp.json, ~/.copilot/mcp-config.json, workspace .mcp.json | ${NAME} references resolved from the host environment |
| Cursor (MCP) | OAuth tokens for remote MCP servers | .cursor/mcp.json and ~/.cursor/mcp.json, with env, envFile, headers and auth | ${env:NAME} interpolation in command, args, env, url and headers |
Gemini CLI's own documentation states it was replaced by Antigravity CLI on 18 June 2026; machines that still run it should be in the audit and on a retirement list.
Before you start
- Do not collect secrets: the audit records that a credential exists, its type (from the prefix, such as
gho_orgithub_pat_), the file, its permissions and its owner. It never copies the value into a ticket, spreadsheet or log. Codex's documentation tells users to treatauth.jsonlike a password and never paste it into tickets or chat. - Use your endpoint tooling: run checks through EDR live query, MDM scripts or osquery with read-only scope, and report file paths, sizes, modes and modification times only.
- Agree the approved state per tool: for example, Claude Code via organisation login only, Copilot CLI via keychain OAuth only, Codex with
cli_auth_credentials_store = "keyring", and no literal secrets in any MCP or.envfile. - Prepare revocation routes: know where each token type is revoked (GitHub Settings > Applications > Authorized OAuth Apps for Copilot CLI OAuth, the Claude Console and OpenAI platform for API keys) before you find anything. See How to audit OpenAI and Anthropic organisation API keys.
Step-by-step
Steps 1 to 5 are read-only. Steps 6 and 7 change configuration and are reversible. Step 8 revokes credentials and cannot be undone for that token.
- Check for plaintext credential files. On each machine, test for the presence, owner and mode of
~/.claude/.credentials.json(and under anyCLAUDE_CONFIG_DIR),~/.copilot/config.json,~/.codex/auth.json(and under anyCODEX_HOME), and Anthropic profile files under~/.config/anthropic. Claude Code writes its file with mode 0600; any group or world readable mode is a finding. On macOS, a Claude Code credentials file means a Keychain write failed at some point, so the token sits on disk instead. - Check environment and shell profiles. Search shell startup files (
~/.bashrc,~/.zshrc, PowerShell profiles) and user environment settings for the variable names in the table above, recording the name only. Gemini CLI's documentation warns that keys exported in a shell startup file are readable by any process launched from that shell. Also check theenvblock in~/.claude/settings.jsonand.claude/settings.json, because Claude Code readsCLAUDE_CODE_OAUTH_TOKENfrom there in every new session until it is removed. - Check the assistant's own view. Ask developers or a scripted session to run Claude Code's
/status, which shows the active login method or profile, marks unused credentials, and listsSetting sourcesincluding any managed source. A long-lived token created byclaude setup-tokenis valid for one year and is printed rather than stored, so look for it in CI variables and password managers as well as on the laptop. - Scan MCP and assistant config files for literal secrets. Check
~/.claude.json(Claude Code stores MCP server configurations there), project.mcp.json,~/.cursor/mcp.jsonand.cursor/mcp.json,.vscode/mcp.json, the VS Code profilemcp.jsonand~/.copilot/mcp-config.json. A value inenv,headersorauththat is not a${env:NAME},${NAME}or${input:...}reference is a literal secret. VS Code's documentation is explicit that a personal configuration file is not a secret store. The How to audit MCP servers on developer laptops runbook covers the servers themselves. - Check repositories for committed assistant files. In source control, search for committed
.claude/settings.local.json,.gemini/.env,.mcp.jsonand.cursor/mcp.jsonfiles containing literal values. Claude Code adds**/.claude/settings.local.jsonto the global git excludes only when it creates the file itself; a hand-made file is not protected. Use your secret scanner for the key formats, as in How to find LLM API keys in code and CI. - Move credentials to safer storage (reversible). For Codex, set
cli_auth_credentials_store = "keyring", which fails rather than falling back when no store is available; administrators can enforce it so users cannot override it inconfig.toml. For Copilot CLI, remove the plaintext file and sign in again withcopilot loginon a machine with a working keychain. For Claude Code on macOS, follow its recovery steps to move the login back into the Keychain. Replace literal MCP secrets with${env:NAME}(Cursor) or VS Codeinputswith"password": true, whose value VS Code stores securely. - Enforce the approved login (reversible). In Claude Code managed settings set
forceLoginMethodandforceLoginOrgUUIDto your organisation, andallowedProvidersto the providers you pay for. UnderforceLoginOrgUUID,ANTHROPIC_API_KEY,ANTHROPIC_AUTH_TOKENandapiKeyHelpercredentials are blocked at startup. Note Anthropic's caveat thatclaude setup-tokenenforces onlyforceLoginMethod, so it can mint a token in another organisation. For Codex, setforced_login_methodandforced_chatgpt_workspace_id; Codex logs out and exits when credentials do not match. - Revoke what was exposed (irreversible for that token). Any credential found in a world-readable file, a committed file or on a lost machine is revoked at the issuer, not just deleted locally. Copilot CLI's
/logoutdoes not revoke; use GitHub's authorised OAuth apps page, and for personal access tokens see How to revoke a leaked GitHub token. Claude Code's/logoutremoves and revokes a credential written by the keyless Console sign-in. Rotate API keys in the issuing console and update the approved store.
Rating what you find
| Finding | Risk | Action |
|---|---|---|
| Credential file readable by group or others | High | Revoke, fix mode, move to keychain |
| Literal API key or bearer header in a committed config file | High | Revoke, remove from history, replace with a reference |
Classic or broad GitHub token in GH_TOKEN used by the assistant | High | Replace with Copilot CLI OAuth or a fine-grained token with only the Copilot Requests permission |
| API key exported in a shell profile | Medium | Move to apiKeyHelper, keychain or a vault-backed helper |
| Plaintext fallback file with mode 0600 on a single-user machine | Medium | Move to keychain at next sign-in |
| Personal login on a corporate machine where organisation login is required | Medium | Enforce login method and organisation in managed settings |
| Keychain storage, organisation login, references only in config | Low | Record as compliant |
Verify it worked
- Files: a repeat scan finds no plaintext credential files outside approved exceptions, and none with permissive modes.
- Login:
/statusin Claude Code shows the organisation login and a managed settings source; Codex starts without forced logout. - Config: no MCP or
.envfile contains a literal secret; every value is a reference. - Revocation: revoked tokens fail at the issuer, and the issuer's logs show no use after revocation.
Rollback and common errors
| Situation | Fix |
|---|---|
Codex fails after setting keyring on a headless host | keyring fails when no store exists; use auto for that host class and record the exception |
Claude Code blocked at startup after enforcing forceLoginOrgUUID | An environment credential or saved API key is still present; remove it, or relax the setting while the developer migrates |
| Copilot CLI uses the wrong account | An environment variable silently overrides the keychain token; unset it |
| MCP server fails after replacing a literal secret | The variable is not set in the environment of the process that starts the server; set it there, or temporarily restore the previous config from backup |
Gemini CLI ignores your .env | Only the first .env found is loaded; a project file shadows ~/.gemini/.env |
Common failure patterns
- Treating logout as revocation: the token stays valid at the issuer.
- Auditing only the keychain: the environment variable that overrides it is the one actually in use.
- Collecting secrets in the audit: copying token values into a spreadsheet creates a new, larger store.
- Forgetting CI:
CLAUDE_CODE_OAUTH_TOKENand API keys built for laptops often end up in pipelines with longer lives than intended. - Ignoring per-project files:
.mcp.jsonand.gemini/.envin a repository travel with every clone.
Hardening afterwards
- Prefer short-lived credentials: use
apiKeyHelperto fetch short-lived tokens from a vault, or cloud provider authentication, instead of static keys on disk. - Deploy managed settings by MDM: Anthropic advises keeping login keys in both device-managed and server-managed settings, because server-managed settings reach only accounts already in your organisation.
- Detect new plaintext files: alert on creation of the fallback files listed above through EDR file monitoring.
- Set token expiry: for fine-grained GitHub tokens used with assistants, see How to set expiry policies on personal access tokens.
Small team or large enterprise
A small team can run the checks as a short script on each laptop, agree one assistant and one login method, and fix findings by hand. A large enterprise should push the checks through EDR or osquery, report findings as counts per team, enforce login method and storage through managed settings for every supported assistant, and block unapproved assistants with application control rather than trying to audit every tool.
Metrics
| Metric | Direction |
|---|---|
| Machines with plaintext assistant credential files | Falling to approved exceptions |
| Literal secrets in MCP and assistant config files | Zero |
| Machines with assistant API keys in shell profiles | Falling |
| Machines with managed login enforcement | Rising to 100 percent of supported assistants |
| Days from exposure finding to issuer revocation | Under 1 |
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev 5 | IA-5 Authenticator Management; IA-5(7) No Embedded Unencrypted Static Authenticators; SC-28 Protection of Information at Rest; CM-6 Configuration Settings | Token storage, plaintext files, enforced assistant settings |
| NIST CSF 2.0 | PR.AA-01; PR.PS-01 | Managed credentials; configuration management |
| CIS Controls v8.1 | Control 4 Secure Configuration of Enterprise Assets and Software | Approved assistant configuration on endpoints |
| MITRE ATT&CK | T1552.001 Credentials In Files; T1555.001 Keychain; T1528 Steal Application Access Token | How attackers harvest assistant tokens |
Practitioner checklist
- Define the approved login method and storage mode for each assistant.
- Check for the Claude Code, Copilot CLI and Codex plaintext files and their modes.
- Search shell profiles and settings
envblocks for assistant credential variables, names only. - Scan every MCP config and
.gemini/.envfor literal secrets. - Search repositories for committed assistant config files.
- Move credentials to the keychain and replace literals with references.
- Enforce login method, organisation and storage through managed settings.
- Revoke exposed tokens at the issuer, not just locally.
Standards and references
- Anthropic: Claude Code authentication and credential management
- Anthropic: Claude Code settings files and precedence
- GitHub Docs: Authenticate Copilot CLI
- OpenAI: Codex authentication
- Visual Studio Code: MCP configuration reference
- Cursor: Model Context Protocol
- Gemini CLI: Authentication
- NIST: SP 800-53 Rev 5 Security and Privacy Controls
Related NHI Mgmt Group resources: Developer endpoints as NHI stores · AI IDE and Coding Agent Security Guide · Claude Code Security Guide · How to audit MCP servers on developer laptops · AI Coding Assistant Buyer's Guide