SSR構成では cookie の有無で認証状態を描画しない
フロントエンド
認証・認可
知識
判断
短命なアクセストークン(数十分)と長命な refresh トークンを cookie で持つ SSR 構成では、「アクセストークン cookie が無い=未ログイン」として描画すると、refresh 可能な状態なのに表示だけが未ログインへ退行する。放置後にヘッダーがログインボタンへ戻り、リロードや画面操作で復活する、という症状で現れる。
退行が起きる3つの経路
- サーバー側の初期描画 — root layout などがアクセストークン cookie の有無だけを見て user=null を初期キャッシュへ書き込む。refresh トークンを見て回復を試みる処理が無い。
- refresh を行う middleware の適用範囲 — matcher が保護ルート限定だと、公開ページでは refresh が一切走らない。「保護ページのリロードでは戻るのに公開ページでは戻らない」という非対称はこれが原因。
- クライアント側の再取得契機 — fetch ラッパーに 401→refresh→リトライがあっても、
refetchOnWindowFocus: falseなどで再取得契機が無いと、画面を開いたまま放置した状態からはその経路に乗れない。
対処
- middleware の適用を全ページへ広げ、トップレベルの document リクエストに限って refresh する。prefetch / RSC リクエストでは refresh しない(同時多発の refresh 競合を避ける)。公開ページでの refresh 失敗はログイン画面へ飛ばさず素通しする。
- タブ復帰(window focus)で自分情報を再取得する設定にし、401 経路から復元させる。
- エラーを一律 null に潰さない。「自分情報の取得失敗=未ログイン」と縮退させると、一瞬のネットワーク断でもログイン表示に化ける。401 のみ null とし、それ以外は throw して前回表示を維持する。
判定の勘所
「refresh の仕組みが動いているか」と「表示を復元する経路があるか」は別問題。前者が正しく動いていても後者が塞がっていれば退行する。サーバー描画・middleware・クライアント再取得の3層すべてに復元経路があるかを確認する。