AID-H-028.002: Client Credential Secure Storage & Lifecycle Management
Pillars: infra, app
Lifecycle: building, operation
Scope boundary
Owns secure local storage, rotation, expiry, revocation propagation, logout cleanup, and deauthorization of credentials held by an MCP or tool client. It does not define server authentication policy or revoke the upstream credential authority's objects.
AID-H-004.002: AID-H-028.002 protects credentials in the MCP client and their local lifecycle; AID-H-004.002 defines service and API authentication controls across the broader system. Secure storage does not determine whether the remote service accepts the credential correctly.
AID-E-001: AID-H-028.002 propagates revocation and cleans local client copies; AID-E-001 owns incident-driven revocation of compromised credentials, sessions, principals, and grants at their authorities. Deleting a local token copy does not invalidate an already issued credential.
Implementation guidance
- AID-H-028.002-G001: Store client-held credentials in the platform-native keychain or a managed encrypted secret store, never in plaintext configuration files.
- AID-H-028.002-G002: Enforce credential TTLs, rotation, and expiry-based invalidation for long-lived client tokens and refresh flows.
- AID-H-028.002-G003: On logout, deauthorization, or uninstall, remove locally accessible credentials and revoke server-side tokens where supported.
AID-H-028.002-G001
Concept:
Keep only a reference to the credential in config. Store the real secret in the OS keychain or secret manager. If the platform has no keychain support, use an encrypted file and lock down file permissions.
Example Python keyring usage
import keyring
SERVICE = "mcp-client"
ACCOUNT = "finance-mcp-api-key"
# Store secret once
keyring.set_password(SERVICE, ACCOUNT, "sk_live_xxx")
# Read secret later
api_key = keyring.get_password(SERVICE, ACCOUNT)
if not api_key:
raise RuntimeError("Missing MCP credential in keychain")
Example config that stores only a reference
{
"servers": [
{
"name": "finance-mcp",
"url": "https://mcp.finance.internal",
"auth": {
"type": "api_key",
"keychain_ref": "mcp-client:finance-mcp-api-key"
}
}
]
}
Operational notes: Add a startup audit that scans known config paths for obvious secret patterns and fails if plaintext credentials are found. Allow a SOPS-encrypted fallback only where keychain APIs are not available.
Production implementation
At credential creation and retrieval, store only through the platform keychain or managed secret API under the exact client identity and server scope, deny plaintext fallback/export, and verify effective ACL and hardware-backed status when policy requires it.
Independent verification
A separately credentialed verifier independently reads back or replays keychain/secret-store metadata, effective ACL, process access and absence from config, logs, backups and crash artifacts using authorized and unauthorized identities.
AID-H-028.002-G002
Concept:
Do not treat client tokens as permanent. Track expiry and refresh before expiry. If refresh fails or the token is revoked, block further use.
Example token metadata file
{
"token_ref": "mcp-client:finance-oauth-access-token",
"expires_at": "2026-03-20T18:00:00Z",
"refresh_ref": "mcp-client:finance-oauth-refresh-token",
"server": "finance-mcp"
}
Example expiry check
from datetime import datetime, timezone
def token_is_stale(expires_at_iso: str, safety_window_seconds: int = 300) -> bool:
expires_at = datetime.fromisoformat(expires_at_iso.replace("Z", "+00:00"))
now = datetime.now(timezone.utc)
return (expires_at - now).total_seconds() <= safety_window_seconds
Operational notes: Log every refresh, rotation, and revocation event with server, credential_type, and result. Where possible, use short-lived access tokens and keep refresh tokens in a stronger store than ordinary app config.
Production implementation
At issuance, refresh and request dispatch, enforce signed policy expiry, rotation generation, audience/resource and revocation state; atomically replace old material and deny stale, superseded or unverifiable credentials.
Independent verification
A separately credentialed verifier independently reads back or replays issuer records, local credential generation, effective token claims and dispatch decisions across expiry, rotation race, clock-boundary, replay and revocation fixtures.
AID-H-028.002-G003
Concept:
Do not promise impossible secure deletion semantics on every OS and filesystem. Instead, remove secrets through the native secret-store API, delete any local references, revoke tokens server-side, and purge accessible client-managed session state.
Example Python cleanup flow
import keyring
SERVICE = "mcp-client"
def cleanup(server_name: str, account_refs: list[str]):
for ref in account_refs:
keyring.delete_password(SERVICE, ref)
# Then call remote revocation endpoint if available.
# Finally delete client-local session files and cached auth metadata.
Operational notes: Maintain a manifest of secret references per server. At uninstall time, iterate through that manifest, remove local secrets, delete cached auth metadata, revoke remote tokens if the server supports revocation, and write a final audit record that the cleanup workflow finished successfully or failed.
Production implementation
At lifecycle teardown, enumerate all locally reachable credential generations, delete them through the native store, invoke issuer revocation where supported, invalidate refresh state and prove subsequent use is denied; unresolved revocation blocks a clean completion state.
Independent verification
A separately credentialed verifier independently reads back or replays the pre/post local inventory, issuer revocation status and fresh authentication probes for every affected credential and device.
Open-source tools
- Python keyring (cross-platform OS keychain access)
- @napi-rs/keyring (native OS credential storage)
- SOPS (encrypted file-based secret storage when keychains are unavailable)
- age (secret-file encryption)
- GnuPG
- pass (GPG-backed password store)
- Infisical (MIT-licensed core; enterprise features excluded)
Source-available / open-weight tools
- HashiCorp Vault Community (BUSL-1.1; source-available)
Commercial / hosted tools
- HashiCorp Vault Enterprise
- HCP Vault Dedicated
- 1Password Secrets Automation
- CyberArk Secrets Manager SaaS
- AWS Secrets Manager
- Azure Key Vault
- Google Secret Manager
Threat mappings
MITRE ATLAS
- AML.T0012 Valid Accounts (credential theft from insecure local storage)
- AML.T0084 Discover AI Agent Configuration (credentials exposed in local config)
- AML.T0098 AI Agent Tool Credential Harvesting (credential harvesting from client storage)
- AML.T0083 Credentials from AI Agent Configuration
MAESTRO
- Agent Identity Attack (L7) (stolen client credentials enable impersonation)
OWASP LLM Top 10 2026
- N/A
OWASP ML Top 10 2023
- N/A
OWASP Top 10 for Agentic Applications 2026
- ASI03:2026 Identity and Privilege Abuse (stolen client credentials)
NIST Adversarial Machine Learning 2025
- N/A
Cisco Integrated AI Security and Safety Framework
- AISubtech-14.1.2 Insufficient Access Controls
- AISubtech-4.3.4 Replay Exploitation (Rejects stale or revoked MCP client credentials on replay)
- AISubtech-7.4.1 Token Theft
- AITech-7.4 Token Manipulation
- AITech-8.2 Data Exfiltration / Exposure
- AITech-8.3 Information Disclosure
Google Secure AI Framework 2.0 - Risks
- N/A
Databricks AI Security Framework 3.0
- Agents - Tools MCP Client 13.27: Insecure Credential Storage
- Agents - Tools MCP Server 13.19: Credential and Token Exposure
- Platform 12.4: Unauthorized privileged access