BYOK for OpenAI API: Secure Key Management with Local Proxy
Problem
When I was building a client application that uses the OpenAI API, I faced a fundamental security problem:
Error: API key exposed in client-side codeActually, it wasn’t an error message - it was worse. My OpenAI API key was sitting in plain text in my frontend JavaScript bundle. Anyone who opened the browser dev tools could see it.
I tried several approaches to hide it:
- Environment variables? Only work server-side
- Obfuscation? Trivial to reverse
- Backend proxy? That’s what I needed, but building one from scratch seemed overkill
The real problem: client-side applications cannot safely store API keys. Any key embedded in a browser app, mobile app, or desktop binary can be extracted.
What Happened?
I was building a chat application and made the classic mistake:
// DON'T DO THIS - Key is visible in browser dev tools!const client = new OpenAI({ apiKey: "sk-proj-xxxxxxxxxxxx", // Exposed! dangerouslyAllowBrowser: true // This should have been a red flag});When I checked the browser’s Network tab, I saw my full API key in the request headers. Then I realized:
- Browser bundles expose everything - Minification doesn’t hide strings
- Logs capture secrets - Server logs, proxy logs, analytics all see the key
- Third-party integrations leak - Any service handling requests sees your key
- Key rotation is painful - When compromised, every client needs updating
I needed a way to use OpenAI’s API without ever exposing the real key to clients.
Why This Matters
The attack surface for exposed API keys is huge:
+------------------+ +------------------+ +------------------+| Your Client | | Your Server | | OpenAI API || (Exposed) |---->| (Logs) |---->| (Billed to YOU)|| API Key: xxx | | API Key: xxx | | |+------------------+ +------------------+ +------------------+ | | v v Browser Dev Tools Server Logs Network Tab Error Messages Source Maps Third-party ServicesEvery hop in this chain is a potential leak point. I needed to reduce this surface area dramatically.
The Solution: BYOK with Local Proxy
BYOK (Bring Your Own Key) with a local proxy flips the architecture:
+------------------+ +------------------+ +------------------+| Your Client | | ai-menshen | | OpenAI API || (Safe) |---->| Proxy |---->| || Proxy Token | | Real Key HERE | | |+------------------+ +------------------+ +------------------+ | v Only ONE place stores real keysClients authenticate with a proxy token. The proxy knows the real OpenAI key and injects it into upstream requests. This means:
- Clients never see real keys - They only have proxy tokens
- Key rotation is centralized - Change once at the proxy
- Access control per client - Each team/user gets their own token
- Full audit trail - Know who made what request
I found ai-menshen, a local-first proxy designed exactly for this use case.
How I Set It Up
Step 1: Install ai-menshen
# One-liner install (macOS/Linux)curl -fsSL https://raw.githubusercontent.com/jiacai2050/ai-menshen/main/install.sh | sh
# Or via Gogo install github.com/jiacai2050/ai-menshen@latestStep 2: Generate Configuration
mkdir -p ~/.config/ai-menshenai-menshen -gen-config > ~/.config/ai-menshen/config.tomlStep 3: Configure BYOK Security
Here’s the critical configuration that keeps keys safe:
# BYOK Configuration - Real keys stay server-side only[auth]enable = truetoken = "${PROXY_TOKEN}" # Use env var, never hardcode
[providers]base_url = "https://api.openai.com/v1"api_key = "${OPENAI_API_KEY}" # Real key, server-side only
[storage.sqlite]path = "./data/ai-menshen.db"
[logging]log_request_body = truelog_response_body = trueNotice the ${VAR} syntax - this pulls from environment variables, keeping secrets out of config files.
Step 4: Set Environment Variables
# NEVER commit these to gitexport PROXY_TOKEN="my-internal-proxy-token"export OPENAI_API_KEY="sk-proj-your-real-key-here"Step 5: Run the Proxy
ai-menshen -config ~/.config/ai-menshen/config.toml
# Output:# 2026/03/30 10:00:00 Starting ai-menshen on :8080# 2026/03/30 10:00:00 Dashboard available at http://localhost:8080/Client-Side: No Real Keys Exposed
Now the client code changes dramatically:
from openai import OpenAI
# Client only knows the proxy token, NOT your OpenAI keyclient = OpenAI( base_url="http://localhost:8080", # Point to proxy api_key="my-internal-proxy-token" # Proxy token, not OpenAI key!)
# This request goes through proxy with real key injectionresponse = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}])print(response.choices[0].message.content)Even if someone extracts this token, they only get access through your proxy - which you control.
Multiple Teams with Separate Tokens
I wanted different tokens for different teams so I could audit and revoke individually:
# Team Alpha's proxy instancePROXY_TOKEN="team-alpha-token" ai-menshen -config config.toml -listen :8081 &
# Team Beta's proxy instancePROXY_TOKEN="team-beta-token" ai-menshen -config config.toml -listen :8082 &Now when I check the audit logs, I know exactly which team made each request.
Key Rotation Without Client Changes
This is where BYOK shines. When a key is compromised:
# Old key compromised? Rotate ONCE at the proxyexport OPENAI_API_KEY="sk-proj-NEW-KEY"
# Restart proxy - clients don't need to change anything# Their tokens still work, but now use the new upstream keysystemctl restart ai-menshenNo client updates. No app store releases. No user notifications.
The Mistake I Almost Made
I almost ran ai-menshen without authentication enabled:
[auth]enable = false # DANGER: Anyone can use your proxy!
[providers]api_key = "${OPENAI_API_KEY}"This would have been worse than nothing - the proxy would act as an open relay for my OpenAI quota. Always enable auth:
[auth]enable = truetoken = "${PROXY_TOKEN}" # Required for all API requestsuser = "admin" # Dashboard Basic Authpassword = "${DASHBOARD_PASSWORD}"Architecture Diagram
Here’s how the complete BYOK architecture looks:
BYOK Security Architecture ============================
+-------------+ +------------------------+ +-------------+| Client A | | | | || Token: AAA |---->| | | |+-------------+ | | | | | ai-menshen Proxy | | OpenAI API |+-------------+ | --------------- |---->| || Client B | | Real API Key: | | Billed to || Token: BBB |---->| sk-proj-xxx | | YOUR Acct |+-------------+ | (Server-side only) | | | | | | |+-------------+ | Audit Log: ✓ | +-------------+| Admin |---->| Cache: ✓ || Basic Auth | | Dashboard: ✓ |+-------------+ +------------------------+
Security Benefits:- Clients only know proxy tokens- Real keys never leave server- Token revocation is instant- Per-client audit trail- Key rotation is centralizedSummary
BYOK with a local proxy is the secure way to use OpenAI’s API. Real keys stay server-side, clients use revocable proxy tokens, and you maintain full audit visibility - all with zero changes to how clients call the API.
The key insights from my implementation:
- Never embed API keys in client code - They will be extracted
- Use environment variables for secrets - Not config files
- Enable authentication - Don’t run an open proxy
- Separate tokens per client/team - Enables revocation and auditing
- Rotate keys at the proxy - Clients don’t need to change
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 ai-menshen GitHub Repository
- 👨💻 OpenAI API Security Best Practices
- 👨💻 OWASP API Security Top 10
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments