# 高级功能 (/docs/plugins/api-key/advanced)

高级 API 密钥功能，包括会话、多种配置、组织密钥、存储模式等



## 来自 API 密钥的会话 [#来自-api-密钥的会话]

每当调用 Better Auth 中带有有效 API 密钥的接口时，可以通过启用 `enableSessionForAPIKeys` 选项，自动创建一个模拟会话以代表用户。

<Callout type="warn">
  这通常不推荐，因为如果使用不当可能导致安全问题。泄露的 API 密钥可以被用来冒充用户。
</Callout>

<Callout>
  **仅限用户拥有的密钥**：会话模拟仅适用于用户拥有的 API 密钥（即 `references: "user"`）。组织拥有的密钥无法模拟用户会话。
</Callout>

<Callout>
  **速率限制说明**：启用 `enableSessionForAPIKeys` 后，每次请求都会验证一次 API 密钥，并相应应用速率限制。
  如果手动验证 API 密钥后再单独获取会话，这两次操作都会增加速率限制计数。使用 `enableSessionForAPIKeys` 可以避免此类重复计数。
</Callout>

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

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

<Tabs items="['Server']">
  <Tab value="Server">
    ```ts
    import { auth } from "@/lib/auth"

    const session = await auth.api.getSession({
          headers: new Headers({
                'x-api-key': apiKey,
          }),
    });
    ```
  </Tab>
</Tabs>

默认的请求头字段是 `x-api-key`，但可在插件选项中通过设置 `apiKeyHeaders` 自定义。

```ts title="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`。

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

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 密钥时非常有用，例如：

* 公钥与私钥
* 只读与读写密钥
* 不同层级的速率限制

### 配置示例 [#配置示例]

将配置对象数组传递给 `apiKey` 插件：

```ts title="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` 参数指定使用哪个配置：

```ts title="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_...
```

<Callout type="warn">
  `get`、`update` 和 `delete` 必须传入与该密钥创建时相同的 `configId`。
  `verify` 会解析密钥自身的配置，因此只有当某个配置在存储或哈希方面与默认配置不同时，才需要传入 `configId`。
</Callout>

### 在 API 密钥操作中使用 configId [#在-api-密钥操作中使用-configid]

所有 API 密钥操作都支持 `configId` 参数，用于指定查询使用哪套配置，特别是在配置使用不同存储后端（如数据库与 Redis）时非常重要：

```ts title="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` 过滤：

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

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

### 全局选项 [#全局选项]

也可以传入第二个参数传递全局选项（比如 `schema`）：

```ts title="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 密钥由组织拥有，适合团队应用场景，API 密钥在组织成员间共享。

### 配置示例 [#配置示例-1]

在配置中设置 `references: "organization"`：

```ts title="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` 与用户上下文一起传入。该用户必须是组织成员，并且有权限创建 API 密钥。该用户会从会话（请求 `headers`）中获取；如果是纯服务端调用且不传 `headers`，则改为在 body 中传入 `userId`：

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

const orgKey = await auth.api.createApiKey({
  body: {
    configId: "org-keys",
    organizationId: "org_123", // 拥有该密钥的组织
    userId: "user_123", // 允许创建密钥的成员；如果传入会话 headers，则省略
  },
});
```

<Callout type="warn">
  **会话模拟限制**：`enableSessionForAPIKeys` 仅适用于用户拥有的 API 密钥。组织拥有的密钥由于没有单一用户关联，无法模拟用户会话。
</Callout>

### 访问控制与权限 [#访问控制与权限]

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

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

#### API 密钥权限 [#api-密钥权限]

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

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

#### 配置组织角色权限 [#配置组织角色权限]

默认情况下，**组织拥有者拥有所有 API 密钥操作的完全访问权限**。对于其他角色（如 `admin` 或 `member`），需在组织插件配置中显式赋予 `apiKey` 权限。

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

```ts title="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"], // [!code highlight]
} as const;

const ac = createAccessControl(statements);

// 定义具有特定 apiKey 权限的角色
const adminRole = ac.newRole({
  // ... 其他权限
  // 管理员可管理 API 密钥
  apiKey: ["create", "read", "update", "delete"], // [!code highlight]
});

const memberRole = ac.newRole({
  // ... 其他权限
  // 成员仅可查看 API 密钥
  apiKey: ["read"], // [!code highlight]
});

export const auth = betterAuth({
  plugins: [
    organization({
      ac,
      roles: {
        admin: adminRole,
        member: memberRole,
      },
      async sendInvitationEmail() {},
    }),
    apiKey([
      {
        configId: "org-keys",
        defaultPrefix: "org_",
        references: "organization",
      },
    ]),
  ],
});
```

<Callout type="info">
  **拥有者访问权限**：组织拥有者（`creatorRole`，默认 `"owner"`）自动具备所有 API 密钥操作的完全访问权限，无需显式配置权限。
</Callout>

#### 权限示例 [#权限示例]

```ts
// 管理员可创建、读取、更新、删除组织 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-密钥对象结构]

API 密钥包含 `configId` 表示所属配置，`referenceId` 表示拥有者 ID：

```ts
type ApiKey = {
  id: string;
  configId: string;    // 所属配置
  referenceId: string; // 拥有者 ID（基于配置为 userId 或 organizationId）
  // ... 其他字段
};
```

