Contents
Credentials Providerとは
Credentials Providerは、NextAuthで独自のユーザー名/パスワード認証を実装するためのプロバイダーです。
データベースの準備
DB準備し.envファイルを編集
データベース接続設定 (.env)
DATABASE_URL="mysql://ユーザー名:パスワード@ホスト:3306/データベース名"
今回はPrisma不使用
ORM (Object-Relational Mapping)
SQLを直接書くことなく、オブジェクトのメソッドでDB操作ができる
SQL文詳しくなくてもDB操作ができる
- PHP APIを作成した理由:
- さくらインターネットのデータベースにアクセスするため
- 外部からの直接接続ができないため、PHPを介してアクセス
- Prismaは:
- データベースに直接接続する必要がある
- さくらインターネットの制限により使用が難しい
- PHP APIの作成はPrismaとは無関係
これからの進め方:
- PHP APIを使用してデータベース操作を行う
- Next.jsからそのAPIを呼び出す
- NextAuthの実装を進める
NextAuthの設定
NextAuthのインストール
npm install next-auth
認証APIルートの作成 (src/app/api/auth/[...nextauth]/route.ts
)
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
const handler = NextAuth({
providers: [
CredentialsProvider({
name: "Email & Password",
credentials: {
email: {
label: "メールアドレス",
type: "email",
placeholder: "email@example.com"
},
password: {
label: "パスワード",
type: "password"
}
},
async authorize(credentials) {
try {
// PHP APIを呼び出してユーザー認証
const res = await fetch("https://あなたのドメイン/auth-api.php", {
method: 'POST',
body: JSON.stringify({
email: credentials?.email,
password: credentials?.password
}),
headers: { "Content-Type": "application/json" }
});
const user = await res.json();
if (user.success) {
return {
id: user.data.id,
name: user.data.name,
email: user.data.email
}
}
return null;
} catch (error) {
console.error("Auth Error:", error);
return null;
}
}
})
],
// pages: {
// signIn: '/auth/signin', // カスタムログインページのパス
// }
});
export { handler as GET, handler as POST };
認証用のPHP API作成 (auth-api.php)
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Allow-Headers: Content-Type');
// POSTデータの取得
$json = file_get_contents('php://input');
$data = json_decode($json, true);
// データベース接続情報
$host = 'xxxx';
$dbname = 'xxxx';
$user = 'xxxx';
$pass = 'xxxx';
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? AND password = ? LIMIT 1');
$stmt->execute([$data['email'], $data['password']]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user) {
echo json_encode([
'success' => true,
'data' => [
'id' => $user['id'],
'email' => $user['email'],
'name' => $user['name']
]
]);
} else {
echo json_encode([
'success' => false,
'error' => 'Invalid credentials'
]);
}
} catch(PDOException $e) {
echo json_encode([
'success' => false,
'error' => $e->getMessage()
]);
}
?>
特定のページのみを認証必須にする
認証状態を確認するためのミドルウェアを作成
src/middleware.ts
を作成import { withAuth } from "next-auth/middleware"
// 特定のパスのみ認証を必須にする
export default withAuth({
// ここで指定したパスのみ認証が必要
pages: {
signIn: "/api/auth/signin",
}
})
// 認証が必要なパスを指定
export const config = {
matcher: ["/nextauth-demo/:path*"]
}
デモページの作成
src/app/nextauth-demo/page.tsx
'use client';
import { useSession, signOut } from "next-auth/react" // signOutをインポート
import { redirect } from "next/navigation"
export default function ProtectedPage() {
const { data: session, status } = useSession()
if (status === "loading") {
return <div>Loading...</div>
}
if (status === "unauthenticated") {
redirect("/api/auth/signin")
}
// ログアウト処理の追加
const handleLogout = async () => {
await signOut({
callbackUrl: '/nextauth-demo' // ログアウト後のリダイレクト先
})
}
return (
<div className="p-4">
<h1 className="text-2xl font-bold mb-4">認証が必要なページ</h1>
<p className="mb-4">ようこそ {session?.user?.email ?? 'ゲスト'} さん</p>
{/* ログアウトボタン追加 */}
<button
onClick={handleLogout}
className="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600"
>
ログアウト
</button>
</div>
)
}
/nextauth-demo/layout.tsx を作成
'use client';
import { SessionProvider } from "next-auth/react";
export default function AuthLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<SessionProvider>
{children}
</SessionProvider>
);
}