Resource centerBlog
Your AI coding assistant is leaking secrets

Your AI coding assistant is leaking secrets

May 12, 2026

AI desktop assistants and coding tools need credentials to reach external services, and many of them store those credentials as plaintext JSON at predictable paths in the user's home directory. This research covers how credential storage works across 14 popular AI tools, where OS keychain integration is present or missing, and eight attack scenarios that turn that exposure into real risk, from malware-based theft to remote session hijacking to supply-chain compromise via MCP servers.

How Claude, Copilot, Cursor, and other AI tools store credentials in plaintext and 8 examples of how attackers exploit them

The problem

AI desktop applications need to authenticate with external services. Claude Code needs your OAuth tokens. GitHub Copilot needs your GitHub auth. Continue.dev needs your API keys. MCP servers need tokens for Azure DevOps, Slack, databases, and everything else you've connected. Where do all these credentials go? Mostly into plaintext JSON files in your home directory. These files are in well-known or predictable locations, making them easy to find with read access to a system.

What I found

I explored 14 popular AI tools (see reference at the end of the blog) across Windows, macOS, and Linux. These are the things that stuck out to me the most.

Claude Code CLI

~/.claude/.credentials.json contains your OAuth access and refresh tokens in plaintext JSON:

      { 
  "claudeAi": { 
    "type": "oauth", 
    "access": "sk-ant-oat01-...", 
    "refresh": "sk-ant-ort01-...", 
    "expires": 1776098433694 
  } 
} 
      

On Linux, the file is created with 0600 permissions (owner-only read/write). On macOS, credentials go to the Keychain. But on WSL, the Windows-side file inherits the mount's default 0777 permissions making your Claude OAuth tokens world-readable to any process on the system. The refresh token is the really dangerous one here. It's long-lived and can generate new access tokens indefinitely. An attacker who obtains it has persistent access to your Claude account until you explicitly revoke it.

MCP server configurations: the aggregation problem

Model Context Protocol (MCP) servers let AI assistants connect to external tools. Each connection needs authentication, and those tokens typically end up in a single config file like this:

      { 
  "mcpServers": { 
    "github": { "env": { "GITHUB_TOKEN": "ghp_realToken..." } }, 
    "azure": { "env": { "ADO_MCP_AUTH_TOKEN": "eyJ0eX..." } }, 
    "slack": { "env": { "SLACK_BOT_TOKEN": "xoxb-..." } }, 
    "database": { "env": { "DB_PASSWORD": "prod_password" } } 
  } 
} 
      

One file. Multiple services. One read operation can compromise them all. Trend Micro found that 48% of 19,402 MCP server implementations recommend plaintext credential storage.

Continue.dev: API keys in plaintext

Continue.dev stores every configured API key directly in ~/.continue/config.json:

      { 
  "models": [{ 
    "provider": "anthropic", 
    "apiKey": "sk-ant-api03-YOUR-ACTUAL-KEY" 
  }] 
} 
      

