# 参考 (/docs/plugins/api-key/reference)

API 密钥插件选项、权限和模式参考。



## API 密钥插件选项 [#api-密钥插件选项]

`configId` <span className="opacity-70">`string`</span>

该配置的唯一标识符。在使用多个配置时必填。默认值为 `"default"`。

`references` <span className="opacity-70">`"user" | "organization"`</span>

API 密钥所引用的对象。这决定了 API 密钥的所有权。默认值为 `"user"`。

* `"user"`：API 密钥归用户所有（创建时需要提供 `userId`）
* `"organization"`：API 密钥归组织所有（创建时需要提供 `organizationId`）

`apiKeyHeaders` <span className="opacity-70">`string | string[];`</span>

用于检测 API 密钥的请求头名称。默认值为 `x-api-key`。

`customAPIKeyGetter` <span className="opacity-70">`(ctx: GenericEndpointContext) => string | null`</span>

自定义函数，从上下文中获取 API 密钥。

`customAPIKeyValidator` <span className="opacity-70">`(options: { ctx: GenericEndpointContext; key: string; }) => boolean | Promise<boolean>`</span>

自定义函数，用于验证 API 密钥。

`customKeyGenerator` <span className="opacity-70">`(options: { length: number; prefix: string | undefined; }) => string | Promise<string>`</span>

自定义函数，用于生成 API 密钥。

`startingCharactersConfig` <span className="opacity-70">`{ shouldStore?: boolean; charactersLength?: number; }`</span>

自定义起始字符配置。

<Accordions>
  <Accordion title="startingCharactersConfig 选项">
    `shouldStore` <span className="opacity-70">`boolean`</span>

    是否将起始字符存储到数据库中。
    若为 false，则设置 `start` 为 `null`。
    默认值为 `true`。

    `charactersLength` <span className="opacity-70">`number`</span>

    要存储在数据库中的起始字符长度。
    该长度包括前缀长度。
    默认值为 `6`。
  </Accordion>
</Accordions>

`defaultKeyLength` <span className="opacity-70">`number`</span>

API 密钥的长度，越长越好。默认值为 64。（不包括前缀长度）

`defaultPrefix` <span className="opacity-70">`string`</span>

API 密钥的前缀。

注意：建议给前缀添加下划线以便更好识别，如 `hello_`。

`maximumPrefixLength` <span className="opacity-70">`number`</span>

前缀的最大长度。

`minimumPrefixLength` <span className="opacity-70">`number`</span>

前缀的最小长度。

`requireName` <span className="opacity-70">`boolean`</span>

是否要求必须填写 API 密钥名称。默认值为 `false`。

`maximumNameLength` <span className="opacity-70">`number`</span>

名称的最大长度。

`minimumNameLength` <span className="opacity-70">`number`</span>

名称的最小长度。

`enableMetadata` <span className="opacity-70">`boolean`</span>

是否启用 API 密钥的元数据。

`keyExpiration` <span className="opacity-70">`{ defaultExpiresIn?: number | null; disableCustomExpiresTime?: boolean; minExpiresIn?: number; maxExpiresIn?: number; }`</span>

自定义密钥过期时间。

<Accordions>
  <Accordion title="keyExpiration 选项">
    `defaultExpiresIn` <span className="opacity-70">`number | null`</span>

    默认的过期时间（毫秒）。
    若为 `null`，则无过期时间。
    默认值为 `null`。

    `disableCustomExpiresTime` <span className="opacity-70">`boolean`</span>

    是否禁止客户端传入的过期时间。
    若设置为 `true`，则过期时间以默认值为准。
    默认值为 `false`。

    `minExpiresIn` <span className="opacity-70">`number`</span>

    允许客户端设置的最小过期时间（天数）。
    默认值为 `1`。

    `maxExpiresIn` <span className="opacity-70">`number`</span>

    允许客户端设置的最大 expiresIn 值（天数）。
    默认值为 `365`。
  </Accordion>
</Accordions>

`rateLimit` <span className="opacity-70">`{ enabled?: boolean; timeWindow?: number; maxRequests?: number; }`</span>

自定义速率限制。

<Accordions>
  <Accordion title="rateLimit 选项">
    `enabled` <span className="opacity-70">`boolean`</span>

    是否启用速率限制。（默认开启）

    `timeWindow` <span className="opacity-70">`number`</span>

    统计请求次数的时间窗口（毫秒）。
    达到 `maxRequests` 后，直到 `timeWindow` 过期之前拒绝请求，随后重置计数。

    `maxRequests` <span className="opacity-70">`number`</span>

    时间窗口内允许的最大请求数。
    达到 `maxRequests` 后，请求将被拒绝，直到 `timeWindow` 结束，此时将重置 `timeWindow`。
  </Accordion>
