AgentCore Gateway に Cognito JWT 認証を追加して AI Starter から呼び出してみた

AgentCore Gateway に Cognito JWT 認証を追加して AI Starter から呼び出してみた

AgentCore Gateway に Cognito User Pool を使った JWT 認証を追加し、AI Starter の MCP クライアントから OAuth 2.0 フローで取得したアクセストークンで呼び出す構成を紹介します。IAM 認証(SigV4)との違い、Resource Server のスコープ設定、Cognito アクセストークンに aud クレームが存在しない問題など、3つのハマりポイントとその解決策を実録します。
2026.07.22

はじめに

前回の記事 では、AgentCore Gateway を MCP サーバーとして登録し、VPC Lambda のサブエージェントを認証なし(None)で呼び出す構成を紹介しました。

構成の動作確認ができたところで、次の疑問が生まれました。

「本番運用するなら認証は必須。でも、どの認証方式を選べばいいのか?」

AgentCore Gateway には認証方式として IAMJWTNone の3種類があります。「既存の Cognito User Pool がある。それを使って JWT 認証を追加できないか?」と思い、試してみました。

結論から言うと、3つのハマりポイントを踏み抜きながらも動かすことができました。このデバッグ記録が同じ構成を試す方の参考になれば幸いです。

Amazon Cognito とは

Amazon Cognito は AWS のユーザー認証・認可サービスです。主に2つのコンポーネントで構成されます。

User Pool(ユーザープール): ユーザーのサインアップ・サインインを管理するディレクトリです。メール/パスワード認証、MFA、ソーシャルログイン連携などを提供します。ログイン成功時に ID トークン・アクセストークン・リフレッシュトークン(いずれも JWT 形式)を発行します。

Identity Pool(ID プール): JWT トークンを AWS 一時認証情報(IAM ロール)に変換します。S3 や DynamoDB を直接操作したい場合に使います。本記事では使用しません。

この記事での Cognito の役割

agentcore-gateway-cognito-jwt-auth-ai-starter-cognito-role