拥有者类型通过配置中 `references` 字段判断：

```ts
const apiKey = await auth.api.getApiKey({
  query: { id: keyId },
  headers,
});

// 依据配置中 `references` 判断拥有者类型
// 对于 org-keys 配置（references: "organization"）：
console.log(`密钥拥有者：${apiKey.referenceId}`);
```

## 存储模式 [#存储模式]

API Key 插件支持多种存储模式，满足不同场景灵活管理 API 密钥。

### 存储模式选项 [#存储模式选项]

#### `"database"`（默认） [#database默认]

仅将 API 密钥存储在数据库中。这是默认模式，无需额外配置。

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

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

#### `"secondary-storage"` [#secondary-storage]

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

```ts title="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",
    }),
  ],
});
```

#### 带回退的二级存储 [#带回退的二级存储]

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

**读取行为：**

* 先检查二级存储
* 如果未命中，则查询数据库
* **自动填充二级存储**（缓存预热），当从数据库回退时
* 确保频繁访问的密钥长期保留在缓存中

**写入行为：**

* 同时写入 **数据库** 和 **二级存储**
* 确保两者之间的一致性

```ts title="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`，提供自定义存储逻辑：

```ts title="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 密钥所使用的所有端点

对于其他不使用 API 密钥的端点/方法，请使用 Better Auth 的 [内置速率限制](/docs/concepts/rate-limit)。

<Callout type="warn">
  **双重速率限制计数增加**：如果你手动使用 `verifyApiKey()` 验证 API 密钥，然后又使用同一个 API 密钥请求头通过 `getSession()` 获取会话，这两个操作都会增加速率限制计数器，导致一次请求被增加两次。为避免这种情况：

  * 使用 `enableSessionForAPIKeys: true`，让 Better Auth 自动处理会话创建（推荐）
  * 或者只验证 API 密钥一次，并复用已验证的结果，而不是分别调用这两个方法
</Callout>

你可以在 [API Key 插件选项](/docs/plugins/api-key/reference#api-密钥插件选项) 中查看默认的速率限制配置。

默认配置示例：

```ts title="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 密钥都可以在创建时自定义速率限制选项。

<Callout>
  速率限制选项只能在服务端的 auth 实例中自定义。
</Callout>

```ts title="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`
* **单个密钥**：在创建或更新 API 密钥时设置 `rateLimitEnabled: false`
* **空值**：如果 `rateLimitTimeWindow` 或 `rateLimitMax` 为 `null`，则该密钥的速率限制实际上被禁用

在禁用时，请求仍然会被允许，但 `lastRequest` 仍会更新用于追踪。

## 剩余额度、补充与过期 [#剩余额度补充与过期]

剩余额度表示 API 密钥仍可发出的请求数量。
补充间隔表示在满足条件时，剩余额度在被补充前必须经过的毫秒数。
过期时间表示 API 密钥何时过期。

### 工作原理 [#工作原理-1]

#### 剩余额度 [#剩余额度]

每次使用 API 密钥时，都会更新 `remaining` 额度。
如果 `remaining` 为 `null`，则可无限使用。
否则，`remaining` 会减 1。
如果它降到 0，API 密钥将被禁用并移除。

#### 补充间隔与补充数量 [#补充间隔与补充数量]

创建 API 密钥时，`refillInterval` 和 `refillAmount` 默认设置为 `null`。
这意味着 API 密钥不会自动补充。
不过，如果同时设置了 `refillInterval` 和 `refillAmount`，那么每次使用 API 密钥时：

* 系统会检查距离上次补充（或创建后的首次补充）是否超过 `refillInterval`
* 如果间隔已过，`remaining` 数量会重置为 `refillAmount`（不是递增）
* `lastRefillAt` 时间戳会更新为当前时间

#### 过期时间 [#过期时间]

默认情况下，创建时 `expiresAt` 为 `null`，表示永不过期。
如果设置了 `expiresIn`，密钥将在指定时间后过期。

## 自定义密钥生成与验证 [#自定义密钥生成与验证]

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

示例：

```ts title="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
      },
    }),
  ],
});
```

<Callout>
  如果你没有使用 `customKeyGenerator` 提供的 `length` 属性，**必须**将 `defaultKeyLength` 属性设置为生成密钥的长度。

  ```ts title="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, // 或密钥长度
      }),
    ],
  });
  ```
</Callout>

如果 API 密钥通过 `customAPIKeyValidator` 验证，我们仍然需要将其与数据库中的密钥进行比较。
不过，通过提供这个自定义方法，你可以提升验证性能，
并且所有无效密钥都可以在不访问数据库的情况下快速拒绝。

## 元数据 [#元数据]

允许你为 API 密钥存储元数据，例如订阅计划信息。

请确保在插件选项中未禁用 metadata：

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

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

然后你就可以将信息存储到 API 密钥对象的 `metadata` 字段中。

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

const apiKey = await auth.api.createApiKey({
  body: {
    metadata: { // [!code highlight]
      plan: "premium", // [!code highlight]
    }, // [!code highlight]
  },
});
```

之后你可以从 API 密钥对象中读取元数据。

```ts title="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"
```

