🤖

CognitoとAgentCore GatewayでMCPサーバーにOAuth認証をつけよう

に公開

こんにちは! AWS でAI/ML Specialist SAをしている Kondo です。
https://x.com/yakitori_eng

本ブログは AWS AI Agent ブログ祭り(Zenn: #awsaiagentblogfes, X: #AWS_AI_AGENT_ブログ祭り)の第 10 日目です。

サマリー

  • DCRなしのOAuth 3LO認証をリモートMCPサーバーに付与しました
  • 構成は、MCPサーバーをAgentCore Gateway、認可サーバーをCognito、クライアントは自作コードです
  • クライアントにClaude DesktopやVSCodeなどの既存のエージェント製品を使うと失敗したのでだれかうまくいった人いたら教えて下さい(追記: Amazon Quick Suiteからの接続には成功しました)
  • コードはすべてこちらで公開しています https://github.com/kondo-kj/mcp-with-oauth

動作イメージ

  1. クライアントからリクエストする

  2. ログイン画面がブラウザで表示される

  3. ログインする

  4. MCPが利用可能になる

MCPサーバーにOAuth認証をつけるとは

MCPサーバーというとstdio方式がまだ主流で、多くの人がローカルの中でMCPサーバーを利用しているかと思います。そんな中、リモートMCPサーバーも徐々に利用されてきています。

MCPの仕様では認可の方式が定められています。
https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization

stdio方式では環境からクレデンシャルを取得して利用することが推奨されており、リモートMCPサーバーではOAuthをベースとした方式が推奨されています。

stdio方式では長期のクレデンシャル情報を保存することが多いですが、リモートMCPサーバーの方式では短命なトークンを扱うため安全性が高いとされています。それ以外にもリモートMCPサーバーのほうが利用バージョンを統一できるなどのメリットもあります。

このあたりのMCPの認証認可の話はこちらの資料がわかりやすいのでおすすめです。
https://speakerdeck.com/hi120ki/mcp-authorization

そんなこんなで、リモートMCPサーバーを立ててOAuth認証を付与するというのはニーズがあるわけです(これが言いたかっただけ)。

MCPの認可仕様

MCPの認可仕様についてざっくり確認していきましょう。私はセキュリティの専門家ではないため、正確ではない可能性がありますのでご承知おきください。AI/MLエンジニアの視点でのMCP認可仕様です。

本記事で参照するのは2025-06-18のSpecです。そろそろ新しくなりそうな気配も感じております。

これが認可フローです。ここでアレルギーが出てしまう人(私)もいると思いますが、ここをちゃんと確認するのが結局近道です。

https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization#authorization-flow-steps

フローの詳細を確認するにはこちらの記事がおすすめです
https://qiita.com/yokawasa/items/74717789465ef2aeedfe

本記事では、ざっくりサマった内容と、各サーバーが持つべき役割にフォーカスしてお伝えします

1. 認可サーバー検出

最初はクライアントとMCPサーバーのやり取りです。このステップはこの後の認可に必要な情報を集めていると思っていただければいいと思います。
最終的にはこのような情報を得られます。認可サーバーとリソースサーバーの情報が得られました。スコープのような情報が含まれることもあります。

{
    "authorization_servers": [
        "https://cognito-idp.us-west-2.amazonaws.com/us-west-2_hogehoge"
    ],
    "resource": "https://fugafuga.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp"
}

このステップからわかるMCPサーバーが持つべき役割

  • トークンなしのリクエストが来たらWWW-AuthenticatedヘッダーにリソースメタデータURLを付与して返す
  • Protected Resource Metadata(PRM)リクエストが来たらメタデータを返す

2. 認可サーバーのメタデータ検出

次は、クライアントと認可サーバーのやりとりです。先程のステップで認可サーバーのURLがわかっているので認可サーバーにリクエストができます。

リクエストの結果、認可サーバーメタデータとして次のようなjsonを取得します。

認可サーバーメタデータ
{
    "authorization_endpoint": "https://fugafuga.auth.us-west-2.amazoncognito.com/oauth2/authorize",
    "end_session_endpoint": "https://fugafuga.auth.us-west-2.amazoncognito.com/logout",
    "id_token_signing_alg_values_supported": [
        "RS256"
    ],
    "issuer": "https://cognito-idp.us-west-2.amazonaws.com/hogehoge",
    "jwks_uri": "https://cognito-idp.us-west-2.amazonaws.com/hogehoge/.well-known/jwks.json",
    "response_types_supported": [
        "code",
        "token"
    ],
    "revocation_endpoint": "https://fugafuga.auth.us-west-2.amazoncognito.com/oauth2/revoke",
    "scopes_supported": [
        "openid",
        "email",
        "phone",
        "profile"
    ],
    "subject_types_supported": [
        "public"
    ],
    "token_endpoint": "https://fugafuga.auth.us-west-2.amazoncognito.com/oauth2/token",
    "token_endpoint_auth_methods_supported": [
        "client_secret_basic",
        "client_secret_post"
    ],
    "userinfo_endpoint": "https://fugafuga.auth.us-west-2.amazoncognito.com/oauth2/userInfo"
}

このステップからわかる認可サーバーが持つべき役割

  • 認可サーバーメタデータ検出(RFC8414)に対応している

Dynamic Client Registration(DCR)について

DCRはクライアント登録を事前にしなくて良くなる仕組み(雑)ですが、以下の理由により本記事ではDCRなしを前提とします

こちらについて、詳細は以下が詳しいです
https://speakerdeck.com/hi120ki/mcp-authorization
https://www.youtube.com/watch?v=XBGLXyDeoRM&t=2017s

※ただし、DCRなしを前提としたせいで発生する問題もありますので後述します

OAuth 2.1 3LO 認可フロー

ここは通常のOAuth2.1のAuthorization Code Grantの認可フローです。やや違うのはResource Indicator(RFC 8707)ですが、詳しくない方はそういうものがあると思ってください。

このステップからわかる認可サーバーが持つべき役割

  • OAuth 2.1のAuthorization Code Grantの認可フローに対応していること(OAuth 2.0に対応していれば十分なこともあります)
  • Resource Indicator(RFC 8707)に対応していること

このステップからわかるMCPサーバーが持つべき役割

  • リクエストに付与されたアクセストークンを検証して認可を行う

各サーバーの役割を整理

MCPサーバーが持つべき役割

  • トークンなしのリクエストが来たらWWW-AuthenticatedヘッダーにリソースメタデータURLを付与して返す
  • Protected Resource Metadata(PRM)リクエストが来たらメタデータを返す
  • リクエストに付与されたアクセストークンを検証して認可を行う

認可サーバーが持つべき役割

  • 認可サーバーメタデータ検出(RFC8414)に対応している
  • OAuth 2.1のAuthorization Code Grantの認可フローに対応していること(OAuth 2.0に対応していれば十分なこともあります)
  • Resource Indicator(RFC 8707)に対応していること

これらの要件は、この役割をもったコードを書けば実現できるわけですが、めんどくさいですよね?なのでマネージドサービスでできたら嬉しいわけです。

MCPサーバーにAgentCore Gateway、認可サーバーにCognitoを用いることでこれらの要件を満たすことができることを本記事では示していきます。

クライアントは主にローカルのPythonコード(client.py)を用いて実験を行いますが、Claude CodeやClaude Desktop, VSCodeのようなエージェント製品もクライアントに用いた実験も軽く行います。

1. Cognitoの設定

まず最初にCognitoのセットアップをします。

⚠️ 注意: setup-cognito.py 内のユーザーID とパスワードを適宜変更してください。

uv run python setup-cognito.py

このスクリプトでは、次のことを実行します。

  • ユーザープールの作成
  • アプリクライアントの作成
  • ユーザープールのドメインを作成し、マネージドログインの有効化
  • テストユーザーの作成
  • ログインURLの表示

ポイント

  • ログイン画面の表示にはHosted UIとマネージドログインの2種類がありますが、RFC8707はマネージドログインしか対応していませんので、こちらを使います。ドメインの設定からマネージドログインを有効化しましょう。

2. AgentCore Gatewayの設定

AgentCore GatewayはMCPサーバーを構築するためのマネージドサービスです。AWS LambdaやOpenAPI SpecのAPIをMCPサーバー化することができます。また、どこか別の場所でホストされたリモートMCPサーバーの前段にGatewayを置くこともできて、トークン検証などを自分で実装する必要がなくなります。

README.mdに従って.envファイルで環境変数を設定してください。

uv run python create_gateway.py

ポイント

  • 1で作成したCognitoのユーザープールをGatewayの認証設定で指定します

3. リソースサーバーの追加

RFC 8707に対応するために、CognitoのリソースサーバーにGatewayのエンドポイントを追加します。

README.mdに従って.envファイルで環境変数を設定してください。

uv run python add_resource_server.py

4. クライアントでの接続

クライアントでMCPサーバーに接続します

uv run python client.py

するとログイン画面が表示されるはずですので、設定したユーザー名とパスワードでログインしてください。ターミナルで >mcp が表示された接続完了です。listコマンドやcallコマンドでmcpを操作してみましょう。

client.pyは、https://github.com/modelcontextprotocol/python-sdk/tree/main/examples/clients/simple-auth-client を参考にカスタマイズしています。

内部では、MCD SDKの機能を使っています。気になる人は見てみてください。最も重要なコンポーネントはOAuthClientProviderです。

ステップbyステップでOAuthのフローを確認してみる

先程ざっくり確認したOAuthのフローをcurlでひとつずつリクエストして、挙動を見てみましょう
clinet.pyが中で行っている挙動を一個一個確認したい人は試してみてください

1. 認可サーバー検出

まずはトークンなしでリクエストします

curl -i -X POST $MCP_SERVER_URL \
-H 'Content-Type: application/json' \                          
-d '{"jsonrpc":"2.0","id":"ping","method":"tools/list"}'

HTTP/2 401 
www-authenticate: Bearer resource_metadata="https://sample-agentcore-gateway-hogehoge.gateway.bedrock-agentcore.us-west-2.amazonaws.com/.well-known/oauth-protected-resource"
x-amzn-remapped-content-type: application/json

仕様通りにwww-authenticateヘッダーにPRMのためのエンドポイントが返ってきました

次は、PRMリクエストです(RFC 9728)

curl -i https://sample-agentcore-gateway-hogehoge.gateway.bedrock-agentcore.us-west-2.amazonaws.com/.well-known/oauth-protected-resource

{
    "authorization_servers": [
        "https://cognito-idp.us-west-2.amazonaws.com/us-west-2_fugafuga"
    ],
    "resource": "https://sample-agentcore-gateway-hogehoge.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp"
}

PRMが返ってきました

2. 認可サーバーのメタデータ検出

次は認可サーバーに対してメタデータをリクエストします(RFC 8414)

curl -i https://cognito-idp.us-west-2.amazonaws.com/us-west-2_fugafuga/.well-known/openid-configuration

認可サーバーのメタデータ
{
  "authorization_endpoint": "https://hogehoge.auth.us-west-2.amazoncognito.com/oauth2/authorize",
  "end_session_endpoint": "https://hogehoge.auth.us-west-2.amazoncognito.com/logout",
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "issuer": "https://cognito-idp.us-west-2.amazonaws.com/us-west-2_fugafuga",
  "jwks_uri": "https://cognito-idp.us-west-2.amazonaws.com/us-west-2_fugafuga/.well-known/jwks.json",
  "response_types_supported": [
    "code",
    "token"
  ],
  "revocation_endpoint": "https://hogehoge.auth.us-west-2.amazoncognito.com/oauth2/revoke",
  "scopes_supported": [
    "openid",
    "email",
    "phone",
    "profile"
  ],
  "subject_types_supported": [
    "public"
  ],
  "token_endpoint": "https://hogehoge.auth.us-west-2.amazoncognito.com/oauth2/token",
  "token_endpoint_auth_methods_supported": [
    "client_secret_basic",
    "client_secret_post"
  ],
  "userinfo_endpoint": "https://hogehoge.auth.us-west-2.amazoncognito.com/oauth2/userInfo"
}

3. OAuth 2.1 認可コードグラントフロー

得られた情報から認証URLを作成します

https://hogehoge.auth.us-west-2.amazoncognito.com/oauth2/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&scope=openid%20email%20profile&resource=https%3A%2F%2Fsample-agentcore-gateway-hogehoge.gateway.bedrock-agentcore.us-west-2.amazonaws.com%2Fmcp&state=YOUR_STATE_VALUE

ログインするとリダイレクトされて、そのURLに認可コードが含まれています。

認可コードを使って、Cognitoに対して、トークンをリクエストします

curl -X POST https://hogehoge.auth.us-west-2.amazoncognito.com/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=YOUR_AUTHORIZATION_CODE" \
  -d "redirect_uri=YOUR_REDIRECT_URI" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "resource=https://sample-agentcore-gateway-hogehoge.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp"

エージェント製品をクライアントにして試してみる

MCPサーバーを使うクライアントとして、最もよく使われるのは既存のエージェント製品です。例えば、Claude Code, Claude Desktop, VSCode, Amazon Q Developer等です。そちらでも検証してみましょう。ただし、これについてはあまりじっくり検証できていません(内部挙動が見えないのでトラブルシュートもうまくできない...)。

先に結論から書くと、うまくできたツールはありませんでした
(追記: Amazon Quick Suiteのみ接続に成功しました)

今回問題なのはDCRなしを前提としていることです。なぜなら上記のエージェント製品の多くはDCRありを前提としているものが多いからです。例えば下記のツールはすでに難しいことがWeb上で指摘されています。

逆に下記のツールはDCRなしでもできる旨の記述が書かれていました

VS Code first starts with a Dynamic Client Registration (DCR) handshake and then falls back to a client-credentials workflow if the IdP does not support DCR. This gives more flexibility to the various IdPs to create static client IDs or specific client ID-secret pairs for each MCP server accordingly.

ただし、上記の記述からclient-credentials workflowしかサポートされていないと解釈することができます。実際、試してみたところ、ログイン画面がうまく表示されませんでした。

7月現在、ユーザーはDCRをサポートしていないサーバーを構成する際に、カスタムクライアントIDとクライアントシークレットを指定することもできます。

こちらはやってみたところ、生成されるログインURLがなぜかCognitoではなくMCPサーバー側のドメインを使っているという間違いが見られました。Claude Desktopのバグか私のミスか。

https://gateway-with-runtime-hogehoge.gateway.bedrock-agentcore.us-west-2.amazonaws.com/authorize?response_type=code&client_id=22zzzzzzzzzzzzzzz&redirect_uri=https%3A%2F%2Fclaude.ai%2Fapi%2Fmcp%2Fauth_callback&code_challenge=dxtp2ntCsF2kziQ7OcoI-uwkHQaW2nzyGMUu__QYJ1Y&code_challenge_method=S256&state=319Kn6QHs0esutZPrkjv9JQuInM6CLS5ZgHqFp1nGNg&scope=claudeai

ChatGPTでも試してみましたが、うまくいきませんでした

(追記)Amazon Quick Suiteをクライアントにした場合(唯一の成功例)

Amazon Quick Suiteはノーコードでエージェントを構築できるようなサービスですが、MCP Serverをコネクトできる機能があります。
https://aws.amazon.com/jp/blogs/machine-learning/connect-amazon-quick-suite-to-enterprise-apps-and-agents-with-mcp/

こちらの機能を用いたところ、接続に成功しました。

  1. インテグレーションの作成

  2. 認証設定

  1. Cognitoのアプリケーション側にリダイレクトURLを設定する

  1. Quick Suiteで接続したMCPを利用したチャットエージェントを作成する

まとめと今後の展望

MCPサーバーにOAuthを付与するという話はいろいろな人と会話する中で、よく話題に上がっていたため、今回その方法をまとめました。先述のようにDCRがあると現状いろいろな観点で難しいですが、DCRなしの議論があまりWeb上で見つからなかったため、今回思い切ってDCRなしの前提としました。

今後また仕様が変わったタイミングでいろいろと検証をしようと思います。

アマゾン ウェブ サービス ジャパン (有志)

Discussion