2. Supabaseの認証機能を使わない認証機能(成功)
パッケージのインストール
npm install next-auth
npm install next-auth bcrypt <--- パスワードをハッシュ化するため
pages/api/auth/[…nextauth].js
import NextAuth from 'next-auth'; <--- NextAuth.jsの主要なライブラリで認証方法が色々あります
import CredentialsProvider from 'next-auth/providers/credentials'; <--- メールアドレスとパスワードによる認証の場合はこのライブラリをインポートします
import bcrypt from 'bcrypt'; <--- パスワードをハッシュ化するためのライブラリ
// 仮のユーザーデータ(本番はDBを使います)
// bcrypt.hashSync()でハッシュ化して安全に保存します
const users = [
{ id: "1", email: "sample@gmail.com", password: bcrypt.hashSync("password123", 10) },
];
export default NextAuth({
providers: [ <--- 認証プロバイダの設定をします
CredentialsProvider({
name: "Email and Password", <--- プロバイダの名前
credentials: { <--- 認証する情報
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
async authorize(credentials) { <--- 認証処理を行う関数
const user = users.find((u) => u.email === credentials?.email);
if (user && bcrypt.compareSync(credentials.password, user.password)) {
return { id: user.id, email: user.email } <--- 2つのパスワードをcompareして
} --- 同じ(Sync)ならユーザー情報をreturn
throw new Error("Invalid email or password");
},
}),
],
pages: { <--- 認証ページを設定します
signIn: "/auth/signin",
},
secret: process.env.NEXTAUTH_SECRET <--- セッションを暗号化するための秘密鍵(.env.localで定義)
});
※[…nextauth]の部分は動的に変わります
※jsファイルでなくtsファイルとした場合、import bcrypt from ‘bcrypt’に対して「Could not find a declaration file for module ‘bcrypt’」というエラーが出ます
.env.local
NEXTAUTH_SECRET=mysecretkey <--- この文字列は適当でも動きます
NEXTAUTH_URL=http://localhost:3000
pages/auth/signin.tsx
import { signIn } from "next-auth/react";
import { useState } from "react";
export default function SingIn() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const result = await signIn("credentials", { email, password, redirect: false });
if (result?.error) {
setError("ログインに失敗しました");
} else {
window.location.href = "/"; <--- ログイン成功時にホームへリダイレクト
}
};
return(
<div>
<h2>LOGIN</h2>
<p>{error}</p>
<form onSubmit={handleSubmit}>
<input type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} required />
<input type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} required />
<button type="submit">LOGIN</button>
</form>
</div>
);
}
ナビゲーションバーにログイン状態を表示する
import { signIn, signOut, useSession } from "next-auth/react";
export default function Navbar() {
const { data: session } = useSession(); <--- ※1
return (
<nav>
{session ? (
<>
<p>ログイン中: {session.user?.email}</p>
<button onClick={() => signOut()}>ログアウト</button>
</>
) : (
<button onClick={() => signIn()}>ログイン</button> <--- ※2
)}
</nav>
);
}
※1 … この行をexport defaultの外で書くと「TypeError: Cannot read properties of null (reading ‘useContext’)」というエラーが出ます
※2 … buttonタグではなく<Link href=”/auth/signIn”>SIGN IN</Link>にした場合、クリックすると「Error: This action with HTTP GET is not supported by NextAuth.js」というエラーが出ます。NextAuth.jsのサインイン処理はセキュリティ上の理由からonClickイベントハンドラで行う必要があります
page/_app.tsx
import { SessionProvider } from "next-auth/react";
import type { AppProps } from "next/app";
export default function App({ Component, pageProps }: AppProps) {
return (
<SessionProvider sessino={pageProps.session}> <--- useSessionを使うためには
<Component {...pageProps} /> --- このタグでラップする必要があります
</SessionProvider>
);
}
pages/dashboard.tsx(認証しないと中身が見れないページの例)
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import { useEffect } from "react";
export default function Dashboard() {
const { data: session, status } = useSession();
const router = useRouter();
useEffect(() => {
if (status === "unauthenticated") {
router.push("/auth/signin");
}
}, [status, router]);
if (status === "loading") return <p>Loading...</p>;
if (!session) return null;
return <h1>ダッシュボード(ログイン済み)</h1>;
}