OAuth2認証・DCRガイド
Custom Remote MCPサーバーにOAuth 2.0認証を適用する方法を説明します。Dynamic Client Registration(DCR)に対応すると、ユーザーはURLを入力するだけで自動的に認証を完了できます。
概要
| 質問 | 回答 |
|---|---|
| Custom MCPでOAuth2をサポートしていますか? | ✅ 完全にサポートしています。 |
| 各ユーザーが個別に認証できますか? | ✅ はい、トークンはユーザーごとに管理されます。 |
| DCRは必須ですか? | ❌ いいえ、オプションです。なければClient ID/Secretを手動で入力します。 |
| DCRがあると何が良いですか? | 自動クライアント登録により、ユーザー体験が大幅に向上します。 |
OAuth2認証方式
AIPはMCPサーバーのURLを入力すると自動的にOAuthメタデータを検出し、以下の3つの方式のいずれかで動作します。
OAuthWithDCR(自動クライアント登録)
- 条件: MCPサーバーがDCRスペックに対応
- ユーザー操作: URLを入力するだけ
- AIP処理: 自動でクライアント登録を実行
- 結果: OAuthポップアップが自動表示
OAuth(手動Client ID/Secret入力)
- 条件: MCPサーバーがDCR非対応
- ユーザー操作: Client ID / Secretを直接入力
- AIP処理: 入力された資格情報でOAuth処理
- 結果: OAuthポップアップ表示
NoAuth(認証なし)
- 条件: OAuthメタデータDiscoveryで利用可能なメタデータを取得できない
- ユーザー操作: なし
- AIP処理: この設定ではOAuthフローを開始しない
- 結果: OAuthポップアップなし
MCPサーバーURLを入力した直後に、AIPが自動的にOAuth Discoveryを実行して認証方式を決定します。
DCRとは
Dynamic Client Registration(DCR) はRFC 7591 標準プロトコルで、OAuth2クライアント(AIP)がAuthorization Serverに自動登録してClient IDとSecretを取得する方式です。
DCRなしの場合(従来方式)
- ユーザーが該当サービスでOAuth Appを手動で作成します。
- Client ID / Secretを取得します。
- AIPに手動で入力します。
- OAuthポップアップが進行します。
DCRありの場合
- ユーザーがAIPにMCP Server URLを入力します。
- AIPが自動的にサーバーにClientを登録します。
- Client ID / Secretが自動的に発行されます。
- OAuthポップアップが進行します(ユーザー入力不要)。
DCR対応有無の判定基準
AIPはMCPサーバーのAuthorization Server Metadataに registration_endpoint フィールドがあるかを確認します:
// Example response from /.well-known/oauth-authorization-server
{
"issuer": "https://mcp.example.com",
"authorization_endpoint": "https://mcp.example.com/oauth/authorize",
"token_endpoint": "https://mcp.example.com/oauth/token",
"registration_endpoint": "https://mcp.example.com/oauth/register" // DCR supported if present
}AIPのOAuth自動検出フロー
MCPサーバーURLを入力すると、AIPバックエンドが以下の順序で自動検出を行います:
- MCPサーバーにPOSTリクエスト送信
- 401応答 +
WWW-Authenticateヘッダー → Protected Resource Metadata URL抽出 - 401以外 →
/.well-known/oauth-protected-resourceを確認
- 401応答 +
- Protected Resource Metadataから
authorization_serversを抽出 - Authorization Server Metadata(ASM)取得 — 優先順位順
- RFC 8414:
/.well-known/oauth-authorization-server/{path} - OIDC (path insertion):
/.well-known/openid-configuration/{path} - OIDC (path appending):
/{path}/.well-known/openid-configuration
- RFC 8414:
- 認証方式の決定
- ASM取得失敗 → NoAuth(メタデータfallback)
- ASMに
registration_endpointあり → OAuthWithDCR - ASMに
registration_endpointなし → OAuth(手動入力)
Discovery処理でOAuthメタデータを解決できない場合、メタデータ判定はNoAuthにfallbackし、OAuthポップアップは表示されません。一方、DCR実行時の失敗は別扱いで、AIPはDCR未対応エラーを返し、UIは手動Client ID / Secret入力に切り替わります。
設定ガイド
DCR対応サーバーの連携(例:Linear)
URL入力
Integrations → All Integrations → カスタムMCP連携設定 をクリックし、MCP Server URLを入力します。
自動認証
AIPが自動的にDCRを実行し、OAuthポップアップが表示されます。サービスアカウントでログインすると連携が完了します。
トークン管理
各ユーザーが独立して認証され、トークンはユーザーごとに個別管理されます。
DCR非対応サーバーの連携(例:Salesforce)
AIPでMCPを追加
- Integrations → All Integrations → カスタムMCP連携設定 でMCP Server URLを入力します。
- Install をクリックすると、Client ID / Secret入力ダイアログが表示されます。ダイアログにはOAuth Callback URL(コピーボタン付き)と必要なScope一覧も表示されます。
OAuth Appの作成
MCPサーバーの管理コンソールでOAuth Appを作成します:
- OAuth Settingsを有効化します。
- Callback URLを入力します。AIPのClient ID / Secret入力ダイアログからOAuth Callback URLをコピーして登録します。
- 必要なScopeを選択します。
- Client IDとClient Secretをメモします。
OAuthログイン
- AIPのClient ID / Secret入力ダイアログに戻り、前の手順でメモしたClient ID / Secretを入力します。
- OAuthポップアップでサービスアカウントにログインします。ログインが完了すると、AIPがサービスから認証トークンを受信し、連携が完了します。内部処理の詳細はOAuth2内部処理フローを参照してください。
Salesforce Custom Domain環境では、AIPが自動的に再ログインを要求してcross-org OAuthエラーを防止します。問題が続く場合は、別のブラウザプロファイルの使用をお試しください。
社内ネットワークサーバーの連携(Edge Tunnel必要)
MCPサーバーが社内ネットワークまたはlocalhostで稼働している場合:
- AIP Desktop CLIでEdge Tunnelを起動します。
- Custom MCP設定時に 「Use Edge Tunnel」 オプションをチェックします。
- 以降のOAuth Discoveryおよび認証フローは同様に進行します。
詳細はEdge Tunnelドキュメントを参照してください。
DCR非対応時の困難
DCRをサポートしないMCPサーバーを連携する際に発生する問題点です:
- 手動クライアント準備が必要 — 連携を設定する主体がOAuth Appを作成または登録し、Client ID / Secretを提供する必要があります。
- Scope設定ミス — AIPが必要なScopeを案内しますが、ユーザーがOAuth Appに手動で追加する必要があります。漏れがあるとツール呼び出しが失敗します。
- Client Secretのセキュリティ — SecretをAIPに直接入力する構造のため、管理負担が増加します。
- 運用の複雑さ — Client ID/Secretの期限切れや再発行時に再入力が必要です。
- 高い導入障壁 — 技術に不慣れなユーザーにとってOAuth App作成は難しい作業です。
DCR実装ガイド(MCPサーバー開発者向け)
MCPサーバーでDCRをサポートするには、メタデータに公開される2つのエンドポイントと、メタデータが指すregistration_endpointの実装の両方が必要です。
1. Protected Resource Metadata
GET /.well-known/oauth-protected-resource
Response:
{
"resource": "https://mcp.example.com",
"authorization_servers": ["https://auth.example.com"]
}2. Authorization Server Metadata
registration_endpoint が含まれていることで、AIPがDCRとして認識します。
GET /.well-known/oauth-authorization-server
Response:
{
"issuer": "https://auth.example.com",
"authorization_endpoint": "https://auth.example.com/authorize",
"token_endpoint": "https://auth.example.com/token",
"registration_endpoint": "https://auth.example.com/register",
"scopes_supported": ["read", "write"],
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["client_secret_basic"]
}registration_endpoint がない場合、AIPは自動的にOAuth(手動入力)方式にfallbackします。
code_challenge_methods_supportedにS256を含めると、AIPが自動的にPKCEを適用してセキュリティが強化されます。含めることを推奨します。
3. Client Registration Endpoint
registration_endpointは、AIPが自動クライアント登録時に直接呼び出すPOSTエンドポイントです。RFC 7591形式のクライアントメタデータを受け取り、client_id、client_secretなどの登録結果を返す必要があります。
POST /oauth/register
Content-Type: application/json
Request:
{
"client_name": "Custom MCP Client of AI Platform",
"redirect_uris": ["https://{aip-domain}/integration/oauth/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"scope": "read write",
"token_endpoint_auth_method": "client_secret_basic"
}
Response (201 Created):
{
"client_id": "aip_123456789",
"client_secret": "replace-with-secure-secret",
"client_id_issued_at": 1712236800,
"client_secret_expires_at": 0,
"redirect_uris": ["https://{aip-domain}/integration/oauth/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "client_secret_basic"
}実装例(Node.js)
const crypto = require('crypto');
// Protected Resource Metadata
app.get('/.well-known/oauth-protected-resource', (req, res) => {
res.json({
resource: 'https://mcp.example.com',
authorization_servers: ['https://auth.example.com']
});
});
// Authorization Server Metadata
app.get('/.well-known/oauth-authorization-server', (req, res) => {
res.json({
issuer: 'https://auth.example.com',
authorization_endpoint: 'https://auth.example.com/authorize',
token_endpoint: 'https://auth.example.com/token',
registration_endpoint: 'https://auth.example.com/register',
scopes_supported: ['read', 'write', 'admin'],
response_types_supported: ['code'],
code_challenge_methods_supported: ['S256'],
grant_types_supported: ['authorization_code', 'refresh_token'],
token_endpoint_auth_methods_supported: ['client_secret_basic']
});
});
// Client Registration Endpoint
app.post('/oauth/register', (req, res) => {
res.status(201).json({
client_id: `aip_${crypto.randomBytes(16).toString('hex')}`,
client_secret: crypto.randomBytes(32).toString('hex'),
client_id_issued_at: Math.floor(Date.now() / 1000),
client_secret_expires_at: 0,
redirect_uris: req.body.redirect_uris ?? [],
grant_types: req.body.grant_types ?? ['authorization_code', 'refresh_token'],
token_endpoint_auth_method: req.body.token_endpoint_auth_method ?? 'client_secret_basic'
});
});実装例(Python)
from flask import Flask, jsonify, request
import time
import secrets
app = Flask(__name__)
@app.route('/.well-known/oauth-protected-resource')
def oauth_resource_metadata():
return jsonify({
'resource': 'https://mcp.example.com',
'authorization_servers': ['https://auth.example.com']
})
@app.route('/.well-known/oauth-authorization-server')
def oauth_authorization_server():
return jsonify({
'issuer': 'https://auth.example.com',
'authorization_endpoint': 'https://auth.example.com/authorize',
'token_endpoint': 'https://auth.example.com/token',
'registration_endpoint': 'https://auth.example.com/register',
'scopes_supported': ['read', 'write', 'admin'],
'response_types_supported': ['code'],
'code_challenge_methods_supported': ['S256'],
'grant_types_supported': ['authorization_code', 'refresh_token'],
'token_endpoint_auth_methods_supported': ['client_secret_basic']
})
@app.route('/oauth/register', methods=['POST'])
def oauth_register():
payload = request.get_json(silent=True) or {}
return jsonify({
'client_id': f'aip_{secrets.token_hex(16)}',
'client_secret': secrets.token_hex(32),
'client_id_issued_at': int(time.time()),
'client_secret_expires_at': 0,
'redirect_uris': payload.get('redirect_uris', []),
'grant_types': payload.get('grant_types', ['authorization_code', 'refresh_token']),
'token_endpoint_auth_method': payload.get('token_endpoint_auth_method', 'client_secret_basic')
}), 201例示は文書説明用の最小実装です。実際の運用環境では、登録されたクライアントを永続化し、許可されたリダイレクトURIの検証とclient secretの保護ポリシーを実装する必要があります。
トラブルシューティング
OAuthポップアップが表示されない場合
- OAuthメタデータDiscoveryが正常に動作するか確認します。
PRM応答に
curl https://mcp.example.com/.well-known/oauth-protected-resourceauthorization_serversが含まれる場合は、その値が指すAuthorization Server Metadataにもアクセスできることを確認してください。 - AIPからMCPサーバーURLへのHTTPアクセスが可能か確認します(ファイアウォール等)。
- 社内ネットワークサーバーの場合、「Use Edge Tunnel」 オプションをチェックしたか確認します。
- MCP Server URLが正確か確認します(例:
https://host/sse)。 - MCPサーバーが標準OAuth2 Authorization Code Flowに対応しているか確認します。
認証失敗エラー
- Client ID / Secretが正確か確認します。
- OAuth AppのリダイレクトURIホワイトリストにAIPのOAuthコールバックURLを追加します。
- サーバーログでOAuthエンドポイントのエラーメッセージを確認します。
ユーザー別認証が動作しない場合
- 別のユーザーアカウントで再度ログインします。
- MCPサーバーでユーザーごとに異なるトークンが発行されているか確認します。