1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NextAuth v5 + Netlify で useSession().update() が壊れる話と回避策 — Set-Cookie 脱落の罠

1
Last updated at Posted at 2026-06-13

この記事は約 5 分で読めます。

筆者プロフィール: ソフトウェアエンジニア。「知った気にならない。いつまでも学び続ける」を信条に、業務と個人開発の両輪で技術を磨いています。AI 駆動開発で複数の個人開発アプリを構築・運用中。
👉 ポートフォリオ: 筆者ホームページ

NextAuth v5 (Auth.js) を Netlify にデプロイすると、useSession().update() が動かない という罠に遭遇します。本記事では、運用中の SaaS 「たすきば Knowledge Relay」 での回避策を整理します。

サービスの機能紹介・画面イメージ・コンセプトは公式プロダクトページをご覧ください。
👉 たすきば Knowledge Relay — 公式プロダクトページ

こんなコード、書いたことありませんか?

// Vercel では正常動作するコード
'use client';
import { useSession } from 'next-auth/react';

export function ProfileImageUploader() {
  const { update } = useSession();

  async function handleUpload(file: File) {
    await uploadProfileImage(file);
    await update();  // セッション再生成 → 画面に新しい画像が反映
  }
}

これを Netlify にデプロイすると、update() 後に画面が更新されません


1. 何が起きていたか

ブラウザの DevTools で確認すると:

観測 状態
update() の API リクエスト送信 正常
API レスポンス 200 で返っている
Set-Cookie ヘッダ 応答から脱落

結果として、新しい JWT が cookie に書き込まれず、セッション状態が古いまま残る という現象が起きます。


2. 原因: Netlify の Set-Cookie 脱落

Netlify は、Next.js のレスポンスを内部の Edge Function 経由でユーザに返します。このとき、特定の条件下で Set-Cookie ヘッダが応答パイプラインから脱落する ことがあります。

具体的には:

  • useSession().update() から呼ばれる internal API route (/api/auth/session) が Set-Cookie を返す
  • Netlify Edge Function が、これを次の layer に正しく転送できない

公式 issue でも報告されているが、根本対応は時間がかかりそうでした。本番リリースを目前に控え、回避策を取る方針 にしました。


3. 回避策 1: テーマは専用 cookie に分離

ユーザがダーク / ライトテーマを切り替える機能。当初 NextAuth セッションの user.theme に格納していましたが、Set-Cookie 脱落で更新されない。

対応: NextAuth セッションから切り離し、専用 cookie (tasukiba_theme) を直接書く ことに。

// src/lib/theme-cookie.ts
import { cookies } from 'next/headers';

export async function setThemeCookie(theme: 'light' | 'dark') {
  const cookieStore = await cookies();
  cookieStore.set('tasukiba_theme', theme, {
    path: '/',
    maxAge: 60 * 60 * 24 * 365,
    sameSite: 'lax',
  });
}

cookie 書き込みは Server Action 内で行います。NextAuth と完全に独立しているため、Set-Cookie 脱落の影響を受けない のがポイント。

結果として、テーマ切り替えは即座に画面に反映されます。


4. 回避策 2: MFA / TZ / Locale は JWT 再署名ヘルパで書く

MFA 設定変更、タイムゾーン変更、Locale 変更などの セッションに直接乗せる必要がある情報 は、update() ではなく JWT を再署名する独自ヘルパ で対処します。

// src/lib/auth-jwt-helper.ts
import { cookies } from 'next/headers';
import { encode } from 'next-auth/jwt';

export async function resignJwt(
  patches: Partial<{ mfaEnabled: boolean; tz: string; locale: string }>,
) {
  const current = await getCurrentJwt();
  const newPayload = { ...current, ...patches };
  const token = await encode({
    token: newPayload,
    secret: process.env.NEXTAUTH_SECRET!,
    salt: 'authjs.session-token',
  });
  const cookieStore = await cookies();
  cookieStore.set('authjs.session-token', token, {
    httpOnly: true,
    secure: true,
    sameSite: 'lax',
    path: '/',
  });
}
ポイント 内容
next-auth/jwtencode JWT を自前で再署名
cookies().set() Server Action 内で直接書き込む (Set-Cookie パイプラインを経由しない)
結果 Netlify の Set-Cookie 脱落の影響を受けない

このヘルパを「MFA 有効化」「タイムゾーン変更」「言語切り替え」の場面で使います。


5. 回避策 3: 新規コードで useSession().update() を使わない

新規実装では、原則として useSession().update() を使わない方針にしました。

代わりに:

用途 採用するアプローチ
表示更新が必要 router.refresh() で Server Component を再評価
セッション情報を変えたい JWT 再署名ヘルパ
軽量な UI 状態 (テーマ等) 専用 cookie

6. "再ログインで対応する" がダメな理由

「いったんログアウトして再ログインすれば、新しいセッションが取れる」は事実です。しかし、これは UX として明確に悪い

ユーザが「テーマを変えただけ」なのに「再ログインしてください」と言われる体験は、信頼を損ないます。特にビジネス利用者は、ログイン状態を頻繁に切らされることに非常に neg な感情 を持ちます。

—— だからこそ、回避策を施してでも update() 相当の体験を実現する必要がありました。


7. Cookie 削除に依存しない設計へ

この問題は、サインアウト時にも影響します。

NextAuth の signOut() は、JWT cookie を削除することでログアウトを実現します。しかし Netlify の Set-Cookie 脱落で delete cookie ヘッダも脱落する ことがある。

結果として「サインアウトしたはずなのにログイン状態が残る」事故が起きる可能性 (severity-1)。

たすきばはこれを構造的に防ぐため、サインアウトを Cookie 削除に依存させない 設計にしました (次回 E-4 で詳説)。


8. 学んだこと — PaaS の挙動を疑う習慣

外部 PaaS の挙動は、ライブラリの想定と必ずしも一致しません。

特に Set-Cookie のような HTTP の根本機能 であっても、PaaS の middleware / Edge Function 層で脱落する可能性があります。

ライブラリ (NextAuth) のドキュメント通りに書いて動かないとき、犯人がライブラリではなく PaaS の挙動 であることはよくあります。

このとき「ライブラリの不具合だ」と決めつけず、HTTP レベルで何が起きているか を DevTools で確認する習慣が、原因究明の鍵になります。


おわりに

機能 NextAuth update() たすきばの対応
テーマ切り替え ❌ Netlify で動かない 専用 cookie tasukiba_theme
MFA 有効化 ❌ 同上 src/lib/auth-jwt-helper.ts で JWT 再署名
TZ / Locale 変更 ❌ 同上 同上
表示更新 ❌ 同上 router.refresh()
サインアウト ❌ Cookie 残留リスク tokenVersion increment + layout DB 照合 (次回 E-4)

新規コードでは useSession().update() を使わないルールとして、CONTRIBUTING.md にも明記しています。

NextAuth v5 + Netlify を使う方は、最初からこの罠を回避する設計 をおすすめします。

本記事の罠と回避策は、運用中の SaaS 「たすきば Knowledge Relay」 で実装したものです。
👉 たすきば Knowledge Relay — 公式プロダクトページ

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?