記事の内容
先日作成した記事で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での移行を進めてログイン・ログアウト認証は
正常動作したので、それまでの作業を残しておきます。
本記事が誰かの参考になれば、幸いです。