Agent Auth

AI Agent 的身份认证、注册、发现以及基于能力的授权。

AI Agents MCP Capabilities

此插件是一个处于重度开发阶段的协议标准实现。它尚未稳定,未来可能会发生变化。请在 Github 上报告任何问题或错误。

Agent Auth 插件让你的 Better Auth 服务器充当一个 Agent Auth 提供者。它是 Agent Auth Protocol 的服务端实现。

它为 AI agents 提供了一种标准方式来发现你的服务、注册自己、请求审批,并使用短期签名 JWT 执行受限能力。它自带 OpenAPIMCP 适配器——因此你无需手写能力,就能将现有的 REST API 或 MCP 服务器转换为支持 agent-auth 的服务。

特性

  • OpenAPI adapter — 从 OpenAPI 3.x 规范直接派生能力、输入/输出 Schema 以及代理 onExecute 处理器
  • MCP adapter — 将 agent-auth 暴露为 MCP 工具,以便任何 MCP 兼容的 AI agent 可以发现并调用你的能力
  • /.well-known/agent-configuration 提供发现文档
  • 能力列表、描述和执行(每个能力可选配 location URL)
  • 委托和自主代理模式
  • 设备授权和 CIBA 审批流程
  • 具有防重放保护的短期签名 JWT
  • 用于审批、授权和执行的审计/事件钩子

安装

安装依赖包

npm install @better-auth/agent-auth

客户端和 CLI 包(可选):

npm install @auth/agent @auth/agent-cli

将插件添加到认证配置

首先定义你的服务所暴露的能力,以及一个用于执行认证代理操作的 onExecute 处理器。

auth.ts
import { betterAuth } from "better-auth";
import { agentAuth } from "@better-auth/agent-auth"; 

export const auth = betterAuth({
  plugins: [
    agentAuth({ 
      providerName: "Acme", 
      providerDescription: "Acme 项目与部署 API,适用于 AI Agent。", 
      modes: ["delegated", "autonomous"], 
      capabilities: [ 
        { 
          name: "deploy_project", 
          description: "将项目部署到生产环境。", 
          input: { 
            type: "object", 
            properties: { 
              projectId: { type: "string" }, 
            }, 
            required: ["projectId"], 
          }, 
        }, 
        { 
          name: "list_projects", 
          description: "列出当前用户可访问的项目。", 
        }, 
      ], 
      async onExecute({ capability, arguments: args, agentSession }) { 
        switch (capability) { 
          case "list_projects": 
            return [{ id: "proj_123", name: "marketing-site" }]; 
          case "deploy_project": 
            return { 
              ok: true, 
              projectId: args?.projectId, 
              requestedBy: agentSession.user.id, 
            }; 
          default: 
            throw new Error(`不支持的能力: ${capability}`); 
        } 
      }, 
    }), 
  ], 
}); 

暴露发现文档

插件提供了 auth.api.getAgentConfiguration(),但你应该将其从应用根路径暴露为 /.well-known/agent-configuration

app/.well-known/agent-configuration/route.ts
import { auth } from "@/lib/auth";
import { NextResponse } from "next/server";

export async function GET() {
  const configuration = await auth.api.getAgentConfiguration();
  return NextResponse.json(configuration);
}

迁移数据库

运行迁移或生成模式以添加 agent、host、grant 和 approval 表。

npx auth migrate
npx auth generate

可选:添加 Better Auth 客户端插件

如果你想从 Better Auth 客户端对插件端点进行类型安全的访问,请同时添加客户端插件。

auth-client.ts
import { createAuthClient } from "better-auth/client";
import { agentAuthClient } from "@better-auth/agent-auth/client"; 

export const authClient = createAuthClient({
  plugins: [
    agentAuthClient(), 
  ],
});

工作原理

Agent Auth 流程通常如下:

  1. Agent 从 /.well-known/agent-configuration 发现你的提供者
  2. Agent 列出能力并决定所需能力
  3. Agent 向你的服务器注册并请求能力授权
  4. 你的用户通过设备授权或 CIBA 完成审批
  5. Agent 签署短期 JWT(aud 匹配所调用的 URL),并在 default_location 或该能力自定义的 location(若已设置)调用每个已授权的能力

发现

发现文档告诉 Agent 如何与你的服务器交互。插件包含提供者元数据、支持模式、审批方法、绝对端点 URL 以及 default_location 字段。

需要执行的重要字段:

  • issuer — 提供者的基础 URL(Better Auth 的 baseURL)。
  • endpoints — 每个路由的绝对 URL(例如 execute 指向该基础路径下的 POST /capability/execute)。
  • default_location — 默认执行端点的完整 URL。它始终与 endpoints.execute 匹配。当能力未定义自定义 URL 时,Agent 会将其用作 JWT 的 aud,并用于调用这些能力。

从应用根路径暴露它:

app/.well-known/agent-configuration/route.ts
import { auth } from "@/lib/auth";
import { NextResponse } from "next/server";

export async function GET() {
  const configuration = await auth.api.getAgentConfiguration();
  return NextResponse.json(configuration);
}

