Next.js 集成

将 Better Auth 与 Next.js 集成。

Better Auth 可以轻松集成到 Next.js 中。在开始之前,确保你已经配置好了 Better Auth 实例。如果还没有,可以查看安装指南

创建 API 路由

我们需要将处理程序挂载到 API 路由中。在 /api/auth/[...all] 目录中创建一个路由文件,并添加以下代码:

api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";

export const { GET, POST } = toNextJsHandler(auth);

你可以在 Better Auth 的配置中更改路径,但建议保持为 /api/auth/[...all]

对于 pages 路由,你需要使用 toNodeHandler 代替 toNextJsHandler,并在 config 对象中将 bodyParser 设置为 false。示例如下:

pages/api/auth/[...all].ts
import { toNodeHandler } from "better-auth/node"
import { auth } from "@/lib/auth"

// 禁用 body 解析,我们将手动解析
export const config = { api: { bodyParser: false } }

export default toNodeHandler(auth.handler)

创建客户端

创建一个客户端实例。文件名可以自定义,这里我们在 lib/ 目录下创建 auth-client.ts 文件。

auth-client.ts
import { createAuthClient } from "better-auth/react" // 确保从 better-auth/react 导入

export const authClient =  createAuthClient({
    // 你可以在这里传入客户端配置
})

创建客户端后,你可以用它完成注册、登录及其他操作。
部分操作是响应式的。客户端使用 nano-store 来存储状态,并在状态变化时重新渲染组件。

客户端还使用 better-fetch 来发起请求,你可以将 fetch 配置传递给客户端。

RSC 和服务器 Actions

从 auth 实例导出的 api 对象包含了所有可在服务器上执行的操作。Better Auth 内的每个端点都可作为函数调用,包括插件端点。

示例:在服务器 Action 中获取 Session

server.ts
import { auth } from "@/lib/auth"
import { headers } from "next/headers"

const someAuthenticatedAction = async () => {
    "use server";
    const session = await auth.api.getSession({
        headers: await headers()
    })
};

示例:在 RSC 中获取 Session

import { auth } from "@/lib/auth"
import { headers } from "next/headers"

export async function ServerComponent() {
    const session = await auth.api.getSession({
        headers: await headers()
    })
    if(!session) {
        return <div>未认证</div>
    }
    return (
        <div>
            <h1>欢迎 {session.user.name}</h1>
        </div>
    )
}
由于 RSC 无法设置 Cookie,Cookie 缓存 不会被刷新,除非通过服务器 Actions 或路由处理器从客户端与服务器交互。

当你在服务器 Action 调用需要设置 Cookie 的函数,如 signInEmailsignUpEmail,Cookie 不会被自动设置。因为服务器 Actions 需要使用 Next.js 的 cookies 助手来设置 Cookie。

为简化操作,你可以使用 nextCookies 插件,它会在响应头中包含 Set-Cookie 时自动设置 Cookie。

auth.ts
import { betterAuth } from "better-auth";
import { nextCookies } from "better-auth/next-js";

export const auth = betterAuth({
    // ...你的配置
    plugins: [nextCookies()] // 确保这是数组中的最后一个插件
})

现在,当你调用设置 Cookie 的函数时,Cookie 会被自动设置。

"use server";
import { auth } from "@/lib/auth"

const signIn = async () => {
    await auth.api.signInEmail({
        body: {
            email: "user@email.com",
            password: "password",
        }
    })
}

认证保护

在 Next.js 的代理(proxy)/中间件中,建议仅检查会话 Cookie 是否存在来处理重定向,以避免通过 API 或数据库调用阻塞请求。

Next.js 16+(代理)

Next.js 16 使用“代理”替代“中间件”。你可以使用 Node.js 运行时来进行完整的带数据库验证的会话校验:

proxy.ts
import { NextRequest, NextResponse } from "next/server";
import { headers } from "next/headers";
import { auth } from "@/lib/auth";

export async function proxy(request: NextRequest) {
    const session = await auth.api.getSession({
        headers: await headers()
    })

    // 这并不安全!
    // 这是推荐用于乐观重定向用户的做法
    // 建议在每个页面/路由中处理认证校验
    if(!session) {
        return NextResponse.redirect(new URL("/sign-in", request.url));
    }

    return NextResponse.next();
}

export const config = {
  matcher: ["/dashboard"], // 指定中间件适用的路由
};

可使用仅基于 Cookie 的校验(速度更快但安全性较低),调用 getSessionCookie

proxy.ts
import { NextRequest, NextResponse } from "next/server";
import { getSessionCookie } from "better-auth/cookies";

export async function proxy(request: NextRequest) {
	const sessionCookie = getSessionCookie(request);

    // 这并不安全!
    // 这是推荐用于乐观重定向用户的做法
    // 建议在每个页面/路由中处理认证校验
	if (!sessionCookie) {
		return NextResponse.redirect(new URL("/", request.url));
	}

	return NextResponse.next();
}

export const config = {
	matcher: ["/dashboard"], // 指定中间件适用的路由
};

**从中间件迁移:**将 middleware.ts 重命名为 proxy.ts,函数名 middleware 改为 proxy。所有 Better Auth 方法保持不变。

Next.js 15.2.0+(Node.js 运行时中间件)

从 Next.js 15.2.0 开始,你可以在中间件中使用 Node.js 运行时来进行完整的带数据库验证的会话校验:

middleware.ts
import { NextRequest, NextResponse } from "next/server";
import { headers } from "next/headers";
import { auth } from "@/lib/auth";

