でじぼうです。
Next.js アプリにログイン機能を実装したい方向けの記事です。

この記事は下記の方がおすすめ!
- Next.js でログイン機能を実装したい
- 全体構成がわからない
- 実装の注意点を知りたい
Instagramフォロワー数9,500人を越える人気のもち・大福店 えにかいたもち
全体構成

認証設定(lib/auth.ts)
Next.js でログイン機能を作るときに必要な「設定ファイル」です。
NextAuth.js というライブラリが、このファイルを見てどう動くか決めます。
NextAuthの設定内容
① ログイン方式の設定(providers)
「どんな方法でログインできるようにするか」を決める場所です。
たとえば
- ユーザー名とパスワードでログイン →
CredentialsProvider
を使う - Google アカウントでログイン →
GoogleProvider
を使う - GitHub アカウントでログイン →
GitHubProvider
を使う
今回は「IDとパスワードでログインしたい」ので CredentialsProvider
を使います。
② セッション管理(session)
「ログイン状態(セッション)をどうやって覚えておくか?」を設定します。
よく使われる方法は2つ
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・パスワードを入力

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

まとめ
Next.js + NextAuth.js を使えば、
- ID・パスワードでのログイン認証(Credentials)
- JWTベースのセッション管理
- セッション情報(ユーザー情報)をアプリ全体で使える
という構成が簡単に作れます。
これをベースに、管理者権限のアクセス制限や、ログアウト処理も簡単に追加できます。
「最低限の認証機能を実装したい」という方は、この記事の構成をそのまま使えばOKです!
コメント