Where should I store AI API keys in a Flutter app?
Never in the Flutter bundle. Store AI API keys on a server you control (Supabase Edge Function, Firebase Cloud Function, or Cloudflare Worker) and have the app call your server, which calls the AI provider.
Why it matters
Any key shipped in a client bundle is extractable. Attackers have run bots that decompile App Store apps and mine leaked OpenAI keys within hours of upload — the bill lands on you.
How to do it
-
Move the key to a server environment variable
In Supabase:
supabase secrets set OPENAI_API_KEY=sk-.... In Firebase: functions.config or environment variables. In Cloudflare: Workers Secrets. -
Write a thin proxy endpoint
The endpoint accepts the user's auth token, validates it, forwards the request to the AI provider with your server-side key, and returns the response.
-
Rate-limit per user
Store request counts in your database keyed by user id + hour. Refuse requests over quota. Prevents a single compromised account from draining your provider budget.
-
Never accept model choice from the client
The client asks for "chat"; the server picks the model. Otherwise a client can request the most expensive model on every call.
-
Log every AI call
Cost, latency, user id, and response length. This is how you catch abuse before your provider does.
Common mistakes
- Storing keys in .env files that ship with the Flutter bundle — .env is not a secret store on mobile.
- Using --dart-define at build time to inject keys — they end up as plaintext strings inside the compiled binary.
- Trusting Firebase Remote Config to hide keys — Remote Config values are readable by any authenticated client.
- Not rate-limiting per user, so one bad account can spend $10k in a night.
Frequently asked
- Can I use a WAF or IP allow-list instead of a proxy?
- No. Mobile clients call from unpredictable IPs and cellular networks. WAF rules do not work; you need a real per-user token check.
- What about signing requests with HMAC from the client?
- Better than nothing but still exploitable — the HMAC secret is in the bundle. Only a real server-side proxy is safe.