</Accordions>

`schema` <span className="opacity-70">`InferOptionSchema<ReturnType<typeof apiKeySchema>>`</span>

API 密钥插件的自定义模式。

`enableSessionForAPIKeys` <span className="opacity-70">`boolean`</span>

API 密钥可以代表一个有效会话，因此如果在请求头中找到有效的 API 密钥，可以模拟用户会话。默认值为 `false`。

`storage` <span className="opacity-70">`"database" | "secondary-storage"`</span>

API 密钥的存储后端。默认值为 `"database"`。

* `"database"`：将 API 密钥存储在数据库适配器中（默认）
* `"secondary-storage"`：将 API 密钥存储在配置的二级存储中（例如 Redis）

`fallbackToDatabase` <span className="opacity-70">`boolean`</span>

当 `storage` 设为 `"secondary-storage"` 时，若未在二级存储中找到密钥，是否回退至数据库查找。
默认值为 `false`。

<Callout>
  当 `storage` 设置为 `"secondary-storage"` 时，必须在 Better Auth 选项中配置 `secondaryStorage`。API 密钥将使用以下键值模式存储：

  * `api-key:${hashedKey}` - 通过哈希密钥进行主要查找
  * `api-key:by-id:${id}` - 通过 ID 查找
  * `api-key:by-ref:${referenceId}` - 引用对象的 API 密钥列表（用户或组织）

  如果 API 密钥具有过期日期（`expiresAt`），则会在二级存储中自动设置 TTL，以确保自动清理。
</Callout>

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

export const auth = betterAuth({
  secondaryStorage: {
    get: async (key) => {
      return 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",
    }),
  ],
});
```

`customStorage` <span className="opacity-70">`SecondaryStorage`</span>

用于 API 密钥的自定义二级存储。如果提供，则会取代 `ctx.context.secondaryStorage`。自定义存储的优先级高于全局二级存储。

当你希望专门为 API 密钥使用不同的存储后端，或需要为存储操作添加自定义逻辑时，此选项非常有用。它必须实现完整的二级存储契约，包括 `getAndDelete` 和 `increment`。

```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) => await customStorage.get(key),
        getAndDelete: async (key) => await customStorage.getAndDelete(key),
        increment: async (key, ttl) => await customStorage.increment(key, ttl),
        set: async (key, value, ttl) => await customStorage.set(key, value, ttl),
        delete: async (key) => await customStorage.delete(key), 
      },
    }),
  ],
});
```

`deferUpdates` <span className="opacity-70">`boolean`</span>

将非关键的更新（如速率限制计数器、时间戳、剩余请求数）推迟到响应发送后，通过全局的 `backgroundTasks` 处理器异步执行。此设置在无服务器架构中显著提升响应速度。默认值为 `false`。

需要在主认证配置中设置 `backgroundTasks.handler`。

<Callout type="warn">
  启用该选项会引入最终一致性，即响应先返回乐观数据，数据库更新稍后完成。只在应用能容忍该延迟情况下启用，以提升性能。
</Callout>

<Tabs items="[&#x22;Vercel&#x22;, &#x22;Cloudflare Workers&#x22;]">
  <Tab value="Vercel">
    ```ts
    import { waitUntil } from "@vercel/functions";

    export const auth = betterAuth({
      advanced: { 
          backgroundTasks: {
             handler: waitUntil,
          },
      }
      plugins: [
        apiKey({
          deferUpdates: true,
        }),
      ],
    });
    ```
  </Tab>

  <Tab value="Cloudflare Workers">
    ```ts
    import { AsyncLocalStorage } from "node:async_hooks";

    const execCtxStorage = new AsyncLocalStorage<ExecutionContext>();

    export const auth = betterAuth({
      advanced: { 
          backgroundTasks: {
             handler: waitUntil,
          },
      }
      plugins: [
        apiKey({
          deferUpdates: true,
        }),
      ],
    });

    // 在请求处理器中，使用 execCtxStorage.run(ctx, ...) 包裹
    ```
  </Tab>
</Tabs>

`permissions` <span className="opacity-70">`{ defaultPermissions?: Statements | ((referenceId: string, ctx: GenericEndpointContext) => Statements | Promise<Statements>) }`</span>

API 密钥的权限设置。

关于权限的详细说明请参见下文 [权限](#permissions)。

<Accordions>
  <Accordion title="permissions 选项">
    `defaultPermissions` <span className="opacity-70">`Statements | ((referenceId: string, ctx: GenericEndpointContext) => Statements | Promise<Statements>)`</span>

    API 密钥的默认权限。根据 `references` 设置，`referenceId` 可以是用户 ID 或组织 ID。
  </Accordion>
</Accordions>

`disableKeyHashing` <span className="opacity-70">`boolean`</span>

是否禁用 API 密钥的哈希存储。

<Callout type="warn">
  安全警告：强烈建议不要禁用哈希。
  以明文存储 API 密钥会使其容易受到数据库泄露的影响，从而可能暴露所有用户的 API 密钥。
</Callout>

***

## 权限 [#权限]

API 密钥可以绑定权限，用以实现细粒度的访问控制。权限结构为资源类型与允许操作数组的映射。

### 设置默认权限 [#设置默认权限]

可以配置默认权限，应用于所有新创建的 API 密钥：

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

export const auth = betterAuth({
  plugins: [
    apiKey({
      permissions: {
        defaultPermissions: {
          files: ["read"],
          users: ["read"],
        },
      },
    }),
  ],
});
```

