高级功能
高级 API 密钥功能,包括会话、多配置、组织密钥、存储模式等。
来自 API 密钥的会话
每当调用 Better Auth 中带有有效 API 密钥的接口时,可以通过启用 enableSessionForAPIKeys 选项,自动创建一个模拟会话以代表用户。
通常不建议这样做,因为如果使用不当,可能导致安全问题。泄露的 API 密钥可用于冒充用户。
仅限用户拥有的密钥:会话模拟仅适用于用户拥有的 API 密钥(即 references: "user")。组织拥有的密钥无法模拟用户会话。
速率限制说明:启用 enableSessionForAPIKeys 后,每次请求都会验证一次 API 密钥,并相应应用速率限制。
若手动验证 API 密钥后再单独获取会话,这两个操作都会增加速率限制计数。使用 enableSessionForAPIKeys 避免此类重复计数。
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"
export const auth = betterAuth({
plugins: [
apiKey({
enableSessionForAPIKeys: true,
}),
],
});import { auth } from "@/lib/auth"
const session = await auth.api.getSession({
headers: new Headers({
'x-api-key': apiKey,
}),
});默认的请求头字段是 x-api-key,但可在插件选项中通过设置 apiKeyHeaders 自定义。
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"
export const auth = betterAuth({
plugins: [
apiKey({
apiKeyHeaders: ["x-api-key", "xyz-api-key"], // 或者直接传一个字符串,例如 "x-api-key"
}),
],
});或可传入 customAPIKeyGetter 函数(接收 HookEndpointContext),由你返回请求中的 API 密钥,若请求无效则返回 null。
import { betterAuth } from "better-auth"
import { apiKey } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
apiKey({
customAPIKeyGetter: (ctx) => {
const has = ctx.request.headers.has("x-api-key");
if (!has) return null;
return ctx.request.headers.get("x-api-key");
},
}),
],
});多配置支持
你可以定义多个不同设置的 API 密钥配置。每个配置通过唯一的 configId 标识,可以定制前缀、速率限制、权限等。
适合不同用途需要不同 API 密钥类型的场景,例如:
- 公开密钥 vs 私密密钥
- 只读密钥 vs 读写密钥
- 不同等级的不同速率限制
配置示例
传入配置对象数组给 apiKey 插件:
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"
export const auth = betterAuth({
plugins: [
apiKey([
{
configId: "public",
defaultPrefix: "pk_",
rateLimit: {
enabled: true,
maxRequests: 100,
timeWindow: 1000 * 60 * 60, // 1 小时
},
},
{
configId: "secret",
defaultPrefix: "sk_",
enableMetadata: true,
rateLimit: {
enabled: true,
maxRequests: 1000,
timeWindow: 1000 * 60 * 60, // 1 小时
},
},
]),
],
});创建指定配置的密钥
创建 API 密钥时,通过 configId 参数指定用哪个配置:
import { auth } from "@/lib/auth"
// 创建公开密钥
const publicKey = await auth.api.createApiKey({
body: {
configId: "public",
userId: user.id,
},
});
// 返回示例: pk_...
// 创建私密密钥
const secretKey = await auth.api.createApiKey({
body: {
configId: "secret",
userId: user.id,
metadata: { plan: "premium" },
},
});
// 返回示例: sk_...注意,针对指定 configId 创建的密钥,get、update、delete 和 verify 操作也必须指定相同的 configId 参数。
在 API 密钥操作中使用 configId
所有 API 密钥操作都支持 configId 参数,指定查询使用哪套配置,特别是配置使用不同存储后端(如数据库与 Redis)时很重要:
// 指定配置获取 API 密钥
const key = await auth.api.getApiKey({
query: {
id: keyId,
configId: "secret"
},
headers,
});
// 指定配置更新 API 密钥
await auth.api.updateApiKey({
body: {
keyId: keyId,
configId: "secret",
name: "Updated Name",
},
});
// 指定配置删除 API 密钥
await auth.api.deleteApiKey({
body: {
keyId: keyId,
configId: "secret",
},
headers,
});
// 指定配置验证 API 密钥
const result = await auth.api.verifyApiKey({
body: {
key: apiKeyValue,
configId: "secret",
},
});按配置筛选密钥
列出 API 密钥时,可通过 configId 过滤:
// 仅列出公开密钥
const publicKeys = await authClient.apiKey.list({
query: { configId: "public" }
});
// 仅列出私密密钥
const secretKeys = await authClient.apiKey.list({
query: { configId: "secret" }
});全局选项
也可以传入第二个参数传递全局选项(比如 schema):
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"
export const auth = betterAuth({
plugins: [
apiKey(
[
{ configId: "public", defaultPrefix: "pk_" },
{ configId: "secret", defaultPrefix: "sk_" },
],
{
schema: {
// 自定义 schema 选项
},
}
),
],
});组织拥有的 API 密钥
默认情况下,API 密钥由用户拥有。你也可以配置 API 密钥由组织拥有,适合团队应用场景,API 密钥在组织成员间共享。
配置示例
在配置中设置 references: "organization":
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"
export const auth = betterAuth({
plugins: [
apiKey([
{
configId: "user-keys",
defaultPrefix: "user_",
references: "user", // 默认,用户拥有
},
{
configId: "org-keys",
defaultPrefix: "org_",
references: "organization", // 组织拥有
},
]),
],
});创建组织拥有的密钥
创建组织拥有的 API 密钥时,需传入 organizationId 而非 userId:
import { auth } from "@/lib/auth"
const orgKey = await auth.api.createApiKey({
body: {
configId: "org-keys",
organizationId: "org_123", // 组织拥有密钥必填
},
});会话模拟限制:enableSessionForAPIKeys 仅适用于用户拥有的 API 密钥。组织拥有的密钥由于没有单一用户关联,无法模拟用户会话。
访问控制与权限
组织拥有的 API 密钥使用组织插件的基于角色的访问控制系统。管理组织 API 密钥的用户必须:
- 是该组织成员
- 拥有执行操作所需的
apiKey权限
API 密钥权限
API 密钥插件使用以下权限:
| 操作 | 权限 | 描述 |
|---|---|---|
| 创建 | apiKey: ["create"] | 创建新组织 API 密钥 |
| 读取/列出 | apiKey: ["read"] | 查看与列出组织 API 密钥 |
| 更新 | apiKey: ["update"] | 修改组织 API 密钥 |
| 删除 | apiKey: ["delete"] | 删除组织 API 密钥 |
配置组织角色权限
默认情况下,组织拥有者拥有所有 API 密钥操作的完全访问权限。对于其他角色(如 admin 或 member),需在组织插件配置中显式赋予 apiKey 权限。
示例配置角色及权限如下:
import { betterAuth } from "better-auth"
import { organization } from "better-auth/plugins"
import { apiKey } from "@better-auth/api-key"
import { createAccessControl } from "better-auth/plugins/access"
// 定义包含 apiKey 权限的访问控制语句
const statements = {
// ... 其他语句
// 添加 apiKey 权限
apiKey: ["create", "read", "update", "delete"],
} as const;
const ac = createAccessControl(statements);
// 定义具有特定 apiKey 权限的角色
const adminRole = ac.newRole({
// ... 其他权限
// 管理员可管理 API 密钥
apiKey: ["create", "read", "update", "delete"],
});
const memberRole = ac.newRole({
// ... 其他权限
// 成员仅可查看 API 密钥
apiKey: ["read"],
});
export const auth = betterAuth({
plugins: [
organization({
ac,
roles: {
admin: adminRole,
member: memberRole,
},
async sendInvitationEmail() {},
}),
apiKey([
{
configId: "org-keys",
defaultPrefix: "org_",
references: "organization",
},
]),
],
});拥有者访问权限:组织拥有者(creatorRole,默认 "owner")自动具备所有 API 密钥操作的完全访问权限,无需显式配置权限。
权限示例
// 管理员可创建、读取、更新、删除组织 API 密钥
const key = await auth.api.createApiKey({
body: { configId: "org-keys", organizationId: "org_123" },
headers: adminHeaders,
});
// 仅有 "read" 权限的成员可列出密钥
const keys = await client.apiKey.list(
{ query: { organizationId: "org_123" } },
{ headers: memberHeaders },
);
// 仅有 "read" 权限的成员尝试创建密钥将报错
const result = await client.apiKey.create(
{ configId: "org-keys", organizationId: "org_123" },
{ headers: memberHeaders },
);
// 错误: INSUFFICIENT_API_KEY_PERMISSIONS错误码说明
访问被拒时,返回以下错误码:
USER_NOT_MEMBER_OF_ORGANIZATION:用户不是该组织成员INSUFFICIENT_API_KEY_PERMISSIONS:用户缺少执行操作所需apiKey权限
API 密钥对象结构
API 密钥包含 configId 表示所属配置,referenceId 表示拥有者 ID:
type ApiKey = {
id: string;
configId: string; // 所属配置
referenceId: string; // 拥有者 ID(基于配置为 userId 或 organizationId)
// ... 其他字段
};拥有者类型通过配置中 references 字段判断:
const apiKey = await auth.api.getApiKey({
query: { id: keyId },
headers,
});
// 依据配置中 `references` 判断拥有者类型
// 对于 org-keys 配置(references: "organization"):
console.log(`密钥拥有者:${apiKey.referenceId}`);存储模式
API Key 插件支持多种存储模式,满足不同场景灵活管理 API 密钥。
存储模式选项
"database"(默认)
仅将 API 密钥存储在数据库中。这是默认模式,无需额外配置。
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"
export const auth = betterAuth({
plugins: [
apiKey({
storage: "database", // 默认,可省略
}),
],
});"secondary-storage"
只存储于二级存储(如 Redis),不回退数据库。适合所有密钥都迁移到二级存储的高性能场景。
import { createClient } from "redis";
import { betterAuth } from "better-auth";
import { apiKey } from "@better-auth/api-key";
const redis = createClient();
await redis.connect();
export const auth = betterAuth({
secondaryStorage: {
get: async (key) => await redis.get(key),
set: async (key, value, ttl) => {
if (ttl) await redis.set(key, value, { EX: ttl });
else await redis.set(key, value);
},
delete: async (key) => await redis.del(key),
},
plugins: [
apiKey({
storage: "secondary-storage",
}),
],
});带回退的二级存储
先查询二级存储,若未命中则回退数据库查询。
读取行为:
- 先查二级存储
- 未命中则查数据库
- 自动填充二级存储(缓存预热)
- 保持高频访问键长时间驻留缓存
写入行为:
- 同时写入数据库和二级存储
- 保持两端数据一致
import { betterAuth } from "better-auth"
import { createClient } from "redis";
const redis = createClient();
await redis.connect();
export const auth = betterAuth({
secondaryStorage: {
get: async (key) => await redis.get(key),
set: async (key, value, ttl) => {
if (ttl) await redis.set(key, value, { EX: ttl });
else await redis.set(key, value);
},
delete: async (key) => await redis.del(key),
},
plugins: [
apiKey({
storage: "secondary-storage",
fallbackToDatabase: true,
}),
],
});自定义存储方法
可为 API 密钥单独覆盖全局的 secondaryStorage,提供自定义存储逻辑:
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"
export const auth = betterAuth({
plugins: [
apiKey({
storage: "secondary-storage",
customStorage: {
get: async (key) => {
// API 密钥的自定义获取逻辑
return await customStorage.get(key);
},
set: async (key, value, ttl) => {
// API 密钥的自定义存储逻辑
await customStorage.set(key, value, ttl);
},
delete: async (key) => {
// API 密钥的自定义删除逻辑
await customStorage.delete(key);
},
},
}),
],
});速率限制
每个 API 密钥都可单独配置速率限制。内置速率限制在每次验证 API 密钥时生效,包括:
- 通过
/api-key/verify端点验证 API 密钥时 - 使用 API 密钥创建会话时(启用
enableSessionForAPIKeys),所有使用该密钥的接口均受限
对于不使用 API 密钥的其他接口/方法,应使用 Better Auth 的内置速率限制。
重复计数问题:若你先手动调用 verifyApiKey() 验证,再用同一密钥头调用 getSession(),这两个操作都会增加计数,导致单次请求计数增加两次。避免方法:
- 使用
enableSessionForAPIKeys: true让 Better Auth 自动处理会话创建(推荐) - 或只验证一次 API 密钥,重复使用验证结果,避免分别调用两个方法
你可参考 API Key 插件选项 中的默认速率限制配置。
示例默认配置:
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"
export const auth = betterAuth({
plugins: [
apiKey({
rateLimit: {
enabled: true,
timeWindow: 1000 * 60 * 60 * 24, // 1 天
maxRequests: 10, // 每天最多 10 次请求
},
}),
],
});每个 API 密钥可在创建时定制速率限制选项。
速率限制选项仅能在服务器端 auth 实例中定制。
import { auth } from "@/lib/auth"
const apiKey = await auth.api.createApiKey({
body: {
rateLimitEnabled: true,
rateLimitTimeWindow: 1000 * 60 * 60 * 24, // 1 天
rateLimitMax: 10, // 每天最多 10 次请求
},
headers: await headers() // 包含用户会话令牌的请求头
});工作原理
速率限制采用滑动窗口方式:
-
首次请求:API 密钥首次使用(无
lastRequest)时,允许请求,并设置requestCount为 1。 -
窗口内请求:后续请求若在
timeWindow内,增加requestCount。达到rateLimitMax时,拒绝请求返回RATE_LIMITED错误。 -
窗口重置:若距离上次请求时间超过
timeWindow,窗口重置:requestCount重新置 1,更新lastRequest时间。 -
超限提示:超限时响应包含
tryAgainIn字段(毫秒),提示下次重置还需等待时间。
关闭速率限制:
- 全局关闭:插件选项中
rateLimit.enabled: false - 针对单密钥:设置
rateLimitEnabled: false rateLimitTimeWindow或rateLimitMax为null时,速率限制自动禁用
关闭时仍允许请求,但会更新 lastRequest 以便统计。
额度剩余、补充和过期
额度剩余表示 API 密钥剩余可用请求数。 补充间隔表示多少毫秒后,额度剩余数会被补充(前提是满足条件)。 过期时间表示 API 密钥的过期时间。
工作原理
额度剩余
每次使用 API 密钥时,都会更新 remaining 剩余额度。
如果 remaining 为 null,则无限制使用。
否则,remaining 递减 1。
若为 0,则 API 密钥被禁用并移除。
补充间隔 & 补充值
API 密钥默认创建时,refillInterval 和 refillAmount 均为 null,表示不自动补充额度。
若两者均设置,则每次使用 API 密钥时:
- 系统检查自上次补充(或未补充,内部于创建时间)起,时间是否超过
refillInterval - 超过时,
remaining直接重置为refillAmount(非累加) - 同时更新
lastRefillAt时间戳为当前时间
过期时间
默认情况下,创建时 expiresAt 为 null,表示永不过期。
如果设置了 expiresIn,则密钥将在指定时间后过期。
自定义密钥生成与验证
你可直接在插件选项中自定义密钥生成与验证逻辑。
示例:
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"
export const auth = betterAuth({
plugins: [
apiKey({
customKeyGenerator: (options: {
length: number;
prefix: string | undefined;
}) => {
const apiKey = mySuperSecretApiKeyGenerator(
options.length,
options.prefix
);
return apiKey;
},
customAPIKeyValidator: async ({ ctx, key }) => {
const res = await keyService.verify(key)
return res.valid
},
}),
],
});如果你没有使用 customKeyGenerator 提供的 length 属性,则必须设置 defaultKeyLength,指明生成密钥的长度。
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"
export const auth = betterAuth({
plugins: [
apiKey({
customKeyGenerator: () => {
return crypto.randomUUID();
},
defaultKeyLength: 36, // 或你生成密钥的实际长度
}),
],
});如果通过 customAPIKeyValidator 验证 API 密钥,我们仍需和数据库中的密钥比对。
但通过提供该自定义方法,你可以提升验证性能,
且所有无效密钥无需访问数据库即可被快速判断。
元数据
允许你为 API 密钥存储元数据,比如订阅计划等信息。
确保插件选项中未禁用元数据:
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"
export const auth = betterAuth({
plugins: [
apiKey({
enableMetadata: true,
}),
],
});然后就可以在 API 密钥对象的 metadata 字段存储信息。
import { auth } from "@/lib/auth"
const apiKey = await auth.api.createApiKey({
body: {
metadata: {
plan: "premium",
},
},
});随后可从 API 密钥对象中读取元数据。
import { auth } from "@/lib/auth"
const apiKey = await auth.api.getApiKey({
body: {
keyId: "your_api_key_id_here",
},
});
console.log(apiKey.metadata.plan); // "premium"