创建数据库适配器
Auth.js 适配器允许您与任何(甚至是多个)数据库/后端服务集成,即使我们还没有提供 官方包。(我们欢迎针对新适配器的 PR!请参阅下面的 指南。)
Auth.js 适配器非常灵活,您可以只实现您需要的那些方法,并且只创建实际上要使用的数据库表/列。
Auth.js 适配器是一个函数,它接收一个 ORM/数据库客户端,并返回一个包含方法的对象(基于 Adapter
接口),这些方法与数据库进行交互。同一个数据库适配器将与任何 Auth.js 库兼容。
可选地,您可以对您的适配器运行我们的 适配器测试,以确保它符合 Auth.js。
用户管理
Auth.js 区分用户和帐户。一个用户可以拥有多个帐户。当用户首次使用某个提供商类型登录时,就会为其创建一个帐户。例如,如果一个用户使用 Google 登录,然后使用 Facebook 登录,那么他们将有两个帐户,分别对应每个提供商。用户首次登录使用的提供商也将用于创建用户对象。请参阅 profile()
提供商方法。
方法和模型
尚未被 Auth.js 调用
虽然 Auth.js 不要求这样做,但出于基本显示目的,我们建议在 User
表中添加以下列:name
、email
、image
。您可以通过 profile()
提供商方法 配置这些列。如果您不需要保存这些属性,请创建一个空的 profile() {}
方法。
虽然 Auth.js 不要求这样做,但 Account
表通常会保存从提供商检索到的令牌。您可以通过 account()
提供商方法 配置这些列。如果您不需要保存令牌,请创建一个空的 account() {}
方法。
数据库会话管理
Auth.js 可以通过两种方式管理会话。了解它们及其优缺点,请访问 概念:会话策略。
方法和模型
如果您想使用数据库会话,您需要实现以下方法
要添加数据库会话管理,您需要扩展您的数据库表/列,如下所示
另请参阅:会话 模型。
验证令牌
当您想支持电子邮件/无密码登录时,Auth.js 使用数据库来存储与用户电子邮件地址绑定的临时验证令牌。
方法和模型
另请参阅:验证令牌 模型。
官方适配器指南
完成以下所有步骤后,您就可以向我们的 存储库 提交 PR。
如果您创建了一个适配器并希望我们将其作为官方包发布,请确保它满足以下要求。查看这个 现有的适配器,以了解包结构、所需文件、测试设置、配置等。
-
适配器必须实现
Adapter
接口 的所有方法 -
适配器测试必须包含在内,并且必须通过。Docker 比服务更受欢迎,因为它可以使 CI 对网络错误具有弹性,并减少 GitHub Action 密钥的数量(这也可以让我们在分支 PR 中运行这些测试)
-
适配器必须遵循以下编码风格
- 使用 TypeScript 编写
- 通过单一仓库的代码风格检查规则
- 不包含 polyfills
- 配置为 ES 模块 (ESM)
- 通过 JSDoc 注释进行文档化
- 从其主模块导出至少一个命名导出。(例如
export function MyAdapter(): Adapter {}
) - 集合/表名应遵循底层 ORM/数据库文档/约定的约定(复数/单数、骆驼式大小写/蛇形大小写)
-
配置单一仓库以帮助我们维护包
- 向 此目录 添加一个(最好是
.svg
)徽标 - 将适配器添加到我们的 GitHub 工作流文件中 这里 和 这里
- 如果存在任何
.gitignore
生成的文件,请确保将其忽略
- 向 此目录 添加一个(最好是
-
适配器必须能够处理来自用户的任何属性
ORM/数据库客户端可能具有自己的数据类型,但 Auth.js 预计这些数据类型将被规范化为纯 JavaScript 对象,以确保一致性。如果您的 ORM/数据库客户端没有自动转换,您需要在从数据库读写时转换这些值。
您可能很想检查属性的名称并根据该名称进行转换,但这不可扩展(例如:一个
User
对象可能有多个Date
属性,而不仅仅是emailVerified
)。相反,我们建议创建用于转换值的实用函数。下面是如何转换日期的示例(如果您的 ORM/数据库客户端使用其他数据类型,请记住也要转换它们,而不仅仅是日期)。它检查值是否可以解析为日期,如果是,则将其转换为
Date
对象。否则,它会保持原始值不变。
// https://github.com/honeinc/is-iso-date/blob/8831e79b5b5ee615920dcb350a355ffc5cbf7aed/index.js#L5
const isoDateRE =
/(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/
const isDate = (val: any): val is ConstructorParameters<typeof Date>[0] =>
!!(val && isoDateRE.test(val) && !isNaN(Date.parse(val)))
export const format = {
/** Takes an object that's coming from a database and converts it to plain JavaScript. */
from<T>(object: Record<string, any> = {}): T {
const newObject: Record<string, unknown> = {}
for (const [key, value] of Object.entries(object))
if (isDate(value)) newObject[key] = new Date(value)
else newObject[key] = value
return newObject as T
},
/** Takes an object that's coming from Auth.js and prepares it to be written to the database. */
to<T>(object: Record<string, any>): T {
const newObject: Record<string, unknown> = {}
for (const [key, value] of Object.entries(object))
if (value instanceof Date) newObject[key] = value.toISOString()
else newObject[key] = value
return newObject as T
},
}
TypeScript
您可以利用框架包提供的类型(即 next-auth/adapters
、@auth/sveltekit/adapters
)。
import type { Adapter } from "next-auth/adapters"
function MyAdapter(): Adapter {
return {
// your adapter methods here
}
}
在 JavaScript 中编写适配器时,您仍然可以使用 JSDoc 来获得有用的编辑器提示和自动完成功能。
/** @return { import("next-auth/adapters").Adapter } */
function MyAdapter() {
return {
// your adapter methods here
}
}