※当サイトはアフィリエイト広告を利用しています。商品リンクにはプロモーションを含む場合があります。
Instagramフォロワー数9,500人を越える人気のもち・大福店 えにかいたもち
困ってた自分に届けたい話
初めてログイン機能を作ったとき、「ログイン状態をどうやって保持すればいいの?」「セッションって何?」と疑問だらけ。
ググって出てきたコードをコピペしてみても、うまく動かず。
認証の仕組みが見えづらくて、「これで本当に安全なのかな?」と不安になることも…。
そんなときに出会ったのが NextAuth.js
。

この記事は、同じように困っていた方への備忘録兼シェアとして書いています。
Next.js をもっと詳しく学びたい人へおすすめの本
▶ 動かしながら基礎から学びたい人に
「まずは手を動かしたい!」という人にぴったりの一冊。基本構造から丁寧に解説されており、Next.jsとReactの連携も自然に身につきます。初学者でもステップを追いやすく、サンプルアプリの構築を通して理解を深められる構成です。
![]() | 新品価格 |

▶ Reactの経験があり、次のステップへ進みたい人に
「Reactは少し触ったことがあるけど、Next.jsを仕事で使いこなしたい」という人向け。実践を意識した構成で、Webアプリの構造、レンダリング戦略、SEO対応など、現場で必要なノウハウが詰まっています。
![]() | React Next.js 実践プログラミング入門: React Next.js モダンWeb開発実践ガイド 新品価格 |

▶ App Routerを本格的に学びたいエンジニアへ
App Routerを軸に、最新のNext.js開発に対応した本格派。中級者以上の方が「一歩先へ進みたい」と感じたときに読みたい一冊。デザインパターンや設計の視点もあり、長期的に役立つ内容です。
![]() | 実践Next.js —— App Routerで進化するWebアプリ開発 エンジニア選書 新品価格 |

全体構成

