Next.js のログイン実装。最小構成サンプル
ログインの骨格はファイル5つで立ちます。
動いているものを触る/demo/loginファイル構成
並んでいるのは、上の画面をいま動かしている本物のファイルです。名前を押すと中身が出ます。
ファイル構成
- app/
- mypage/
app/layout.tsxBuild
全ページの外側。本物のプロジェクトでは html と body をここで書きます
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "ログインの最小構成",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return <main>{children}</main>;
}- .tsx
- 画面を返すファイル。HTML のような書き方ができます
- .ts
- 画面を返さないファイル。処理とデータだけを置きます。ここに画面を書くと原因を名指ししないエラーで落ちます
- Build
- ビルド時に一度だけ動きます
- Server
- アクセスのたびにサーバーで動きます
- Browser
- 利用者の手元で動きます
ログインは3つの部品でできている
パスワードもメールも外して残るのは、この3つです。
| 部品 | 置き場所 | 役目 |
|---|---|---|
| セッション | サーバー | 誰なのかを覚えておく |
| Cookie | ブラウザ | そのセッションの鍵を持ち歩く |
| 守り | ページごと | 鍵が無ければ入れないようにする |
このサンプルはこの3つだけで組んであります。足りないものは意図的に外してあります(下の「入れていないもの」)。
ブラウザへ渡すのは鍵だけ
サーバーに残るもの
名前
有効期限
セッションの一覧
ブラウザが持つもの
推測できない鍵(セッションID)
越えられる: 鍵だけ
越えられない: 名前・権限・有効期限
Cookie に入れているのは crypto.randomUUID() で作った鍵だけです。名前は app/session.ts の中に残ります。
Cookie の設定は3つとも意味があります。
| 設定 | 何を防ぐか |
|---|---|
httpOnly | ページの JavaScript から読めなくする |
sameSite: 'lax' | 他所のサイトからの送信で使わせない |
secure | 暗号化されていない通信に乗せない |
守るのはページ自身の仕事
app/mypage/page.tsx は、自分でセッションを見て、無ければ追い返します。
const session = readSession(jar.get(COOKIE)?.value)
if (!session) redirect('/demo/login')
ログアウトで消すものは2つ
Cookie を消すだけでは足りません。サーバー側の記録も消します。
destroySession(jar.get(COOKIE)?.value)
jar.delete(COOKIE)
片方だけだと、鍵を控えておいた人が後から入れてしまいます。この2行は必ず組で書きます。
誰でも呼べる入口だという前提
app/actions.ts の関数は、画面を経由せずに呼べます。だからその中で、値を絞ることと、回数を絞ることの両方をやっています。
- Browser名前を送る
普通の form の送信。
- Server回数と値を確かめる
誰でも呼べる入口なので、まずここで絞る。
- Serverセッションを作って鍵を渡す
名前はサーバーに残し、Cookie には鍵だけを入れる。
- Server守られたページへ送る
そのページが自分で鍵を確かめる。
ここから足していくとき
- パスワードかメールを足す。このサイト本体は、パスワードを持たずにメールのリンクで確認する形にしています。理由はこのサイトについてにあります
- セッションをデータベースへ移す。いまはプロセスの中なので、入れ替えると全員ログアウトになります
- 権限を足す。
readSessionの戻り値に役割を持たせて、ページ側で見ます
どれを足しても、鍵と中身の分け方は変わりません。そこが崩れると、あとから足した守りは全部効かなくなります。
入れていないもの
このまま本番に持っていかないでください。省いてあるのは次の点です。
- パスワードとメール送信。誰でも押せる画面なので、この稿では入れていません
- データベース。セッションはプロセスの中に置いてあり、入れ替えで消えます
- CSRF 対策の追加実装。Server Function と SameSite で足りる範囲に留めています
- 権限の区別。ログインしているかどうかだけを見ています
出典
- nextjs.org/docs/app/api-reference/functions/cookies
- nextjs.org/docs/app/api-reference/directives/use-server
- nextjs.org/docs/app/guides/authentication
- developer.mozilla.org/ja/docs/Web/HTTP/Cookies
このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。