我们发现了什么
theAuth 面向 AI 代理:身份与限定范围权限。> theAuth 是面向 AI 代理和人类的开源身份认证方案。
- 来源:DEV Community(发现于 2026-10-11)
- 证据等级:D · 发现产品或需求信号,暂未获得可核验的商业证据。
- 商业模式:待核验
- 主题:AI Agent
- 初筛评分:19.1/100 · 收录 1 次
证据,比故事更重要。
规则清洗与初筛,未经人工商业核验。原文语境、实际客户和付费情况仍需自行验证。
引用与数字披露
来源类型(原作者自述/第三方测算/媒体转引)需采集端标注,本版尚未落字段。
- 作者
- 未标注
- 抓取日期
- 来源类型
- 未标注
- 币种
- 未标注
- 口径
- 未标注
- 披露主体
- 未标注
- 披露日期
- 未标注
中文辅助译文(全文)
<!-- links-top --> > theAuth 是面向 AI 智能体(agent)和人类的开源身份认证方案。在 GitHub 上给仓库点 Star · 阅读文档 · 运行快速入门 · theauth.dev <!-- /links-top -->
去年春天,我在四个地方发现了同一个 API 密钥。它分别出现在一个定时任务、一个 Slack 机器人、一个代码审查智能体,以及一本被同事遗忘的笔记本里。这把密钥属于一个真人,它能读取这个真人能读的所有内容,也能写入其中大部分内容。
当时一切都还正常。后来我问了一个简单的问题:上周二那次删除操作,是这四个里的哪一个干的?我答不上来。日志显示是那个真人做的。
这本指南要解决的正是这个问题。你将为每个 AI 智能体分配独立的身份、独立的令牌,以及一份简短的允许操作清单。学完之后,你将得到一个可运行的程序,它能够创建智能体、拒绝不合法的调用、记录每一次决策,以及在不牵连其他智能体的前提下撤销某一个智能体。
我用 theAuth 来实现这一点——一个用 TypeScript 编写的开源库(@glinr/theauth)。我参与了部分编写工作,所以请相应地权衡我的观点。我也会明确指出它不适用的场景。
<!-- series-nav --> > 本篇是 theAuth 指南 8 篇中的第 5 篇。它可以独立阅读,所以你完全可以从这里开始。它之前没有前置内容,是第 6 到第 8 篇的基础。
| 指南 | 标题 | 阅读时机 |
|---|---|---|
| 1 | 为已有的 Next.js 应用添加登录功能 | 你有一个还没有身份认证的应用 |
| 2 | 无密码登录:通行密钥、链接、OTP | 你想抛弃密码或加入两步验证 |
| 3 | 多租户 SaaS 身份认证:组织、RBAC、SSO、SCIM | 你的产品面向团队和公司销售 |
| 4 | 从 Auth0 或 Clerk 迁移 | 你已经在使用其他身份认证服务 |
| 5 | 为每个 AI 智能体分配独立身份(本篇) | 你在运行 AI 智能体并需要从零起步 |
| 6 | 限制智能体开销并要求人工审批 | 你的智能体会花钱或执行高风险操作 |
| 7 | 为生产环境加固 MCP 服务器 | 你通过 MCP 暴露工具 |
| 8 | 为 AI 智能体的操作构建审计日志 | 总有人会问你的智能体都做了什么 |
为人类构建?从指南 1 开始。为 AI 智能体构建?从指南 5 开始。每篇指南都会链接到其所涉及概念对应的文档页面。 <!-- /series-nav -->
TL;DR
| 步骤 | 你要做的事 | 产出结果 |
|---|---|---|
| 1 | 安装并创建一个实例 | 一个本地 SQLite 数据库,并启用了智能体功能 |
| 2 | 植入一个所有者 | 一个真人记录行,智能体挂载在其下 |
| 3 | 为每个任务创建一个智能体 | 每个智能体一个 kv_ 令牌,仅显示一次 |
| 4 | 在每次操作前调用 authorize() | 返回允许或拒绝的结果,并附带审计 ID |
| 5 | 添加约束条件 | 速率限制、参数模式、时间窗口 |
| 6 | 在中间件中校验令牌 | 在 HTTP 边界返回 401 和 403 |
| 7 | 委派给子智能体 | 更窄的作用范围,并设置过期时间 |
| 8 | 读取审计日志、轮换令牌、撤销令牌 | 能够回答“是哪个智能体干的” |
前置条件
你需要 Node 20 或更高版本、TypeScript,以及一个包管理器。你不需要运行中的服务器或外部数据库。示例使用磁盘上的 SQLite,这样之后可以直接打开文件查看。
你还需要一个诚实的假设:你系统中的智能体会调用你可控的工具。theAuth 提供决策点,你的代码必须在执行前调用它。如果一个智能体能够凭借自己的凭证直接访问数据库,那世上任何权限列表都拦不住它。
为什么共享密钥行不通
共享的人类密钥存在三个问题,并且它们会叠加。
首先,爆炸半径等同于真人的权限。一个只需要读取 pull request 的代码审查智能体,却继承了所有内容的写入权限。
其次,你无法单独撤销某一个智能体。轮换密钥,四个进程会同时崩溃。你只能等待,而暴露的密钥在此期间仍然有效。
第三,审计日志会撒谎。每一行都写着那个真人的名字。你的合规问题以及你自己的调试工作,都会撞上一堵墙。
为每个智能体分配独立身份可以解决以上三个问题。每个智能体持有一个令牌,该令牌对应一条数据行、一个所有者、一份权限清单。核心概念页面 用一句话概括了这个循环:用户创建代理,代理在执行前调用 authorize(),每一次决策都落入审计日志。
有一个要点需要尽早明确。代理没有邮箱、没有密码、没有会话、也没有 OAuth 账户。它拥有一个持有者令牌(bearer token)和一套权限。如果你发现自己正在给代理配置密码重置流程,那说明你真正需要的是一个用户。代理身份页面 划出了这条界线。
步骤 1:安装并创建实例
mkdir agent-identity-demo && cd agent-identity-demo
npm init -y
npm install @glinr/theauth
npm install -D tsx typescript现在创建 demo.ts。我会逐段构建它,完整文件就是下面这些代码块的总和。
import { createTheAuth } from '@glinr/theauth';
const theauth = await createTheAuth({
database: { provider: 'sqlite', url: 'theauth.db' },
agents: {
enabled: true,
maxPerUser: 10,
auditAll: true,
tokenExpiry: '24h',
},
});有两个设置值得说明。auditAll 会记录每一次 authorize() 调用,并且默认开启。tokenExpiry 设定在不传入 expiresAt 时新建智能体的有效期。默认 24 小时意味着你遗忘的智能体到第二天就会停止工作。我很喜欢这个默认值——一个被遗忘的凭证应该过期失效,而不是一直留存。
如果你想要脚手架版的 Next.js 设置,快速入门里也有相同的流程:https://docs.theauth.dev/quickstart。
步骤 2:植入所有者
每个智能体都有一个所有者,所有者必须作为一行存在于 theauth_users 中。该列是一个外键。跳过这一步,在你第一次创建调用时就会看到 FOREIGN KEY constraint failed。每个人都会遇到一次。
如果你已经在使用 theAuth 自带的身份认证模块,注册流程会自动为你创建这一行。在临时脚本中,手动插入一行:
import { users } from '@glinr/theauth';
theauth.db.insert(users).values({
id: 'user-1',
email: 'owner@example.com',
name: 'Owner',
createdAt: new Date(),
updatedAt: new Date(),
}).run();这种插入方式来自快速入门中的故障排查一节,适用于 SQLite。对于 Postgres,数据库文档列出了对应的调用方式。
步骤 3:为每个任务创建一个智能体
我遵循的规则是:一个智能体对应一个任务,而不是一个智能体对应一个人。即便是同一个人设置的,夜间 pull request 审查器和退款机器人也不应共享身份。
const reviewer = await theauth.agent.create({
ownerId: 'user-1',
name: 'pr-reviewer',
type: 'autonomous',
permissions: [
{ resource: 'mcp:github:*', actions: ['read'] },
],
expiresAt: new Date(Date.now() + 7 * 24 * 3_600_000),
metadata: { purpose: 'nightly PR review' },
});
const refunder = await theauth.agent.create({
ownerId: 'user-1',
name: 'refund-bot',
type: 'autonomous',
permissions: [
{ resource: 'billing:refunds', actions: ['read', 'write'] },
],
});
令牌以 kv_ 开头,后接 32 个 base64url 编码的随机字节,总共 46 个字符。theAuth 仅存储其 SHA-256 哈希值。即便完整导出数据库,也无法还原出有效的令牌。
这也有代价:你只能在创建时看到一次明文令牌。请在进程退出前将其存入密钥管理器,或者通过轮换获取新令牌。之后无法再找回它。
选择正确的智能体类型
type 字段有三个取值。
autonomous(自主型)智能体自行运行,除非权限约束要求,否则没有审批步骤。定时任务和无人值守的助手适合使用此类型,也是默认选择。
delegated(委派型)智能体通过委派链从另一个智能体接收权限。适用于为某项任务临时启动、生命周期短暂的子任务智能体。
service(服务型)智能体是为基础设施准备的长期身份,例如 MCP 服务器或内部微服务。请将其视作一个服务账号来对待。
注意每用户上限
默认情况下,一个用户最多拥有 10 个处于活跃状态的智能体。第 11 次创建调用会抛出一个普通的 Error,错误信息为 User <id> has reached the maximum of <n> active agents.。该错误没有专用错误码,REST 端点会将其报告为 500。如果你运行的是智能体集群,请在初始化时调高 maxPerUser,并通过错误信息而不是错误码来捕获该错误。
步骤 4:在每次操作前进行校验
智能体身份本身不会做任何事——除非你的代码主动询问。请在每个敏感操作前加入一次调用。
const allowed = await theauth.authorize(reviewer.id, {
action: 'read',
resource: 'mcp:github:repos',
});
console.log(allowed.allowed); // true
const denied = await theauth.authorize(reviewer.id, {
action: 'write',
resource: 'mcp:github:repos',
});
console.log(denied.allowed, denied.reason); // false, plus a reason
console.log(denied.auditId); // links to the audit row返回结果为 { allowed, reason?, auditId }。审查器只拥有 read 权限。对同一资源的 write 操作会失败,并且该拒绝结果连同原因一起被记录到审计日志中。
资源模式如何匹配
资源是冒号分隔的字符串,具体约定由你决定。mcp:github:repos、billing:refunds 和 db:users:write 都可以使用。一致性比语法更重要。
通配符有一个容易让人意外的行为。* 段并不是单段通配符。匹配器在遇到第一个 * 时停止,并接受其后的所有内容——也包括“什么都没有”。这意味着 mcp:github:* 既能匹配 mcp:github:repos,也能匹配 mcp:github:repos:comments,甚至匹配 mcp:github 本身。
请只将 * 放在最后位置。类似 mcp:*:repos 的模式看起来像“任意服务器,仅限 repos”,但它也会接受 mcp:slack:channels。如果没有通配符,模式和资源必须具有相同的段数。完整对照表见 权限页面。
操作(action)是自由格式的
操作列表没有固定集合。read、write、execute 和 delete 是常见的,但如果你在日志里看起来更顺眼,也可以定义 comment 或 refund。theAuth 会检查所请求的操作是否出现在权限的 actions 数组中,它本身并不解释这些词的含义。
步骤 5:添加约束
一项权限可以附带约束,并且每一个约束都必须通过。这正是最小权限原则具体落地之处。“可以写文件”会变成“可以写入 /tmp/agent/ 下的文件,每小时最多 20 次,且仅限办公网络”。
const filer = await theauth.agent.create({
ownerId: 'user-1',
name: 'file-writer',
type: 'autonomous',
permissions: [
{
resource: 'tool:file_write',
actions: ['execute'],
constraints: {
maxCallsPerHour: 20,
allowedArgPatterns: ['^/(home/agent|tmp)/'],
timeWindow: { start: '09:00', end: '17:00' },
ipAllowlist: ['10.0.0.0/8'],
},
},
],
});
const outside = await theauth.authorize(filer.id, {
action: 'execute',
resource: 'tool:file_write',
arguments: { path: '/etc/passwd' },
ip: '10.1.2.3',
});
console.log(outside.all
……(正文超出本站单页篇幅上限,此处截断;完整表述请见下方原文入口。)译文由上游机器翻译生成,可能有误;判断请以英文原文为准。
英文原文(来源本站未改写)
<!-- links-top --> > theAuth is open-source auth for AI agents and humans. Star the repo on GitHub · Read the docs · Run the quickstart · theauth.dev <!-- /links-top -->
Last spring I found one API key in four places. It sat in a cron job, a Slack bot, a code-review agent, and a notebook a teammate had forgotten about. The key belonged to a human. It could read everything that human could read, and it could write most of it too.
Nothing was wrong yet. Then I asked a simple question: which of the four did that delete last Tuesday? I could not answer it. The logs said the human did it.
That is the problem this guide fixes. You will give each AI agent its own identity, its own token, and a short list of things it may do. By the end you will have a runnable script that creates agents, denies the wrong calls, logs every decision, and revokes one agent without touching the others.
I use theAuth for this, an open-source TypeScript library (@glinr/theauth). I wrote parts of it, so weigh my opinions accordingly. I will say plainly where it does not fit.
<!-- series-nav --> > This is guide 5 of 8 in the theAuth guides. It stands on its own, so you can start right here. Nothing comes before it. It is the base for guides 6 to 8.
| Guide | Title | Read it when |
|---|---|---|
| 1 | Add login to an existing Next.js app | You have an app with no auth yet |
| 2 | Passwordless login: passkeys, links, OTP | You want to drop passwords or add 2FA |
| 3 | Multi-tenant SaaS auth: orgs, RBAC, SSO, SCIM | You sell to teams and companies |
| 4 | Migrate from Auth0 or Clerk | You already run another provider |
| 5 | Give every AI agent its own identity (this guide) | You run AI agents and need to start somewhere |
| 6 | Cap agent spend and require human approval | Your agents spend money or act on risky things |
| 7 | Secure an MCP server for production | You expose tools over MCP |
| 8 | Build an audit trail for AI agent actions | Someone will ask what your agents did |
Building for people? Start at guide 1. Building for AI agents? Start at guide 5. Every guide links to the docs page for each concept it touches. <!-- /series-nav -->
TL;DR
| Step | What you do | Result |
|---|---|---|
| 1 | Install and create an instance | A local SQLite database with agents enabled |
| 2 | Seed an owner | One human row that agents hang off |
| 3 | Create one agent per job | A kv_ token per agent, shown once |
| 4 | Call authorize() before every action | An allow or deny with an audit ID |
| 5 | Add constraints | Rate limits, argument patterns, time windows |
| 6 | Check tokens in middleware | 401 and 403 at your HTTP edge |
| 7 | Delegate to sub-agents | A narrower scope, with an expiry |
| 8 | Read the audit trail, rotate, revoke | Answers to "which agent did that" |
Prerequisites
You need Node 20 or newer, TypeScript, and a package manager. You do not need a running server or an external database. The examples use SQLite on disk so you can open the file afterward.
You also need one honest assumption: the agents in your system call tools you control. theAuth gives you the decision point. Your code must ask it before acting. If an agent can reach a database directly with its own credentials, no permission list in the world will stop it.
Why a shared key fails
A shared human key has three problems, and they stack.
First, the blast radius equals the human's permissions. A code reviewer that only needs to read pull requests inherits write access to everything.
Second, you cannot revoke one agent. Rotate the key and all four processes break at once. You wait, and the exposed key stays live.
Third, the audit trail lies. Every row names the human. Your compliance questions, and your own debugging, hit a wall.
A per-agent identity fixes all three. Each agent holds a token that maps to one row, one owner, and one permission list. The core concepts page describes the loop in one sentence: a user creates agents, agents call authorize() before acting, and every decision lands in the audit trail.
One distinction matters early. An agent is not a user. It has no email, no password, no session, and no OAuth account. It has a bearer token and a permission set. If you catch yourself wiring password reset for an agent, you want a user. The agent identity page draws that line.
Step 1: Install and create an instance
mkdir agent-identity-demo && cd agent-identity-demo
npm init -y
npm install @glinr/theauth
npm install -D tsx typescriptNow create demo.ts. I will build it up one piece at a time, and the full file is the sum of the blocks below.
import { createTheAuth } from '@glinr/theauth';
const theauth = await createTheAuth({
database: { provider: 'sqlite', url: 'theauth.db' },
agents: {
enabled: true,
maxPerUser: 10,
auditAll: true,
tokenExpiry: '24h',
},
});Two settings deserve a note. auditAll records every authorize() call, and it defaults to on. tokenExpiry sets how long a new agent lives when you do not pass expiresAt. A 24 hour default means an agent you forget about stops working by tomorrow. I like that default. A forgotten credential should expire, not linger.
The quickstart walks through the same setup if you want the scaffolded Next.js version instead: https://docs.theauth.dev/quickstart.
Step 2: Seed an owner
Every agent has an owner, and the owner must exist as a row in theauth_users. That column is a foreign key. Skip this step and you will see FOREIGN KEY constraint failed on your first create call. Everyone hits it once.
If you already run theAuth's own auth modules, sign-up creates the row for you. In a scratch script, insert one:
import { users } from '@glinr/theauth';
theauth.db.insert(users).values({
id: 'user-1',
email: 'owner@example.com',
name: 'Owner',
createdAt: new Date(),
updatedAt: new Date(),
}).run();This insert pattern comes from the quickstart troubleshooting section and works for SQLite. For Postgres, the database docs list the matching call.
Step 3: Create one agent per job
Here is the rule I follow: one agent per job, not one agent per person. A nightly pull-request reviewer and a refund bot should never share an identity, even if the same human set up both.
const reviewer = await theauth.agent.create({
ownerId: 'user-1',
name: 'pr-reviewer',
type: 'autonomous',
permissions: [
{ resource: 'mcp:github:*', actions: ['read'] },
],
expiresAt: new Date(Date.now() + 7 * 24 * 3_600_000),
metadata: { purpose: 'nightly PR review' },
});
const refunder = await theauth.agent.create({
ownerId: 'user-1',
name: 'refund-bot',
type: 'autonomous',
permissions: [
{ resource: 'billing:refunds', actions: ['read', 'write'] },
],
});
console.log(reviewer.token); // kv_..., shown onceThe token starts with kv_, followed by 32 random bytes in base64url, 46 characters in total. theAuth stores only the SHA-256 hash. A full database dump cannot reveal a live token.
That has a cost. You see the plaintext exactly once, at creation. Put it in your secrets manager before the process exits, or rotate to get a new one. You cannot recover it later.
Pick the right agent type
The type field takes three values.
An autonomous agent runs on its own, with no approval step unless a permission constraint demands one. Cron jobs and unattended assistants fit here. This is the default choice.
A delegated agent receives its permissions from another agent through a delegation chain. Use it for short-lived workers spun up for one task.
A service agent is a long-lived identity for infrastructure, such as an MCP server or an internal microservice. Treat it like a service account.
Mind the per-user cap
By default one user can own 10 active agents. The eleventh create call throws a plain Error with the message User <id> has reached the maximum of <n> active agents. The error has no dedicated code, and the REST endpoint reports it as a 500. Raise maxPerUser at initialization if you run a fleet, and catch the error by message, not by code.
Step 4: Check before every action
An agent identity does nothing until your code asks the question. Put one call in front of every sensitive operation.
const allowed = await theauth.authorize(reviewer.id, {
action: 'read',
resource: 'mcp:github:repos',
});
console.log(allowed.allowed); // true
const denied = await theauth.authorize(reviewer.id, {
action: 'write',
resource: 'mcp:github:repos',
});
console.log(denied.allowed, denied.reason); // false, plus a reason
console.log(denied.auditId); // links to the audit rowThe result is { allowed, reason?, auditId }. The reviewer holds only read. A write on the same resource fails, and the denial lands in the audit log with its reason.
How resource patterns match
Resources are colon-separated strings. You pick the convention. mcp:github:repos, billing:refunds, and db:users:write all work. Consistency matters more than syntax.
The wildcard has one behavior that surprises people. A * segment is not a single-segment wildcard. The matcher stops at the first * and accepts everything after it, including nothing. That means mcp:github:* matches mcp:github:repos, mcp:github:repos:comments, and mcp:github itself.
Put * only in the last position. A pattern like mcp:*:repos looks like "any server, repos only," but it accepts mcp:slack:channels too. Without a wildcard, the pattern and resource must have the same number of segments. The full table lives on the permissions page.
Actions are free-form
The action list has no fixed set. read, write, execute, and delete are common, but you can define comment or refund if that reads better in your logs. theAuth checks that the requested action appears in the permission's actions array. It does not interpret the word.
Step 5: Add constraints
A permission can carry constraints, and every one must pass. This is where least privilege gets specific. "May write files" becomes "may write files under /tmp/agent/, 20 times an hour, from the office network."
const filer = await theauth.agent.create({
ownerId: 'user-1',
name: 'file-writer',
type: 'autonomous',
permissions: [
{
resource: 'tool:file_write',
actions: ['execute'],
constraints: {
maxCallsPerHour: 20,
allowedArgPatterns: ['^/(home/agent|tmp)/'],
timeWindow: { start: '09:00', end: '17:00' },
ipAllowlist: ['10.0.0.0/8'],
},
},
],
});
const outside = await theauth.authorize(filer.id, {
action: 'execute',
resource: 'tool:file_write',
arguments: { path: '/etc/passwd' },
ip: '10.1.2.3',
});
console.log(outside.all
... (truncated at the site's per-page length limit; see the source link below for the full text.)出处https://dev.to/thegdsks/theauth-for-ai-agents-identity-and-scoped-permissions-3523
这条还缺什么证据?
下面每条都由本条已有字段推出(等级、理由、商业模式、来源次数、是否演示), 本站不生成推测性结论;通用验证方法放在方法论页。
- 可核验的收入或付费证据查官网定价页与付费口径;第三方数据源(如 GetLatka)只作旁证,需标注来源与时点。
- 商业模式未定确认按席位/按用量/授权还是开源托管版收费;开源项目另查 LICENSE 与是否存在付费版。
- 只有单一来源找一手站点或其他渠道是否重复出现同一产品;社区热帖数量不等于商业进展。
通用验证清单(谁有这个问题/谁愿意付费/一个人能交付哪一小步)见我们的筛选方法。