Cognito はトークン発行機関(Issuer) として機能します。具体的には:

  1. AI Starter のログイン基盤: AI Starter はもともと Cognito User Pool でユーザー認証しています
  2. Resource Server でスコープ定義: MCP Gateway 専用のカスタムスコープ(https://mcp.internal/access)を User Pool に追加します
  3. OAuth 2.0 フローでトークン発行: ユーザーが「接続」をクリックすると、Cognito が認可コードフロー(PKCE)を実行し、カスタムスコープ付きのアクセストークンを発行します
  4. OIDC 検出エンドポイントを提供: AgentCore Gateway は /.well-known/openid-configuration を参照して Cognito の公開鍵を取得し、受け取った JWT を検証します

つまり Cognito は「ユーザーが誰か」を証明するだけでなく、「このユーザーは MCP Gateway にアクセスしてよい」という認可情報をトークンに埋め込む役割も担っています。

なぜ IAM 認証ではなく JWT 認証を選んだか

まず最初の選択肢の整理です。

AgentCore Gateway の認証方式:

方式 仕組み 向いている用途
None 認証なし 検証・プロトタイプ
IAM SigV4 署名リクエスト AWS サービス間通信
JWT Bearer トークン ユーザー認証済みアプリ

IAM 認証を選ばなかった理由: AI Starter の MCP クライアントは Authorization: Bearer <token> ヘッダーで通信します。IAM 認証は SigV4 署名が必要なため、Bearer トークンを送るクライアント実装とは根本的に相性が合いません。

SigV4 と Bearer トークンの違い

2つの方式は「クライアントが何を送るか」が根本的に異なります。

SigV4 はリクエスト全体に署名する方式です。クライアントは送信のたびに、リクエストのメソッド・URL・ヘッダー・ボディのハッシュ・タイムスタンプを組み合わせて HMAC-SHA256 で署名を計算し、Authorization ヘッダーに埋め込みます。

Authorization: AWS4-HMAC-SHA256
  Credential=AKID.../aws4_request,
  SignedHeaders=host;x-amz-date,
  Signature=abc123...(リクエストごとに異なる)

署名にタイムスタンプとボディハッシュが含まれるため、トークンの盗用・改ざんへの耐性が高い反面、クライアント側でリクエスト送信時に毎回 HMAC 計算が必要です。

Bearer トークン(JWT) はあらかじめ発行されたトークンをそのまま送る方式です。

Authorization: Bearer eyJhbGci...(固定のトークン文字列)

サーバーはトークンの署名・issscope などのクレームを検証するだけで済みます。クライアントは毎回の計算なしにトークンを渡すだけです。

AI Starter の MCP SDK は OAuth フローで取得したアクセストークンを DynamoDB に保存し、API 呼び出し時にそのまま Bearer として送ります。SigV4 のように「送信直前に HMAC を計算してヘッダーを組み立てる」実装は含まれていないため、IAM 認証とは根本的に相性が合いません。

JWT を選んだ理由: AI Starter はユーザーが Cognito でログインする仕組みです。同じ Cognito User Pool に Resource Server(カスタムスコープ)を追加すれば、ユーザーが OAuth 2.0 フローで取得したトークンをそのまま Gateway の JWT 認証に使えます。

Cognito JWT 認証の設定

Resource Server の作成

Cognito User Pool に Resource Server を追加します。Resource Server は「このトークンがアクセスして良い対象(API)」を定義するものです。

Cognito コンソール → ユーザープール → アプリの統合 → リソースサーバー

設定
リソースサーバー名 任意(例: MCP Gateway
識別子(identifier) https://mcp.internal(任意の URI 形式)
スコープ名 access

SCR-20260722-kibp (1)

アプリクライアントの作成

OAuth フロー用のアプリクライアントを作成します。

  • クライアントタイプ: 従来のWEBアプリケーション
  • 許可されている OAuth フロー: 認証コード
  • PKCE の要求: 有効化(AI Starter の MCP SDK が PKCE を使用)
  • 許可されているスコープ: https://mcp.internal/access
  • コールバック URL: https://YOUR_APP_URL/auth/integrate/{AuthProviderId}/callback

SCR-20260722-jxmu (1)

AI Starter でローカル開発している場合は http://localhost:{PORT}/auth/integrate/{AuthProviderId}/callback を登録します。

Gateway の JWT 設定

Bedrock コンソール → AgentCore → ゲートウェイ → 編集

設定
検出 URL https://cognito-idp.{region}.amazonaws.com/{USER_POOL_ID}/.well-known/openid-configuration
許可されたクライアント Cognito アプリクライアント ID
Allowed scopes https://mcp.internal/access(フルスコープ文字列)

SCR-20260722-nish (1)
検出 URLはCognitoのユーザープール画面からコピーできます。

SCR-20260722-njit (1)

AI Starter の設定

AI Starter の管理コンソールで外部サービスを登録します。

AuthProvider の登録

管理コンソール → 外部サービス → 新規追加(種別: AuthProvider)

フィールド
ID Cognitoのアプリクライアントに設定したAuthProviderId
表示名 Cognito MCP Auth(任意)
種別 OAuth 認証プロバイダー
クライアント ID Cognito アプリクライアント ID
クライアント シークレット Cognito アプリクライアントシークレット
認可エンドポイント https://{COGNITO_DOMAIN}/oauth2/authorize
トークンエンドポイント https://{COGNITO_DOMAIN}/oauth2/token
スコープ https://mcp.internal/access

SCR-20260722-nouh (1)
クライアント IDクライアント シークレット は Cognito → ユーザープール  →  ドメイン からコピーできます。

SCR-20260722-kbnc (1)
COGNITO_DOMAIN は Cognito → ユーザープール  →  ドメイン からコピーできます。

SCR-20260722-kbzt (1)

McpServer の登録

管理コンソール → 外部サービス → 新規追加(種別: McpServer)

フィールド
表示名 (任意)
種別 MCP サーバー
サーバー URL AgentCore Gateway の MCP エンドポイント
認証プロバイダー ID 上で作成した AuthProvider の ID

SCR-20260722-kcgc (1)

OAuth 連携(ユーザー操作)

管理コンソールではなく、AI Starter のユーザー画面から操作します。

agentcore-gateway-cognito-jwt-auth-ai-starter-oauth-flow

  1. 設定 → 連携サービス を開く

  2. 登録した AuthProvider が表示されるので「接続」をクリック
    SCR-20260722-kesc

  3. Cognito のログイン画面にリダイレクトされる

  4. ログイン → 認可 → コールバック URL に戻ってトークンが保存される
    SCR-20260722-kqqd

これで DynamoDB の UserIntegration テーブルに Cognito のアクセストークンが保存されます。

403 デバッグ実録

OAuth 連携に成功してもアシスタントを呼び出すと MCP サーバーがスキップされ続けました。

WARN: Failed to resolve MCP server, skipping
  mcpServerId: "YOUR_MCP_SERVER_ID"
  error: { "code": 403 }

3つのハマりポイントがありました。

ハマり1: Gateway の検出 URL がプール ID を間違えていた

Gateway の JWT 設定を編集するとき、OIDC 検出 URL に別のユーザープール ID を入力していました。

AI Starter サーバーログを見ると起動時に Issuer が表示されます:

INFO: Discovered issuer
  issuer: "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXX"

この pool ID と、Gateway の検出 URL の pool ID が一致しているか確認してください。

修正: Gateway の検出 URL を正しいユーザープール ID で設定し直す。

ハマり2: Gateway のスコープがフル形式ではなかった

Gateway の Allowed scopes に access とだけ設定していましたが、Cognito が発行するトークンのスコープクレームにはフル形式 https://mcp.internal/access が入ります。

JWT 検証でスコープの完全一致チェックが走るため、accesshttps://mcp.internal/access で 403 になります。

修正: Gateway の Allowed scopes を access から https://mcp.internal/access(フルスコープ文字列)に変更する。

ハマり3: Cognito アクセストークンに aud クレームがない

上記2つを修正してもまだ 403。実際に DynamoDB から取得したトークンを Base64 デコードして確認しました。

# DynamoDB からトークンを取得してデコード
TOKEN=$(aws dynamodb get-item \
  --table-name YOUR_TABLE-user-integration \
  --key '{"userId":{"S":"USER_ID"},"serviceId":{"S":"AUTH_PROVIDER_ID"}}' \
  --query 'Item.accessToken.S' --output text)

echo $TOKEN | cut -d'.' -f2 | python3 -c "
import sys,base64,json
p=sys.stdin.read().strip()
p+='='*(4-len(p)%4)
print(json.dumps(json.loads(base64.b64decode(p)),indent=2))
"

デコード結果:

{
  "sub": "...",
  "iss": "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXX",
  "client_id": "YOUR_CLIENT_ID",
  "scope": "https://mcp.internal/access",
  "token_use": "access",
  ...
}

aud クレームが存在しません。Cognito のアクセストークンには aud が含まれず、代わりに client_id クレームが使われます。Gateway の「許可されたオーディエンス」チェックは aud クレームを検証しようとしますが、クレームがないため検証に失敗していました。

修正: Gateway の JWT 設定で Allowed audiences(許可されたオーディエンス)のチェックを外すissclient_idscope の3つで十分に検証できます。

3つ修正後の動作確認

サーバーログに以下が出れば成功です:

DEBUG: Assitant message completed  # ※サーバーログ出力ママ(Assistant のスペルミス)
  message: {
    "content": [
      {
        "type": "tool",
        "toolName": "YOUR_TARGET___YOUR_TOOL",
        "state": "completed",
        "result": "..."
      }
    ]
  }

MCP ツールが呼び出され、Lambda サブエージェントが実行されたことが確認できます。

まとめ

AgentCore Gateway に Cognito JWT 認証を追加し、AI Starter から OAuth 2.0 フローで取得したトークンでサブエージェントを呼び出すことができました。

設定のポイント:

  • MCP クライアントが Authorization: Bearer を使う場合、IAM 認証(SigV4)は使えない → JWT 一択
  • Gateway のスコープ設定はフルスコープ文字列(https://mcp.internal/access)で指定する
  • Cognito アクセストークンに aud クレームはない → Gateway の Allowed audiences は無効化する
  • コールバック URL のホスト・ポートは環境変数によって変わることに注意

Cognito アクセストークンの JWT クレーム構成(まとめ):

クレーム Gateway での使われ方
iss Cognito User Pool URL 検出 URL で自動検証
client_id アプリクライアント ID Allowed clients で検証
scope フルスコープ文字列 Allowed scopes で検証
aud 存在しない Allowed audiences は無効化

JWT を使うことで、AI Starter のユーザーごとに個別のアクセストークンを使った Gateway 呼び出しが実現できます。マルチテナントで各ユーザーの権限を細かく制御したい場合に有効な構成です。

この記事をシェアする

関連記事