DEV Community

Cover image for Add auth to your AI agents in 5 minutes with KavachOS
GDS K S
GDS K S

Posted on

Add auth to your AI agents in 5 minutes with KavachOS

Auth libraries handle human sign-in. But what happens when your AI agent needs to read from GitHub, deploy to production, or call another agent's API? You end up building a custom permissions layer, rolling your own tokens, and hoping your audit trail holds up.

I got tired of building that layer from scratch for every project, so I built KavachOS. It handles both human auth and agent identity in one library. Here's how to get it running.

What you'll build

┌─────────────────────────────────────────────┐
│                  Your App                   │
│                                             │
│  ┌──────────┐   ┌──────────┐   ┌──────────┐ │
│  │  Human   │   │  Agent   │   │  Agent   │ │
│  │  (Ada)   │   │ (reader) │   │ (deploy) │ │
│  └────┬─────┘   └────┬─────┘   └────┬─────┘ │
│       │              │              │       │
│       ▼              ▼              ▼       │
│  ┌──────────────────────────────────────┐   │
│  │          KavachOS Auth Layer         │   │
│  │  sessions · tokens · permissions ·   │   │
│  │  delegation · audit · MCP OAuth      │   │
│  └──────────────────────────────────────┘   │
│       │                                     │
│       ▼                                     │
│  ┌──────────┐                               │
│  │ SQLite / │                               │
│  │ Postgres │                               │
│  └──────────┘                               │
└─────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

A Node.js API where humans sign in with email/password, AI agents get their own identity with scoped permissions, and every action is authorized and logged.

About 5 minutes if you type fast.

Setup

mkdir my-app && cd my-app
npm init -y
npm install kavachos @kavachos/hono hono
Enter fullscreen mode Exit fullscreen mode

Create index.ts:

import { createKavach } from "kavachos";
import { emailPassword } from "kavachos/auth";
import { createHonoAdapter } from "@kavachos/hono";
import { Hono } from "hono";

const kavach = await createKavach({
  database: { provider: "sqlite", url: "kavach.db" },
  plugins: [emailPassword()],
});

const app = new Hono();
app.route("/api/auth", createHonoAdapter(kavach));
Enter fullscreen mode Exit fullscreen mode

That gives you a full auth API. Sign-up, sign-in, sign-out, session management. The SQLite database is created automatically.

Create a user

curl -X POST http://localhost:3000/api/auth/sign-up/email \
  -H "Content-Type: application/json" \
  -d '{"email": "ada@example.com", "password": "secret123", "name": "Ada"}'
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "data": {
    "token": "kv_sess_abc123...",
    "user": { "id": "user-1", "email": "ada@example.com", "name": "Ada" }
  }
}
Enter fullscreen mode Exit fullscreen mode

Now the interesting part: agent identity

This is where KavachOS is different from other auth libraries. You can give AI agents their own identity with scoped permissions.

// Create an agent that can read GitHub repos but needs approval to deploy
const agent = await kavach.agent.create({
  ownerId: "user-1",
  name: "github-reader",
  type: "autonomous",
  permissions: [
    { resource: "mcp:github:*", actions: ["read"] },
    {
      resource: "mcp:deploy:production",
      actions: ["execute"],
      constraints: { requireApproval: true },
    },
  ],
});

console.log(agent.token); // kv_agent_xyz789...
Enter fullscreen mode Exit fullscreen mode

The agent gets a cryptographic bearer token and a set of permissions. The permissions use wildcard matching, so mcp:github:* matches mcp:github:repos, mcp:github:issues, and anything else under that namespace.

How wildcard matching works

Permission:  mcp:github:*

  mcp:github:repos      ✅ match
  mcp:github:issues     ✅ match
  mcp:github:pulls      ✅ match
  mcp:deploy:staging    ❌ no match
  mcp:slack:send        ❌ no match
Enter fullscreen mode Exit fullscreen mode

Authorize agent actions

Before your agent does something, check if it's allowed:

const result = await kavach.authorize(agent.id, {
  action: "read",
  resource: "mcp:github:repos",
});

if (result.allowed) {
  // Go ahead, read the repos
  console.log("Authorized. Audit ID:", result.auditId);
} else {
  console.log("Denied:", result.reason);
}
Enter fullscreen mode Exit fullscreen mode

Every authorization check is logged with an audit ID. You can trace what any agent did, when, and whether it was allowed.

Delegation chains

Agents can delegate permissions to other agents, with limits:

Ada (human)
  └── github-reader (agent)
        permissions: [mcp:github:* → read, mcp:deploy:* → execute]
        │
        └── repo-scanner (sub-agent)
              permissions: [mcp:github:repos → read]
              ↑ subset of parent's permissions only
Enter fullscreen mode Exit fullscreen mode
const subAgent = await kavach.agent.create({
  ownerId: agent.id, // owned by another agent, not a human
  name: "repo-scanner",
  type: "autonomous",
  permissions: [
    { resource: "mcp:github:repos", actions: ["read"] },
  ],
});
Enter fullscreen mode Exit fullscreen mode

The sub-agent can only have permissions that are a subset of its parent's permissions. If the parent agent gets revoked, all its sub-agents lose access too. You can also set depth limits to prevent chains from going too deep.