也可以提供一个函数动态返回权限：

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

export const auth = betterAuth({
  plugins: [
    apiKey({
      permissions: {
        defaultPermissions: async (referenceId, ctx) => {
          // referenceId 是 userId 或 orgId，取决于配置
          // 根据用户/组织角色或其他数据决定权限
          return {
            files: ["read"],
            users: ["read"],
          };
        },
      },
    }),
  ],
});
```

### 创建带有权限的 API 密钥 [#创建带有权限的-api-密钥]

创建 API 密钥时可以指定自定义权限：

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

const apiKey = await auth.api.createApiKey({
  body: {
    name: "My API Key",
    permissions: {
      files: ["read", "write"],
      users: ["read"],
    },
    userId: "userId",
  },
});
```

### 验证带权限的 API 密钥 [#验证带权限的-api-密钥]

验证 API 密钥时，可以检测是否具备所需权限：

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

const result = await auth.api.verifyApiKey({
  body: {
    key: "your_api_key_here",
    permissions: {
      files: ["read"],
    },
  },
});

if (result.valid) {
  // API 密钥有效且拥有所需权限
} else {
  // API 密钥无效或权限不足
}
```

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

可以修改已有 API 密钥的权限：

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

const apiKey = await auth.api.updateApiKey({
  body: {
    keyId: existingApiKeyId,
    permissions: {
      files: ["read", "write", "delete"],
      users: ["read", "write"],
    },
  },
  headers: user_headers,
});
```

### 权限结构 [#权限结构]

权限采用基于资源的结构：

```ts
type Permissions = {
  [resourceType: string]: string[];
};

// 示例：
const permissions = {
  files: ["read", "write", "delete"],
  users: ["read"],
  projects: ["read", "write"],
};
```

验证时，API 密钥必须包含所有请求的所需权限，才能校验通过。

## 模式 [#模式]

表：`apikey`



<DatabaseTable name="apikey" fields="apiKeyTableFields" />

### 从旧版本迁移 [#从旧版本迁移]

从旧版本升级时，需要将 `userId` 字段迁移至新的 `referenceId` 系统：

```sql
-- 添加新列
ALTER TABLE apikey ADD COLUMN config_id VARCHAR(255) NOT NULL DEFAULT 'default';
ALTER TABLE apikey ADD COLUMN reference_id VARCHAR(255);

-- 迁移现有数据（把 userId 拷贝到 referenceId）
UPDATE apikey SET reference_id = user_id WHERE reference_id IS NULL;

-- 将 reference_id 设为非空并创建索引
ALTER TABLE apikey ALTER COLUMN reference_id SET NOT NULL;
CREATE INDEX idx_apikey_reference_id ON apikey(reference_id);
CREATE INDEX idx_apikey_config_id ON apikey(config_id);

-- 迁移确认无误后，可选择删除旧列
-- ALTER TABLE apikey DROP COLUMN user_id;
```

<Callout type="warn">
  **重大变更**：`userId` 字段已被替换为 `referenceId`。API 响应中返回 `referenceId` 而非 `userId`。拥有者类型（用户或组织）由配置的 `references` 决定，不会存储在每个密钥上。
</Callout>

