API 密钥
API Key 插件允许您为应用程序创建和管理 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 migrate
npx @better-auth/cli generate
请参阅模式部分以手动添加字段。
添加客户端插件
import { createAuthClient } from "better-auth/client"
import { apiKeyClient } from "better-auth/client/plugins"
export const authClient = createAuthClient({
plugins: [
apiKeyClient()
]
})
使用方法
您可以在此处查看 API Key 插件选项的完整列表:API Key 插件选项。
创建 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,});
属性 | 描述 | 类型 |
---|---|---|
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, },});
属性 | 描述 | 类型 |
---|---|---|
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});
属性 | 描述 | 类型 |
---|---|---|
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",});
属性 | 描述 | 类型 |
---|---|---|
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});
属性 | 描述 | 类型 |
---|---|---|
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 Key 插件选项的默认配置中参考速率限制的默认设置。
一个默认值的示例:
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
将被重置。
剩余次数、补充和过期
剩余次数(remaining)是指在 API 密钥被禁用之前还可以进行的请求次数。
补充间隔(refill interval)是以毫秒为单位的间隔,在此间隔内 remaining
计数会按天补充。
过期时间(expiration time)是 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, // 或者实际长度
}),
],
});
如果 API 密钥通过你的 customAPIKeyValidator
验证,我们仍然需要将其与数据库中的密钥进行匹配。
但是,通过提供这个自定义函数,你可以提高 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 Key 插件选项
apiKeyHeaders
string | string[];
用于检查 API Key 的请求头名称。默认为 x-api-key
。
customAPIKeyGetter
(ctx: GenericEndpointContext) => string | null
一个自定义函数,用于从上下文(context)中获取 API Key。
customAPIKeyValidator
(options: { ctx: GenericEndpointContext; key: string; }) => boolean | Promise<boolean>
一个自定义函数,用于验证 API Key。
customKeyGenerator
(options: { length: number; prefix: string | undefined; }) => string | Promise<string>
一个自定义函数,用于生成 API Key。
startingCharactersConfig
{ shouldStore?: boolean; charactersLength?: number; }
自定义起始字符配置。
defaultKeyLength
number
API Key 的长度。越长越好。默认为 64。(不包括前缀长度)
defaultPrefix
string
API Key 的前缀。
注意:建议你在前缀后附加一个下划线,以使前缀更易于识别。(例如 hello_
)
maximumPrefixLength
number
前缀的最大长度。
minimumPrefixLength
number
前缀的最小长度。
requireName
boolean
是否要求为 API Key 提供一个名称。默认为 false
。
maximumNameLength
number
名称的最大长度。
minimumNameLength
number
名称的最小长度。
enableMetadata
boolean
是否为 API Key 启用元数据功能。
keyExpiration
{ defaultExpiresIn?: number | null; disableCustomExpiresTime?: boolean; minExpiresIn?: number; maxExpiresIn?: number; }
自定义密钥过期设置。
rateLimit
{ enabled?: boolean; timeWindow?: number; maxRequests?: number; }
自定义速率限制。
schema
InferOptionSchema<ReturnType<typeof apiKeySchema>>
API Key 插件的自定义 schema。
disableSessionForAPIKeys
boolean
一个 API Key 可以代表一个有效的会话(session),因此如果我们在请求头中发现有效的 API Key,我们会自动为用户模拟一个会话。
permissions
{ defaultPermissions?: Statements | ((userId: string, ctx: GenericEndpointContext) => Statements | Promise<Statements>) }
API Key 的权限。
在此处阅读更多关于权限的信息 here。
disableKeyHashing
boolean
禁用 API Key 的哈希处理。
⚠️ 安全警告:强烈建议不要禁用哈希。 以明文形式存储 API Key 会使它们容易受到数据库泄露的影响,可能会暴露所有用户的 API Key。
权限
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 = {
};
// 示例:
const permissions = {
files: ["read", "write", "delete"],
users: ["read"],
projects: ["read", "write"],
};
在验证 API 密钥时,API 密钥的权限中必须包含所有必需的权限,验证才能成功。
数据库表结构
表名: apiKey
字段名称 | 类型 | Key | 描述 |
---|---|---|---|
id | string | API 密钥的 ID | |
name | string | API 密钥的名称 | |
start | string | API 密钥的起始字符。在用户界面中显示 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 | 希望与密钥一起存储的任何附加元数据。 |