The explosion of generative AI has pushed thousands of web developers to add intelligent features to their apps.
The first instinct is usually to call the Gemini API’s SDK directly from the browser. That instinct comes with a serious security risk: exposing your API key to the world.
In this article, you’ll learn why shipping a raw Gemini API key to the client is dangerous, how Firebase AI Logic’s proxy architecture solves it, and how Firebase App Check closes the second half of the problem that a proxy alone doesn’t fix.
By the end, you’ll have a working, production-style setup: a protected AI Logic client, a properly configured App Check flow (debug token included), and real usage patterns, streaming, multi-turn chat, and structured JSON output, not just a single console.log.
Table of Contents
Prerequisites
Before you start, make sure you have the following:
-
Node.js v18 or later (
node --version) -
A Google account to create a Firebase project (the free Spark plan works for the Gemini Developer API)
-
Basic familiarity with JavaScript,
async/await, and ES modules -
A code editor and a terminal
You don’t need prior experience with Firebase, App Check, or the Gemini API, as this guide builds that understanding from the ground up.
The Problem With Client-Side API Keys
Embedding an API key inside a JavaScript bundle, or in an .env file that ends up shipped to the browser, is a critical security flaw, and it’s trivially easy to exploit. Here’s what that actually looks like in practice.
Say you call the Gemini API directly from client code like this:
// DON'T do this in a browser-shipped app
const genAI = new GoogleGenerativeAI("AIzaSyD4-your-real-key-here");
Bundle that with any build tool and the key lands in your output JS as plain text. Anyone can find it in under a minute, no special tools required:
# Anyone can run this against your deployed bundle
curl -s https://your-app.com/assets/main.js | grep -oE "AIzaSy[A-Za-z0-9_-]{33}"
That one command extracts a Gemini API key from a minified production bundle if it’s in there. From the Network tab in the browser’s dev tools, it’s even more visible, every outgoing request to generativelanguage.googleapis.com shows the key directly in the query string or headers.
If your Gemini API key leaks this way, an attacker can:
-
Drain your entire usage quota
-
Cause your Cloud bill to spike unpredictably (Gemini calls are billed per token, unlike a flat-rate database read)
-
Use your resources to run their own requests, which can get your Google Cloud project suspended for abuse
Historically, the only fix was to build, deploy, and maintain a custom backend server (Node.js, Python, Go…) that acted as a proxy between your app and the Gemini API, just to keep one string secret. That’s real infrastructure to run for what should be a simple feature.
Step 1 – How Firebase AI Logic’s Proxy Architecture Works
Firebase AI Logic gives you that proxy gateway without you having to build or host it. You still write client-side code, but the key never leaves Google’s infrastructure.
[Web Browser] ──(Authenticated Request)──> [Firebase AI Logic Proxy] ──(Key Injected Server-Side)──> [Gemini API]
Your Gemini API key stays stored securely, tied to your Firebase project. The client SDK sends a request to the proxy gateway, and the proxy injects the key and forwards the call to your chosen “Gemini API” provider. The key is never present in your JS bundle, your network requests, or anything the browser can inspect.
Firebase AI Logic supports two providers, chosen when you set up the service in the console:
| Gemini Developer API | Agent Platform Gemini API (formerly Vertex AI) | |
|---|---|---|
| Billing plan | Works on the free Spark plan | Requires the Blaze (pay-as-you-go) plan |
| Best for | Getting started fast, prototyping, most web/mobile apps | Data-residency requirements, teams already on Google Cloud/Vertex AI |
| Region control | Limited | Choose specific regions (us, eu) for model access, depending on the model |
| Setup friction | Minimal, no billing needed | Requires linking a Cloud Billing account |
For most apps, start with the Gemini Developer API, that’s what this tutorial uses. Switching providers later is a config change, not a code rewrite, since both go through the same getGenerativeModel() interface.
Step 2 – What Firebase App Check Actually Does
A proxy hides the key. It does not, by itself, stop anyone from calling that proxy. Your Firebase config object (apiKey, projectId, and so on) isn’t a secret, it’s meant to be public, and it’s visible in every deployed app’s bundle by design. Without another layer, a bot could copy that config, initialize its own Firebase app pointed at your project, and call your AI Logic proxy directly, running up your Gemini bill with none of your actual users involved.
This is exactly what Firebase App Check is for, and it’s worth understanding precisely what it does, because it’s easy to confuse with authentication.
App Check is attestation, not authentication. Firebase Authentication answers “who is this user?” App Check answers a different question: “is this request coming from a genuine, untampered instance of my app, and not a script, a bot, or someone else’s app using my config?” You can, and should, use both together, but App Check is what protects you from abuse even when a request comes with zero user context.
How the flow actually works, step by step:
-
When your app initializes App Check, the SDK triggers an attestation challenge with the configured provider. On the web, that’s reCAPTCHA Enterprise, it runs an invisible risk assessment (mouse movement, browser fingerprint, network signals) and returns a token asserting “this looks like a legitimate browser session.”
-
Your app’s SDK sends that reCAPTCHA token to Firebase’s App Check backend, which exchanges it for a Firebase App Check token, a short-lived, signed JWT.
-
That App Check token is cached locally and automatically attached to every subsequent request your app makes to Firebase AI Logic (and other App Check-integrated services like Firestore or Cloud Functions).
-
Before the AI Logic proxy forwards your request to Gemini, it verifies the App Check token’s signature and validity. No valid token, no request reaches the model.
The reason this matters specifically for generative AI, more than for a typical CRUD backend, is cost shape. A blocked Firestore read costs you nothing. A blocked Gemini call, if it weren’t blocked, could cost real money per request, and at scale, a scripted abuse loop can burn through a monthly budget in hours. App Check is the gate that makes sure only your actual app can trigger that spend.
Heads up: Google has announced that App Check enforcement will become mandatory for Firebase AI Logic starting November 2, 2026. Starting even earlier, in July 2026, the guided setup workflow in the Firebase console already enables App Check automatically for new AI Logic integrations. Any unverified request made after the enforcement date will be rejected outright, so it’s worth wiring this up now rather than scrambling later.
Step 3 – Set Up Your Firebase Project
-
Go to the Firebase console and create a new project (or open an existing one).
-
In the left sidebar, open Build and then AI Logic, then click Get started. Choose Gemini Developer API as your provider to follow along without setting up billing.
-
Back in Project settings, go to General and then Your apps. Register a web app if you haven’t already, and copy the Firebase config object. You’ll need it in the next steps.
We’ll leave the App Check setup for the next step, as it deserves its own walkthrough.
Step 4 – Integrate Firebase App Check
Register Your App for reCAPTCHA Enterprise
In the Firebase console, go to Build and then App Check, select your web app, and choose reCAPTCHA Enterprise as the provider. Firebase generates a site key tied to your app’s domain. Copy it, as you’ll pass it into your code below.
Install the SDK
npm install firebase
That’s the only package you need. ReCaptchaEnterpriseProvider ships inside firebase/app-check, part of the same firebase package. There’s no separate reCAPTCHA SDK to install and no