Wire it to MCP

If you're using the Model Context Protocol, KavachOS ships an OAuth 2.1 authorization server:

import { mcpOAuth } from "kavachos/mcp";

const kavach = await createKavach({
  database: { provider: "sqlite", url: "kavach.db" },
  plugins: [emailPassword(), mcpOAuth()],
});
Enter fullscreen mode Exit fullscreen mode

This gives you PKCE S256, dynamic client registration (RFC 7591), and resource indicators (RFC 8707). Your MCP tools can authenticate through standard OAuth flows.

What the MCP OAuth flow looks like

MCP Client                    KavachOS                    MCP Server
    │                            │                            │
    │  1. Authorization Request  │                            │
    │  (PKCE + code_challenge)   │                            │
    │ ─────────────────────────► │                            │
    │                            │                            │
    │  2. Auth Code              │                            │
    │ ◄───────────────────────── │                            │
    │                            │                            │
    │  3. Token Exchange         │                            │
    │  (code + code_verifier)    │                            │
    │ ─────────────────────────► │                            │
    │                            │                            │
    │  4. Access Token           │                            │
    │ ◄───────────────────────── │                            │
    │                            │                            │
    │  5. API Call with Token    │                            │
    │ ──────────────────────────────────────────────────────► │
    │                            │                            │
Enter fullscreen mode Exit fullscreen mode

Add more auth methods

Want magic links? Passkeys? OAuth with Google? They're all plugins:

import {
  emailPassword,
  magicLink,
  passkey,
  totp,
} from "kavachos/auth";

const kavach = await createKavach({
  database: { provider: "postgres", url: process.env.DATABASE_URL },
  plugins: [
    emailPassword(),
    magicLink({
      sendMagicLink: async (email, url) => {
        await resend.emails.send({
          to: email,
          subject: "Sign in to MyApp",
          html: `<a href="${url}">Click to sign in</a>`,
        });
      },
    }),
    passkey(),
    totp(),
  ],
});
Enter fullscreen mode Exit fullscreen mode

All 14 auth methods

Method Plugin Notes
Email + password emailPassword() HIBP breach checking built in
Magic link magicLink() Bring your own email sender
Email OTP emailOtp() 6-digit code via email
Phone SMS phoneOtp() Bring your own SMS sender
Passkey/WebAuthn passkey() Passwordless biometric
TOTP 2FA totp() Google Authenticator, Authy
Anonymous anonymous() Guest sessions
Google One-tap googleOneTap() One-click sign-in
Username/password usernamePassword() For non-email systems
Sign In With Ethereum siwe() Web3 wallet auth
Device authorization deviceAuth() TV/CLI flows (RFC 8628)
Captcha captcha() Turnstile, reCAPTCHA
Password reset passwordReset() Signed, expiring tokens
Session freshness sessionFreshness() Re-auth for sensitive ops

Framework adapters

KavachOS runs on whatever you're using:

Framework Install Adapter
Hono npm i @kavachos/hono createHonoAdapter(kavach)
Express npm i @kavachos/express createExpressRouter(kavach)
Next.js npm i @kavachos/nextjs createNextjsHandler(kavach)
Fastify npm i @kavachos/fastify kavachPlugin(kavach)
Nuxt npm i @kavachos/nuxt createNuxtHandler(kavach)
SvelteKit npm i @kavachos/sveltekit createSvelteKitHandler(kavach)
Astro npm i @kavachos/astro createAstroHandler(kavach)
NestJS npm i @kavachos/nestjs KavachModule.register(kavach)
SolidStart npm i @kavachos/solidstart createSolidHandler(kavach)
TanStack Start npm i @kavachos/tanstack createTanStackHandler(kavach)

React hooks

On the frontend:

import { KavachProvider, useSession, useSignIn } from "@kavachos/react";

function App() {
  return (
    <KavachProvider>
      <LoginPage />
    </KavachProvider>
  );
}

function LoginPage() {
  const { signIn } = useSignIn();
  const { session } = useSession();

  if (session) return <p>Signed in as {session.user.email}</p>;

  return (
    <button onClick={() => signIn("ada@example.com", "secret123")}>
      Sign in
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Also available: @kavachos/vue, @kavachos/svelte, @kavachos/expo (React Native), @kavachos/electron.

Deploy to the edge

Three runtime dependencies: drizzle-orm, jose, zod. That's it. Runs on Cloudflare Workers, Deno, Bun, and Node without code changes.

Runtime support:

  Node.js 20+          ✅
  Cloudflare Workers   ✅  (D1 database)
  Deno                 ✅
  Bun                  ✅

Dependencies:

  drizzle-orm    ORM + query builder
  jose           JWT signing/verification
  zod            Schema validation

  Total: 3
Enter fullscreen mode Exit fullscreen mode
// Cloudflare Workers + D1
const kavach = await createKavach({
  database: { provider: "d1", binding: env.KAVACH_DB },
  plugins: [emailPassword()],
});
Enter fullscreen mode Exit fullscreen mode

What's next

KavachOS is MIT licensed and open source. The full docs are at docs.kavachos.com.

Code: github.com/kavachos/kavachos


gdsks #glincker #kavachOS #askverdict #auth0 #authlibrary

Top comments (0)