安全なMCPへの第一歩 : Authorization の仕様を理解する

MCP の便利さを感じる一方、セキュリティに不安を感じる方もいるのではないでしょうか。例えば、公式で提供されている [PostgreSQL の MCP](https://github.com/modelcontextprotocol/servers/blob/main/src/postgres/README.md) では user/password が素のテキストで Config ファイルに記載されていますし、[GitHub の MCP](https://github.com/modelcontextprotocol/servers/tree/main/src/github) でも `input` で入力受付が可能な VSCode 以外はアクセストークンが Config ファイルに平書きされています。環境変数を使った回避方法もありますが、様々なツールを扱う Config ファイルが誤って Commit などされた場合のダメージは計り知れないものがあります。

```json:GitHub の MCP 設定例
{
  "mcpServers": {
    "github": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "GITHUB_PERSONAL_ACCESS_TOKEN",
        "ghcr.io/github/github-mcp-server"
      ],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>"
      }
    }
  }
}
```

[MCP Specification の `2025-03-26` 版](https://modelcontextprotocol.io/specification/2025-03-26) では、待望の Authorization について仕様が追加されました。本記事では、その内容と実装のアプローチをお伝えします。なお、可能な限り正確に書くよう努めていますが著者はセキュリティの専門家ではないため、もし記載内容に不備や曖昧な点があればコメントを頂ければ幸いです。

以下に要点をまとめます。

* **MCP の Authorization には OAuth が採用された** : `2025-03-26` 版での Authorization は、OAuth 2.1/2.0 をベースにしています。これにより MCP Server 個別に利用可能な機能範囲を許可できます (GitHub なら、Server A には Private Repository へのアクセスを許可するけど B にはしないなど)。OAuth 2.1 ではクライアント固有の id である code verifier をアクセストークン発行時に認可サーバー側で検証をする [PKCE (Proof Key for Code Exchange)](https://datatracker.ietf.org/doc/html/rfc7636) が必須となっています。これにより悪意ある MCP サーバーのインストールで認可コードが奪取された場合の被害を一定防ぐことが出来ます。野良の MCP をふらっとインストールする機会は不正なモバイルアプリ等をインストールする機会より多いと考えられ、その意味で安全な対策が取られています
* **MCP Server ごとに認証・認可する課題が残る** : OAuth は認可の仕様であり、アプリケーションごとに行う必要があります。一度「認証」したユーザーに MCP Server A, B, C の利用を許可するといった Single Sign On (SSO) のような体験を実現するには認証の仕組みである [OpenID Connect](https://www.okta.com/openid-connect/) など補完的な仕組みが不可欠です。こうした仕様は今後検討されると思われます
* **MCP Authorization の実装はこれから** : 理由として、仕様自体がまだ不確定なためです。`2025-03-26` 版で初めて策定され、落ち着くには一定時間がかかる理解です。ベースとしている [OAuth 2.1 自体が本記事執筆時点でまだ確定していなかったり](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-00)、追加される仕様もそれなりにあると思われます。特に、2025/5 時点で draft に記載されている [Protected Resource Metadata (RFC9728)](https://datatracker.ietf.org/doc/html/rfc9728) は Agent が MCP Server に必要な認証・認可プロセスを始めるのに不可欠な仕様です。こうした状況のためライブラリやサポートしている Managed も少ないのが現状です

以降のセクションでは、上記 3 点をより深く理解するための技術的背景と現時点の実装について記載します。MCP の登場により HTTP 世界の基礎的な理解が改めて問われている印象があり、今一度学び直す良いタイミングが来ていると思います。私が調べられる限りでまとめた内容や参考文献を書いていますので、お役に立てば幸いです！

## OAuth 2.1 による MCP Server の認可プロセス

[`2025-03-26` 版の Authorization](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization) にそって、MCP Server に対し GitHub / Google など外部リソースにアクセスする認可を与えるプロセスを示します。[2.2 Example: authorization code grant](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization#2-2-example%3A-authorization-code-grant) に認可のフローがありますが、本文中で言及がある通り MCP Server 自身で認可を行っているため下記ではより一般的な外部認可 (Google や GitHub) を想定したフローを記載しています。

```mermaid
sequenceDiagram
    participant UA as User-Agent (Browser)
    participant Client as MCP Client
    participant MCP as MCP Server
    participant AuthZ as External AuthZ Server<br/>(GitHub/Google)

    Client->>MCP: MCP Request
    MCP-->>Client: HTTP 401 Unauthorized

    rect rgb(255, 255, 200)
    Note over Client: Generate PKCE code_verifier and code_challenge
    end

    Client->>UA: Open browser with authorization URL + code_challenge
    UA->>AuthZ: GET /authorize with PKCE challenge

    rect rgb(255, 255, 200)
    Note over AuthZ: User logs in and authorizes
    end

    AuthZ-->>UA: Redirect to callback URL with auth code
    UA->>Client: Callback with authorization code

    Client->>AuthZ: Token Request with code + code_verifier
    AuthZ-->>Client: Access Token (+ Refresh Token)

    Client->>MCP: MCP Request with Access Token
    MCP-->>Client: MCP Response (authenticated)

    rect rgb(200, 255, 200)
    Note over Client, MCP: Begin standard MCP message exchange
    end
```

OAuth による認可は下記記事がとても分かりやすいです。ただ、OAuth 2.1 では PKCE が必須となりインプリシットフロー、リソースオーナー・パスワード・クレデンシャルズフロー (ROPC) が削除されます ([参考](https://stytch.com/blog/oauth-2-1-vs-2-0/))。

https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f

OAuth 2.1 の主な変更点は次の通りです

* PKCE の標準化 : PKCE ではクライアント側で一意にもつキー (code verifier) で認証を行いアクセストークン発行時にこのキーを必須とすることで (`Token Request with code + code_verifer` の箇所) 認可コードが URL 等から奪取されてもアクセストークン発行を防止する効果があります
* インプリシットフローの削除 : インプリシットフローでは、認可コードでなくアクセストークンを直接発行するため、Callback URL でさらされるアクセストークンを奪取された場合たやすく不正な利用につながるため危険です
* ROPC の削除 : ROPC では OAuth の根幹でもあるアプリケーションからの認証・認可の委譲に反してユーザー自身の id/password を直接扱ってしまうため、奪取された場合の被害は有効期間のあるトークンの比ではないリスクがあります

この他、認可後のリダイレクト先の exact match や Query String でトークンをさらすことの禁止、トークンのリフレッシュの推奨が含まれる予定です。これらは、1) 不正なリダイレクト先の用意 2) (HTTPS でも) 素面でさらされる Query Parameters からの認可コード奪取、といったケースに対応するための措置です。

OAuth 2.1 はセキュリティの強化に加え脆弱性あるフローの削除を行っており、仕様の複雑化を押さえながらよりセキュアな方向に進化していると思います。

## OAuth の課題 : OpenID Connect の生まれた背景

OAuth の関心はアプリケーション個別の適切な認可であり、途中の認証はそのための手段になります。フォーカスが "アプリケーション個別" であるため、一度の「認証」で複数のアプリケーションへのアクセスを許可する Single Sign On (SSO) 体験を OAuth の枠組みで提供するのは困難です。OAuth を拡張し認証のプロセス・認証情報を得るためのスコープなどを共通化し SSO 体験の実現を図るための枠組みが OpenID Connect になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25990/17b243c9-1022-4656-bcb2-00ee20930e7e.png)
[Okta : "OpenID Connect、OAuth、SAMLの違いと用途とは？" より引用](https://www.okta.com/jp/identity-101/whats-the-difference-between-oauth-openid-connect-and-saml/)

OpenID Connect は認証によりユーザー情報が含まれた (JWT で暗号化された) ID Token を得るプロセスになります。仕組みは OAuth にのっとっていますが、ID Token は OAuth で扱う認可コード / アクセストークンいずれとも異なる別個の Token になります。そのため MCP Server ごとに認証をする必要がなくなっても、認可を個別にする必要は継続します。Gmail と GitHub でそもそも定義しているスコープが全然違うように、認可は認証に比べ共通・効率化が難しいことも事実です。

## Agentic な世界に不可欠な仕様 : Protected Resource Metadata (RFC9728)

2025/5 時点で MCP Authorization の draft に入っている Protected Resource Metadata (PRM)([RFC9728](https://datatracker.ietf.org/doc/html/rfc9728/)) は、まさに認可の効率化を実現する仕様です。PRM は、文字通り Protect された `resource`  (API など) を扱うためにどんな `authorization_servers` からどんな `scopes_supported` の認可を受けてくればいいのか情報を提供します。

* 認可サーバーのエンドポイント等、OAuth のプロセスを始めるための情報は別途 [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414) で定義される OAuth 2.0 Authorization Server Metadata から得ることが出来ます。ただ、認可サーバーで OAuth のプロセスを行うには事前にアプリケーションを識別するためのクライアント登録が必要で、Agent (MCP Client) がいきなりプロセスを開始することはできません
* MCP Client が事前に必要な認可サーバーを全部把握するのは無理でしょ、という問題を解決するのが [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) で定義される Dynamic Client Registration Metadata です。先ほどの Authorization Server Metadata に含まれる `registration_endpoint` からクライアントの登録を行うことで認可を開始します

いろんな RFC が出てきたので、RFC の名前込みで MCP の Authorization の流れをまとめてみます。

```mermaid
sequenceDiagram
    participant Agent
    participant MCP as MCP Server
    participant AuthServer as 認可サーバー
    participant User as ユーザー

    Note over Agent, AuthServer: 1. MCP Server探索・要求認可情報取得
    Agent->>MCP: MCP Server探索・アクセス
    MCP->>Agent: 401 Unauthorized<br/>WWW-Authenticate ヘッダ<br/>(RFC9728: Protected Resource Metadata)

    Note over Agent, AuthServer: 2. 認可サーバーメタデータ取得
    Agent->>AuthServer: 認可サーバーメタデータ要求<br/>(RFC8414: OAuth 2.0 Authorization Server Metadata)
    AuthServer->>Agent: 認可エンドポイント等の情報取得

    Note over Agent, AuthServer: 3. クライアント動的登録
    Agent->>AuthServer: クライアント登録要求<br/>(RFC7591: Dynamic Client Registration)
    AuthServer->>Agent: client_id, client_secret等返却

    Note over Agent, AuthServer: 4. PKCE認可フロー開始
    Agent->>Agent: code_verifier生成<br/>code_challenge生成<br/>(RFC7636: PKCE)
    
    Agent->>User: 認可URL提示<br/>(code_challenge含む)
    User->>AuthServer: 認可リクエスト<br/>(ブラウザ経由)
    AuthServer->>User: 認証・認可画面表示
    User->>AuthServer: 認証・認可同意
    AuthServer->>Agent: 認可コード返却<br/>(リダイレクト)

    Note over Agent, AuthServer: 5. アクセストークン取得
    Agent->>AuthServer: アクセストークン要求<br/>(認可コード + code_verifier)
    AuthServer->>Agent: アクセストークン返却

    Note over Agent, MCP: 6. 認証済みアクセス
    Agent->>MCP: MCP Server再アクセス<br/>(Authorization: Bearer token)
    MCP->>Agent: 200 OK + データ返却
```

[MCP Specification の `2025-03-26` 版](https://modelcontextprotocol.io/specification/2025-03-26)では Authorization の仕様に RFC8414 と RFC7591 が既に含まれています。PRM (RFC9728) が含まれなかったのは RFC9728 の発行が 2025/4 で 3 月の仕様発行に間に合わなかったからだと思われます。次の版では多分確実に含まれて、これで Agent による自律的な MCP Server の認可ができるようになるはずです。

## AWS での実装

draft 中の PRM を [Dave Patten](https://www.linkedin.com/in/dpatten/) さんが AWS で実装されていた！ので紹介します。

https://medium.com/@dave-patten/building-secure-ai-contexts-hands-on-with-mcp-authorization-oauth-2-1-and-aws-cognito-17a7bce76b79

こちらの記事では認可サーバーとして [Amazon Cognito](https://aws.amazon.com/jp/cognito/) を使用しており、PRM の仕様を実装した MCP Server (Express.js) からメタデータをクライアントに提供しています。さらに素晴らしいことに、Cognito がまだ対応していない Dynamic Client Registration を AWS Lambda で実装されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25990/4854ef8e-3c50-47db-95a2-ccb4e755229b.png)

1. MCP Server にアクセスした際、Unauthorized であれば PRM を返す
   * [Protected Resource Metadata の実装 (`createResourceMetadata`)](https://github.com/empires-security/mcp-oauth2-aws-cognito/blob/main/src/mcp-server/resource-metadata.js)
2. PRM から Authorization Server を特定し、認可を開始するためのエンドポイント情報などが含まれる Authorization Server Metadata を取得
   * 本来は Metadata 取得の URL にアクセスするが、Cognito では提供していないので Cognito の情報を基に Client 側で組み立てている : [Authorization Server Metadata の実装 (`authServerMetadata`)](https://github.com/empires-security/mcp-oauth2-aws-cognito/blob/e499dcaeff95b27867b1ea70c9a1f4c274a17042/src/client/discovery.js#L44)
3. Cognito による PKCE を使用した OAuth を実施
   * [OAuth の実装 (`initiateAuthFlow`)](https://github.com/empires-security/mcp-oauth2-aws-cognito/blob/main/src/client/auth-flow.js)
   * OAuth 2.1 で推奨となっている Refresh Token も実装されていました
4. Access Token を利用した MCP Server との通信を開始

GitHub で実装を公開いただいているので、ぜひ参考ください。MCP Server 自体はサーバーレスな AWS Lambda でも理論上は実装可能だと思います。

https://github.com/empires-security/mcp-oauth2-aws-cognito

こちらの実装では Express の Middleware 機能を使用して認証部分をきれいに書いています。

https://github.com/aarora79/streamable-mcp-serverless/tree/main

* [Protected Resource Metadata の実装](https://github.com/aarora79/streamable-mcp-serverless/blob/main/src/auth/oauthMetadata.ts)
* [Cognito による OAuth 認証 (`authMiddleware`)](https://github.com/aarora79/streamable-mcp-serverless/blob/main/src/server.ts#L436)

## おわりに

今回は MCP の仕様に Authorization が加わったことをきっかけに、背景となる OAuth の仕組みを深堀してみました。安全な MCP への第一歩となれば幸いです！

## 参考文献

* [OAuth 2.1 vs 2.0: What developers need to know](https://stytch.com/blog/oauth-2-1-vs-2-0/)
* [What is OpenID Connect?](https://www.okta.com/openid-connect/)
* [What is Dynamic Client Registration?](https://www.descope.com/learn/post/dynamic-client-registration)
* [What Is Protected Resource Metadata (PRM) and How It Works](https://www.descope.com/learn/post/prm)
* [Building Secure AI Contexts: Hands-On with MCP Authorization, OAuth 2.1, and AWS Cognito](https://medium.com/@dave-patten/building-secure-ai-contexts-hands-on-with-mcp-authorization-oauth-2-1-and-aws-cognito-17a7bce76b79)