↑ ↓ navigate↵ selectesc close

Securing AI agents and tool calls in Firebase: Web Architecture & Implementation Guide

CodingCat.dev tutorial cover image

Calling frontier multimodal models directly from a web or mobile client is a security minefield if you expose raw API keys or hardcode brittle system prompts in your frontend bundle. Following the announcement of Securing AI agents and tool calls by Alexander Nohe on the Official Firebase Blog (2025-12-02), here is our hands-on CodingCat.dev breakdown of what actually shipped, how it works under the hood, and how we wire it into production TypeScript, Next.js, Astro, SvelteKit, React, and Angular applications. Securing AI agents helps stop prompt injection from letting users access unauthorized data. However, this can be harder than it seems because you need to ensure your tool definitions use authorized application context and the principle of least privilege.

The Mental Model: Why Securing AI agents and tool calls Matters for Web Devs

As a Google Developer Expert (GDE) for Firebase and Senior Solutions Engineer at CodingCat.dev, I evaluate every Firebase update through one lens: does it reduce boilerplate while keeping our runtime security and cloud bills predictable? Firebase AI Logic lets you invoke Gemini and Imagen models from client and edge apps using the Firebase Web SDK while keeping your API credentials off the wire, enforcing Firebase App Check, and managing server-side prompt templates centrally.

AJ Pro-Tip from Alex: Combine Firebase AI Logic with server prompt templates, Firebase Remote Config feature flags, and project spend caps so you can iterate on prompts or swap Gemini models instantly without redeploying your Next.js or Astro frontend.

How It Works: Technical Breakdown & Implementation

AI agents are growing in popularity, leading many of us to connect them with our existing app services to execute key actions. For example, querying existing user data using an AI agent. You can hand the agent a set of tools that have parameters that you would normally define in a non-AI function, something like, fetchUserNotes(userId:string) {}. On the surface, this seems fine to hand off to your AI agent. Having a tool with this method signature can actually lead to a much larger problem since our client app can send whatever they want in the prompt to inject extra information into the request context. In this blog article, we are going to go into detail on what the hidden problem is with this method signature for a tool call.

Tool calling needs extra care

Calling tools may seem straightforward. You may even consider designing your tool calling schemas in the same way you would normally design them in an SDK. However, when it comes to agentic systems, this is not a great idea because it risks exposing more data to the requesting user than is authorized. In the following code snippet, we have a tool definition with two structs and types associated with it.

type
    UID 
}

// this is an anti-pattern, do not do this
getUserNotesTool 
    "
    "
    func
        // ommitted for brevity
})

userInfoFlow 
    "
    func
    ac 
    // ommitted decoding app check token

    // ommitted reading UID


    mr, err 
        ai.
            fmt.
            You are a cheerful helpful assistant for the notes app. The user id
            of the currently logged in user is %s. Please help the user complete
            their request : %s
            )
        ),
        ai.
        if
            return
        }

    return
})

You can see that the input is a GetUserNotesRequest which takes a user ID as the input value into the function and outputs a list of notes. Can you spot what may be wrong with this definition of a tool?

You will notice that in the genkit.Generate call, we supply a prompt that supplies the user id to the AI system via us injecting it with the logged in user. The problem is that as we make that call, our end user can insert a note in their prompt to fetch all user notes for a different user id, i.e., fetch all notes for user ‘1234’. This removes our additional context that we provide in the prompt and returns arbitrary user records for any user when the tool call is made.

Using application context and least privilege

There are two separate and distinct ways to handle this type of design and issue within AI applications. One would be to use the context that your application is operating in and the other is to design a least privileged system for access. Let’s start with the context design first.

In a context designed system, you likely have the user id from the request because you are already restricting requests to authorized users, but what if you wanted to pass that context to other tools? You can use a built-in context object offered in lots of AI platforms to send that additional context in requests for each tool request you want to make. When making requests in Genkit, you parse the headers and extract the Authorization token. You can parse the token and extract the UID as In your example or you could pass the entire token around to different methods. An example of this may look like the following code snippet:

mux 
mux.
    genkit.
        genkit.
            func
                ctx 
            ) (
                ac 

                // get userID from bearer token
                ac[
                authTokenResult, err 
                    ctx, strings.
                if
                    ac[
                }

                // add app check token to context
                ac[

                return
})))

This context can then be used in other locations. For instance, looking back at our getUserNotesTool the tool definition may now look like the following.

type

getUserNotesTool 
  "
  "
  func
    // the uid is supplied from the Headers
    uid 
    v 
    return
})

Now, instead of fetching whatever user ID was passed to it, the getUserNotes function will only fetch notes for the uid that was retrieved from the Authorization token.

Even better, make it impossible for the getUserNotes tool to read another user’s notes. In your example, we were using an account with a heightened privilege to the database. This is great for certain scenarios where we need heightened access like correlating data between multiple tables and accounts. However, the AI workflow we created is user centric and should focus on the access that the user has, not what an administrator has access to.

Our getUserNotes tool should really only be able to access the notes that the user has access to, not all notes. So to make this better, we should instead replace our heightened account request with a user level request. This means we should not call our backend with a highly privileged account that can access all tables. Instead, we should call our backend with the user’s credential on a user-available endpoint. This limits the amount of data that can be accessed with that API key. This would provide an extra layer of security in the event the user found another way to switch the tool call to calling a different user id In your example.

package

import
	"
	"
	"
	"
	"
	"
	"
	"

	firebase 
	"
	"
	"
	"
)

const
	PROJECT_ID     
	CLOUD_LOCATION 
)

func
	ctx 

	app, err 
	if
		log.
	}

	auth, err 
	if
		log.
	}

	appCheck, err 
	if
		log.
	}

	g 
		genkit.
		genkit.
	)

	type
		UID 
	}

	type
		Notes []
	}

	type

	getUserNotesTool 
		func
			uid 
			v 
			fmt.
			return
		})

	type
		Request 
	}

	type
		Response 
	}
	userInfoFlow 
		ac 
		_, err 
		if
			return
		}
		uid 
		if
			return
		}

		mr, err 
		if
			return
		}
		response 

		return
	})

	mux 
	mux.
		ac 
		ac[
		ac[

		authTokenResult, err 
		if
			ac[
		}

		ac[

		return
	})))
	server 
		Addr:    
		Handler: 
	}
	go
		log.
		log.
		if
			log.
		}
	}()

	quit 
	signal.
	<-
	log.

	ctx, cancel 
	defer

	if
		log.
	}
	log.
}

func
	return
		w.
		w.
		w.
		if
			w.
			return
		}
		next.
	})
}

Production Web Architecture & Security Checklist

Whether we are deploying on Firebase App Hosting, Cloudflare Workers, or Vercel, shipping AI features to production requires defense-in-depth across three layers:

  • Enforce Firebase App Check & Attestation: Protect your web endpoints, Cloud Functions, Data Connect GraphQL resolvers, and Firebase AI Logic calls against unauthorized replay or billing abuse using Firebase App Check with reCAPTCHA Enterprise.
  • Validate Locally with the Firebase Emulator Suite: Run deterministic integration tests against local Firestore, Auth, Functions, and Data Connect PostgreSQL emulators before promoting schema or rule changes to production.
  • Keep Server & Client Boundaries Type-Safe: Use the modular Firebase Web SDK and generated TypeScript bindings so SSR frameworks like Next.js, Astro, SvelteKit, and Angular serialize state cleanly without hydration flickers.

Conclusion

Accelerate your full-stack web and AI development by pairing Securing AI agents and tool calls with strict Security Rules, Firebase App Check, and local emulator testing. Come tell us on X (@CodingCatDev) and LinkedIn (CodingCat.dev) how you are using Firebase in your stack, and subscribe to the CodingCat.dev YouTube Channel so you don't miss what we build next!

Watch Our Related Firebase Video Tutorial on YouTube

Want to see how we architect and debug Firebase web applications live? Watch our related CodingCat.dev session Firebase Security Rules: Effortless control over your app's data. below, and make sure to Subscribe to CodingCat.dev on YouTube for weekly full-stack web and AI deep dives:

Resources, Official Docs & Related CodingCat.dev Guides

Frequently Asked Questions (FAQ)

What are the key capabilities introduced in Securing AI agents and tool calls?

Securing AI agents and tool calls (announced 2025-12-02 by the Firebase team) enhances the platform for Firebase, Google Cloud, Web Dev, AI, TypeScript, Security workloads. Securing AI agents helps stop prompt injection from letting users access unauthorized data. However, this can be harder than it seems because you need to ensure your tool definitions use authorized application context and the principle of least privilege. Developers can review the primary announcement on the Firebase Blog alongside our implementation patterns and production checklist above.

How do web developers use Securing AI agents and tool calls with Next.js, SvelteKit, React, or Angular?

Web developers can integrate these features using the modular Firebase JavaScript/TypeScript SDK, Firebase Data Connect web bindings, and Firebase App Hosting. Always pair client-side SDK initialization with Firebase App Check and strict Security Rules, and test locally with the Firebase Local Emulator Suite before deploying.

Where can I watch video tutorials on Firebase Web and AI development?

You can watch our hands-on Firebase video walkthroughs on the CodingCat.dev YouTube Channel, including Firebase Security Rules: Effortless control over your app's data., and follow us on X (@CodingCatDev) for weekly full-stack tutorials.