0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Next.js】NextAuth→Auth.jsへの移行

Last updated at Posted at 2025-05-11

記事の内容

先日作成した記事でNextAuthでの認証ロジックについて書きましたが、
NextAuth(V4)→Auth(V5)への移行を行ったので、
備忘録のために記事を書きます。
(移行が完璧かは不明ですが、とりあえず動いたので記事を作成)

■下記公式ドキュメントを参考にして、移行を実施しました。

■以前書いたNextAuthの記事はこちらです。

Auth.jsとNextAuth.jsの違い

  • NextAuth.js:
    Next.jsアプリケーション専用の認証ライブラリ
  • Auth.js:
    NextAuth.jsの後継で、Next.jsだけでなく
    SvelteKitやSolidStartなど
    他のフレームワークでも使える汎用認証ライブラリ

開発環境

パッケージ バージョン
Next.js 15.3.1
NextAuth(Auth.js) 5.0.0-beta.28
React 19.0.0

環境構築準備

  • パッケージのインストール
    npm install next-auth@beta

実装例

20025/05/18更新

  • provided設定をAuth.js形式に変更
    (ClientIDやSecretKeyをAuth.js指定の定義名に変更し、Auth.tsには環境変数の設定を削除)
  • FacebookにallowDangerousEmailAccountLinkingの設定を追加
    (これをつけないとなぜかVercelでエラーが発生する)

Auth.js設定ファイル

app/lib/auth/auth.ts
import GitHub from "next-auth/providers/github"
import Google from "next-auth/providers/google"
import Facebook from "next-auth/providers/facebook"
import NextAuth from "next-auth"

export const { auth, handlers, signIn, signOut } = NextAuth({
    debug: process.env.NODE_ENV === 'development',
    providers: [
        GitHub,
        Google,
        Facebook({ allowDangerousEmailAccountLinking: true })
    ],
    callbacks: {
        authorized({ auth, request: { nextUrl } }) {
            // プロフィールページは認証済みユーザーのみアクセス可
            const isLogIn = !!auth?.user
            const isProfilePage = nextUrl.pathname.startsWith('/profile')
            if (isProfilePage) {
                return isLogIn
            }
            // 他のページは誰でもアクセス可
            return true
        },
        // ログイン時に作られる認証トークンにユーザーIDを追加
        jwt({ token, user }) {
            if (user) {
                token.id = user.id
            }
            return token
        },
        // ブラウザのセッションにユーザーIDを追加
        session: ({ session, token }) => {
            if (session.user && token.id) {
                session.user.id = token.id as string
            }
            return session
        },
        // ログイン・ログアウト後、HOME画面へリダイレクト
        redirect({ baseUrl }) {
            return baseUrl
        }
    },
    // 認証エラー用画面へ遷移
    pages: {
        error: '/auth/error'
    },
    // ユーザーのログイン状態を「JWT(JSON Web Token)」形式に指定
    session: {
        strategy: 'jwt'
    }
})

認証APIルートの設定

app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/app/lib/auth/auth'

export const { GET, POST } = handlers

認証プロバイダーコンポーネント

app/lib/auth/provider.tsx
"use client"

import { SessionProvider } from "next-auth/react"
import { FC, PropsWithChildren } from "react"

export const AuthProvider: FC<PropsWithChildren> = ({ children }) => {
    return <SessionProvider>{children}</SessionProvider>
}

レイアウトファイル

app/layout.tsx
import type { Metadata } from "next"
import { Noto_Sans_JP } from "next/font/google"
import "./globals.css"
import Header from "./components/Header";
import { Suspense } from "react";
import LoadingSpinner from "./components/Loading";
import { AuthProvider } from "./lib/auth/provider";

const notoSansJP = Noto_Sans_JP({
  variable: "--font-noto-sans-jp",
  subsets: ["latin"],
  // 日本語フォントを使用するためにweight設定を追加
  weight: ["400", "500", "700"],
  // 必要に応じてpreloadを追加(日本語の場合は多くの文字があるため)
  preload: false
})

export const metadata: Metadata = {
  title: "Book Commerce",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="ja">
      <body
        className={`${notoSansJP.variable} antialiased`}
      >
          <AuthProvider>
            <Header />
            <div className="pt-20">
              <Suspense fallback={<LoadingSpinner />}>
                {children}
              </Suspense>
            </div>
          </AuthProvider>
      </body>
    </html>
  );
}

ログイン・ログアウトコンポーネント(例はヘッダー部コンポーネント)

app/components/Header.tsx
import Image from "next/image"
import Link from "next/link"
import { auth } from "../lib/auth/auth"

const Header = async () => {

    const session = await auth()
    const user = session?.user

    return (
        <header className="bg-slate-700 text-gray-100 shadow-lg fixed top-0 left-0 right-0 z-30">
            <nav className="flex items-center justify-between p-4">
                <div className="flex items-center gap-2">
                    {user ? (
                        <Link
                            href={"/api/auth/signout"}  // Auth.jsが用意したログアウトの標準API
                            className="text-gray-300 hover:text-white px-3 py-2 rounded-md text-sm font-medium"
                        >
                            ログアウト
                        </Link>
                    ) : (
                        <Link
                            // href={"/login"}
                            href={"/api/auth/signin"}   // Auth.jsが用意したログインの標準API
                            className="text-gray-300 hover:text-white px-3 py-2 rounded-md text-sm font-medium"
                        >
                            ログイン
                        </Link>
                    )}
                </div>
            </nav>
        </header>
    )
}

export default Header

環境変数

.env
AUTH_GITHUB_ID=
AUTH_GITHUB_SECRET=

AUTH_GOOGLE_ID=
AUTH_GOOGLE_SECRET=

AUTH_FACEBOOK_ID=
AUTH_FACEBOOK_SECRET=

まとめ

移行自体は割と簡単に進められました。
coreパッケージは未インストールのため、
純粋はAuth.jsの実装方法と異なる箇所がありますが、
とりあえず、V5での移行を進めてログイン・ログアウト認証は
正常動作したので、それまでの作業を残しておきます。

本記事が誰かの参考になれば、幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?