この記事の狙い
Next.jsでログイン機能を作るとき
実装方法の選択肢が多すぎて事故りやすいです
- Cookieセッション
- JWTをlocalStorage
- OAuth2.0 OIDC
- NextAuth Auth.js
- BFF構成
本記事はライブラリの使い方紹介ではなく
設計として破綻しない選び方 をまとめます
先に結論
- ログインはOIDC OAuth2.0単体で作らない
- トークンをブラウザに置かない構成 BFF が安全寄り
- Cookieは HttpOnly Secure SameSite を適切に付ける
- 認証と認可を分ける 認可はサーバーで完結させる
よくある事故
localStorageにJWTを置く
便利ですが
XSSが起きた瞬間にトークンが抜かれて終わります
回避策
- まずXSS対策を強くする CSPなど
- それでも 重要な権限があるなら BFFに寄せる
フロントから直接Refresh Tokenを扱う
Refresh Tokenは長命で
漏洩時の被害が深刻です
回避策
- Refresh Tokenはサーバー側に閉じる
- 回転と失効を設計する
OIDCのID TokenをAPIに投げる
ID Tokenはログインの証拠であり
APIのアクセス権ではありません
回避策
- APIはAccess Token
- ログインはID Token検証
まず押さえる全体像
構成を二択に絞ると考えやすいです
- 構成A BFFで同一オリジンに寄せる
- 構成B SPAとして分離しBearerトークンでAPIを叩く
Next.jsはフルスタックなので
構成Aを取りやすいのが強みです
構成A BFF セッション方式
ブラウザはBFFにログインし
BFFが外部IdPとOIDC連携してトークンを保持します
メリット
- トークンがブラウザに出ない
- CookieセッションでCSRF対策が整理しやすい
- APIは同一オリジンでCORSが楽になる
注意点
- CSRF対策が必要
- サーバー側のスケールを考える
構成B Bearerトークン方式
APIはAuthorizationヘッダでBearerを受けます
メリット
- APIが純粋なResource Serverとして作れる
- モバイルなど多クライアントに向く
注意点
- ブラウザにトークンを保持する問題と戦う
- CORSとトークン更新が複雑
Cookie設定の最小安全ライン
- HttpOnly true JSから読ませない
- Secure true https必須
- SameSite Lax 基本
クロスサイト連携で必要ならNoneになりますが
その場合はCSRF対策を強める必要があります
Next.js App Routerでの配置の考え方
- 認証コールバックはRoute Handlersで受ける
- UIはServer Componentを基本にし
ログイン状態の判定はサーバー側で行う - Client Componentsはインタラクションに限定する
この構造にすると
認証情報がクライアントに漏れにくい設計になります
実務チェックリスト
- ログインはOIDCになっている
- stateとnonceを検証している
- Redirect URIは完全一致
- CookieにHttpOnly Secure SameSiteが付いている
- セッション失効 ログアウトが設計されている
- 重要操作は再認証やステップアップを検討している
- 認可はサーバーで完結し クライアント入力を信用していない
まとめ
Next.jsはBFFに寄せやすいので
迷ったら
- OIDCでログイン
- BFFでトークンをサーバーに閉じる
- Cookie属性とCSRFをセットで設計
が堅い選択になります
まずは事故るパターンを避ける設計から始めると
後から機能を増やしても破綻しにくいです