Skip to content

Hono Auth.js 集成

¥Hono Auth.js Integration

本指南演示如何使用 Auth.js(以前称为 NextAuth.js)为你的 Hono 应用添加身份验证。

¥This guide shows you how to add authentication to your Hono applications using Auth.js (formerly NextAuth.js).

[!重要] @hono/auth-js 包目前仅支持 React 进行客户端集成。

¥[!IMPORTANT] The @hono/auth-js package currently only supports React for client-side integration.

快速入门

¥Quick Start

5 分钟内即可完成身份验证:

¥Get authentication running in 5 minutes:

  1. 安装 → npm install @hono/auth-js @auth/core

    ¥Installnpm install @hono/auth-js @auth/core

  2. 设置环境变量 → 复制下面的 .env 示例

    ¥Set environment variables → Copy the .env example below

  3. 创建数据库表 → 运行模式迁移

    ¥Create database tables → Run the schema migration

  4. 添加身份验证路由 → 复制 Hono 的设置

    ¥Add auth routes → Copy the Hono setup

  5. 测试 → 使用客户端示例

    ¥Test it → Use the client example

安装

¥Installation

bash
npm install hono @hono/auth-js @auth/core

设置

¥Setup

步骤 1:环境变量

¥Step 1: Environment Variables

在项目根目录中创建 .env 文件:

¥Create a .env file in your project root:

properties
AUTH_SECRET=your-auth-secret-here
GITHUB_ID=your-github-client-id
GITHUB_SECRET=your-github-client-secret
GOOGLE_ID=your-google-client-id
GOOGLE_SECRET=your-google-client-secret

使用以下命令生成一个强大的 `AUTH_SECRET` 文件:`openssl rand -base64 32` 或使用:`npx auth secret`

¥[!TIP] Generate a strong AUTH_SECRET with: openssl rand -base64 32 or use: npx auth secret

步骤 2:数据库设置

¥Step 2: Database Setup

