跳至内容
从 NextAuth.js v4 迁移?阅读 我们的迁移指南.
指南创建数据库适配器

创建数据库适配器

Auth.js 适配器允许您与任何(甚至是多个)数据库/后端服务集成,即使我们还没有提供 官方包。(我们欢迎针对新适配器的 PR!请参阅下面的 指南。)

Auth.js 适配器非常灵活,您可以只实现您需要的那些方法,并且只创建实际上要使用的数据库表/列。

Auth.js 适配器是一个函数,它接收一个 ORM/数据库客户端,并返回一个包含方法的对象(基于 Adapter 接口),这些方法与数据库进行交互。同一个数据库适配器将与任何 Auth.js 库兼容。

可选地,您可以对您的适配器运行我们的 适配器测试,以确保它符合 Auth.js。

用户管理

Auth.js 区分用户和帐户。一个用户可以拥有多个帐户。当用户首次使用某个提供商类型登录时,就会为其创建一个帐户。例如,如果一个用户使用 Google 登录,然后使用 Facebook 登录,那么他们将有两个帐户,分别对应每个提供商。用户首次登录使用的提供商也将用于创建用户对象。请参阅 profile() 提供商方法

方法和模型

另请参阅:用户帐户 模型。

虽然 Auth.js 不要求这样做,但出于基本显示目的,我们建议在 User 表中添加以下列:nameemailimage。您可以通过 profile() 提供商方法 配置这些列。如果您不需要保存这些属性,请创建一个空的 profile() {} 方法。

虽然 Auth.js 不要求这样做,但 Account 表通常会保存从提供商检索到的令牌。您可以通过 account() 提供商方法 配置这些列。如果您不需要保存令牌,请创建一个空的 account() {} 方法。

数据库会话管理

Auth.js 可以通过两种方式管理会话。了解它们及其优缺点,请访问 概念:会话策略

方法和模型

如果您想使用数据库会话,您需要实现以下方法

要添加数据库会话管理,您需要扩展您的数据库表/列,如下所示

另请参阅:会话 模型。

验证令牌

当您想支持电子邮件/无密码登录时,Auth.js 使用数据库来存储与用户电子邮件地址绑定的临时验证令牌。

方法和模型

另请参阅:验证令牌 模型。

官方适配器指南

完成以下所有步骤后,您就可以向我们的 存储库 提交 PR。

如果您创建了一个适配器并希望我们将其作为官方包发布,请确保它满足以下要求。查看这个 现有的适配器,以了解包结构、所需文件、测试设置、配置等。

  1. 适配器必须实现 Adapter 接口 的所有方法

  2. 适配器测试必须包含在内,并且必须通过。Docker 比服务更受欢迎,因为它可以使 CI 对网络错误具有弹性,并减少 GitHub Action 密钥的数量(这也可以让我们在分支 PR 中运行这些测试)

  3. 适配器必须遵循以下编码风格

    • 使用 TypeScript 编写
    • 通过单一仓库的代码风格检查规则
    • 不包含 polyfills
    • 配置为 ES 模块 (ESM)
    • 通过 JSDoc 注释进行文档化
    • 从其主模块导出至少一个命名导出。(例如 export function MyAdapter(): Adapter {}
    • 集合/表名应遵循底层 ORM/数据库文档/约定的约定(复数/单数、骆驼式大小写/蛇形大小写)
  4. 配置单一仓库以帮助我们维护包

  5. 适配器必须能够处理来自用户的任何属性

    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
  }
}

资源

Auth.js © Balázs Orbán 和团队 -2024