メインコンテンツへスキップ
kt-tech.blog

【設計】ECS Fargate + Next.js + FastAPI のアクセス制御設計 — SSR内部通信の認証をどう解決するか

設計7分で読めます

この記事でわかること

  • ALB が付与する x-amzn-oidc-data ヘッダーが、SSR の内部通信だけ届かない理由
  • middleware / Server Component / CSR の3つで認可チェックを置いたときの差
  • x-internal-api-key で SSR の内部通信を「信頼された経路」として扱う設計

AWS ECS Fargate + ALB(Cognito OIDC 認証)/ Next.js 16(App Router、standalone、basePath あり)/ FastAPI / 同一 VPC 内で Cloud Map 経由のサービス間通信

概要: AWS ALB + Cognito OIDC で認証されたNext.js + FastAPI構成において、SSR内部通信では認証ヘッダーが届かない問題をどう解決するか。Next.js middleware / Server Component / CSR の3パターンを比較検討し、最適な設計を選定した記録。

はじめに

AWS ECS Fargate上でNext.js(フロントエンド)とFastAPI(バックエンド)を動かし、ALB + Cognito + Entra IDでOIDC認証を行っている環境に、「許可されたメールアドレスのみアクセス可能」というアクセス制御を導入することになった。

一見シンプルな要件だが、SSR(Server Side Rendering)の内部通信がALBを経由しないため、認証ヘッダーが届かないという根本的な問題に直面した。

通信経路の整理

まず、リクエストがどの経路を通るかを理解する必要がある。

ブラウザ → ALB → バックエンド(CSR)

ブラウザからのAPI呼び出しはALBを経由する。ALBはCognito認証後に x-amzn-oidc-data ヘッダー(JWT)を自動付与するため、バックエンドはこのJWTからメールアドレスを取得できる。

Next.js SSR → バックエンド(内部通信)

Server Componentからのデータ取得はECS内部のCloud Map DNS経由で直接バックエンドに接続する。ALBを経由しないため、x-amzn-oidc-data ヘッダーは存在しない。

flowchart LR
    B["ブラウザ"] -->|CSR| ALB["ALB<br>Cognito OIDC"]
    ALB -->|ヘッダーあり| API["FastAPI"]
    SSR["Next.js SSR"] -.->|Cloud Map<br>ヘッダーなし| API

この「SSR内部通信にはOIDCヘッダーがない」という事実が、設計の核心的な課題となる。

検討した3つのパターン

パターンA: Next.js middleware で認可チェック

middleware.tsで全リクエストをインターセプトし、x-amzn-oidc-data からメールを取得。バックエンドの /auth/check に内部通信で問い合わせる方式。

ポイント
この却下理由は執筆時点のものです。Next.js 15.5 で Node.js runtime の middleware が stable になり、middleware ファイルに export const runtime = 'nodejs' を書けば Node.js API や api-server.ts を使えます(Next.js 16 でも既定は Edge のまま)。ただし下記の basePath の二重化や実装の複雑さは残るので、Server Component を選んだ判断自体は変わりません。

問題点:

  • middlewareは既定でEdge Runtimeで動作し、api-server.ts(Node.js Runtime)を直接importできない
  • 内部通信用のfetchを別途実装する必要があり、ヘッダー転送やエラー処理が複雑化
  • basePath(/sm)の二重化問題が発生しやすい
  • 実際にstg環境で複数の問題が発生しデプロイ不可に

パターンB: CSR コンポーネントで認可チェック

layout.tsx<Authenticator>クライアントコンポーネントを配置。useEffectでマウント後にブラウザからALB経由で/auth/checkを呼ぶ方式。

メリット: 実装が最もシンプルで、SSR内部通信の問題が完全に無関係。

デメリット: SSRのデータフェッチが認可チェック前に実行され、ローディング表示が一瞬入る。

パターンC: Server Component(layout.tsx)で認可チェック【採用】

ルートのlayout.tsxをasync関数にし、headers()x-amzn-oidc-dataを取得してメールを抽出。api-server.ts経由でバックエンドに許可チェックする方式。

メリット: Node.js Runtimeで動作し、api-server.tsを直接importできる。Edge Runtime制約なし、basePath問題なし。未許可ユーザーにはページのレンダリング自体が実行されない。

比較表

評価軸 A: middleware B: CSR C: Server Component
セキュリティ
UX コンテンツ見えない ローディング挟む コンテンツ見えない
実装の複雑さ 低〜中
SSR内部通信 fetchを別途実装 無関係 api-server.ts流用
Edge Runtime制約 あり なし なし

採用した設計: x-internal-api-key パターン

パターンCを採用し、SSR内部通信には共有秘密鍵(x-internal-api-key)で「信頼された内部通信」と識別する設計とした。

バックエンドの認証ミドルウェアは、リクエストを上から順に判定する:

  1. x-internal-api-key がある → 信頼された内部通信、認証スキップ
  2. x-amzn-oidc-data がある → JWT検証 → メール抽出 → 許可リストチェック
  3. どちらもない → 401

フロントエンドでは、layout.tsxheaders()からOIDCヘッダーを取得し、api-server.ts経由でバックエンドに認可チェックを行う。

TypeScript
// (authenticated)/layout.tsx
export default async function AuthenticatedLayout({ children }) {
  const { authorized, is_admin } = await checkAuth();
  if (!authorized) redirect("/unauthorized");

  return (
    <AuthProvider isAdmin={is_admin ?? false}>
      <Header />
      <main>{children}</main>
    </AuthProvider>
  );
}

Edge RuntimeとNode.js Runtimeの違い

Next.jsには2つのサーバーサイド実行環境がある:

  • Edge Runtime: 軽量だが制約あり。fsnet等のNode.js APIが使えない。middlewareはデフォルトでこちら
  • Node.js Runtime: フル機能。Server Components、API Routesはこちら

middlewareからはapi-server.tsを直接importできないが、layout.tsx(Server Component)からは同じNode.js Runtimeなのでそのまま使える。

Tips

  • ALBのx-amzn-oidc-dataはALB→ターゲット間でのみ付与される。SSR内部通信では使えない
  • x-internal-api-keyはECS内部通信でのみ使用し、セキュリティグループで外部からのアクセスを遮断する前提
  • Server ComponentのルートグループでCSR/SSRの認証パスを分離すると見通しがよい
  • x-internal-api-key の実体は Secrets Manager に置き、タスク定義の secrets から注入する。環境変数に直書きするとタスク定義の履歴に残り続ける

まとめ

  • SSR内部通信ではALBのOIDCヘッダーが届かないため、別の認証手段が必要
  • Next.js middlewareはEdge Runtime制約があり、Server Componentでの認可チェックが最もシンプル
  • x-internal-api-keyパターンでSSR内部通信を信頼済みとして扱う設計が有効
  • layout.tsxでの認可チェックにより、未許可ユーザーにはレンダリング自体が実行されないセキュリティを実現

参考リンク

同じ ECS + FastAPI 構成での設計はこちらでも書いています。

更新履歴

  1. 目次を追加、参考リンクと関連記事へのリンクを追加、x-internal-api-key の保管場所について Tips に追記
  2. Next.js 15.5 で Node.js runtime の middleware が stable になった点を注記。通信経路を図解に変更

この記事のタグ