AutoGen Tutorial (TypeScript): adding authentication for advanced developers
This tutorial shows how to add authentication to an AutoGen TypeScript agent setup so your agents can call protected APIs, enforce per-user access, and keep credentials out of your agent logic. You need this when your assistant is doing real work against internal systems like customer records, policy services, or billing APIs.
What You'll Need
- •Node.js 18+ and a TypeScript project
- •
autogen-agentchatinstalled - •
openaiinstalled - •
expressinstalled for the protected API - •
jsonwebtokeninstalled for JWT auth - •
dotenvinstalled for environment variables - •An OpenAI API key
- •A JWT signing secret for local development
Install the packages:
npm install autogen-agentchat openai express jsonwebtoken dotenv
npm install -D typescript tsx @types/express @types/jsonwebtoken @types/node
Step-by-Step
- •Start by defining a protected service that requires a bearer token. This gives you something realistic for the agent to call instead of mocking auth in the agent itself.
// server.ts
import express from "express";
import jwt from "jsonwebtoken";
const app = express();
app.use(express.json());
const SECRET = process.env.JWT_SECRET ?? "dev-secret";
app.get("/customer/:id", (req, res) => {
const auth = req.headers.authorization;
if (!auth?.startsWith("Bearer ")) return res.status(401).json({ error: "missing_token" });
try {
jwt.verify(auth.slice(7), SECRET);
res.json({ id: req.params.id, status: "active", tier: "gold" });
} catch {
res.status(403).json({ error: "invalid_token" });
}
});
app.listen(3001, () => console.log("Auth API listening on http://localhost:3001"));
- •Next, create a token issuer for your local tests. In production this would be your identity provider, but for development a signed JWT is enough to validate the flow end to end.
// token.ts
import jwt from "jsonwebtoken";
const SECRET = process.env.JWT_SECRET ?? "dev-secret";
export function issueToken(userId: string) {
return jwt.sign(
{ sub: userId, role: "agent-user" },
SECRET,
{ expiresIn: "15m", issuer: "local-auth" }
);
}
if (require.main === module) {
console.log(issueToken("user-123"));
}
- •Now build an authenticated tool that injects the token into the outbound request. The important part is that the agent never sees raw credentials; it only calls a tool that already knows how to authenticate.
// tools.ts
export async function getCustomerProfile(customerId: string, token: string) {
const response = await fetch(`http://localhost:3001/customer/${customerId}`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!response.ok) {
const body = await response.text();
throw new Error(`API error ${response.status}: ${body}`);
}
return response.json() as Promise<{ id: string; status: string; tier: string }>;
}
- •Wire the tool into an AutoGen assistant and pass auth context from your application layer. This is the pattern you want in production: user identity enters at the edge, then gets threaded into tools through explicit context.
// agent.ts
import { AssistantAgent } from "autogen-agentchat";
import OpenAI from "openai";
import { getCustomerProfile } from "./tools";
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const assistant = new AssistantAgent({
name: "support_agent",
modelClient: client,
systemMessage:
"You are a support agent. Use tools only when needed and never invent customer data.",
});
async function main() {
const token = process.env.USER_TOKEN!;
const profile = await getCustomerProfile("42", token);
console.log(profile);
const result = await assistant.run({
messages: [{ role: "user", content: "Summarize customer 42." }],
});
console.log(result.messages.at(-1));
}
main().catch(console.error);
- •If you need per-request authentication inside a larger app, wrap token retrieval in middleware and keep it outside AutoGen entirely. That keeps your agent reusable across users while your web layer handles session validation.
// app.ts
import express from "express";
import { issueToken } from "./token";
import { getCustomerProfile } from "./tools";
const app = express();
app.get("/debug/profile/:id", async (req, res) => {
const userId = String(req.header("x-user-id") ?? "");
if (!userId) return res.status(401).json({ error: "missing_user" });
const token = issueToken(userId);
const profile = await getCustomerProfile(req.params.id, token);
res.json({ userId, profile });
});
app.listen(3000, () => console.log("App listening on http://localhost:3000"));
Testing It
Start the auth API first with JWT_SECRET=dev-secret npx tsx server.ts. Then run your app or call issueToken("user-123") and use that value as USER_TOKEN before executing agent.ts.
Verify three cases:
- •A valid token returns customer data with HTTP 200.
- •No token returns HTTP 401.
- •A tampered token returns HTTP 403.
If you want to test the full path, hit /debug/profile/42 on the Express app and confirm it gets a fresh signed token, calls the protected API, and returns JSON without exposing secrets in logs or prompts.
Next Steps
- •Add OAuth2 client credentials flow instead of local JWT signing.
- •Move tool auth into a shared request context object so multiple tools can reuse it.
- •Add audit logging for user ID, tool name, and downstream API status codes.
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit