在 Next.js 14 中使用 NextAuth.js 进行身份验证

90 min read

设置项目

首先,通过以下命令在终端中创建 Next.js 项目:

npx create-next-app@latest

选择以下配置:

  • 项目名称
  • 使用 TypeScript
  • 使用 ESLint
  • 使用 Tailwind CSS
  • 使用 src/ 目录
  • 使用 App Router
  • 自定义默认导入别名 (@/*)

安装 NextAuth.js

在项目设置完成后,通过 npm 安装 NextAuth.js:

npm install next-auth

创建文件夹和文件

app 目录中创建以下文件夹和 route.ts 文件:

api/auth/[...nextauth]/route.ts

app 目录中创建 components 文件夹,并在根目录(与 package.json 文件同级)创建 .env 文件。

目录结构如下:

my-next-app
│
├── app
│   ├── api
│   │   └── auth
│   │       └── [...nextauth]
│   │           └── route.ts
│   └── components
├── .env
├── package.json
...

设置环境变量

.env 文件中添加以下内容:

NEXTAUTH_SECRET=98E3B2CC28F61492C6934531C828C
NEXTAUTH_URL=http://localhost:3000/

NEXTAUTH_SECRET 是用于哈希令牌、签名/加密 Cookie 和生成加密密钥的随机字符串。NEXTAUTH_URL 是用户登录后的重定向路径。

配置 route.ts

app/api/auth/[...nextauth]/route.ts 文件中添加以下代码:

import NextAuth from "next-auth";
import GithubProvider from "next-auth/providers/github";
import GoogleProvider from "next-auth/providers/google";

const handler = NextAuth({
  providers: [
    GithubProvider({
      clientId: process.env.GITHUB_ID as string,
      clientSecret: process.env.GITHUB_SECRET as string,
    }),
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    })
  ]
});

export { handler as GET, handler as POST };

获取 GitHub 和 Google 的 ClientID 和 ClientSecret

GitHub

  1. 登录 GitHub
  2. 点击个人资料图片,选择 Settings
  3. 滚动到 Developer settings
  4. 选择 Oauth Apps
  5. 点击 Register a new application
  6. 填写应用名称、主页 URL 和授权回调 URL,然后点击 Register application
  7. 复制 Client ID 和生成的 Client Secret,并在 .env 文件中添加以下内容:
GITHUB_ID=<your clientID>
GITHUB_SECRET=<your clientSecret>

Google

  1. 前往 Google Cloud Console
  2. 选择项目并获取 Client ID 和 Client Secret,添加到 .env 文件中:
GOOGLE_CLIENT_ID=<your clientID>
GOOGLE_CLIENT_SECRET=<your clientSecret>

创建 SessionWrapper 组件

components 文件夹中创建 SessionWrapper.tsx 文件:

"use client";
import { SessionProvider } from "next-auth/react";
import React from 'react';

const SessionWrapper = ({children}: {children: React.ReactNode}) => {
  return (
    <SessionProvider>{children}</SessionProvider>
  )
}

export default SessionWrapper;

在 layout.tsx 中使用 SessionWrapper

编辑 layout.tsx 文件:

import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
import SessionWrapper from './components/SessionWrapper';

const inter = Inter({ subsets: ['latin'] });

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <SessionWrapper>
      <html lang="en">
        <body className={inter.className}>{children}</body>
      </html>
    </SessionWrapper>
  );
}

在 page.tsx 文件中添加登录/注销功能

编辑 page.tsx 文件:

"use client";
import { useSession, signIn, signOut } from "next-auth/react";
import Image from "next/image";

export default function Home() {
  const { data: session } = useSession();

  if (session) {
    return (
      <div className="w-full h-screen flex flex-col justify-center items-center">
        <div className="w-44 h-44 relative mb-4">
          <Image
            src={session.user?.image as string}
            fill
            alt=""
            className="object-cover rounded-full"
          />
        </div>
        <p className="text-2xl mb-2">Welcome <span className="font-bold">{session.user?.name}</span>. Signed In As</p>
        <p className="font-bold mb-4">{session.user?.email}</p>
        <button className="bg-red-600 py-2 px-6 rounded-md" onClick={() => signOut()}>Sign out</button>
      </div>
    )
  }

  return (
    <div className="w-full h-screen flex flex-col justify-center items-center">
      <p className="text-2xl mb-2">Not Signed In</p>
      <button className="bg-blue-600 py-2 px-6 rounded-md mb-2" onClick={() => signIn('google')}>Sign in with google</button>
      <button className="bg-none border-gray-300 border py-2 px-6 rounded-md mb-2" onClick={() => signIn('github')}>Sign in with github</button>
    </div>
  )
}

配置 next.config.js

编辑根目录下的 next.config.js 文件以允许加载来自远程主机的图像:

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'lh3.googleusercontent.com',
      },
      {
        protocol: 'https',
        hostname: 'avatars.githubusercontent.com',
      },
    ],
  },
};

module.exports = nextConfig;

现在,你已经成功在 Next.js 14 中配置了 NextAuth.js 并实现了身份验证功能。