Next.js App Router × Supabase Google認証で、公式通りなのに詰まった話
Next.js App Router と Supabase を使って
Google OAuth のログインを実装しようとしました。
公式ドキュメント通りに
callback を作り、cookie を受け取り、
getUser() も呼んでいます。
ログも出ている。
cookie も見えている。
それなのに、管理画面 /admin に入れない。
最終的には
「構成として間違っていた」
という結論に辿り着きました。
この記事では、
なぜ詰まったのか
何が地雷だったのか
どういう構成なら素直に動いたのか
を、実装ログベースで整理します。
App Router × Supabase 認証で
「公式通りなのに動かない」状態にハマった人の
時間を少しでも減らせたら嬉しいです。
本当の地雷①
callback で「cookie を積んだ Response」を返していなかった
App Router の Route Handler では、
supabase.auth.exchangeCodeForSession(code)
この時点では、cookie は 「response に書き込まれる」だけ です。
つまり、次の形で cookie を積んだ response をそのまま返さないと意味がありません。
const response = NextResponse.redirect(...)
await supabase.auth.exchangeCodeForSession(code)
return response
この response をそのまま返さないと、セッションは保存されません。
やってはいけない例
途中で以下をやるとアウトです。
NextResponse.redirect(...) を作り直す
NextResponse.next() を使う
cookie をコピーし忘れる
これをやると、
ログインできている「気がする」だけ
という状態になります。
本当の地雷②
App Router で「middleware と layout の二重ガード」
当初、こんな構成にしていました。
middleware.ts
/admin をガード
app/admin/layout.tsx
getUser() で弾く
これ、やると詰まります。
理由は単純で、
middleware は Edge Runtime
layout は Server Component
cookie の見え方・更新タイミングがズレる
その結果
callback 直後:cookie 未反映
middleware が先に走る
永遠に /auth/login に戻される
公式サンプルでも、この 二重構成は推奨されていません。
本当の地雷③
App Router に NextResponse.next() を持ち込んだ
これは完全に自分のミスです。
NextResponse.next()
は middleware 専用 です。
Route Handler(app/auth/callback/route.ts)
で使うと、即エラーになります。
NextResponse.next() was used in a app route handler
App Router は、
「返す Response は最初から最後まで一貫して扱う」
という思想です。
最終的に動いた構成
最終的に 安定した構成 はこれでした。
1️⃣ callback は「cookie を積んだ redirect を返すだけ」
exchangeCodeForSession
redirect
余計な処理をしない
2️⃣ /admin の認可は layout で一元管理
const {
data: { user },
} = await supabase.auth.getUser()
if (!user) redirect("/auth/login")
3️⃣ middleware は使わない
App Router × 認証では、
middleware はほぼ不要
なぜ「公式通り」でも詰まったのか
理由はこれです。
Supabase の公式は Pages Router 前提
App Router のライフサイクル差分が暗黙
cookie の「いつ・どこで確定するか」が違う
つまり、
コードは正しくても、構成が間違っていた
これが今回の本質でした。
同じところで詰まっている人へ
もし今、
cookie は見える
でも getUser() が null
redirect ループする
この状態なら、
「どこで cookie を確定させているか」
を一度疑ってみてください。
おわりに
こういう
「公式通りなのにハマる話」
は、また書いていきます。
Discussion