API 密钥
API 密钥插件允许您为您的应用程序创建和管理 API 密钥。它提供了一种通过验证 API 密钥来认证和授权 API 请求的方法。
功能
- 创建、管理和验证 API 密钥
- 内置限流
- 自定义过期时间、剩余计数和补充系统
- API 密钥的元数据
- 自定义前缀
- 从 API 密钥获取会话
安装
将插件添加到服务器
import { betterAuth } from "better-auth"
import { apiKey } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
apiKey()
]
})迁移数据库
运行迁移或生成架构,以向数据库添加必要的字段和表。
npx @better-auth/cli migratenpx @better-auth/cli generate请参阅 架构 部分以手动添加字段。
添加客户端插件
import { createAuthClient } from "better-auth/client"
import { apiKeyClient } from "better-auth/client/plugins"
export const authClient = createAuthClient({
plugins: [
apiKeyClient()
]
})使用方法
您可以查看 API 密钥插件选项列表 此处。
创建 API 密钥
您可以通过使用服务器方法来调整更具体的 API 密钥配置。
const { data, error } = await authClient.apiKey.create({ name: 'project-api-key', expiresIn: 60 * 60 * 24 * 7, prefix: 'project-api-key', metadata: { someKey: 'someValue' }, permissions,});| Prop | Description | Type |
|---|---|---|
name? | API 密钥的名称。 | string |
expiresIn? | API 密钥的过期时间(秒)。 | number |
prefix? | API 密钥的前缀。 | string |
metadata? | API 密钥的元数据。 | any | null |
permissions? | API 密钥的权限。 | Record<string, string[]> |
结果
它将返回包含 key 值的 ApiKey 对象供您使用。
否则,如果抛出错误,将抛出 APIError。
验证 API 密钥
const permissions = { // 要检查的权限是可选的。 projects: ["read", "read-write"],}const data = await auth.api.verifyApiKey({ body: { key: "your_api_key_here", // required permissions, },});| Prop | Description | Type |
|---|---|---|
key | 要验证的密钥。 | string |
permissions? | 要验证的权限。可选。 | Record<string, string[]> |
结果
type Result = {
valid: boolean;
error: { message: string; code: string } | null;
key: Omit<ApiKey, "key"> | null;
};获取 API 密钥
const { data, error } = await authClient.apiKey.get({ id: "some-api-key-id", // required});| Prop | Description | Type |
|---|---|---|
id | API 密钥的 ID。 | string |
结果
您将收到 API 密钥的所有详细信息,但不包括 key 值本身。
如果失败,将抛出 APIError。
type Result = Omit<ApiKey, "key">;更新 API 密钥
const { data, error } = await authClient.apiKey.update({ keyId: "some-api-key-id", // required name: "some-api-key-name",});| Prop | Description | Type |
|---|---|---|
keyId | 要更新的 API 密钥的 ID。 | string |
name? | 密钥的名称。 | string |
结果
如果失败,抛出 APIError。
否则,您将收到 API 密钥详细信息,但不包括 key 值本身。
删除 API 密钥
此端点试图从用户的角度删除 API 密钥。它将检查用户的 ID 是否与密钥所有者匹配,以允许删除它。如果您想在不进行这些检查的情况下删除密钥,我们建议您使用 ORM 直接变异您的数据库。
const { data, error } = await authClient.apiKey.delete({ keyId: "some-api-key-id", // required});| Prop | Description | Type |
|---|---|---|
keyId | 要删除的 API 密钥的 ID。 | string |
结果
如果失败,抛出 APIError。
否则,您将收到:
type Result = {
success: boolean;
};列出 API 密钥
const { data, error } = await authClient.apiKey.list();结果
如果失败,抛出 APIError。
否则,您将收到:
type Result = ApiKey[];删除所有已过期 API 密钥
此功能将删除所有过期日期已到期的 API 密钥。
const data = await auth.api.deleteAllExpiredApiKeys();每次调用 apiKey 插件端点时,我们都会自动删除过期的 API 密钥,但是它们被限流为每次调用 10 秒的冷却时间,以防止多次调用数据库。
从 API 密钥获取会话
每次在 Better Auth 中调用端点时,如果标头中有有效的 API 密钥,我们将自动创建一个模拟会话来代表用户。
const session = await auth.api.getSession({
headers: new Headers({
'x-api-key': apiKey,
}),
});默认标头键是 x-api-key,但可以通过在插件选项中设置 apiKeyHeaders 选项来更改。
export const auth = betterAuth({
plugins: [
apiKey({
apiKeyHeaders: ["x-api-key", "xyz-api-key"], // 或者您可以只传递一个字符串,例如:"x-api-key"
}),
],
});或者,您可以选择性地向插件选项传递一个 apiKeyGetter 函数,该函数将使用 GenericEndpointContext 调用,从那里您应该返回 API 密钥,或者如果请求无效则返回 null。
export const auth = betterAuth({
plugins: [
apiKey({
apiKeyGetter: (ctx) => {
const has = ctx.request.headers.has("x-api-key");
if (!has) return null;
return ctx.request.headers.get("x-api-key");
},
}),
],
});限流
每个 API 密钥都可以有自己的限流设置,但是内置限流仅适用于给定 API 密钥的验证过程。 对于其他所有端点/方法,您应该利用 Better Auth 的 内置限流。
您可以在 API 密钥插件选项中参考以下限流默认配置。
一个默认值的示例:
export const auth = betterAuth({
plugins: [
apiKey({
rateLimit: {
enabled: true,
timeWindow: 1000 * 60 * 60 * 24, // 1 天
maxRequests: 10, // 每天 10 个请求
},
}),
],
});对于每个 API 密钥,您可以在创建时自定义限流选项。
您只能在服务器 auth 实例上自定义限流选项。
const apiKey = await auth.api.createApiKey({
body: {
rateLimitEnabled: true,
rateLimitTimeWindow: 1000 * 60 * 60 * 24, // 1 天
rateLimitMax: 10, // 每天 10 个请求
},
headers: user_headers,
});它是如何工作的?
对于每个请求,计数器(内部称为 requestCount)都会递增。
如果达到 rateLimitMax,请求将被拒绝,直到 timeWindow 过去,此时 timeWindow 将被重置。
剩余计数、补充和过期
剩余计数是 API 密钥被禁用前的剩余请求数。
补充间隔是每天补充 remaining 计数的间隔(毫秒)。
过期时间是 API 密钥的过期日期。
它是如何工作的?
剩余计数:
每次使用 API 密钥时,remaining 计数都会更新。
如果 remaining 计数为 null,则密钥使用没有上限。
否则,remaining 计数递减 1。
如果 remaining 计数为 0,则 API 密钥被禁用并移除。
refillInterval & refillAmount:
每次创建 API 密钥时,refillInterval 和 refillAmount 被设置为 null。
这意味着 API 密钥不会自动补充。
但是,如果设置了 refillInterval 和 refillAmount,则 API 密钥将相应补充。
过期:
每次创建 API 密钥时,expiresAt 被设置为 null。
这意味着 API 密钥永不过期。
但是,如果设置了 expiresIn,则 API 密钥将在 expiresIn 时间后过期。
自定义密钥生成和验证
您可以直接从插件选项自定义密钥生成和验证过程。
这是一个示例:
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 属性为生成密钥的长度。
export const auth = betterAuth({
plugins: [
apiKey({
customKeyGenerator: () => {
return crypto.randomUUID();
},
defaultKeyLength: 36, // 或者任何长度
}),
],
});如果从您的 customAPIKeyValidator 验证了 API 密钥,我们仍然必须将其与数据库的密钥匹配。
但是,通过提供此自定义函数,您可以改进 API 密钥验证过程的性能,
因为所有失败的密钥可以在不查询数据库的情况下无效化。
元数据
我们允许您在 API 密钥旁边存储元数据。这对于存储密钥的信息很有用,例如订阅计划。
要存储元数据,请确保您没有在插件选项中禁用元数据功能。
export const auth = betterAuth({
plugins: [
apiKey({
enableMetadata: true,
}),
],
});然后,您可以在 API 密钥对象的 metadata 字段中存储元数据。
const apiKey = await auth.api.createApiKey({
body: {
metadata: {
plan: "premium",
},
},
});然后,您可以从 API 密钥对象中检索元数据。
const apiKey = await auth.api.getApiKey({
body: {
keyId: "your_api_key_id_here",
},
});
console.log(apiKey.metadata.plan); // "premium"API 密钥插件选项
apiKeyHeaders string | string[];
检查 API 密钥的标头名称。默认为 x-api-key。
customAPIKeyGetter (ctx: GenericEndpointContext) => string | null
从上下文中获取 API 密钥的自定义函数。
customAPIKeyValidator (options: { ctx: GenericEndpointContext; key: string; }) => boolean | Promise<boolean>
验证 API 密钥的自定义函数。
customKeyGenerator (options: { length: number; prefix: string | undefined; }) => string | Promise<string>
生成 API 密钥的自定义函数。
startingCharactersConfig { shouldStore?: boolean; charactersLength?: number; }
自定义起始字符配置。
defaultKeyLength number
API 密钥的长度。更长越好。默认为 64。(不包括前缀长度)
defaultPrefix string
API 密钥的前缀。
注意:我们建议您在前缀后附加下划线,以使前缀更容易识别。(例如 hello_)
maximumPrefixLength number
前缀的最大长度。
minimumPrefixLength number
前缀的最小长度。
requireName boolean
是否要求 API 密钥有名称。默认为 false。
maximumNameLength number
名称的最大长度。
minimumNameLength number
名称的最小长度。
enableMetadata boolean
是否为 API 密钥启用元数据。
keyExpiration { defaultExpiresIn?: number | null; disableCustomExpiresTime?: boolean; minExpiresIn?: number; maxExpiresIn?: number; }
自定义密钥过期。
rateLimit { enabled?: boolean; timeWindow?: number; maxRequests?: number; }
自定义限流。
schema InferOptionSchema<ReturnType<typeof apiKeySchema>>
API 密钥插件的自定义架构。
disableSessionForAPIKeys boolean
API 密钥可以代表有效的会话,因此如果我们在请求标头中找到有效的 API 密钥,我们会自动为用户模拟一个会话。
permissions { defaultPermissions?: Statements | ((userId: string, ctx: GenericEndpointContext) => Statements | Promise<Statements>) }
API 密钥的权限。
阅读有关权限的更多信息 此处。
disableKeyHashing boolean
禁用 API 密钥的哈希。
⚠️ 安全警告:强烈建议不要禁用哈希。 以明文形式存储 API 密钥会使它们容易受到数据库泄露的影响,可能暴露所有用户的 API 密钥。
权限
API 密钥可以有关联的权限,从而允许您在细粒度级别控制访问。权限结构为资源类型到允许操作数组的记录。
设置默认权限
您可以配置默认权限,这些权限将应用于所有新创建的 API 密钥:
export const auth = betterAuth({
plugins: [
apiKey({
permissions: {
defaultPermissions: {
files: ["read"],
users: ["read"],
},
},
}),
],
});您还可以提供一个动态返回权限的函数:
export const auth = betterAuth({
plugins: [
apiKey({
permissions: {
defaultPermissions: async (userId, ctx) => {
// 获取用户角色或其他数据以确定权限
return {
files: ["read"],
users: ["read"],
};
},
},
}),
],
});带权限创建 API 密钥
创建 API 密钥时,您可以指定自定义权限:
const apiKey = await auth.api.createApiKey({
body: {
name: "My API Key",
permissions: {
files: ["read", "write"],
users: ["read"],
},
userId: "userId",
},
});带所需权限验证 API 密钥
验证 API 密钥时,您可以检查它是否具有所需的权限:
const result = await auth.api.verifyApiKey({
body: {
key: "your_api_key_here",
permissions: {
files: ["read"],
},
},
});
if (result.valid) {
// API 密钥有效且具有所需的权限
} else {
// API 密钥无效或没有所需的权限
}更新 API 密钥权限
您可以更新现有 API 密钥的权限:
const apiKey = await auth.api.updateApiKey({
body: {
keyId: existingApiKeyId,
permissions: {
files: ["read", "write", "delete"],
users: ["read", "write"],
},
},
headers: user_headers,
});权限结构
权限遵循基于资源的结构:
type Permissions = {
[resourceType: string]: string[];
};
// 示例:
const permissions = {
files: ["read", "write", "delete"],
users: ["read"],
projects: ["read", "write"],
};验证 API 密钥时,所有必需的权限必须存在于 API 密钥的权限中,验证才能成功。
架构
表:apiKey
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | API 密钥的 ID。 | |
| name | string | API 密钥的名称。 | |
| start | string | API 密钥的起始字符。在 UI 中显示 API 密钥的前几个字符有助于用户轻松识别。 | |
| prefix | string | API 密钥前缀。以明文形式存储。 | |
| key | string | - | 已哈希的 API 密钥本身。 |
| userId | string | 与 API 密钥关联的用户的 ID。 | |
| refillInterval | number | 补充密钥的间隔(毫秒)。 | |
| refillAmount | number | 补充密钥剩余计数的数量。 | |
| lastRefillAt | Date | 最后补充密钥的日期和时间。 | |
| enabled | boolean | - | API 密钥是否启用。 |
| rateLimitEnabled | boolean | - | API 密钥是否启用限流。 |
| rateLimitTimeWindow | number | 限流的时间窗口(毫秒)。 | |
| rateLimitMax | number | 在 `rateLimitTimeWindow` 内允许的最大请求数。 | |
| requestCount | number | - | 在限流时间窗口内发出的请求数。 |
| remaining | number | 剩余请求数。 | |
| lastRequest | Date | 向密钥发出的最后请求的日期和时间。 | |
| expiresAt | Date | 密钥将过期的日期和时间。 | |
| createdAt | Date | - | 创建 API 密钥的日期和时间。 |
| updatedAt | Date | - | 更新 API 密钥的日期和时间。 |
| permissions | string | 密钥的权限。 | |
| metadata | Object | 您想与密钥一起存储的任何附加元数据。 |