Next.js 使用 Next Auth 管理登录权限

Next Auth 最新版已集成到 Auth.js ,可以很方便地集成账号登录、第三方登录等 Web 身份验证功能。

框架网站: https://authjs.dev/

安装

https://authjs.dev/getting-started/installation?framework=next.js

安装 beta 版本:

npm install next-auth@beta

安装教程配置:

  • 初始化 AUTH_SECRET
  • 创建 auth.ts 文件并导出 handlers, signIn, signOut, auth
  • 创建 app/api/auth/[...nextauth]/route.ts 文件并从 handlers 导出 { GET, POST }
  • 创建 middleware.ts 并将 auth 重命名为 middleware 导出

Next 中间件与 matcher

https://nextjs.org/docs/app/building-your-application/routing/middleware

中间件在缓存内容和路由匹配之前运行。

可配置 matcher ,利用正则排除不需要权限限制的路径:

https://authjs.dev/reference/nextjs#in-middleware

export const config = {
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
}

凭据登录

https://authjs.dev/getting-started/providers/credentials

Next Auth 集成了大量的登录方式,包括最原始的账号密码凭据登录方式。

import NextAuth from "next-auth"
import Credentials from "next-auth/providers/credentials"

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    Credentials({
      credentials: {
        username: { label: "Username" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials) {
        if (credentials.username === 'admin' && credentials.password === 'admin') {
          const user = {
            name: credentials.username,
          }
          return user
        }
        return null
      },
    }),
  ],
})

身份验证

https://authjs.dev/reference/nextjs#callbacks

  • 身份验证由 callbacks.authorized 回调完成,默认只会预定义的 User 接口,仅包含 id name email image 字段
  • 可通过 callbacks.jwt 和 callbacks.session 将信息存到 session ,使 callbacks.authorized 中可获取
callbacks: {
  async authorized({ request, auth }) {
    const url = request.nextUrl
    const user = auth?.user;
    
    if (url.pathname.startsWith('/admin') && !user) {
      return false
    }

    return true
  }
}

自定义用户对象

providers.Credentials.authorize 想返回一个自定义的用户格式,如:

const user = {
  id: "1",
  username: credentials.username,
  role: 'admin'
}

拓展 next-auth 的 Session、User、JWT 类型使得 ts 可正常运行:

https://stackoverflow.com/questions/74425533

import { Session } from "next-auth";
import { JWT } from "next-auth/jwt";

declare module "next-auth" {
    interface Session {
        id: string;
        username: string;
        role: string;
    }

    interface User {
        id: string;
        username: string;
        role: string;
    }
}

declare module "next-auth/jwt" {
    interface JWT {
        id: string;
        username: string;
        role: string;
    }
}

如需补充额外字段,可参照基于角色的访问控制教程,如将角色写进来:

https://authjs.dev/guides/role-based-access-control


callbacks: {
  jwt({ token, user }) {
    if (user && user.id) {
      token.id = user.id
      token.username = user.username
      token.role = user.role
    }
    return token
  },
  session({ session, token }) {
    session.user.id = token.id
    session.user.username = token.username
    session.user.role = token.role
    return session
  },
  // ... authorized
}

自定义登录页面

next-auth 配置增加:

pages: {
  signIn: "/login",
},

这样就会默认走到 /login 路由,在 app 目录创建路由编写页面即可。

确认登录时,调用 @/auth.ts 中导出的 signIn 方法。

登出

调用 @/auth.ts 中导出的 signOut 方法即可。

本文收录于专栏
使用 Next.js 搭建 SSR 全栈 demo ,以及构建 SSG 纯静态博客,记录学习和使用笔记