Demo, all content is generated
Question

Users bring their own OpenAI key, how do I store it without being the next leak?

Solved · 2305 views · asked by kwame_o · edited

My app lets users paste their own OpenAI key so they pay their own usage. Bolt stored it in a api_key text column in the profiles table and the frontend reads it and calls OpenAI directly from the browser. It works but it feels very wrong. If my database leaks, I leak everybody's keys.

What's the normal way to do this?

What I’ve tried

Asked Bolt to 'encrypt the key'. It base64-encoded it, which I know is not encryption. Looked at Supabase Vault but didn't understand if it's meant for this.

Comment
+1 following, I have the same setup for an Anthropic key. dana_ships · edited

3 answers

Marked as helpful by the asker
chidi_eze · edited

Your instinct is right. Rules for bring-your-own-key:

  1. The key goes in once and never comes back out to the browser. The frontend sends it to a server function on save; after that the UI only shows sk-...a1b2.
  2. Store it encrypted with a secret the database doesn't hold. In Supabase, Vault does exactly this (encrypted at rest, key managed outside your tables). Or encrypt in your Edge Function with a key from an env var (AES-GCM via Web Crypto) and store only the ciphertext.
  3. Calls to OpenAI happen server-side: the browser calls your Edge Function, which checks the user, decrypts their key, calls OpenAI, returns the result.
  4. RLS: nobody, not even the owner, can select the encrypted column from the client. Only the function (service role) reads it.

That way a leaked table dump or a buggy policy gives an attacker ciphertext, not working keys.

Comment
ok so the browser never touching the key again is the main change. makes sense kwame_o · edited
Moved it to an edge function + Vault. Bolt did most of the work once I told it exactly these 4 rules. kwame_o · edited
Add a rate limit to that edge function too, otherwise one user's script can burn another user's key if you ever mix up ids. nils_tw · edited
Did not know about Vault. Using this for my Replit app too. halima_s · edited
lena_ops · edited

Adding to Chidi: validate the key when it's saved (one cheap call, e.g. list models) so you store only working keys, and let users delete it themselves. Put in your privacy text that you store it encrypted and what you use it for. People are rightly careful with this.

Comment
nils_tw · edited

Nice UX touch: in the 'paste your key' screen, tell users to create a separate key just for your app, with a project budget limit in their OpenAI dashboard. If anything goes wrong they revoke one key and their other projects keep working.

Comment