Bring your own key
The BYOK pattern lets users run a feature with their own API key, kept on their device and never sent to your server. Learn the data flow and the real risks.
In one line: BYOK lets a user run a model feature with their own API key — kept on their device, used to call the provider directly, and never sent to your server.
What it is
BYOK — "bring your own key" — is a pattern where the user supplies their own provider API key instead of using one you ship. The key stays on their device, and the browser calls the model provider directly with it. Your application code orchestrates the request, but it never holds, forwards, or stores the secret.
The contrast is the server-proxy model, where your backend holds the key and the browser talks to your server, which then talks to the provider. Both are legitimate; they just answer different questions about who pays and where the secret lives.
Why it matters
BYOK lets a free or open tool offer real model features without you paying for inference or running a proxy. The user controls their own spend, their own rate limits, and the data they send. For a demo site, a side project, or a power-user tool, that is exactly the right trade: no billing relationship, no server to keep up, no liability for someone else's prompts.
It also keeps you out of the secret-handling business entirely. A key you never receive is a key you can never leak, log, or have subpoenaed.
See it
Tweak it1
Switch between the three modes and watch where the key sits in the flow. Compare the flows and see who can read the key in each one — the recommended BYOK path keeps it on the browser, the proxy path keeps it on your server, and the anti-pattern leaks it into the bundle.
How it works
The whole pattern is one rule: the key never leaves the user's device for anywhere you control.
- Client-only storage. Hold the key in memory (a module variable or a small store) for the session, or persist it to
localStorageonly if the user opts in. Both are on their machine. - Direct browser → provider calls. The browser makes the HTTPS request to the provider with the user's key in the
Authorizationheader. The request never passes through your origin. - Your server stays blind. No endpoint of yours ever receives the key, so nothing of yours can log it, cache it, or include it in an error report.
- A clear "forget key" control. Give the user an explicit way to wipe the key from memory and storage. Forgetting must be as easy as entering.
- Versus the proxy model. If you want to pay and meter usage, hold the key server-side instead and proxy the call. That is a different product decision — your key, your bill — not a BYOK feature.
Build it
A safe key store keeps the secret in memory by default, persists only on explicit opt-in, and offers a hard clearKey(). The one invariant that matters: this value is never sent to your server and never logged.
// IMPORTANT: this key is the USER's secret. It is NEVER sent to your own
// server, never logged, never put in a URL, analytics event, or error report.
// It exists only in the browser, for the user's own direct calls to the provider.
const STORAGE_KEY = "byok:provider-key";
// In-memory by default — gone on refresh unless the user opted into persistence.
let memoryKey: string | null = null;
export function setKey(key: string, persist = false): void {
memoryKey = key;
if (persist && typeof localStorage !== "undefined") {
// Opt-in only. Persisting to localStorage is a deliberate user choice.
localStorage.setItem(STORAGE_KEY, key);
}
}
export function getKey(): string | null {
if (memoryKey !== null) return memoryKey;
if (typeof localStorage !== "undefined") {
memoryKey = localStorage.getItem(STORAGE_KEY);
}
return memoryKey;
}
export function hasKey(): boolean {
return getKey() !== null;
}
// The "forget key" control: wipe it from memory AND storage.
export function clearKey(): void {
memoryKey = null;
if (typeof localStorage !== "undefined") {
localStorage.removeItem(STORAGE_KEY);
}
}Make it yours
Use the controls beside the demo above to change key-handling flow — each change updates the example live.
Experiment in the playground- Step through all three modes and say out loud who can read the key in each — that sentence is the whole security model.
- Sketch your own feature's flow: does the user pay, or do you? That answer picks BYOK versus server-proxy before you write any code.
- Add a "forget key" button to the in-memory store above and confirm a refresh with persistence off truly loses the key.
Reproduce it with an LLM
Reproduce it with an LLM
You are a security-minded front-end engineer. Implement the bring-your-own-key (BYOK) pattern for a client-side app that calls a model provider's API with the USER's own key. Requirements: the key is entered by the user and stored ONLY on their device (in-memory or, if persisted, clearly opt-in) — it is NEVER sent to or logged by your server; calls go directly from the browser to the provider over HTTPS; explain the one real risk (a third-party script on your page could read the key, so a strict CSP and minimal dependencies matter) and how you mitigate it; provide a clear way to clear the key; and never put the key in the URL, analytics, or error reports. Contrast this with the server-proxy model (your key, your bill, your rate limits) and say when each is appropriate. Return the key-handling code plus a short threat-model note.
Pitfalls & accessibility
- Offer a "show key" toggle for the masked input — but keep the field's accessible name stable so assistive tech does not lose track of it when the visual state flips.