The community acknowledges this as a problem (GitHub issue #1729). Environment variable substitution is supported (${VAR_NAME}) but isn't the default behavior.

Cline: credentials synced to the cloud

Cline's MCP settings live in an unencrypted JSON file within VS Code's globalStorage. The problem is VS Code Settings Sync automatically uploads this file to GitHub. If you have Settings Sync enabled, your MCP credentials are sitting in GitHub's cloud.

The VS Code SecretStorage confusion

GitHub Copilot, Cline, and other VS Code extensions use the SecretStorage API, which encrypts secrets in a SQLite database (state.vscdb) using AES-128-CBC. This sounds secure until you realize:

  • VS Code extensions run without sandboxing
  • The encryption key sits in the OS keychain, accessible by any user-level process
  • Any malicious extension can read other extensions' secrets
  • The extension identification system is vulnerable to spoofing

The encryption provides defense against offline disk access but not against any process running as the same user.

Attack surface examples

I identified 8 real-world attack scenarios I wanted to point out enabled by AI credential exposure.

1. Credential theft via malware

The file paths are predictable and consistent across installations:

      ~/.claude/.credentials.json 
~/.continue/config.json 
~/.aws/credentials 
~/.config/gcloud/application_default_credentials.json 
~/Library/Application Support/Claude/claude_desktop_config.json 
~/.claude.json 
      

Infostealer malware already targets browser cookies, SSH keys, and cloud credentials. Adding a handful of AI tool paths to the target list is trivial. No privilege escalation required; standard user-level file read is sufficient.

A single malware infection could yield Claude OAuth tokens, all API keys in Continue.dev, every MCP server token, and AWS credentials. The paths are the same on every installation of every tool.

Risk: CRITICAL. High likelihood, high impact.

2. Hijacking a Claude Code remote control session

This is the scenario that should scare you. Claude Code has a Remote Control feature (claude remote-control) that lets users control their local Claude Code session from a phone, tablet, or browser via claude.ai/code. All code execution stays on the local machine. The remote device is just a control interface.

Here's the problem: the remote connection doesn't require re-authentication. Once a session is active, anyone with the session URL can send instructions. And all those instructions are executed locally on the developer's machine.

Now combine that with --dangerously-skip-permissions.

This flag disables all interactive permission prompts. File writes, shell commands, network requests, MCP tool calls, everything executes automatically without asking the user. It's intended for isolated CI/CD containers, but sometimes users use it on their workstations for convenience. It can also be set as a persistent default in settings.json via "defaultMode": "bypassPermissions".

The attack chain:

  1. Attacker steals OAuth tokens from ~/.claude/.credentials.json (via malware, SSRF, or any file-read vulnerability)
  2. Tokens are portable so they work from any machine
  3. If the victim has an active Remote Control session, the attacker connects and takes over
  4. If the victim runs with --dangerously-skip-permissions, the attacker now has unrestricted shell access as the victim's user:
    • Read/write any file on the system
    • Execute arbitrary commands (curl, ssh, docker, package managers, etc.)
    • Install backdoors, exfiltrate data, pivot to other machines
    • Access SSH keys, cloud credentials, and every other secret on the system
    • Modify git repos, CI/CD pipelines, and deployment configs

Even without --dangerously-skip-permissions, Claude Code in acceptEdits mode auto-approves file edits and common filesystem commands. This can still be dangerous. The permission model was designed to protect against accidental actions, not against a malicious actor controlling the session.

Risk: CRITICAL (with --dangerously-skip-permissions), HIGH (default permissions).

3. Lateral movement via Claude OAuth tokens

An attacker who obtains a Claude OAuth refresh token from ~/.claude/.credentials.json can:

  1. Generate new access tokens remotely (access tokens expire in ~60 minutes, but the refresh token is long-lived)
  2. Access the victim's Claude conversations and workspace files
  3. Use any MCP servers the victim has configured (accessing their GitHub repos, Azure DevOps projects, Slack workspaces, and databases through the AI assistant as a proxy)

The tokens are portable. Copy the credentials file to another machine, and Claude Code will use it. Anthropic implements refresh token rotation, so using a stolen token may eventually invalidate the original, but the initial use succeeds and the attacker may gain access before rotation kicks in.

Risk: HIGH. Medium-high likelihood, high impact.

4. MCP token aggregation: lateral movement

A single MCP config file aggregates tokens for multiple external services. For example, one file could yield:

  • GITHUB_PERSONAL_ACCESS_TOKEN → code repos, PRs, CI/CD
  • ADO_MCP_AUTH_TOKEN → Azure DevOps projects
  • SLACK_BOT_TOKEN → internal Slack workspace
  • Database passwords → production data
  • JIRA_API_TOKEN → project management, internal tickets

Unlike distributed credential storage where compromising one channel exposes only a subset of credentials, MCP's aggregation pattern means a single file disclosure cascades to compromise all connected services.

Risk: CRITICAL. High likelihood, high impact.

5. Malicious VS Code extension: credential exfiltration

VS Code extensions run without sandboxing. All extensions share the same process permissions. A malicious or compromised extension can:

  1. Access the state.vscdb SQLite database directly
  2. Retrieve the encryption key from the OS keychain (accessible by any user-level process)
  3. Decrypt all extension secrets: Copilot tokens, Cline API keys, MCP credentials

Cline's cline_mcp_settings.json doesn't even need decryption. It's unencrypted plaintext in VS Code's globalStorage, and it gets synced to the cloud via Settings Sync.

The package.json publisher and name fields that identify extensions are vulnerable to spoofing, making it possible to impersonate legitimate extensions.

Risk: HIGH. Medium likelihood, high impact.

6. WSL permission escalation

Developers using WSL get hit from both sides. Windows-installed AI tools store credentials under C:\Users\<you>\, which is mounted in WSL as /mnt/c/Users/<you>/ with 0777 permissions by default. The same credential file that's NTFS-protected on the Windows side is world-readable from the Linux side.

In multi-user WSL environments or compromised containers, this is easy to exploit. Any process running on the WSL system can read Claude OAuth tokens, MCP configs, etc., stored on the Windows side.

Risk: HIGH for WSL users. High likelihood, medium-high impact.

7. .mcp.json committed to git: credential exfiltration

Claude Code's .mcp.json at the repo root is designed to be version-controlled so teams can share MCP server configurations. If a developer includes inline tokens instead of env var references:

  1. Tokens appear in git history
  2. Even after removal, git log -p .mcp.json reveals them
  3. Automated scanners (TruffleHog, GitLeaks, etc.) can find them at scale across GitHub/GitLab

Anthropic and GitHub's secret scanning partnership catches some exposed API keys, but MCP config tokens for other services (Azure DevOps, Slack, databases) are not covered by automated scanning. There's no GitHub secret scanner pattern for ADO_MCP_AUTH_TOKEN or SLACK_BOT_TOKEN embedded in JSON env blocks.

Risk: MEDIUM. Medium likelihood, medium-high impact.

8. Supply chain attack via MCP servers

MCP servers are often npm packages. The supply chain risk is the same as any npm dependency, but with a critical difference: MCP servers receive credentials at startup.

  1. Popular MCP server package on npm is compromised (or a typosquat is published)
  2. Users configure it in their claude_desktop_config.json with real tokens in the env block
  3. The MCP server process has access to all environment variables passed to it, including tokens
  4. Compromised server exfiltrates tokens to attacker's infrastructure

Without proper OAuth 2.1 audience validation (RFC 8707), a malicious MCP server could also receive access tokens and replay them against other services (token passthrough attack).

The 48% plaintext credential statistic means nearly half of all MCP server setups hand credentials directly to the server process on launch. If that server process is compromised, every user who installed it is compromised too.

Risk: MEDIUM. Low-medium likelihood, high impact.

Bonus: CI/CD token theft

The claude setup-token command generates a one-year OAuth token (CLAUDE_CODE_OAUTH_TOKEN) for CI/CD pipelines. This creates a unique risk:

  • The token works from any machine for up to a year
  • CI/CD systems often have weak secrets management and limited job isolation
  • Build logs may accidentally expose environment variable values
  • A compromised CI/CD runner yields long-lived access to Claude with whatever permissions the pipeline runs

If your CI/CD pipeline runs Claude Code with --dangerously-skip-permissions (as many do for automation), a stolen CLAUDE_CODE_OAUTH_TOKEN grants unrestricted shell execution on any machine where the token is used.

Risk: HIGH. Medium likelihood, high impact.

What needs to change

For users (right now)

  • Run AIHound. Know what's exposed on your machine.
  • Fix permissions: chmod 600 on credential files.
  • Never use **--dangerously-skip-permissions** on machines with real credentials or network access. If you need it, use it inside an isolated container with no mounted secrets.
  • Use env var references in MCP configs instead of inline tokens:
      "env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" } 
      
  • Rotate tokens that have been in world-readable files or git history.
  • Disable VS Code Settings Sync if you use Cline with MCP servers.
  • Treat **CLAUDE_CODE_OAUTH_TOKEN** like a root password. Rotate it regularly, never put it in build logs, and scope CI/CD permissions to the minimum necessary.
  • Add **.mcp.json** to **.gitignore** if it contains inline secrets.

For tool builders

  • Use OS keychains. macOS Keychain, Windows Credential Manager, and Linux libsecret exist for exactly this purpose. Never store credentials with plaintext JSON.
  • Create files with **0600** permissions. Not 0644 or 0777.
  • Warn users when you detect inline secrets in MCP configurations.
  • Don't sync credentials to the cloud through settings sync mechanisms.
  • Implement OAuth 2.1 with proper token audience validation for remote MCP servers.
  • Require re-authentication for Remote Control sessions. A session URL should not be a bearer token for arbitrary code execution.
  • Audit MCP server packages before allowing installation, and support server allowlisting.

For the ecosystem

The OWASP MCP Top 10 lists "Token Mismanagement and Secret Exposure" as MCP01-2025. Anthropic and GitHub's partnership for automatic API key detection on GitHub is a great start, but it only covers Anthropic keys in public repos. MCP tokens for Azure DevOps, Slack, databases, and other services have no equivalent protection.

We need:

  • Secret scanning that covers MCP configuration patterns, not just API key formats
  • Credential helpers for MCP (similar to git credential helpers) that retrieve tokens from secure storage at runtime
  • Mandatory OS keychain integration as the default
  • Sandboxing for VS Code extensions
  • Token-scoped Remote Control - Remote sessions should require their own authentication, not inherit the local session's full capabilities

