DEV Community

Cover image for 5 open source auth libraries that actually handle AI agents (2026)
GDS K S
GDS K S

Posted on • Originally published at kavachos.com

5 open source auth libraries that actually handle AI agents (2026)

I hit this problem a few months back. About 50 agents, each talking to different MCP tool servers, and I couldn't answer a basic question: which agent did what, and who said it could?

Most auth libraries didn't help. They were built for humans logging into web apps. Sessions, cookies, redirect flows. My agents don't have browsers. They need their own tokens, scoped permissions, and when one spins up a sub-agent, I need some way to track that chain. Without it you're just staring at logs guessing which agent made which call.

I tried five libraries. Here's what I found.

TL;DR -- KavachOS is the only one on this list built for agents. Quickstart here. Read on if you want the full picture.

At a glance

Library Agent identity MCP OAuth Delegation chains Self-host Edge runtime License
Better Auth - - - - MIT
KavachOS MIT
Lucia - - - - MIT
Keycloak - - - - Apache 2.0
Supabase Auth - - - - Apache 2.0

Agent capabilities

This is where things get thin. Only one library was actually built with agents in mind.

Feature Better Auth KavachOS Lucia Keycloak Supabase
Agent identity tokens - - - -
Per-agent permission scoping - - Partial -
Delegation chains - - - -
Trust scoring - - - -
Agent audit trail - - - -
MCP OAuth 2.1 (PKCE, RFC 9728) - - - -

Human auth (all roughly equal here)

Feature Better Auth KavachOS Lucia Keycloak Supabase
Email/password
Social OAuth
Passkeys - -
Magic link - -
MFA -
Enterprise SSO (SAML) - Paid

Infrastructure and DX

Feature Better Auth KavachOS Lucia Keycloak Supabase
TypeScript native - -
Edge runtime (Workers, Deno) - - - -
SQLite / D1 - -
PostgreSQL
npm install setup Docker Hosted

The core problem, visualized

Human auth (what most libraries give you):

  Browser ──> Login page ──> Session cookie ──> API calls
                                 └── expires, user re-authenticates

Agent auth (what you actually need):

  Orchestrator Agent
       │
       ├── Token (kv_abc..., scoped, 24h TTL)
       │     └── Permissions: [repo:read, pr:comment]
       │
       ├── Delegates to Sub-Agent
       │     └── Token (kv_xyz..., subset, 1h TTL)
       │           └── Permissions: [repo:read] only
       │
       └── Audit Trail
             └── Every authorize() logged: agent, action, result, timestamp
Enter fullscreen mode Exit fullscreen mode

See the gap? Most libraries only handle the top half.


Better Auth

github.com/better-auth/better-auth

Best TypeScript auth library for regular apps, full stop. The plugin system covers basically anything you'd want for human auth. I've used it on two other projects and never had complaints.

import { betterAuth } from "better-auth";

const auth = betterAuth({
  database: { provider: "sqlite", url: "./auth.db" },
  emailAndPassword: { enabled: true },
  plugins: [twoFactor(), organization()],
});
Enter fullscreen mode Exit fullscreen mode

I tried making it work for agents by creating "service users" with restricted roles. Got basic tokens going within a day. But then I needed delegation, agent A passing a subset of its permissions to agent B, and there's just no clean way to do that. The abstractions assume a human on the other end.

Also no MCP OAuth support. If you're running MCP tool servers, you'd need to build that layer yourself.

Perfectly good library. Wrong problem.

KavachOS

github.com/kavachos/kavachos

This is what I ended up using. I'll be upfront: I'm biased because it solved my problem. But I'll be honest about the rough edges too.

The difference is that agents are the primary concept here, not users. You create an AgentIdentity with scoped permissions, not a session with a cookie.

import { createKavach } from "kavachos";

const kavach = await createKavach({
  database: { provider: "sqlite", url: "./kavach.db" },
});

const agent = await kavach.agent.create({
  ownerId: "user-123",
  name: "code-reviewer",
  type: "autonomous",
  permissions: [
    { resource: "mcp:github:*", actions: ["read"] },
    { resource: "mcp:github:pull_requests", actions: ["read", "comment"] },
  ],
});

// logged to audit trail automatically
const result = await kavach.authorize(agent.id, {
  action: "read",
  resource: "mcp:github:repos",
});

// delegate a subset to a sub-agent, 1h expiry, max 2 hops
await kavach.delegate({
  fromAgent: agent.id,
  toAgent: subAgent.id,
  permissions: [{ resource: "mcp:github:issues", actions: ["read"] }],
  expiresAt: new Date(Date.now() + 3600_000),
  maxDepth: 2,
});
Enter fullscreen mode Exit fullscreen mode

