第187章:復習:認証は“UI”より“ガード”が本体🧱🔑
認証って、ログイン画面を「作ること」よりも、「入っちゃダメな人を絶対に通さない仕組み(ガード)」を作ることが本体だよ〜!😺✨ ボタンを隠す・ページを見えなくする…だけだと、簡単に突破されちゃうの🥲💥
1) “UIだけ認証”が危ない理由😱🚨
たとえば…
- ✅ 画面では「編集ボタン」が非表示になってる
- ❌ でも、APIやServer Actionが誰でも叩けるままだと… → URL直打ち or DevToolsでリクエスト作って 編集できちゃう😇💣
つまり… UIは“親切”のためで、 安全は“サーバー側ガード”で守るのが大前提だよ!🛡️✨
2) ガードは「3段構え」で考えるのがラク😎🧱🧱🧱
- ① 入口ガード:Middleware(ルートに入る前の門番)🧤
- ② 中身ガード:Server Component / layout(ページ本体で最終確認)🧊
- ③ 操作ガード:Route Handler / Server Actions(更新・削除など“操作”の守り)🚪
図で見るとこんな感じ👇💡
3) それぞれの役割(ここがテストに出る😆📌)
① Middleware:まず“入場”を止める🧤🚦
- 向いてる:
/dashboardとか「ログイン必須ゾーン」への入場制限🏰 - 気持ち:未ログインならページ描画すらさせずログインへ送る📮
② Server Component / layout:ページ側で“最終確認”🧊✅
Middlewareがあっても、ページ側でも確認しておくと安心感MAX🥹🛡️ (設定ミス・例外ルート・直アクセス…ぜんぶあり得るので💦)
- 向いてる:ページ表示条件(例:ログイン必須、権限必須)👑
③ Route Handler / Server Actions:いちばん大事な“操作”の守り🚪💥
本当に危ないのはここ!😱 「更新」「削除」「投稿」みたいな操作は、UIで隠してても関係なく叩かれる前提で守るよ🧱✨
- 向いてる:CRUD全部(特に Update/Delete)✏️🗑️
- やること:サーバーでセッション確認 → 権限確認 → ダメなら拒否🙅♀️
4) ミニ実装テンプレ(“守りの型”)🧩✨
※Auth.js(NextAuth系)の書き方はプロジェクト構成で少し変わるけど、考え方はこれでOKだよ〜!😺
A) Middlewareで「/dashboard配下」をログイン必須にする🧤
// middleware.ts(例)
import { NextResponse } from "next/server";
// 例:Auth.js側で用意した「セッション取得関数」を使う想定
import { auth } from "@/auth";
export default auth((req) => {
const isLoggedIn = !!req.auth;
if (!isLoggedIn) {
const url = req.nextUrl.clone();
url.pathname = "/login";
url.searchParams.set("from", req.nextUrl.pathname);
return NextResponse.redirect(url);
}
return NextResponse.next();
});
export const config = {
matcher: ["/dashboard/:path*"],
};
B) layoutで“ページ本体の最終確認”🧊
// app/dashboard/layout.tsx(例)
import { redirect } from "next/navigation";
import { auth } from "@/auth";
export default async function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
const session = await auth();
if (!session) {
redirect("/login");
}
return (
<div>
<h1>Dashboard</h1>
{children}
</div>
);
}
C) Server Actions / Route Handlerで“操作を守る”🚪🛡️
「ボタン押せたか」じゃなくて、サーバーが許可したかだけが正義!😼✨
// app/api/todos/[id]/route.ts(例)
import { NextResponse } from "next/server";
import { auth } from "@/auth";
export async function DELETE(
_req: Request,
{ params }: { params: { id: string } }
) {
const session = await auth();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// ここで「そのTODOがこのユーザーの物か?」みたいな所有者チェックも必須👏
// if (!isOwner(session.user.id, params.id)) return 403
// delete...
return NextResponse.json({ ok: true });
}
5) よくある落とし穴チェック✅🕳️
- ❌ 「ボタン非表示にしたから安全」→ 安全じゃない😇
- ❌ 「ページでセッション見たからOK」→ 操作も必ず守る🚪
- ❌ 「ログインしてるかだけ見た」→ “権限(admin等)”も別で見る👑
- ✅ 「入口 + 本体 + 操作」3点セットで安心🧱🧱🧱✨
6) ミニ演習(15分)⏳🌸
次の3つを“全部”できたら勝ち〜!🎮🎉
/dashboardに未ログインで入ったら/loginに飛ばす🧤🚦/dashboardのlayout.tsxでも未ログインをredirect()する🧊/api/todos/[id]のDELETEを未ログインなら401にする🚪🛡️
できたら、最後に自分にご褒美スイーツ🍰でOKです😆💕
まとめ🍀✨
- 認証の本体は UIじゃなく“ガード” 🧱🔑
- ガードは 入口(Middleware)→本体(Server)→操作(API/Actions) の順で固める💪
- 特に大事なのは “操作の守り”(更新・削除は絶対サーバーで止める)🚪🛡️
次の章からは、ここまでの守りを土台にして「認証付きTODO」を完成形に寄せていくよ〜!😺🔥