event-manual / chapter 9

本人確認と認可

@event/fours-sdk@event/shell

verify4SUser — なりすまし防止の要

client が送ってくる idToken を、サーバが 4S GET /users/me に Bearer で投げる。200 なら本人であり、返ってきた id(sub) / slug / name が「正典」 — client 申告の name / avatar は一切信用しない。JWKS の自前検証は持たず、検証は 4S に委ねる(4S が唯一の検証者)。

const user = await verify4SUser(config, idToken);
// => { id, slug, name, email?, avatar? } | null

実装上の防御(3実装の分岐をこの方式に統一):

jose httpOnly cookie セッション

推奨デフォルトの認証モデルは spikes 由来の jose HS256 httpOnly cookie である。createSessionCodec は sign / verify の純粋なコーデックだけを提供し、cookie の読み書き(next/headers)はアプリ側の責務に残す。

import { createSessionCodec } from "@event/fours-sdk";

const codec = createSessionCodec({ cookieName: "myevent_4s_session" });

// handoff consume → verify4SUser 成功後:
const token = await codec.sign({ fourSUserId: user.id, slug: user.slug });
cookieStore.set(codec.cookieName, token, codec.cookieAttributes);

// 以降のリクエスト:
const session = await codec.verify(cookieStore.get(codec.cookieName)?.value);
// => { fourSUserId, slug } | null

設計上の不変条件:

isResourceOwner — 所有権ガード

Server Action 冒頭で必ず通す純関数。3条件のいずれかで所有と判定する:

  1. resource.founderSlug === session.slug
  2. resource.meta.ownerFourSUserId === session.fourSUserId(sub は不変。最も堅牢
  3. resource.meta.editorSlugssession.slug が含まれる

slug は 4S 側でユーザーが変更できる可変キーなので、新規リソースの所有権は原則 ownerFourSUserId(sub)で記録する。

middleware は「境界」であって「認可」ではない

CVE-2025-29927(x-middleware-subrequest ヘッダで middleware をスキップできた Next.js 脆弱性)を踏まえ、統治原則を固定する:

// server action の標準形
"use server";
export async function updateLp(slug: string, patch: LpPatch) {
  const session = await codec.verify((await cookies()).get(codec.cookieName)?.value);
  const lp = await loadLp(slug);
  if (!isResourceOwner(session, lp)) throw new Error("forbidden");
  // ...
}

なお edge で動く middleware から @event/core の barrel を import すると contentStore の node:fs / node:crypto が edge bundle に混入する。middleware 関連は @event/core/auth subpath から import する(第46章の落とし穴カタログ参照)。

legacy adapter — localStorage tokenStore

xdeal / ivs の現行方式(Cognito トークンを 4s-cognito-tokens-v1 キーで localStorage 保持 + client 側 refresh)は @event/fours-sdk/client に legacy adapter として温存している。loadTokens / saveTokens / isIdTokenExpired / decodeJwt(表示用の無検証 decode。本人判定は常にサーバの /users/me)を提供する。新規イベントは jose cookie を既定とし、client 側トークン保持が必要な場合のみ opt-in する。