export async function middleware(request: NextRequest) {
    const session = await auth.api.getSession({
        headers: await headers()
    })

    // 这并不安全!
    // 这是推荐用于乐观重定向用户的做法
    // 建议在每个页面/路由中处理认证校验
    if(!session) {
        return NextResponse.redirect(new URL("/sign-in", request.url));
    }

    return NextResponse.next();
}

export const config = {
  runtime: "nodejs", // 调用 auth.api 需要此配置
  matcher: ["/dashboard"], // 指定中间件适用的路由
};

Node.js 运行时中间件在 Next.js 16 之前版本仍属实验性质。建议升级到 Next.js 16+ 以获得稳定的代理支持。

Next.js 13-15.1.x(Edge 运行时中间件)

旧版本 Next.js 的中间件运行在 Edge 运行时,无法执行数据库调用。使用基于 Cookie 的校验实现乐观重定向:

getSessionCookie() 函数不会自动引用 auth.ts 中指定的 auth 配置。因此,如果你自定义了 Cookie 名称或前缀,需要确保 getSessionCookie() 的配置与 auth.ts 中定义的配置一致。

Next.js 版本 15.1.7 及以下

如果你需要完整的 session 对象,必须从 /api/auth/get-session API 路由获取。由于 Next.js 中间件不能直接执行 Node.js API,你必须发起 HTTP 请求。

示例如使用 better-fetch,你也可以使用任何其他 fetch 库。

middleware.ts
import { betterFetch } from "@better-fetch/fetch";
import type { auth } from "@/lib/auth";
import { NextRequest, NextResponse } from "next/server";

type Session = typeof auth.$Infer.Session;

export async function middleware(request: NextRequest) {
	const { data: session } = await betterFetch<Session>("/api/auth/get-session", {
		baseURL: request.nextUrl.origin,
		headers: {
			cookie: request.headers.get("cookie") || "", // 转发请求中的 Cookie
		},
	});

	if (!session) {
		return NextResponse.redirect(new URL("/sign-in", request.url));
	}

	return NextResponse.next();
}

export const config = {
	matcher: ["/dashboard"], // 仅对特定路由应用中间件
};

Next.js 版本 15.2.0 及以后

从 Next.js 15.2.0 起,你可在中间件中使用 Node.js 运行时,进行完整的带数据库验证的会话校验:

请参考 Next.js 文档 了解运行时配置及开启方法。
注意该新运行时为实验特性,可能存在破坏性变更。

middleware.ts
import { NextRequest, NextResponse } from "next/server";
import { headers } from "next/headers";
import { auth } from "@/lib/auth";

export async function middleware(request: NextRequest) {
    const session = await auth.api.getSession({
        headers: await headers()
    })

    if(!session) {
        return NextResponse.redirect(new URL("/sign-in", request.url));
    }

    return NextResponse.next();
}

export const config = {
  runtime: "nodejs",
  matcher: ["/dashboard"], // 仅对特定路由应用中间件
};
middleware.ts
import { NextRequest, NextResponse } from "next/server";
import { getSessionCookie } from "better-auth/cookies";

export async function middleware(request: NextRequest) {
	const sessionCookie = getSessionCookie(request);

    // 这并不安全!
    // 这是推荐用于乐观重定向用户的做法
    // 建议在每个页面/路由中处理认证校验
	if (!sessionCookie) {
		return NextResponse.redirect(new URL("/", request.url));
	}

	return NextResponse.next();
}

export const config = {
	matcher: ["/dashboard"], // 指定中间件适用的路由
};

安全警告:getSessionCookie 只检测会话 Cookie 是否存在;并不验证其有效性。仅依赖此检测进行安全保护非常危险,因为任何人都可以手动制造 Cookie 绕过。你必须在服务器端对会话进行验证,才能保护受限制的操作或页面。

如果你使用自定义的 Cookie 名称或前缀,可以通过参数传递给 getSessionCookie

const sessionCookie = getSessionCookie(request, {
    cookieName: "my_session_cookie",
    cookiePrefix: "my_prefix"
});

你也可以使用 getCookieCache 助手从 Cookie 缓存获取 session 对象。

middleware.ts
import { getCookieCache } from "better-auth/cookies";

export async function middleware(request: NextRequest) {
	const session = await getCookieCache(request);
	if (!session) {
		return NextResponse.redirect(new URL("/sign-in", request.url));
	}
	return NextResponse.next();
}

如何在每个页面/路由中处理认证校验

此示例中,我们在服务器组件内使用 auth.api.getSession 获取 session 对象,然后验证其有效性。如果无效,跳转至登录页面。

app/dashboard/page.tsx
import { auth } from "@/lib/auth";
import { headers } from "next/headers";
import { redirect } from "next/navigation";

export default async function DashboardPage() {
    const session = await auth.api.getSession({
        headers: await headers()
    })

    if(!session) {
        redirect("/sign-in")
    }

    return <h1>欢迎 {session.user.name}</h1>
}

Next.js 16 兼容性

Better Auth 完全兼容 Next.js 16。主要变化是“middleware”改为“proxy”。详见上文认证保护章节中的 Next.js 16+ 代理示例。

迁移指南

可使用 Next.js 提供的 codemod 自动迁移:

npx @next/codemod@canary middleware-to-proxy .

或手动迁移:

  • 重命名 middleware.tsproxy.ts
  • 函数名由 middleware 改为 proxy

所有 Better Auth 方法保持不变。详情参见 Next.js 迁移指南