从 [Auth.js Drizzle 适配器文档](https://authjs.dev/getting-started/adapters/drizzle) 中复制最新的 schema。

¥[!NOTE] Copy the latest schema from the Auth.js Drizzle adapter docs.

以下是使用 Drizzle ORM 的 SQLite 数据库模式:

¥Here's a schema for SQLite with Drizzle ORM:

ts
import { createClient } from '@libsql/client'
import { drizzle } from 'drizzle-orm/libsql'

const client = createClient({
  url: 'DATABASE_URL',
  authToken: 'DATABASE_AUTH_TOKEN',
})
export const db = drizzle(client)
ts
import {
  integer,
  sqliteTable,
  text,
  primaryKey,
} from 'drizzle-orm/sqlite-core'
import type { AdapterAccountType } from 'next-auth/adapters'

export const users = sqliteTable('user', {
  id: text('id')
    .primaryKey()
    .$defaultFn(() => crypto.randomUUID()),
  name: text('name'),
  email: text('email').unique(),
  emailVerified: integer('emailVerified', { mode: 'timestamp_ms' }),
  image: text('image'),
})

export const accounts = sqliteTable(
  'account',
  {
    userId: text('userId')
      .notNull()
      .references(() => users.id, { onDelete: 'cascade' }),
    type: text('type').$type<AdapterAccountType>().notNull(),
    provider: text('provider').notNull(),
    providerAccountId: text('providerAccountId').notNull(),
    refresh_token: text('refresh_token'),
    access_token: text('access_token'),
    expires_at: integer('expires_at'),
    token_type: text('token_type'),
    scope: text('scope'),
    id_token: text('id_token'),
    session_state: text('session_state'),
  },
  (account) => ({
    compoundKey: primaryKey({
      columns: [account.provider, account.providerAccountId],
    }),
  })
)

export const sessions = sqliteTable('session', {
  sessionToken: text('sessionToken').primaryKey(),
  userId: text('userId')
    .notNull()
    .references(() => users.id, { onDelete: 'cascade' }),
  expires: integer('expires', { mode: 'timestamp_ms' }).notNull(),
})

export const verificationTokens = sqliteTable(
  'verificationToken',
  {
    identifier: text('identifier').notNull(),
    token: text('token').notNull(),
    expires: integer('expires', { mode: 'timestamp_ms' }).notNull(),
  },
  (verificationToken) => ({
    compositePk: primaryKey({
      columns: [
        verificationToken.identifier,
        verificationToken.token,
      ],
    }),
  })
)

export const authenticators = sqliteTable(
  'authenticator',
  {
    credentialID: text('credentialID').notNull().unique(),
    userId: text('userId')
      .notNull()
      .references(() => users.id, { onDelete: 'cascade' }),
    providerAccountId: text('providerAccountId').notNull(),
    credentialPublicKey: text('credentialPublicKey').notNull(),
    counter: integer('counter').notNull(),
    credentialDeviceType: text('credentialDeviceType').notNull(),
    credentialBackedUp: integer('credentialBackedUp', {
      mode: 'boolean',
    }).notNull(),
    transports: text('transports'),
  },
  (authenticator) => ({
    compositePK: primaryKey({
      columns: [authenticator.userId, authenticator.credentialID],
    }),
  })
)

基本用法

¥Basic Usage

路由设置

¥Route Setup

在 Hono 应用中创建 API 路由:

¥Create an API route in your Hono application:

ts
import { Hono } from 'hono'
import {
  initAuthConfig,
  verifyAuth,
  authHandler,
  DrizzleAdapter,
} from '@hono/auth-js'
import { GitHub, Google } from '@auth/core/providers'
import { db } from './db'
import {
  users,
  accounts,
  authenticators,
  sessions,
  verificationTokens,
} from './schema'

const v1Router = new Hono()
  .use(
    '*',
    initAuthConfig((c) => ({
      adapter: DrizzleAdapter(c.get('db'), {
        usersTable: users,
        accountsTable: accounts,
        authenticatorsTable: authenticators,
        sessionsTable: sessions,
        verificationTokensTable: verificationTokens,
      }),
      secret: c.env.AUTH_SECRET,
      providers: [
        GitHub({
          clientId: c.env.GITHUB_ID,
          clientSecret: c.env.GITHUB_SECRET,
        }),
        Google({
          clientId: c.env.GOOGLE_ID,
          clientSecret: c.env.GOOGLE_SECRET,
        }),
      ],
      session: { strategy: 'jwt' },
    }))
  )
  .use('*', verifyAuth())
  .use('/auth/*', authHandler())

const app = new Hono().route('/api/v1', v1Router)
export default app

使用示例

¥Usage Examples

保护路由

¥Protecting Routes

ts
app.get('/protected', (c) => {
  const auth = c.get('authUser')
  if (!auth) return c.json({ error: 'Unauthorized' }, 401)
  return c.json(auth)
})

客户端集成 (React)

¥Client-side Integration (React)

tsx
import {
  SessionProvider,
  useSession,
  signIn,
} from '@hono/auth-js/react'

function App() {
  const { data: session } = useSession()
  return session ? (
    <p>Hello {session.user?.name}</p>
  ) : (
    <button onClick={() => signIn('github')}>
      Sign in with GitHub
    </button>
  )
}

export default function Root() {
  return (
    <SessionProvider>
      <App />
    </SessionProvider>
  )
}

配置参考

¥Configuration Reference

在 Hono 应用中自定义 Auth.js:

¥Customize Auth.js in your Hono app with:

  • 适配器 → 连接到你的数据库(如上所示的 Drizzle)

    ¥Adapter → Connects to your database (Drizzle shown above)

  • 提供程序 → GitHub、Google 或任何 Auth.js 提供程序

    ¥Providers → GitHub, Google, or any Auth.js provider

  • 会话 → "jwt"(无状态)或 "database"(持久)

    ¥Session"jwt" (stateless) or "database" (persistent)

  • 回调 → 钩子到登录或会话事件

    ¥Callbacks → Hook into sign-in or session events

示例:

¥Example:

ts
initAuthConfig((c) => ({
  adapter: DrizzleAdapter(c.get('db'), {
    /* tables */
  }),
  secret: c.env.AUTH_SECRET,
  providers: [
    GitHub({
      clientId: c.env.GITHUB_ID,
      clientSecret: c.env.GITHUB_SECRET,
    }),
    Google({
      clientId: c.env.GOOGLE_ID,
      clientSecret: c.env.GOOGLE_SECRET,
    }),
  ],
  session: { strategy: 'jwt' },
  callbacks: {
    async session({ session }) {
      return session
    },
  },
}))

了解更多

¥Learn More

  • Auth.js 文档 - 提供程序、模式参考

    ¥Auth.js Docs – providers, schema reference

  • Hono 文档 - 路由和中间件模式

    ¥Hono Docs – routing & middleware patterns

  • 使用指南:基于角色的访问控制、密码重置、邮箱验证

    ¥Recipes: role-based access, password reset, email verification

Hono v4.11 中文网 - 粤ICP备13048890号