发现路由应位于 /.well-known/agent-configuration,即使你的 Better Auth 基础路径是 /api/auth

OpenAPI 适配器

如果你的服务已有 OpenAPI 规范,只需几行代码就能将整个 API 转换为 Agent Auth 提供者。createFromOpenAPI 读取规范并生成插件所需的一切:每个 operationId 对应一个能力、输入/输出 JSON Schema、代理 onExecute 处理器,以及可选的 providerName / providerDescription(从 info 中获取)。

auth.ts
import { betterAuth } from "better-auth";
import { agentAuth } from "@better-auth/agent-auth";
import { createFromOpenAPI } from "@better-auth/agent-auth/openapi";

const spec = await fetch("https://api.example.com/openapi.json").then((r) =>
  r.json(),
);

export const auth = betterAuth({
  plugins: [
    agentAuth({
      ...createFromOpenAPI(spec, {
        baseUrl: "https://api.example.com",
      }),
    }),
  ],
});

就这么简单。规范中每个具有 operationId 的操作都会成为一个能力,其名称为该 ID。路径、查询和头参数以及 JSON 请求体被合并为一个 input Schema,而 200/201 响应体成为 output

上游认证

代理处理器代表 Agent 调用你的上游 API。使用 resolveHeaders 注入每个请求所需的凭证(例如内部服务令牌或从 agentSession 查找的用户作用域访问令牌)。

auth.ts
createFromOpenAPI(spec, {
  baseUrl: "https://api.example.com",
  async resolveHeaders({ agentSession }) {
    const token = await getAccessToken(agentSession.user.id);
    return { Authorization: `Bearer ${token}` };
  },
});

默认主机能力

控制哪些能力自动授予新主机。你可以传递 true(全部)、单个 HTTP 方法字符串、方法数组,或接收完整运行时上下文的回调。

auth.ts
createFromOpenAPI(spec, {
  baseUrl: "https://api.example.com",
  defaultHostCapabilities: ["GET", "HEAD"],
});

每个方法的审批强度

将 HTTP 方法映射到 approvalStrength,使修改操作需要更强的用户验证(例如 WebAuthn),而读取操作使用普通会话。

auth.ts
createFromOpenAPI(spec, {
  baseUrl: "https://api.example.com",
  approvalStrength: {
    GET: "session",
    POST: "webauthn",
    PUT: "webauthn",
    DELETE: "webauthn",
  },
});

每个能力的 location

当你设置 location 时,每个派生的能力都会获得该 URL。Agent 会直接调用它(使用 Agent JWT),而不是通过默认执行端点——当你希望 Agent 访问真实 API URL 并在你的中间件中处理会话时很有用,而不是通过 onExecute 代理。

auth.ts
createFromOpenAPI(spec, {
  baseUrl: "https://api.example.com",
  location: "https://api.example.com/agent/execute",
});

单独使用各个部分

如果你只需要部分管道,适配器也导出了较低级别的辅助函数:

  • fromOpenAPI(spec) — 仅返回 Capability[](不包含处理器和主机能力)。
  • createOpenAPIHandler(spec, opts) — 仅返回 onExecute 代理处理器,因此你可以将其与手写能力或自行过滤规范搭配使用。
auth.ts
import {
  fromOpenAPI,
  createOpenAPIHandler,
} from "@better-auth/agent-auth/openapi";

const capabilities = fromOpenAPI(spec);
const onExecute = createOpenAPIHandler(spec, {
  baseUrl: "https://api.example.com",
});

agentAuth({ capabilities, onExecute });

功能

功能是应用程序与代理之间的契约。每个功能都有名称、描述,以及可选的 JSON Schema input 定义。

默认情况下,代理调用发现中的 default_location(执行 URL),插件运行 onExecute。如果在功能上设置了 location,代理将改为调用该绝对 URL —— 例如现有的 REST 路由 —— 并且这些请求不会使用 onExecute;你需要使用下面的辅助工具解析代理会话,并自行实现处理器。

使用功能来公开狭窄、可审查的操作,而不是广泛的 API 访问。

定义功能

auth.ts
agentAuth({
  capabilities: [
    {
      name: "create_issue",
      description: "在当前工作空间中创建问题。",
      input: {
        type: "object",
        properties: {
          title: { type: "string" },
          body: { type: "string" },
        },
        required: ["title"],
      },
    },
  ],
});

可选的 location — 代理使用代理 JWT 调用此 URL,而不是默认执行 URL:

auth.ts
{
  name: "create_issue",
  description: "在当前工作空间中创建问题。",
  location: "https://api.example.com/v1/issues",
}

默认执行与自定义 location

  • location — Agent 向 default_locationendpoints.execute)发送 POST 请求,并携带 { capability, arguments }。插件验证 JWT 和附加的 agentSession 后,运行 onExecute
  • 带有 location — Agent 直接调用该 URL(你的 REST 处理器、其他服务或 OpenAPI 操作 URL)。onExecute 不会为此调用运行;你需要使用下面的会话辅助工具解析 agentSession,并自行实现处理器。

