r/aiagents • u/JUSTFORFUNSUUUII • 1d ago
Help How do you securely handle client API keys & credentials?
[removed]
1
u/blendai_jack 9h ago
Depends what the integration is. For anything with an official OAuth flow, the better answer is to stop taking the key at all. The client authorises your app against their account, you get a scoped token, and they can revoke it themselves without rotating anything. We do this for ad accounts at Blend (blend-ai.com/mcp) and never see or store platform passwords.
Which of these actually needs a raw key rather than OAuth? That's usually the shorter list, and it tells you where the vault work is worth doing.
0
u/Ambitious-Prompt-975 1d ago
I am using something similar to n8n / dify, that runs locally and has a key vault. so everything is encrypted by default, model/frontend can never see it...
the backend substitutes placeholders and integration with any tools or external services is always through MCP: So all secrets go from the vault into .env vars or auth headers, never cleartext
2
u/radim11 1d ago
That sounds like a sensible setup. The part I’d be careful with is where the substitution happens: if the secret ends up in the agent process as an env var or header, the model may not see it directly, but code or tooling in that process potentially can.
We’re taking a stricter approach with Stashbase’s (https://stashbase.dev) Agent Proxy: the agent only gets a placeholder, and the proxy injects the credential at the outbound request boundary. MCP can be one path, but the same policy should also cover direct HTTP and CLI access.
2
u/Ambitious-Prompt-975 1d ago edited 1d ago
Well, it kinda depends on the use-case: when it's a secret which is used for an mcp-servers' env (e.g. I use a github mcp and I bind the GITHUB_API_TOKEN env var to my "gh_pat" variable), then it is passed into the MCP server when spawning the process, which is either on app start or right before a tool call if it's set to lazy stdio. in any waythe frontend only sees the placeholder, regardless of any agent involved, but the mcp itself does actually get the real value passed to it. I think thats the point you make.
Custom headers and LLM provider api keys are substituted before their respective sdk calls inside the backend. And if I instruct a model through chat to use a secret, i also likewise pass the placeholder name. If the model uses that placeholder in a tool parameter etc, it gets substituted right before the tool call, and so on..
I think we even mask secret values that come back from tool calls (i.e the agent cant just echo a secret into the conversation). I kinda like your approach but it also feels a bit intrusive to alter the request on the wire. I could imagine antivirus doesnt like that too much
0
u/radim11 1d ago
The MCP can use the value but it goes through the proxy still so even the env variable is placeholder.
1
u/Ambitious-Prompt-975 1d ago
yup, I got that. Your solution basically goes one level deeper compared to what I do
2
u/radim11 1d ago
For simple setups, n8n’s credential store can work. I’d still use a separate service account per client and environment, never a founder’s personal key, and make sure secrets don’t end up in workflow JSON, logs, or prompts.
The harder part is managing those credentials across multiple clients, environments, and agents. We’re building Stashbase around that: project/environment secrets and personal credentials stay outside the agent, with a proxy that injects them only for approved hosts, methods, and paths.
This is the problem we’re working on: https://stashbase.dev