The bottom line

The AI tooling ecosystem moved fast to ship features and left credential security as a bit of an afterthought. Almost half of MCP servers store credentials in plaintext. AI assistants keep OAuth tokens in JSON files on disk. Remote Control features let those tokens become remote code execution. And --dangerously-skip-permissions turns a stolen credential into full shell access on someone else's machine. The more tools you connect, the greater your risk becomes. A lot of AI credentials are not stored securely, so be sure to implement best practices for credential storage. Tools like AIHound can help you find these plaintext credentials on your systems.

AI desktop application credential storage locations for reference

This reference catalogs every credential location of some of the most popular AI desktop applications, coding assistants, and CLI tools.

Claude Code CLI

Developer: Anthropic Auth method: OAuth 2.0 (Claude.ai subscription) or API key

Credential files

Platform

Path

Format

Encryption

Contents

Linux

~/.claude/.credentials.json

JSON

Plaintext (mode 0600)

OAuth access/refresh tokens, multi-provider auth

macOS

Keychain: Claude Code-credentials

Keychain

OS Keychain (encrypted)

OAuth access/refresh tokens

Windows

%USERPROFILE%\.claude\.credentials.json

JSON

Plaintext (NTFS ACLs)

OAuth access/refresh tokens

WSL

Both Linux path AND /mnt/c/Users/<user>/.claude/.credentials.json

JSON

Plaintext (often 0777 via mount)

Same as above

Credential file structure

      { 
  "claudeAi": { 
    "type": "oauth", 
    "access": "sk-ant-oat01-...", 
    "refresh": "sk-ant-ort01-...", 
    "expires": 1776098433694 
  } 
} 
      

Every OAuth connection stores its own entry. The file contains credentials for Claude.ai, Claude API, Azure Auth, Bedrock Auth, and Vertex Auth depending on what the user has configured.

Key finding: On WSL, the Windows-side credential file inherits the mount's default permissions (0777), making it world-readable, which is a CRITICAL risk that doesn't exist on native Linux where the file is created with 0600.

Configuration files with potential secrets

Path

Contents

~/.claude.json

Global MCP server configurations (may contain inline tokens)

.mcp.json (repo root)

Project-scoped MCP server configs

~/.claude/settings.json

User preferences and permissions

Authentication precedence order

