Server Actions とは
Server Actions は、Next.js App Routerでサーバ側関数として更新処理を書ける機能です。
フォーム送信やデータ更新を、サーバ側の関数に直接つなげるような書き方ができます。
"use server";
export async function createPost(formData: FormData) {
const title = String(formData.get("title"));
await db.post.create({ data: { title } });
}
認証付きアプリでは、この関数の中で必ずsessionを確認します。
Cookie は自動で送られる
ブラウザがログイン済みで、HttpOnly Cookie にセッションIDを持っているとします。
Cookie: session_id=abc123
ユーザーがフォームを送信すると、ブラウザはNext.jsサーバへリクエストを送ります。このとき、条件に合うCookieが自動で付与されます。
Server Action側では、そのCookieをもとにsessionを確認できます。
"use server";
import { auth } from "@/auth";
export async function createOrder(formData: FormData) {
const session = await auth();
if (!session) {
throw new Error("Unauthorized");
}
await createOrderForUser(session.user.id, {
title: String(formData.get("title")),
});
}
access token はサーバ側で使う
外部APIを呼ぶ必要がある場合も、access tokenはサーバ側で取り出して使います。
"use server";
import { auth } from "@/auth";
import { getAccessToken } from "@/lib/server/access-token";
export async function createOrder(formData: FormData) {
const session = await auth();
if (!session) throw new Error("Unauthorized");
const accessToken = await getAccessToken();
const res = await fetch("https://api.example.com/orders", {
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
title: String(formData.get("title")),
}),
});
if (!res.ok) throw new Error("Failed to create order");
}
この accessToken はブラウザに返しません。Server Actionの関数内だけで使います。
戻り値に秘密を含めない
Server Actionの戻り値は、クライアント側で受け取れる場合があります。
そのため、戻り値にaccess token、refresh token、秘密情報を含めてはいけません。
// 悪い例
return {
ok: true,
accessToken,
};
返すのは、UI表示に必要な最小限の情報だけにします。
return {
ok: true,
message: "作成しました",
};
注意: Server Action はサーバで実行されますが、戻り値やClient Componentへ渡した値はブラウザ側に出る可能性があります。秘密を返さないことが重要です。
CSRF 対策も考える
Cookie認証では、Cookieが自動送信されます。そのため、CSRF対策も必要です。
Next.jsや認証ライブラリの仕組みに任せられる部分もありますが、設計としては次を意識します。
- 状態変更はGETで行わない
- SameSite Cookie を適切に設定する
- 必要に応じてCSRFトークンを使う
- Origin / Referer 検証を検討する
- 重要操作では再認証を求める
Server ActionsだからCSRFを考えなくてよい、ではありません。
認可チェックを省略しない
ログイン済みかどうかだけでなく、その操作をしてよいユーザーかも確認します。
const order = await db.order.findUnique({
where: { id: orderId },
});
if (!order || order.userId !== session.user.id) {
throw new Error("Forbidden");
}
認証は「誰か」を確認すること、認可は「その操作をしてよいか」を確認することです。
まとめ
Server Actionsでは、フォーム送信や更新処理をサーバ側に置けます。
- ブラウザはCookie付きでNext.jsへリクエストする
- Server Action内でsessionを確認する
- access tokenはサーバ側で使い、ブラウザに返さない
- 戻り値に秘密を含めない
- CSRF対策を考える
- 認証だけでなく認可も確認する
参考リソース
- 公式ドキュメント - Server Actions と Cookie 認証 - フォーム送信時に何が起きるか を確認するための一次情報