Skip to content

Latest commit

 

History

History
 
 

README.md

Auth Agent

Demonstrates how to protect agent WebSocket and HTTP connections with JWT authentication.

This is a demo, not a production auth system. The /api/token endpoint hands out JWTs to anyone — it simulates your existing auth service. In production, replace it with your own identity provider. The patterns to copy are onBeforeConnect and onBeforeRequest — those stay the same regardless of how you issue tokens.

What it shows

  • Protecting WebSocket connections via onBeforeConnect
  • Protecting HTTP agent routes via onBeforeRequest
  • Issuing JWTs with jose (HMAC-SHA256)
  • Passing user identity from JWT claims into the agent

Getting started

npm install

# Create .env with your secret
echo "AUTH_SECRET=$(openssl rand -base64 32)" > .env

# Start dev server
npm start

The pattern you should copy

Use onBeforeConnect to protect WebSocket connections and onBeforeRequest to protect HTTP requests. Everything else in this example (the token endpoint, the login form, localStorage) is scaffolding you will replace with your own auth.

Protect WebSocket connections

WebSocket upgrade requests do not support custom headers. Pass the JWT as a query parameter and verify it in onBeforeConnect:

routeAgentRequest(request, env, {
  onBeforeConnect: async (req) => {
    const token = new URL(req.url).searchParams.get("token");
    if (!token)
      return Response.json({ error: "Missing token" }, { status: 401 });

    const payload = await verifyToken(env, token);
    if (!payload)
      return Response.json({ error: "Unauthorized" }, { status: 401 });

    // Return the original request to allow the connection
    return req;
  },

On the client, pass the token via the query option on useAgent:

const agent = useAgent({
  agent: "ChatAgent",
  name: userName,
  query: async () => ({ token: getToken() || "" })
});

Protect HTTP requests

The SDK also makes HTTP requests (e.g. fetching initial messages). These use the same query option, so check both the Authorization header and the query parameter:

  onBeforeRequest: async (req) => {
    const authHeader = req.headers.get("Authorization");
    const token = authHeader?.startsWith("Bearer ")
      ? authHeader.slice(7)
      : new URL(req.url).searchParams.get("token");
    if (!token)
      return Response.json({ error: "Missing token" }, { status: 401 });

    const payload = await verifyToken(env, token);
    if (!payload)
      return Response.json({ error: "Unauthorized" }, { status: 401 });
    return req;
  }
});

How the demo works end-to-end

Browser                          Worker                        Durable Object
──────                          ──────                        ──────────────
1. POST /api/token          ──► issueToken(name)              ← REPLACE THIS
   { "name": "Alice" }           with your own auth service
                                  ◄──── { token: "eyJ..." }

2. WebSocket /agents/*       ──► onBeforeConnect:             ← COPY THIS
   ?token=<jwt>                   jwtVerify(token, secret)
                                  ◄──── 401 or upgrade

3. HTTP /agents/*            ──► onBeforeRequest:             ← COPY THIS
   Bearer header or ?token=       jwtVerify(token, secret)
                                  ◄──── 401 or response

Personalised responses

The Durable Object name is set to the user's name from the JWT sub claim. The system prompt includes this name so the LLM can address the user personally:

const userName = this.name; // DO name = user name from JWT
const result = streamText({
  system: `You are a helpful assistant. The user's name is ${userName}.`,
  ...
});

File overview

File Purpose
src/server.ts Worker entry — token endpoint (replace), JWT verify (copy)
src/auth-client.ts Client-side token fetch and storage (replace with your auth)
src/client.tsx React UI — name form + chat
src/index.tsx React root
src/styles.css Tailwind + Kumo imports

Environment variables

Variable Required Description
AUTH_SECRET Yes HMAC secret for signing/verifying JWTs. Put in .env.

Deploying

  1. Set the secret: wrangler secret put AUTH_SECRET
  2. Deploy: npm run deploy

Related examples

  • ai-chat — chat agent without auth
  • playground — kitchen-sink showcase of all SDK features