What is AgentGate?
AgentGate is a zero-trust proxy layer for AI agents. Instead of letting agents call Gmail, GitHub, or any API directly, every action passes through AgentGate first. The policy engine evaluates it and returns one of three outcomes: ALLOWED (executes immediately), BLOCKED (rejected with a reason), or STEP-UP (held for human approval via a real-time dashboard).
Inspiration
As AI agents get deployed in real companies, they accumulate dangerous levels of access. A single prompt injection can turn a helpful assistant into an attacker with full inbox and repository access. Most defenses focus on the input side better prompts, better filters. AgentGate focuses on the output side: what the agent is actually doing.
How I Built It
- Backend: FastAPI + SQLAlchemy + PostgreSQL, deployed on Railway
- Frontend: React dashboard with WebSocket live feed, deployed on Vercel
- Auth: Auth0 for user login + Token Vault for runtime credential fetching
- Policy Engine: Custom expression parser (no eval()) matching action type + parameters against stored rules
- Step-Up Auth: Pending challenges broadcast via WebSocket to dashboard, 15-minute approval window, agent retries after human resolves
AgentGate is framework-agnostic by design. Any AI agent, LangChain, AutoGPT, Claude, GPT-4 with tools, or a fully custom agent can plug in by simply sending HTTP requests to /execute instead of calling APIs directly. The demo agent simulates 10 real-world actions to showcase the full policy enforcement flow, but the proxy layer, Token Vault integration, step-up auth, and audit trail all work identically regardless of what agent sits behind it.
Challenges
The hardest part was real time synchronization during step-up auth. When
a human approves a challenge, three things need to happen simultaneously
without race conditions: the agent learns it can retry, the dashboard
updates the status, and the backend marks the challenge resolved. This
required careful WebSocket message sequencing and a resolvedMap state
system in React.
Cross-domain cookies between Vercel (frontend) and Railway (backend) were
another challenge Auth0 session cookies needed samesite=none; secure
to work across domains without breaking local development.
What I Learned
Agent security is fundamentally an authorization problem, not an input validation problem. Once an attacker controls an agent, input filters are irrelevant , they can issue perfectly legitimate API calls. The right solution is to externalize authorization from the agent entirely, which is exactly what AgentGate does.
Auth0 Token Vault turned out to be the cleanest part of the architecture. The abstraction it provides fetch credentials at runtime, never persist them locally solved the hardest security problem almost automatically.
Bonus Blog Post
Building AgentGate: The Credential Problem Nobody Is Talking About
There's a security crisis quietly building in every company deploying AI agents, and it has nothing to do with prompt injection.
It has to do with your .env file.
When I started building AgentGate, my first instinct was exactly what every developer does: create a .env, paste in the Gmail OAuth token, paste in the GitHub token, and get moving. It took me about 20 minutes to realize what I'd actually built an AI agent that, if compromised, would hand an attacker persistent, full-scope access to an inbox and a codebase. No expiry. No audit trail. No way to revoke access without redeploying the entire application.
That's when I stopped writing feature code and started thinking about the actual problem.
The real threat surface isn't the input. It's the output.
Most AI security conversation focuses on prompt injection, preventing an attacker from hijacking what the agent thinks. But once an attacker controls an agent's reasoning, input filtering is irrelevant. The agent still has your OAuth tokens. It can still issue perfectly legitimate API calls to Gmail, GitHub, Slack and those calls look completely normal to every existing monitoring system.
The vulnerability isn't in what the agent is told to do. It's in what the agent is allowed to do, and what credentials it's holding to do it.
AgentGate is my answer to that problem. It's a zero-trust proxy layer that sits between any AI agent and the external APIs it needs to call. Every action send email, push code, delete a branch passes through AgentGate before it touches a real API. The policy engine evaluates it and returns one of three outcomes: execute immediately, block with a reason, or hold it and wake up a human for approval.
How Auth0 Token Vault changed the architecture
The design of AgentGate only became clean once I brought in Auth0 Token Vault. Before that, I was going in circles trying to figure out where credentials should live.
Here's the flow that ended up working: when a user authenticates via Auth0, their OAuth tokens for Gmail and GitHub are stored in the Token Vault keyed to their user ID. When an agent submits an action to AgentGate and the policy engine approves it, the backend authenticates to Auth0 using M2M (client credentials flow), fetches the user's token from /api/v2/users/{id} at that moment, uses it for the API call, and discards it. The token never touches the PostgreSQL database. It's never cached. It lives in Auth0's vault until the exact moment it's needed.
async def get_token(action: str, user_id: str):
# Step 1: Authenticate AgentGate to Auth0 via M2M
token_resp = await client.post(
f"https://{AUTH0_DOMAIN}/oauth/token",
json={
"grant_type": "client_credentials",
"client_id": AUTH0_M2M_CLIENT_ID,
"client_secret": AUTH0_M2M_CLIENT_SECRET,
"audience": f"https://{AUTH0_DOMAIN}/api/v2/"
}
)
m2m_token = token_resp.json()["access_token"]
# Step 2: Fetch the user's stored token from Token Vault
vault_resp = await client.get(
f"https://{AUTH0_DOMAIN}/api/v2/users/{user_id}",
headers={"Authorization": f"Bearer {m2m_token}"}
)
identities = vault_resp.json().get("identities", [])
# Return the right token based on action type (gmail vs github)
The whole file is under 60 lines. That simplicity surprised me. A problem that felt architecturally enormous how do you securely give an autonomous agent access to credentials without giving it credentials had a remarkably clean solution.
The practical security benefit is real. If the AgentGate PostgreSQL instance is compromised tomorrow, the attacker gets audit logs. They can see what actions were taken. They cannot replay them, because there are no credentials in the database to replay with.
Step-up authentication: the hardest 200 lines I've written
The policy engine was the fun part. The hard part was what happens when a policy says "hold this action and ask a human."
When an agent tries to push to a protected branch, AgentGate creates a challenge in the database, broadcasts it over WebSocket to the dashboard, and returns {"step_up_required": true, "challenge_id": "..."} to the agent. The agent waits. A human sees the action parameters in the dashboard the exact repo, branch, and commit and clicks approve or deny. The backend marks the challenge resolved, broadcasts the resolution over WebSocket, and the agent retries with the challenge ID to confirm.
Three things need to happen simultaneously without race conditions: the agent learns it can retry, the dashboard updates its status, and the backend closes the challenge. Getting that sequencing right required a resolvedMap state system in React and careful WebSocket message ordering on the backend. I rewrote that flow three times before it was reliable.
The 15-minute approval window was a deliberate choice. Long enough that a human isn't pressured to approve blindly. Short enough that an attacker can't wait out an overnight window.
The pattern I think the industry needs to standardize on
Building AgentGate taught me something I didn't expect: agent security is an authorization problem, not an authentication or input validation problem.
Authentication tells you who the agent is. Input validation tries to control what it thinks. But authorization controls what it can actually do and that control needs to live outside the agent entirely.
Token Vault is what makes that possible. Agents should never hold credentials. They should earn access per-action, with a human-controlled revocation path that requires no changes to the agent itself. If you want to revoke an agent's Gmail access, you revoke it in the Auth0 dashboard. The agent doesn't need to be redeployed. The access simply stops.
That's the model AgentGate is built around. I think it's the right one and I think the industry is going to learn that lesson one breach at a time if we don't start building this way deliberately.
If you're shipping agentic systems today, Token Vault shouldn't be an afterthought. It should be the first dependency you add.
Built With
- auth0
- fastapi
- groq
- httpx
- postgresql
- python
- railway
- react
- sqlalchemy
- token-vault
- vercel
- websocket
Log in or sign up for Devpost to join the conversation.