The delegation part is what got me. My orchestrator creates sub-agents for specific tasks, each gets a time-limited slice of permissions that auto-expires. When a compliance person asked "show me every action this agent took last Tuesday" I exported the CSV in about 10 seconds. That felt good.

MCP OAuth 2.1 works out of the box. PKCE S256, resource indicators, server metadata discovery, dynamic client registration. I'm running the whole thing on Cloudflare Workers.

Now the bad parts. Community is small. If you hit a weird edge case you're reading source code, not Stack Overflow. The plugin ecosystem is thin compared to Better Auth. Docs are getting better but there are still gaps. If you only need human auth, don't use this, it's more machinery than the job needs.

Lucia

github.com/lucia-auth/lucia

I'd used Lucia before and always liked the simplicity. Small API, clean types, no magic. The creator deprecated it in late 2024 in favor of oslo and arctic, but plenty of projects still run it.

import { Lucia } from "lucia";
import { DrizzleSQLiteAdapter } from "@lucia-auth/adapter-drizzle";

const adapter = new DrizzleSQLiteAdapter(db, sessionTable, userTable);
const lucia = new Lucia(adapter, {
  sessionCookie: { attributes: { secure: true } },
});

const session = await lucia.createSession(userId, {});
Enter fullscreen mode Exit fullscreen mode

For what it does, nothing to criticize. Clean session auth with no extra weight.

For agents it's a dead end. Sessions are a browser concept. There's no token model for background agents, no permissions, no audit trail. I thought about storing agent tokens in the session table for about five minutes before realizing I'd be fighting the library the whole way. And since it's deprecated, nothing new is coming.

Good for simple human auth. Wrong tool for agents.

Keycloak

github.com/keycloak/keycloak

The enterprise standard. Java, Red Hat-backed, been around since 2014. If you work at a company with more than 500 people, there's a decent chance Keycloak is running somewhere.

Keycloak keycloak = KeycloakBuilder.builder()
    .serverUrl("http://localhost:8080")
    .realm("master")
    .clientId("admin-cli")
    .username("admin")
    .password("admin")
    .build();

ClientRepresentation client = new ClientRepresentation();
client.setClientId("my-agent-service");
client.setServiceAccountsEnabled(true);
keycloak.realm("my-realm").clients().create(client);
Enter fullscreen mode Exit fullscreen mode

You can model agents as service accounts with client credentials grants. I've seen teams do it and it works fine for basic cases. But there's no delegation, no agent-specific audit, no MCP support. And you can't run it on edge infrastructure because JVM.

The real barrier is complexity. Setting up Keycloak takes a week if you're doing it properly. Minimum 512MB RAM. Config happens through an admin UI, not code. For a team of three building an agent system, it felt like way too much.

If your company already runs Keycloak, use it. If you're starting from scratch, don't.

Supabase Auth

github.com/supabase/auth

GoTrue under the hood, written in Go, deeply integrated with Supabase and PostgreSQL. Free tier gives you 50K MAU. If you're already on Supabase, auth comes for free.

import { createClient } from "@supabase/supabase-js";
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);

const { data, error } = await supabase.auth.signUp({
  email: "dev@example.com",
  password: "secure-password-123",
});
Enter fullscreen mode Exit fullscreen mode

Row-level security is the interesting part. Policies on Postgres tables, enforced automatically. For data authorization this works well.

For agents, not so much. Auth is coupled to PostgreSQL, so if you're using SQLite or D1, you're importing a whole database engine just for auth. The service role key is all-or-nothing. RLS handles "can this user read this row" but not "can this agent run a deployment." No delegation, no MCP support.

Good if you're already on Supabase. Not worth adopting just for auth.


The short version

Your situation Use this
Regular web app, no agents Better Auth
Agent infrastructure, MCP servers KavachOS
Enterprise with Java/SSO already Keycloak (service accounts)
Already on Supabase Supabase Auth + KavachOS
Need MCP OAuth 2.1 KavachOS (only option right now)

The tradeoff with KavachOS is a smaller community and docs that are still catching up. If that's a dealbreaker, you'll end up building agent auth on top of Better Auth or Keycloak yourself. Most teams are doing that right now, honestly.

Agent auth tooling is early. Most of the industry is duct-taping human auth onto agent systems and hoping nobody asks hard questions. That works for a while.


Links:

If you're using something else for agent auth, or you've rolled your own, I'd like to hear about it in the comments.

Top comments (0)