
AgentCore Gateway に Cognito JWT 認証を追加して AI Starter から呼び出してみた
はじめに
前回の記事 では、AgentCore Gateway を MCP サーバーとして登録し、VPC Lambda のサブエージェントを認証なし(None)で呼び出す構成を紹介しました。
構成の動作確認ができたところで、次の疑問が生まれました。
「本番運用するなら認証は必須。でも、どの認証方式を選べばいいのか?」
AgentCore Gateway には認証方式として IAM・JWT・None の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 の役割

Cognito はトークン発行機関(Issuer) として機能します。具体的には:
- AI Starter のログイン基盤: AI Starter はもともと Cognito User Pool でユーザー認証しています
- Resource Server でスコープ定義: MCP Gateway 専用のカスタムスコープ(
https://mcp.internal/access)を User Pool に追加します - OAuth 2.0 フローでトークン発行: ユーザーが「接続」をクリックすると、Cognito が認可コードフロー(PKCE)を実行し、カスタムスコープ付きのアクセストークンを発行します
- 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...(固定のトークン文字列)
サーバーはトークンの署名・iss・scope などのクレームを検証するだけで済みます。クライアントは毎回の計算なしにトークンを渡すだけです。
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 |

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

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(フルスコープ文字列) |

検出 URLはCognitoのユーザープール画面からコピーできます。

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 |

クライアント ID、クライアント シークレット は Cognito → ユーザープール → ドメイン からコピーできます。

COGNITO_DOMAIN は Cognito → ユーザープール → ドメイン からコピーできます。

McpServer の登録
管理コンソール → 外部サービス → 新規追加(種別: McpServer)
| フィールド | 値 |
|---|---|
| 表示名 | (任意) |
| 種別 | MCP サーバー |
| サーバー URL | AgentCore Gateway の MCP エンドポイント |
| 認証プロバイダー ID | 上で作成した AuthProvider の ID |

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

-
設定 → 連携サービス を開く
-
登録した AuthProvider が表示されるので「接続」をクリック

-
Cognito のログイン画面にリダイレクトされる
-
ログイン → 認可 → コールバック URL に戻ってトークンが保存される

これで 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 検証でスコープの完全一致チェックが走るため、access ≠ https://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(許可されたオーディエンス)のチェックを外す。iss・client_id・scope の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 呼び出しが実現できます。マルチテナントで各ユーザーの権限を細かく制御したい場合に有効な構成です。






