高级功能

高级 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 自定义。

auth.ts
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

auth.ts
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 插件:

auth.ts
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 参数指定用哪个配置:

create-api-key.ts
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 创建的密钥,getupdatedeleteverify 操作也必须指定相同的 configId 参数。

在 API 密钥操作中使用 configId

所有 API 密钥操作都支持 configId 参数,指定查询使用哪套配置,特别是配置使用不同存储后端(如数据库与 Redis)时很重要:

api-key-operations.ts
// 指定配置获取 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 过滤:

list-api-keys.ts
// 仅列出公开密钥
const publicKeys = await authClient.apiKey.list({
  query: { configId: "public" }
});

// 仅列出私密密钥
const secretKeys = await authClient.apiKey.list({
  query: { configId: "secret" }
});

全局选项

也可以传入第二个参数传递全局选项(比如 schema):

auth.ts
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"

auth.ts
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

create-org-api-key.ts
import { auth } from "@/lib/auth"

const orgKey = await auth.api.createApiKey({
  body: {
    configId: "org-keys",
    organizationId: "org_123", // 组织拥有密钥必填
  },
});

会话模拟限制enableSessionForAPIKeys 仅适用于用户拥有的 API 密钥。组织拥有的密钥由于没有单一用户关联,无法模拟用户会话。

访问控制与权限

组织拥有的 API 密钥使用组织插件的基于角色的访问控制系统。管理组织 API 密钥的用户必须:

  1. 是该组织成员
  2. 拥有执行操作所需的 apiKey 权限

API 密钥权限

API 密钥插件使用以下权限:

操作权限描述
创建apiKey: ["create"]创建新组织 API 密钥
读取/列出apiKey: ["read"]查看与列出组织 API 密钥
更新apiKey: ["update"]修改组织 API 密钥
删除apiKey: ["delete"]删除组织 API 密钥

配置组织角色权限

默认情况下,组织拥有者拥有所有 API 密钥操作的完全访问权限。对于其他角色(如 adminmember),需在组织插件配置中显式赋予 apiKey 权限。

示例配置角色及权限如下:

auth.ts
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 密钥存储在数据库中。这是默认模式,无需额外配置。

auth.ts
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"

export const auth = betterAuth({
  plugins: [
    apiKey({
      storage: "database", // 默认,可省略
    }),
  ],
});

"secondary-storage"

只存储于二级存储(如 Redis),不回退数据库。适合所有密钥都迁移到二级存储的高性能场景。

auth.ts
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",
    }),
  ],
});

带回退的二级存储

先查询二级存储,若未命中则回退数据库查询。

读取行为:

  • 先查二级存储
  • 未命中则查数据库
  • 自动填充二级存储(缓存预热)
  • 保持高频访问键长时间驻留缓存

写入行为:

  • 同时写入数据库和二级存储
  • 保持两端数据一致
auth.ts
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,提供自定义存储逻辑:

auth.ts
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 插件选项 中的默认速率限制配置。

示例默认配置:

auth.ts
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 实例中定制。

create-api-key.ts
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() // 包含用户会话令牌的请求头
});

工作原理

速率限制采用滑动窗口方式:

  1. 首次请求:API 密钥首次使用(无 lastRequest)时,允许请求,并设置 requestCount 为 1。

  2. 窗口内请求:后续请求若在 timeWindow 内,增加 requestCount。达到 rateLimitMax 时,拒绝请求返回 RATE_LIMITED 错误。

  3. 窗口重置:若距离上次请求时间超过 timeWindow,窗口重置:requestCount 重新置 1,更新 lastRequest 时间。

  4. 超限提示:超限时响应包含 tryAgainIn 字段(毫秒),提示下次重置还需等待时间。

关闭速率限制:

  • 全局关闭:插件选项中 rateLimit.enabled: false
  • 针对单密钥:设置 rateLimitEnabled: false
  • rateLimitTimeWindowrateLimitMaxnull 时,速率限制自动禁用

关闭时仍允许请求,但会更新 lastRequest 以便统计。

额度剩余、补充和过期

额度剩余表示 API 密钥剩余可用请求数。 补充间隔表示多少毫秒后,额度剩余数会被补充(前提是满足条件)。 过期时间表示 API 密钥的过期时间。

工作原理

额度剩余

每次使用 API 密钥时,都会更新 remaining 剩余额度。 如果 remainingnull,则无限制使用。 否则,remaining 递减 1。 若为 0,则 API 密钥被禁用并移除。

补充间隔 & 补充值

API 密钥默认创建时,refillIntervalrefillAmount 均为 null,表示不自动补充额度。 若两者均设置,则每次使用 API 密钥时:

  • 系统检查自上次补充(或未补充,内部于创建时间)起,时间是否超过 refillInterval
  • 超过时,remaining 直接重置为 refillAmount(非累加)
  • 同时更新 lastRefillAt 时间戳为当前时间

过期时间

默认情况下,创建时 expiresAtnull,表示永不过期。 如果设置了 expiresIn,则密钥将在指定时间后过期。

自定义密钥生成与验证

你可直接在插件选项中自定义密钥生成与验证逻辑。

示例:

auth.ts
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,指明生成密钥的长度。

auth.ts
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 密钥存储元数据,比如订阅计划等信息。

确保插件选项中未禁用元数据:

auth.ts
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"

export const auth = betterAuth({
  plugins: [
    apiKey({
      enableMetadata: true,
    }),
  ],
});

然后就可以在 API 密钥对象的 metadata 字段存储信息。

create-api-key.ts
import { auth } from "@/lib/auth"

const apiKey = await auth.api.createApiKey({
  body: {
    metadata: { 
      plan: "premium", 
    }, 
  },
});

随后可从 API 密钥对象中读取元数据。

get-api-key.ts
import { auth } from "@/lib/auth"

const apiKey = await auth.api.getApiKey({
  body: {
    keyId: "your_api_key_id_here",
  },
});

console.log(apiKey.metadata.plan); // "premium"