onExecute 之外的代理会话

对于自定义 location 路由(或任何非执行处理器),代理仍会发送带有代理 JWT 的 Authorization: Bearer 头。无论你使用什么框架,获取传入的 Headers(例如 request.headers,或你运行时的等效项)并传递它们 — 验证流程相同:签名、aud、重放(jti)、过期,以及(存在时)请求绑定声明。

auth.api.getAgentSession({ headers }) 在进程内运行该流程并返回 AgentSessionnullverifyAgentRequest(request, auth) 同样通过将 Request 的 headers 转发到 GET /agent/session(通过 auth.handler)来实现相同功能 — 选择适合你代码结构的方式;没有框架区分,只有“headers 进、session 出”。

api/issues/route.ts
import { auth } from "@/lib/auth";

export async function POST(request: Request) {
  const agentSession = await auth.api.getAgentSession({
    headers: request.headers,
  });
  if (!agentSession) {
    return new Response("Unauthorized", { status: 401 });
  }
  // 检查授权、执行约束、运行你的处理器……
}
// 当你已有 `Request` + `auth` 并偏好辅助工具时的等效写法:
import { verifyAgentRequest } from "@better-auth/agent-auth";
const agentSession = await verifyAgentRequest(request, auth);

检查授权和输入

获取 agentSession 后,检查 agentSession.agent.capabilityGrants。这些是与执行时相同的数据库活跃授权(与 JWT 中的 capabilities 声明取交集)。对于此路由实现的功能,确保存在匹配的授权:

const CAP = "create_issue";
const allowed = agentSession.agent.capabilityGrants.some(
  (g) => g.capability === CAP && g.status === "active",
);
if (!allowed) {
  return new Response("Forbidden", { status: 403 });
}

如果该授权有 constraints,以与 POST /capability/execute 相同的方式验证请求体或查询 —— 否则客户端可以通过调用你的自定义 URL 绕过约束。插件不会在任意路由上重新运行执行的约束辅助工具;该逻辑保留在你的处理器中(或调用你从 onExecute 路径中提取的共享代码)。

会话中包含的内容

  • agentSession.user — 解析出的用户(托管用户或 resolveAutonomousUser)。
  • agentSession.agent — ID、名称、模式、capabilityGrants、托管 ID 和元数据。
  • agentSession.host — 当 Agent 与托管关联时的托管记录。

类型从 @better-auth/agent-auth 导出(例如 AgentSession)。

JWT 受众(aud

JWT aud 必须与你正在调用的 URL 匹配:

  • 无每个能力的 location — 使用 default_location / endpoints.execute,或插件已允许的发行者 / 基础路径值。
  • 带有 locationaud 应为该相同的绝对 URL。GET /capability/list 在设置时会包含 location。配置中的无效 location 值会在启动时失败。

单一功能的 JWT — 如果 capabilities 只列出一个 ID,设置时 aud 可以等于该功能的 location

多个功能的 JWT — 不接受每个功能的 location 值作为 aud;改用发行者、基础路径或默认执行端点。

在反向代理后面,如果需要 Host / X-Forwarded-Protoaud 验证对齐,请设置 trustProxy

过滤可见的功能

使用 resolveCapabilities 向不同调用者展示不同的功能集,例如按计划、用户或组织限制的功能。

onExecute

用于使用默认执行 URL(无每个功能的 location)的功能。插件验证 JWT(包括 aud)、附加 agentSession、检查授权,然后运行 onExecute。具有自定义 location 的功能永远不会到达此路径 — 你使用上面的会话辅助工具在自己的路由中处理它们。

auth.ts
agentAuth({
  capabilities: [
    {
      name: "create_issue",
      description: "在当前工作空间中创建问题。",
    },
  ],
  async onExecute({ capability, arguments: args, agentSession }) {
    if (capability !== "create_issue") {
      throw new Error("不支持的功能");
    }

    return {
      ok: true,
      title: args?.title,
      createdBy: agentSession.user.id,
    };
  },
});

授权流程

插件支持两种授权方法:

  • device_authorization 用于基于浏览器的审批(用户代码)
  • ciba 用于后台通道审批流程

默认情况下,两者都启用。你可以使用 approvalMethodsresolveApprovalMethod 来限制或自定义它们。

auth.ts
agentAuth({
  approvalMethods: ["ciba", "device_authorization"],
  resolveApprovalMethod: ({ preferredMethod, supportedMethods }) => {
    if (preferredMethod && supportedMethods.includes(preferredMethod)) {
      return preferredMethod;
    }
    return "device_authorization";
  },
  deviceAuthorizationPage: "/device/capabilities",
});

插件不会为你渲染设备授权 UI。你的应用必须提供 deviceAuthorizationPage 引用的页面。

事件与审计

  • 代理创建和撤销

  • 主机创建和注册

  • 功能请求和授权

  • 功能执行

  • agent creation and revocation

  • host creation and enrollment

  • capability requests and approvals

  • capability execution

配置

Agent Auth 插件支持许多选项。这些是你通常首先使用的:

Prop

Type