認証設定(lib/auth.ts)
Next.js でログイン機能を作るときに必要な「設定ファイル」です。
NextAuth.js というライブラリが、このファイルを見てどう動くか決めます。
NextAuthの設定内容
ログイン方式の設定(providers)
「どんな方法でログインできるようにするか」を決める場所です。
例:
- ユーザー名とパスワードでログイン : CredentialsProvider を使う
- Google アカウントでログイン : GoogleProvider
- GitHub アカウントでログイン : GitHubProvider を使う
→ 今回は「IDとパスワードでログインしたい」ので CredentialsProvider
を使います。
セッション管理(session)
「ログイン状態(セッション)をどうやって覚えておくか?」を設定します。
例:
- jwt:サーバーに保存せず、ログイン情報をクッキーに入れて管理(軽い)
- database:ログイン情報をデータベースに保存(管理がしっかり)
→ 今回は簡単な構成にしたいので、jwt
を使っています。
コールバック関数(callbacks)
「ログインしたあと、追加で何かしたいとき」に使います。
例:
- ログインしたユーザーの「名前」や「権限(adminなど)」をセッションに保存する
- トークンに extra 情報を加える
→ 今回は、「管理者かどうか(role)」をセッションに入れています。
lib 配下にファイルを配置する理由
lib
は「ロジック(処理)や設定などの共通ユーティリティを置く場所」という意味で使われます。auth.ts
はアプリ全体で使う「認証の設定」 をまとめたものなので、lib
に置くのが自然です。
🗂 配置イメージ
ディレクトリ | 役割 |
---|---|
lib/ 配下 | アプリ全体で使い回すロジックや設定の置き場 |
app/api/ 配下 もしくは pages/api/ 配下 | APIルート(APIエンドポイント) |
components/ 配下 | UIの部品化されたパーツの置き場 |
実装内容
⚠️ 実装時の注意点
- ログインID・パスワードは直書き厳禁。今回は.envに直書きしてしまっています。
(次回、Azure SQL Databaseにパスワード等を格納する記事出します!) role
など独自の情報をセッションやトークンに保持したい場合は、callbacks
を使って明示的に追加します。
import CredentialsProvider from "next-auth/providers/credentials";
import type { Session } from "next-auth";
import type { JWT } from "next-auth/jwt";
interface Token extends JWT {
role?: string;
}
export const authOptions = {
// CredentialsProvider の設定(IDとパスワードでログインさせるための仕組み)
providers: [
CredentialsProvider({
// ログイン画面のフォームの中身
credentials: {
username: { label: "ユーザーID", type: "text" },
password: { label: "パスワード", type: "password" },
},
// 入力されたIDとパスワードが正しいかチェックする関数
async authorize(credentials) {
if (
credentials?.username === process.env.["任意の環境変数名"] &&
credentials?.password === process.env.["任意の環境変数名"]
) {
return { id: "1", name: "管理者", role: "admin" };
}
return null;
},
}),
],
// 独自に作成するページのパス
pages: {
signIn: "/auth/signin",
},
// セッションの設定
session: {
strategy: "jwt" as const,
maxAge: 100,
},
// ログイン後、ユーザー情報(user)やトークン(token)に追加情報(ここでは role)を保持・共有
callbacks: {
// useSession() で取り出すとき、session.user にも role を渡す
async session({ session, token }: { session: Session; token: Token }) {
session.user = {
...session.user,
role: token.role ?? null,
} as typeof session.user & { role?: string | null };
return session;
},
// ログイン後、トークンに role を保存
async jwt({ token, user }: { token: Token; user?: unknown }) {
if (user && typeof user === "object" && "role" in user) {
token.role = (user as { role?: string }).role;
}
return token;
},
},
};
認証エンドポイント(app/api/auth/[...nextauth]/route.ts)
APIルートの設定です。NextAuth の設定を使ってログイン処理などを実行できるようにします。
⚠️ 実装時の注意点
route.ts
では NextAuth をラップし、GET/POST 両方のメソッドをエクスポートする必要があります。authOptions
を正しくlib/auth.ts
からインポートできていることを確認してください。
import NextAuth from "next-auth/next";
import { authOptions } from "@/lib/auth";
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
アプリ全体で認証を使う(components/Providers.tsx)(app/layout.tsx)
セッション(ログイン状態)をアプリ全体で共有できるようにするためのコンポーネントです。
アプリのレイアウト全体を Providers
で囲み、どのページでもログイン情報を使えるようにします。
SessionProviderとは
SessionProvider
は、ログイン状態(セッション)をアプリ全体で使えるようにするための部品です。
囲んでいないと、useSession()
(ログイン状態を取得する関数)getSession()
(セッション情報を取得する関数)
などが正しく動作しません。
SessionProvider を直接 layout.tsxに記載しない理由
Next.js App Router では layout.tsx
はサーバーコンポーネントであることが前提 です。なので、以下のような理由で SessionProvider
を直接 layout.tsx
に書けないことがあります。
❗問題の背景
SessionProvider
は クライアントコンポーネント("use client"
が必要)- 一方で、
layout.tsx
はデフォルトで サーバーコンポーネント - サーバーコンポーネントに
use client
をつけると、metadata
などのサーバー専用機能が使えなくなる
SessionProvider
を直接 layout.tsx
に書くと、layout.tsx
全体がクライアントコンポーネントになり、Next.js 本来の機能(metadata
など)が使えなくなる。
そのため、別のクライアントコンポーネントに切り出してラップするのが推奨されているわけです。
実装内容
✅ 解決策:<Providers>
コンポーネントを別に作成して、ラップする!!
Providers.tsx
を作って "use client"
をつけることで、
"use client";
import { SessionProvider } from "next-auth/react";
export function Providers({ children }: { children: React.ReactNode }) {
return <SessionProvider>{children}</SessionProvider>;
}
これを layout.tsx
側で下記のようにラップする。
import type { Metadata } from "next";
import { Providers } from "@/components/Providers";
import "./globals.css";
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
ログインフォーム(app/auth/signin/page.tsx)
ユーザーがログインIDとパスワードを入力し、ログイン処理を実行するフォームです。
⚠️ 実装時の注意点
signIn("credentials")
の引数redirect: false
を忘れると、自動遷移してしまいエラー処理が難しくなります。
"use client";
import { useState } from "react";
import { signIn } from "next-auth/react";
export default function SignInForm() {
const [userId, setUserId] = useState("");
const [password, setPassword] = useState("");
const [errorMsg, setErrorMsg] = useState("");
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setErrorMsg("");
const res = await signIn("credentials", {
redirect: false,
username: userId,
password,
});
if (res?.error) {
setErrorMsg("ログインIDまたはパスワードが間違っています");
} else if (res?.ok) {
window.location.href = "/download";
} else {
setErrorMsg("不明なエラーが発生しました");
}
};
return (
<div className="flex items-center justify-center min-h-screen bg-cover bg-center" style={{ backgroundImage: "url('/images/white-background.png')" }}>
<form onSubmit={handleSubmit} className="bg-white p-8 rounded shadow-md w-[500px] mx-auto">
<h1 className="text-2xl font-bold mb-6 text-center">ログイン</h1>
<label className="block mb-4">
<span className="block text-gray-700 font-semibold mb-1">ログインID</span>
<input type="text" value={userId} onChange={(e) => setUserId(e.target.value)} required className="w-full px-3 py-2 border border-gray-300 rounded" />
</label>
<label className="block mb-4">
<span className="block text-gray-700 font-semibold mb-1">パスワード</span>
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} required className="w-full px-3 py-2 border border-gray-300 rounded" />
</label>
{errorMsg && <p className="text-red-600 text-center mb-4">{errorMsg}</p>}
<button type="submit" className="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700">ログイン</button>
</form>
</div>
);
}
未ログイン時のログイン誘導(components/Login.tsx)
ユーザーが未ログインだった場合に、ログインボタンを表示してログイン画面に誘導する処理です。
⚠️ 実装時の注意点
useSession()
を使うには、呼び出し元がSessionProvider
配下である必要があります。
"use client";
import { signIn } from "next-auth/react";
export default function Login() {
return (
<div className="mt-8 text-center">
<button
className="text-blue-600 underline"
onClick={() => signIn(undefined, { callbackUrl: "/auth/signin" })}
>
🔒 管理者ログインはこちら
</button>
</div>
);
}
実行画面
下記のリンク押下で、ログイン画面が表示される

ログインID・パスワードを入力

ログイン完了したら、管理者画面が表示される(この画面のコードはなし)


おつかれさまでした!
コメント