背景

因为升级到了Next-auth:5.0,鉴权结构变了,middleware的代码如下:

  1. import { auth as middleware } from "@/lib/auth";
  2. export const config = {
  3. matcher: `/admin/:path*`,
  4. // 不是管理员就跳转到主页
  5. };
  6. export default middleware;

其中auth文件如下:

  1. import NextAuth from "next-auth";
  2. import Credentials from "next-auth/providers/credentials";
  3. import GithubProvider from "next-auth/providers/github";
  4. import { PrismaAdapter } from "@auth/prisma-adapter";
  5. import { NODE_ENV } from "@/config";
  6. import { PATHS } from "@/constants";
  7. import { getUserByEmailAndPassword } from "@/features/auth";
  8. import { prisma } from "./prisma";
  9. import { isAdmin, myLog } from "./utils";
  10. export const runtime = "nodejs";
  11. export const { handlers, auth, signOut, signIn } = NextAuth({
  12. adapter: PrismaAdapter(prisma),
  13. providers: [
  14. GithubProvider,
  15. Credentials({
  16. // You can specify which fields should be submitted, by adding keys to the `credentials` object.
  17. // e.g. domain, username, password, 2FA token, etc.
  18. credentials: {
  19. email: { label: "email", type: "email" },
  20. password: { label: "password", type: "password" },
  21. },
  22. authorize: async (credentials) => {
  23. try {
  24. // 避免在这里直接打印 credentials
  25. const { email, password } = credentials as {
  26. email: string;
  27. password: string;
  28. };
  29. // myLog("credentials", JSON.stringify(credentials));
  30. const user = await getUserByEmailAndPassword(email, password);
  31. // myLog("user", JSON.stringify(user));
  32. return {
  33. id: user.id,
  34. email: user.email,
  35. name: user.name,
  36. };
  37. } catch (error) {
  38. return null;
  39. }
  40. },
  41. }),
  42. ],
  43. // 解决这个错误:Error: PrismaClient is not configured to run in Vercel Edge Functions or Edge Middleware.
  44. // 参考:https://github.com/prisma/prisma/issues/21310#issuecomment-1840428931
  45. session: { strategy: "jwt" },
  46. trustHost: true,
  47. pages: {
  48. signIn: PATHS.AUTH_SIGN_IN,
  49. },
  50. debug: NODE_ENV === "development",
  51. callbacks: {
  52. // signin
  53. async signIn({ user }) {
  54. // myLog("signIn", user);
  55. // 模拟promise
  56. return new Promise((resolve) => resolve(true));
  57. },
  58. session({ session, token }) {
  59. myLog("session", session, token);
  60. if (session.user && token?.sub) {
  61. session.user.id = token.sub;
  62. }
  63. return session;
  64. },
  65. authorized({ request, auth }) {
  66. myLog("authorized", request, auth);
  67. // 将来用作 Next.js middleware,如果是访问后台页面,校验是否登录
  68. if (request.nextUrl.pathname.startsWith(PATHS.ADMIN_HOME)) {
  69. // 是否是管理员
  70. if (!isAdmin(auth?.user?.email)) {
  71. return Response.redirect(new URL(PATHS.SITE_HOME, request.url));
  72. } else {
  73. return true;
  74. }
  75. }
  76. // 其它路径直接放行
  77. return true;
  78. },
  79. },
  80. });

然后因为getUserByEmailAndPassword这个方法里有用到redis,就导致报错了····

这让我很是疑惑啊,middleware 是一个中间件,只能用于EdgeRuntime,但是EdgeRuntime不能够使用io-redis,怎么办呢?

解决方式

绕过middleware中间件

https://medium.com/@chxiuy/mongodb-in-nextjs-overcoming-the-edge-runtime-middleware-hurdle-4beee31eaa30

新建文件

参考:https://stackoverflow.com/questions/78713102/using-redis-with-auth-js-and-next-js-middleware-on-edge-runtime-dns-module-er
我们可以在 Next.js 的 Edge 运行时使用数据库函数吗?当然可以,但也不能直接使用。我找到了一个解决方案,一种方法是创建一个 API 路由处理程序,并在 Edge 运行时使用 fetch 来实现。
在 auth.js 中,您可以创建一个独立的 auth.config.ts 文件(该文件不会执行任何边缘运行时不支持的数据库操作) 官方解释:

https://authjs.dev/guides/edge-compatibility#the-solution

关于在middleware里拿不到数据:

参考issue:https://github.com/nextauthjs/next-auth/issues/9836

Nextjs的middleware提示node_modules\.pnpm\redis-errors@1.2.0\node_modules\redis-errors\index.js (3:1) - 图1