💬

Next.js App Router × Auth.js(NextAuth) × FastAPIで作る“サーバ完結”認証 — 設計方針を理解

に公開

今回は Auth.js(NextAuth) を使った認証を、Next.js App Router と FastAPI の組み合わせで“サーバ完結”に設計する基本をまとめます。
ポイントは 「認証・秘密・DBアクセスをサーバ内に留める」 こと。ブラウザには最小の表示用データだけを渡すので、安全で速くなります。

前提

  • Next.jsのApp Routerはレイアウトとページはデフォルトでサーバーコンポーネント。
  • Auth.jsはNext.js のプロジェクトに next-auth というライブラリパッケージをインストールして使う認証ライブラリ。プロバイダ設定・セッション管理・CSRFなどを提供し、auth()/getServerSession() でサーバからセッション参照できる。
  • FastAPIは内部トークンを検証し、必要に応じてDBで権限/所有チェックを行う
  • Next.js x Auth.js x FastAPIの組み合わせにより、認証処理は、サーバーサイドで完結して行える。
  • この組み合わせのメリットは、秘密情報や認証判定、DBアクセスをサーバーの中に留めることができること。ブラウザ側に最低限の表示用情報しか渡さないので安全&速い。

<サーバで認証を完結させる流れ>

[Browser]
   │ (HTML/CSS/最小JSだけ) ← 個人データは no-store / private(キャッシュ禁止)
   ▼
[Next.js App Router (Server Component)]
   ├─ 認証判定: auth()/getServerSession()でユーザー認証(サーバ内)
   ├─ 未認証なら redirect('/login')
   └─ 認証OKなら Route Handler へ
          │  Authorization: Bearer <内部トークン>を発行  (ブラウザには出さない)
          ▼
      [FastAPI]
         ├─ 内部トークン検証
         ├─ (必要なら)DBで権限/所有チェック
         ▼
      [PostgreSQL (RLS)]
         └─ 行レベルで最終防衛

内部/外部トークンとは

項目 内部トークン / 内部API 外部トークン / 外部API
想定の利用者 サーバ→サーバ(Next→FastAPIなど) ユーザー→サーバ(ブラウザ/アプリ→API)
露出範囲 非公開(ブラウザに出さない) 公開(インターネットから到達)
保存場所 サーバ環境だけ(env/Secrets) ブラウザCookieやモバイル、外部クライアントにも渡る
有効期限 超短寿命(数分〜十数分、1回ごとでもOK) サービス要件次第(短寿命が推奨だが、内部より長めになりがち)
スコープ/Audience きわめて限定(例:aud=fastapi, scope=profile) ユーザー操作に応じて広め(OAuthスコープ等)
ネットワーク 社内/VPC/Allowlist/mTLSなどでさらに絞る 一般公開(WAF/レート制限/CORS 等で防御)
盗難時の影響 被害はサーバ間の限定用途に限定 ユーザー権限を伴うため被害が拡大しやすい

知っておいたほうがよさそうなこと🧐

❌ページ/レイアウトをクライアント化してしまう

ページやレイアウトをクライアントコンポーネント化してしまうと(= 'use client'を使う)、Auth.jsの認証はサーバー専用のため直接呼べないので、結局ブラウザ→API→サーバ…と回り道になるし、ブラウザに機密情報が渡るので安全性も低下します。
データ取得・認証はサーバー、UIの相互作用はクライアント部品として分けるのがベストプラクティスです。

ページファイルを作るときは以下を認識しておいたほうがよさそうです。

✅クライアントコンポーネントからクライアントコンポーネントを呼び出す
✅サーバーコンポーネント  からクライアントコンポーネントを呼び出す
❌クライアントコンポーネントからサーバーコンポーネント  を呼び出す
ただし、最後のケースにおいても、クライアントコンポーネントのchildrenとしてサーバーコンポーネントを渡すことで✅にできます。

Zenn ざっくりApp Router入門【Next.js】より引用

❌ CORSがあるから大丈夫

CORS(Cross-Origin Resource Sharing)はブラウザが“他サイトのJSからの読み出し”を制限する仕組みであって、認証・権限・秘密の保護の代わりにはなりません。

❌ 環境変数を使えばフロントサーバーで認証処理を行なっても大丈夫

NEXT_PUBLIC_ が付いた環境変数は クライアントバンドルに埋め込まれ、誰でも閲覧可能になります。Secretは NEXT_PUBLIC_ を付けない&'use client'のファイルで呼び出さないが鉄則です。

(補足)たとえ .env.local が .gitignore で守られていても、クライアントコードが NEXT_PUBLIC_FOO を使えば、デプロイ後のJSに値が焼き付くので漏れます。

Discussion