はじめに
セキュリティ・キャンプ2026のAIセキュリティクラスにチューターとして参加し、MCPの認可制御について学ぶ機会がありました。その後、企業インターンでは実際にauthentikを使い、StageとFlowを組み立てて認証・認可のフローを構築しました。
OAuthやOIDCという言葉自体は以前から知っていたものの、Authorization Server、Access Token、ID Tokenあたりが一つの図に出てくると、最初はそれぞれが何をしているのかかなり混乱しました。
原因のひとつは、認証と認可が同じID基盤の中で行われることが多く、外から見ると一続きの処理に見えることでした。ただ実際に通信を追ってみると、「そのユーザーが誰なのかを確認する処理」と「そのユーザーに何を許すかを決める処理」は別のものです。一度これを分けて図にしてみたところ、かなり整理しやすくなりました。
この記事では、AIがMCP経由で外部データを取りに行くときの認可の流れを、①から⑳まで順番に追ってみます。
なお、認証と認可を分けて考えるというのは、あくまで自分が理解しやすかった整理の仕方です。これが唯一の入り口というわけではないので、その前提で読んでいただければと思います。
この記事の前提
最近は、SaaSが自社のデータをMCP Serverとして公開し、AIから直接触れるようにする例が増えてきました。ユーザーから見れば「AIに頼んだら勝手にデータを取ってきてくれる」という体験になりますが、その裏では、実際に描いてみるとかなり複雑な認証・認可の仕組みが動いています。
この記事で扱うのは、ユーザーがいて、そのユーザーの代わりにMCP Clientが外部リソースへアクセスするケースです。OAuthではAuthorization Code Flowと呼ばれます。
MCPの認可仕様にはこのほかにも細かい規定がありますが、この記事では認証と認可の基本的な流れに絞ります。
1. 認証と認可は何が違うのか
- 認証(Authentication):アクセスしようとしている相手が誰なのかを確かめること
- 認可(Authorization):その相手に、どこまでのアクセスを許すかを決めること
「あなたは誰か」と「あなたは何をしてよいか」で、答えている問いが違います。
これが一番はっきり見えたのが、インターンでauthentikを触ったときでした。authentikでは、ログインの流れをStage(1ステップ分の処理)とFlow(Stageを並べたもの)で組み立てます。そしてFlowには用途の指定があり、AuthenticationとAuthorizationが別物として分かれています。
- Authentication Flow:Identification Stage(ユーザー名の入力)、Password Stage、User Login Stageなどを並べる
- Authorization Flow:アプリケーションごとに設定し、同意を取るならConsent Stageを入れる
OAuth2/OIDCのProviderを作るときは、この2つを別々に選択します。同じ製品の中で扱っていても、設定としてははっきり分かれていることが分かりました。
OAuthのロールに「認証サーバ」はない
理解のために、最初は「認証サーバ」と「認可サーバ」を対等な2つの箱として並べた図を描いていました。分けたほうが自分には見通しがよかったからです。ただ、これは仕様上のロールとしては正確ではありません。
OAuth 2.1が定義しているロールは4つです。
- Resource Owner(リソースの持ち主。多くの場合ユーザー)
- Client(リソースにアクセスしたいソフトウェア)
- Authorization Server(Access Tokenを発行する)
- Resource Server(保護されたリソースを持つ)
この中に「認証を担当するサーバ」という独立したロールはありません。認可サーバ(Authorization Server)がユーザーとのやり取りを行い、ユーザーの認証を経て認可を行い、Access Tokenを発行します。認可サーバ自身が認証することもあれば、外部のIdPに委譲することもあります。
点線の箱の中身は自由で、パスキーもここに入ります。パスキーはOAuthの機能ではなく、認可サーバやその先のIdPがユーザーを本人確認するときに使える手段のひとつです。
authentikで言えば、Authentication Flowが点線の中、Authorization Flowが外側にあたります。authentik自身がOP(自分で認証する側)にもRP(外部IdPに委譲する側)にもなれるので、点線の中を外部に出すこともできます。
2. MCPでは誰が誰になるのか
| MCPの登場人物 | OAuthのロール |
|---|---|
| ユーザー | Resource Owner |
| AIアプリ / Host | ロールではない。MCP Clientを内包するアプリケーション |
| MCP Client | Client |
| 認可サーバ | Authorization Server |
| MCP Server | Resource Server |
| Tool | ロールではない。MCP Serverが提供する機能 |
最初に整理したときは、ここをいくつか取り違えていました。
ユーザーとMCP Clientは別です。ユーザーはResource Owner、MCP Clientはソフトウェアです。ユーザーがMCP Clientにパスワードを渡すのではなく、ブラウザ経由で認可サーバの画面に行って、そこで直接認証します。なのでこのあとの図にはブラウザが登場します。
ToolはMCP Clientではありません。ToolはMCP Serverが提供する機能の側です。
MCP Serverはデータそのものではありません。MCP ServerはOAuthのResource Serverにあたり、実際のDBや外部APIはさらにその奥にあります。
3. 全体のシーケンス図
この図では、⑦〜⑨が認証、⑩〜⑪が認可にあたります。
なお、実際にはこの流れにPKCEが加わります。まず骨組みを追いたいので、そこは5章で足します。
4. ①から⑳まで追う
① ユーザー → AI:データ取得要求
「先月の売上をまとめて」のような自然言語の指示です。
② AI → MCP Client:MCP経由でデータ取得依頼
AIが「この指示にはMCP Serverのget_sales_dataが必要だ」と判断し、MCP Clientに依頼します。
③ MCP Client → MCP Server:データアクセス要求
まだAccess Tokenを持っていないので、そのままリクエストを投げます。
④ MCP Server → MCP Client:Access Tokenが無いため拒否
MCP Serverが401を返します。ここから認可のフローが始まります。
⑤ MCP Client → ブラウザ:認可を開始
MCP Clientがブラウザを開き、認可サーバへリダイレクトさせます。ここで処理をブラウザに渡すのがポイントで、MCP Client自身がユーザーにパスワードを尋ねることはありません。
Authorization Code Flowはリダイレクトを前提とした仕組みなので、これはauthentikに限った話ではありません。
⑥ ブラウザ → 認可サーバ:認可リクエスト
ブラウザが認可サーバの認可エンドポイントに到達します。
GET /authorize
?response_type=code
&client_id=...
&redirect_uri=http%3A%2F%2F127.0.0.1%3A3000%2Fcallback
&scope=files%3Aread
&state=<CSRF対策のランダム値>
⑦ 認可サーバ → ユーザー:本人確認を要求
ログイン画面が表示されます。パスワード、パスキー、MFA、社内SSOへのリダイレクトなど、何を使うかは認可サーバ側の裁量です。authentikで言えばAuthentication Flowが動いている部分にあたります。
なお、すでにログイン済みのセッションがあれば画面が出ないこともあります。「既存のセッションを使う」のも認証方法のひとつです。
⑧ ユーザー → 認可サーバ:認証情報を提示
ユーザーが認可サーバの画面で直接認証します。MCP Clientはこのやり取りに関与しません。
⑨ 認可サーバ:ユーザーを識別(認証の完了)
ここで「このセッションは誰なのか」が確定します。認証はここまでで、この先が認可です。
⑩ 認可サーバ → ユーザー:アクセス範囲の確認
「このアプリケーションが、あなたのファイルの読み取り(files:read)を求めています。許可しますか?」という同意画面です。authentikで言えばConsent Stageにあたります。
⑪ ユーザー → 認可サーバ:承認
ユーザーが許可します。過去に同じ許可をしていれば、⑩⑪が省略される設定もあります。
⑫⑬ 認可サーバ → ブラウザ → MCP Client:Authorization Code
認可サーバが、ブラウザのリダイレクトを使ってMCP ClientにAuthorization Codeを返します。Locationに入っているのは、⑥のredirect_uriで指定したMCP Client自身のコールバックURLです。
HTTP/1.1 302 Found
Location: http://127.0.0.1:3000/callback?code=SplxlOBeZQ&state=xyz
この例が127.0.0.1になっているのは、MCP Clientがローカルで動いているケースを想定しているためです。OAuth 2.1はプロトコルで使うURLをすべてhttpsにすることを求めていますが、ループバックアドレスへのリダイレクトだけは例外としてhttpが許されています。Webアプリとして動くClientなら、ここはhttps://app.example.com/callbackのような自分のドメインになります。
Codeそのものは、Access Tokenではなく、次のステップでAccess Tokenと交換するための一時的なコードです。
⑭ MCP Client → 認可サーバ:Authorization Codeを提示
ここで初めて、MCP Clientがブラウザを経由せず認可サーバと直接通信します。
POST /token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=SplxlOBeZQ
&client_id=...
⑮ 認可サーバ → MCP Client:Access Token発行
{
"access_token": "eyJhbGciOiJI...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "files:read"
}
⑯ MCP Client → MCP Server:Access Token付きでアクセス
③で拒否されたリクエストを、今度はAccess Tokenを付けてやり直します。
GET /mcp HTTP/1.1
Host: mcp.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
MCP ServerはTokenを確認し、許可されたアクセスであればToolを実行します。
⑰⑱⑲⑳ データが返り、AIが回答する
MCP Serverがデータを返し、MCP ClientがAIに渡し、AIが自然言語に整形してユーザーに返します。
ユーザーから見れば、①で頼んで⑳で返ってきただけです。実際に画面を見たのは⑦〜⑪の間だけで、MCP Clientは一度もパスワードに触れていません。
5. OAuth 2.0と2.1の違い、そしてPKCE
ここまでの①〜⑳には、PKCE(ピクシー)が出てきていません。
PKCEはOAuth 2.0の拡張仕様として登場しました。OAuth 2.1では、これがAuthorization Code Flowに組み込まれています。この記事で扱うユーザー代理のフローではPKCEを使います。
PKCEがないとどうなるか
PKCEがない状態だと、⑫⑬で受け取ったAuthorization Codeを持っている人なら、誰でも⑭でAccess Tokenと交換できてしまいます。MCP Clientはデスクトップアプリやローカルで動くエージェントなど、クライアントシークレットを安全に持てない形態が多いので、ここを守る仕組みが必要になります。
PKCEは、このCodeを「認可を開始したClient本人しか使えない」ように結び付けます。
PKCEを足すと2ステップ増える
①〜⑳に足すのは、赤で示したⓐⓑの2ステップと、⑥・⑭に乗せる値だけです。
こちらが、実際にMCP Clientを動かすときの形になります。
ⓐⓑ で何をしているか
ⓐ code_verifier をランダム生成(MCP Client の中だけに保持)
ⓑ code_challenge = BASE64URL(SHA256(code_verifier))
code_verifierが先で、そこからcode_challengeを導出します。逆にすると、ネットワークに流れた値から元の値が計算できてしまい、意味がなくなります。
そして、
-
⑥の認可リクエストで送るのが
code_challenge(ハッシュした側) -
⑭のトークンリクエストで送るのが
code_verifier(元の値)
です。認可サーバは⑭で、送られてきたcode_verifierをハッシュして、⑥で保存しておいたcode_challengeと一致するかを確認します。
つまり、Authorization Codeを傍受されても、code_verifierを知らなければAccess Tokenに交換できません。code_verifierがネットワークに出るのは⑭のときだけで、これはMCP Clientと認可サーバの直接通信です。
OAuth 2.0 と 2.1
| OAuth 2.0 | OAuth 2.1 | |
|---|---|---|
| PKCE | 拡張仕様として存在。使うかは任意 | Authorization Code Flowに組み込まれている |
| Implicit Grant | あり | 削除 |
| ROPC | あり | 削除 |
MCPのClientについては、PKCEを使うものと考えて差し支えありません。
6. Access TokenとID Token
最初に整理したときは、「認証サーバからIdentity Tokenを受け取り、それを認可サーバに渡してAccess Tokenと交換する」という流れだと考えていました。ただ、実際のAuthorization Code Flowでは⑫〜⑮のとおりで、Access Tokenと交換するのはAuthorization Codeです。
ID Token(Identity Token)は、OIDCを併用するときに登場します。⑥の認可リクエストのscopeにopenidを含めると、⑮のレスポンスにID Tokenが加わります。
{
"access_token": "eyJhbGciOiJI...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "openid files:read",
"id_token": "eyJhbGciOiJS..."
}
別のサーバに取りに行くわけではなく、同じ認可サーバが同じレスポンスで両方返します。
| Access Token | ID Token | |
|---|---|---|
| どの仕様 | OAuth 2.x | OIDC |
| 主な役割 | リソースへのアクセス権を表す | Clientにユーザーの認証結果を伝える |
| 宛先 | Resource Server(MCP Server) | Client 自身 |
| MCP Serverに送るか | 送る(⑯) | 送らない |
ID Tokenの宛先はClient自身なので、これをMCP Serverへのアクセス券として使うことはできません。
7. AIと認証・認可
MCPによってAIから外部のデータやToolを呼び出せるようになると、AIは単に回答を生成するだけでなく、ユーザーの代わりに外部システムへアクセスするようになります。
そのとき効いてくるのが、「誰としてアクセスしているのか」と「どこまで操作してよいのか」を分けて考えることです。今回のフローでいえば、前者が⑦〜⑨の認証、後者が⑩⑪の同意と、そこで決まったscopeにあたります。
MCPについては「便利だが脆弱だ」という話も見かけますが、OAuthで扱えるのは「誰が、どのリソースに、どの権限でアクセスできるか」の部分です。AIが正しい権限の範囲内で意図しない操作をしてしまう、といった問題まで解決するわけではありません。
MCPによってAIが外部システムへ接続しやすくなった分、認可を間違えたときの影響範囲も大きくなった。今回整理してみて、そういう捉え方をするようになりました。
まとめ
認証と認可を分けて考えるという入口は、自分にはかなり有効でした。ただ、OAuthのロールとして「認証サーバ」が定義されているわけではなく、認可サーバが認可を行う過程で認証も扱う、という形になっています。
MCPの登場人物をOAuthに当てはめると、MCP ClientがClient、MCP ServerがResource Server、ユーザーがResource Ownerです。ToolはMCP Serverが提供する機能で、ロールではありません。
PKCEはOAuth 2.0の拡張仕様として登場し、OAuth 2.1ではAuthorization Code Flowに組み込まれています。①〜⑳に足すのはⓐⓑの2ステップと、⑥・⑭に乗せる値だけです。
Access Tokenと交換するのはAuthorization Codeで、ID Tokenではありません。ID TokenはOIDCを併用するときに、Clientへユーザーの認証結果を伝えるために返されるものです。
この記事では基本的な流れに絞ったので、細かい部分はまた別途整理してみようと思います。
間違いや説明が不正確な箇所があればご指摘いただけると助かります。