Claude Code checks credentials in this order (first match wins):

  1. Cloud provider env vars (CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, CLAUDE_CODE_USE_FOUNDRY)
  2. ANTHROPIC_AUTH_TOKEN environment variable
  3. ANTHROPIC_API_KEY environment variable
  4. apiKeyHelper script output (for dynamic/rotating credentials)
  5. CLAUDE_CODE_OAUTH_TOKEN environment variable (long-lived, from claude setup-token)
  6. OAuth credentials from ~/.claude/.credentials.json or Keychain

Known bug

A documented bug (GitHub issue #36779) shows that OAuth credentials are not properly persisted to macOS Keychain. Only the mcpOAuth field is stored while the primary auth token is missing, forcing re-authentication in new sessions.

Claude Desktop

Developer: Anthropic Auth method: Claude.ai session (Keychain-backed on macOS)

Configuration files

Platform

Path

Format

Encryption

macOS

~/Library/Application Support/Claude/claude_desktop_config.json

JSON

Plaintext (often 0644)

Windows

%APPDATA%\Claude\claude_desktop_config.json

JSON

Plaintext

Linux

~/.config/Claude/claude_desktop_config.json

JSON

Plaintext

MSIX virtualization issue (Windows)

On Windows, the MSIX installer creates filesystem virtualization. The "Edit Config" button opens %APPDATA%\Claude\claude_desktop_config.json, but the application may actually read from a virtualized path inside the MSIX container. This means:

  • Users may edit the wrong file
  • MCP server configurations are silently ignored
  • No error messages indicate the problem

Pre-MSIX installations are grandfathered and read from the real AppData path.

What's in the config

The config file primarily holds MCP server definitions:

      { 
  "mcpServers": { 
    "azure-devops": { 
      "command": "npx", 
      "args": ["-y", "@anthropic/azure-devops-mcp"], 
      "env": { 
        "ADO_MCP_AUTH_TOKEN": "actual-token-here" 
      } 
    } 
  } 
} 
      

The env block frequently contains plaintext tokens for external services.

GitHub Copilot

Developer: GitHub / Microsoft Auth method: GitHub OAuth

Credential storage

Platform

Location

Format

Encryption

macOS

Keychain: copilot-cli

Keychain

OS Keychain

Windows

Credential Manager

Credential Manager

DPAPI

Linux (with libsecret)

GNOME Keyring / KWallet

libsecret

OS-level

Linux (no libsecret)

~/.copilot/config.json

JSON

Plaintext (fallback)

VS Code extension storage

Platform

Path

Linux

~/.config/Code/User/globalStorage/state.vscdb

macOS

~/Library/Application Support/Code/User/globalStorage/state.vscdb

Windows

%APPDATA%\Code\User\globalStorage\state.vscdb

The state.vscdb file is a SQLite database encrypted with Electron's safeStorage API (AES-128-CBC). However, security research has shown this is vulnerable to malicious VS Code extensions. Any extension can access other extensions' secrets due to lack of sandboxing.

The encryption key is stored in the OS keychain (Code Safe Storage on macOS), accessible by any process with user permissions.

Token types

Prefix

Type

Supported

gho_

OAuth token (default via copilot login)

Yes

github_pat_

Fine-grained PAT (needs "Copilot Requests" permission)

Yes

ghu_

GitHub App user-to-server token

Yes (env var only)

ghp_

Classic PAT

Not supported

GitHub CLI auth (also used by Copilot)

Platform

Path

Format

Linux

~/.config/gh/hosts.yml

YAML

macOS

~/Library/Application Support/gh/hosts.yml

YAML

Windows

%APPDATA%\GitHub CLI\hosts.yml

YAML

Contains oauth_token fields in plaintext YAML.

Cursor IDE

Developer: Anysphere Auth method: Cursor account + optional API keys

Storage locations

Platform

Paths

macOS

~/Library/Application Support/Cursor/, ~/.cursor/

Windows

%APPDATA%\Cursor\, %USERPROFILE%\.cursor\

Linux

~/.config/Cursor/, ~/.cursor/

MCP configuration

~/.cursor/mcp.json, which follows the same mcpServers JSON structure as Claude Desktop. May contain inline auth tokens.

Known issues

Cursor stores conversation logs with world-readable permissions. These logs can contain credentials if users paste tokens into the chat interface.

Continue.dev

Developer: Continue (open source) Auth method: Direct API keys per provider

Configuration files

Platform

Path

Format

Encryption

All

~/.continue/config.json

JSON

Plaintext

All

~/.continue/config.yaml

YAML

Plaintext

Credential format

      { 
  "models": [ 
    { 
      "title": "Claude 3.5 Sonnet", 
      "provider": "anthropic", 
      "model": "claude-3-5-sonnet-20241022", 
      "apiKey": "sk-ant-api03-ACTUAL-KEY-HERE" 
    } 
  ] 
} 
      

All API keys are stored in plaintext at the user root. This is acknowledged by the Continue.dev community as a security concern (GitHub issue #1729).

Mitigation

Continue.dev supports environment variable substitution:

      "apiKey": "${CONTINUE_ANTHROPIC_API_KEY}" 
      

It checks .env files at the project root and custom locations specified via envFiles. However, the default behavior is plaintext if env vars aren't configured.

Cline (VS Code extension)

Developer: Saoud Rizwan Extension ID: saoudrizwan.claude-dev

MCP settings (plaintext)

Platform

Path

macOS

~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json

Windows

%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json

Linux

~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json

Critical: These files are plaintext JSON and are automatically synced to the cloud through VS Code Settings Sync. This means API keys and MCP credentials get uploaded to GitHub if sync is enabled.

API key storage

Cline stores API keys through VS Code's SecretStorage API (the same AES-128-CBC encrypted state.vscdb used by Copilot). Subject to the same malicious extension vulnerability.

Windsurf / Codeium

Developer: Codeium Auth method: Codeium account

Storage locations

Platform

Path

All

~/.codeium/windsurf/

Look for config.json, auth.json, credentials.json within this directory.

MCP configuration

~/.codeium/windsurf/mcp_config.json with the same mcpServers structure.

Credential details are not well-documented publicly. Authentication appears to go through Codeium's centralized backend rather than local token storage in many cases.

ChatGPT Desktop

Developer: OpenAI Auth method: OpenAI account session

Storage locations

Platform

Path

Encryption

macOS

~/Library/Application Support/ChatGPT/

Keychain for session

macOS

~/Library/Application Support/com.openai.chat/

Keychain for session

Windows

%APPDATA%\OpenAI\ChatGPT\

Credential Manager

Windows

%APPDATA%\com.openai.chat\

Credential Manager

ChatGPT Desktop primarily uses OS credential stores (Keychain on macOS, Credential Manager on Windows) for session tokens. JSON config files in the app data directory may also contain session data.

Amazon Q Developer / AWS

Developer: Amazon Web Services Auth method: IAM Identity Center / AWS Builder ID / IAM credentials

Credential files

Path

Format

Contents

~/.aws/credentials

INI

Plaintext access key ID, secret access key, session token (per profile)

~/.aws/config

INI

Region, output format, SSO configuration

~/.aws/sso/cache/*.json

JSON

Plaintext SSO access tokens (cached)

Credential file structure

      [default] 
aws_access_key_id = AKIAIOSFODNN7EXAMPLE 
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY 
aws_session_token = FwoGZXIvYXdzEBY... 
      

Notes

  • Amazon Q itself authenticates through IAM Identity Center or Builder ID, not through local file storage
  • Default SSO session duration is 90 days (for setups created after April 2024)
  • Static credentials in ~/.aws/credentials are the primary risk. Consider using SSO/IAM Identity Center instead

Google Gemini CLI / GCloud

Developer: Google Auth method: Google account OAuth, API key, or service account

API key storage

Location

Format

Contents

$GEMINI_API_KEY env var

Environment variable

Gemini API key

$GOOGLE_API_KEY env var

Environment variable

Google API key

~/.gemini/.env

Dotenv

API keys

~/.env

Dotenv

API keys

Application default credentials (ADC)

Platform

Path

Format

Linux

~/.config/gcloud/application_default_credentials.json

JSON

macOS

~/Library/Application Support/gcloud/application_default_credentials.json

JSON

macOS (alt)

~/.config/gcloud/application_default_credentials.json

JSON

Windows

%APPDATA%\gcloud\application_default_credentials.json

JSON

ADC files contain client_secret, refresh_token, and potentially private_key (for service accounts) in plaintext JSON.

Authentication methods

Method

How

Local Storage

Sign in with Google

gemini auth login (browser flow)

Cached locally for future sessions

Gemini API Key

GEMINI_API_KEY env var

Environment or .env file

Service Account

GOOGLE_APPLICATION_CREDENTIALS env var pointing to key file

JSON key file on disk

ADC via gcloud

gcloud auth application-default login

application_default_credentials.json

Cloud Shell

Automatic (metadata server)

No local storage

OpenClaw

Developer: OpenClaw (open source) Auth method: OAuth 2.0 per provider, API keys, channel-specific tokens

Overview

OpenClaw is a local-first AI agent platform that connects to 20+ messaging channels (WhatsApp, Telegram, Slack, Discord) and executes tools on the local machine. It stores credentials for both LLM providers and messaging channels.

Credential files

Path

Format

Encryption

Contents

~/.openclaw/agents/<agentId>/agent/auth-profiles.json

JSON

Plaintext (0600)

OAuth access/refresh tokens + API keys per LLM provider

~/.openclaw/credentials/whatsapp/<accountId>/creds.json

JSON

Plaintext

WhatsApp Baileys auth state

~/.openclaw/credentials/oauth.json

JSON

Plaintext

Legacy OAuth tokens (migration file)

~/.openclaw/credentials/<channel>-allowFrom.json

JSON

Plaintext

Channel pairing allowlists

~/.openclaw/secrets.json

JSON

Plaintext

Optional secrets payload for SecretRef resolution

~/.openclaw/openclaw.json

JSON5

Plaintext

Main config with gateway auth token, channel tokens, agent API keys

~/.openclaw/.env

Dotenv

Plaintext

Environment variable overrides (may contain API keys)

Auth profile structure

Each agent has its own auth-profiles.json containing provider credentials:

      { 
  "anthropic": { 
    "accessToken": "sk-ant-...", 
    "refreshToken": "sk-ant-ort01-...", 
    "expiresAt": 1776098433694 
  }, 
  "openai": { 
    "apiKey": "sk-proj-..." 
  } 
} 
      

All tokens are stored in plaintext JSON by default. No OS keychain integration.

SecretRef system (opt-in)

OpenClaw supports an opt-in SecretRef system that replaces plaintext values with references to external sources:

Provider

Example

Description

env

env:ANTHROPIC_API_KEY

Reads from environment variable

file

file:~/.secrets/api-key.txt

Reads from file (JSON pointer or single value)

exec

exec:pass show openclaw/anthropic

Runs vault-like executable

Users must run openclaw secrets configure to scrub existing plaintext values and migrate to SecretRefs. The default behavior is plaintext storage.

Gateway authentication

The gateway uses a token for local access control:

      { 
  "gateway": { 
    "auth": { 
      "mode": "token", 
      "token": "long-random-string-here" 
    } 
  } 
} 
      

This token can be a SecretRef but is commonly hardcoded in openclaw.json.

Known security issues

Issue

Severity

Details

Plaintext credential storage

HIGH

OAuth tokens and API keys in auth-profiles.json with no keychain

Agent tool bypass

HIGH

Agents with filesystem/exec tools can cat credential files, bypassing config.get redaction (GitHub #11829)

Tailscale exposure

MEDIUM

Remote access via Tailscale could expose the local gateway if token auth isn't configured

OAuth refresh failures

MEDIUM

Silent token refresh failures, Anthropic token truncation bugs

Session persistence

LOW

WhatsApp sessions not always persisted across restarts

Credential locations summary

All files live under ~/.openclaw/ on all platforms (Linux, macOS, Windows). On WSL, both Linux-native and Windows-mounted paths should be checked.

MCP server configurations

Config file locations

Tool

Path

Scope

Claude Desktop (macOS)

~/Library/Application Support/Claude/claude_desktop_config.json

Application

Claude Desktop (Windows)

%APPDATA%\Claude\claude_desktop_config.json

Application

Claude Code (local)

~/.claude.json

User (private)

Claude Code (project)

.mcp.json

Repository (shared)

Cursor

~/.cursor/mcp.json

User

VS Code

.vscode/mcp.json

Workspace

Cline

cline_mcp_settings.json in VS Code globalStorage

Extension

Common MCP auth environment variables

Variable

Service

ADO_MCP_AUTH_TOKEN

Azure DevOps

GITHUB_PERSONAL_ACCESS_TOKEN

GitHub

GITHUB_TOKEN

GitHub

SLACK_BOT_TOKEN

Slack

JIRA_API_TOKEN

Atlassian Jira

NOTION_API_KEY

Notion

LINEAR_API_KEY

Linear

Environment variables

The following environment variables are commonly set by users or tools and contain AI-related secrets:

Anthropic / Claude

Variable

Description

ANTHROPIC_API_KEY

Anthropic API key (sk-ant-api03-...)

ANTHROPIC_AUTH_TOKEN

Bearer auth token

CLAUDE_CODE_OAUTH_TOKEN

Long-lived OAuth token (from claude setup-token, valid ~1 year)

OpenAI

Variable

Description

OPENAI_API_KEY

OpenAI API key (sk-...)

OPENAI_ORG_ID

Organization identifier

Google

Variable

Description

GEMINI_API_KEY

Gemini API key

GOOGLE_API_KEY

Google API key (AIza...)

GOOGLE_APPLICATION_CREDENTIALS

Path to service account JSON key file

GitHub

Variable

Description

GITHUB_TOKEN

GitHub token

GH_TOKEN

GitHub CLI token

GITHUB_PERSONAL_ACCESS_TOKEN

GitHub PAT

COPILOT_GITHUB_TOKEN

Copilot-specific token

AWS

Variable

Description

AWS_ACCESS_KEY_ID

AWS access key (AKIA...)

AWS_SECRET_ACCESS_KEY

AWS secret key

AWS_SESSION_TOKEN

Temporary session token

Azure

Variable

Description

ADO_MCP_AUTH_TOKEN

Azure DevOps MCP token

AZURE_OPENAI_API_KEY

Azure OpenAI key

Other AI providers

Variable

Description

HF_TOKEN / HUGGING_FACE_HUB_TOKEN

Hugging Face

COHERE_API_KEY

Cohere

REPLICATE_API_TOKEN

Replicate

TOGETHER_API_KEY

Together AI

GROQ_API_KEY

Groq

MISTRAL_API_KEY

Mistral AI

DEEPSEEK_API_KEY

DeepSeek

XAI_API_KEY

xAI / Grok

PERPLEXITY_API_KEY

Perplexity

FIREWORKS_API_KEY

Fireworks AI

Share on

Learn More

About the author

Darryl baker headshot

Darryl Baker

Senior Staff Security Researcher

Darryl G. Baker is a Senior Staff Security Researcher at Netwrix and a recognized authority in Identity and Active Directory security. With over a decade of identity systems experience, he has led enterprise security assessments, identity security trainings, and threat emulations focused on Active Directory, Entra ID, and Azure environments. Darryl has delivered highly rated trainings and demos at BlueTeamCon, BSidesCT, The Experts Conference, and Wild Wild West Hackin’ Fest. He’s the architect behind numerous hands on attack emulation labs—leveraging current red team and blue team tools to help defenders master everything from attack path analysis to threat hunting. In his sessions, Darryl blends deep technical insight with real world case studies, empowering blue team professionals to strengthen their identity security posture and defend against evolving adversary techniques.