Microsoft Entra SDK for Agent ID を使用して、トークンの取得と HTTP 通信の両方を 1 回の操作で処理します。 SDK は、受信トークンをダウンストリーム API にスコープ指定されたトークンと交換し、HTTP 呼び出しを行い、応答を返します。 このガイドでは、ダウンストリーム API の構成、TypeScript とPythonでの呼び出しの実装、さまざまな HTTP メソッドの処理、再試行によるエラーの管理を行う方法について説明します。
[前提条件]
- アクティブなサブスクリプションを持つAzure アカウント。 無料でアカウントを作成できます。
- Microsoft Entra SDK for AgentID環境でデプロイされて実行されます。 セットアップ手順については 、インストール ガイド を参照してください。
- SDKでは、呼び出すAPIのベースURLと必要なスコープを用いてダウンストリームAPIを構成します。
- 認証されたクライアントからのベアラー トークン - アプリケーションは、SDK に転送するクライアント アプリケーションからトークンを受け取ります。
- Microsoft Entra ID のアクセス許可を適用する - アカウントには、アプリケーションを登録し、API アクセス許可を付与するためのアクセス許可が必要です。
コンフィギュレーション
Microsoft Entra SDK for Agent ID 環境設定でダウンストリーム API を構成します。
env:
- name: DownstreamApis__Graph__BaseUrl
value: "https://graph.microsoft.com/v1.0"
- name: DownstreamApis__Graph__Scopes__0
value: "User.Read"
- name: DownstreamApis__Graph__Scopes__1
value: "Mail.Read"
この構成では、次を指定します。
- BaseUrl: ダウンストリーム API のルート エンドポイント
- スコープ: ダウンストリーム API へのアクセスに必要なアクセス許可
TypeScript/Node.js
次の例は、TypeScript および Node.js アプリケーションからダウンストリーム API を呼び出す方法を示しています。 このコードは、再利用可能な関数と Express.jsとの統合の両方を示しています。
interface DownstreamApiResponse {
statusCode: number;
headers: Record<string, string>;
content: string;
}
async function callDownstreamApi(
incomingToken: string,
serviceName: string,
relativePath: string,
method: string = 'GET',
body?: any
): Promise<any> {
const sdkUrl = process.env.ENTRA_SDK_URL || 'http://localhost:5000';
const url = new URL(`${sdkUrl}/DownstreamApi/${serviceName}`);
url.searchParams.append('optionsOverride.RelativePath', relativePath);
if (method !== 'GET') {
url.searchParams.append('optionsOverride.HttpMethod', method);
}
const requestOptions: any = {
method: method,
headers: {
'Authorization': incomingToken
}
};
if (body) {
requestOptions.headers['Content-Type'] = 'application/json';
requestOptions.body = JSON.stringify(body);
}
const response = await fetch(url.toString(), requestOptions);
if (!response.ok) {
throw new Error(`SDK error: ${response.statusText}`);
}
const data = await response.json() as DownstreamApiResponse;
if (data.statusCode >= 400) {
throw new Error(`API error ${data.statusCode}: ${data.content}`);
}
return JSON.parse(data.content);
}
// Usage examples
async function getUserProfile(incomingToken: string) {
return await callDownstreamApi(incomingToken, 'Graph', 'me');
}
async function listEmails(incomingToken: string) {
return await callDownstreamApi(
incomingToken,
'Graph',
'me/messages?$top=10&$select=subject,from,receivedDateTime'
);
}
async function sendEmail(incomingToken: string, message: any) {
return await callDownstreamApi(
incomingToken,
'Graph',
'me/sendMail',
'POST',
{ message }
);
}
次の例では、ミドルウェアとルート ハンドラーを使用して、これらの関数を Express.js アプリケーションに統合する方法を示します。
// Express.js API example
import express from 'express';
const app = express();
app.use(express.json());
app.get('/api/profile', async (req, res) => {
try {
const incomingToken = req.headers.authorization;
if (!incomingToken) {
return res.status(401).json({ error: 'No authorization token' });
}
const profile = await getUserProfile(incomingToken);
res.json(profile);
} catch (error) {
console.error('Error:', error);
res.status(500).json({ error: 'Failed to fetch profile' });
}
});
app.get('/api/messages', async (req, res) => {
try {
const incomingToken = req.headers.authorization;
if (!incomingToken) {
return res.status(401).json({ error: 'No authorization token' });
}
const messages = await listEmails(incomingToken);
res.json(messages);
} catch (error) {
console.error('Error:', error);
res.status(500).json({ error: 'Failed to fetch messages' });
}
});
app.post('/api/messages/send', async (req, res) => {
try {
const incomingToken = req.headers.authorization;
if (!incomingToken) {
return res.status(401).json({ error: 'No authorization token' });
}
const message = req.body;
await sendEmail(incomingToken, message);
res.json({ success: true });
} catch (error) {
console.error('Error:', error);
res.status(500).json({ error: 'Failed to send message' });
}
});
app.listen(8080, () => {
console.log('Server running on port 8080');
});
Python
次の例では、HTTP 処理のために要求ライブラリと Flask を使用して、Python アプリケーションからダウンストリーム API を呼び出す方法を示します。
import os
import json
import requests
from typing import Dict, Any, Optional
def call_downstream_api(
incoming_token: str,
service_name: str,
relative_path: str,
method: str = 'GET',
body: Optional[Dict[str, Any]] = None
) -> Any:
"""Call a downstream API via the Microsoft Entra SDK for AgentID."""
sdk_url = os.getenv('ENTRA_SDK_URL', 'http://localhost:5000')
params = {
'optionsOverride.RelativePath': relative_path
}
if method != 'GET':
params['optionsOverride.HttpMethod'] = method
headers = {'Authorization': incoming_token}
json_body = None
if body:
headers['Content-Type'] = 'application/json'
json_body = body
response = requests.request(
method,
f"{sdk_url}/DownstreamApi/{service_name}",
params=params,
headers=headers,
json=json_body
)
if not response.ok:
raise Exception(f"SDK error: {response.text}")
data = response.json()
if data['statusCode'] >= 400:
raise Exception(f"API error {data['statusCode']}: {data['content']}")
return json.loads(data['content'])
# Usage examples
def get_user_profile(incoming_token: str) -> Dict[str, Any]:
return call_downstream_api(incoming_token, 'Graph', 'me')
def list_emails(incoming_token: str) -> Dict[str, Any]:
return call_downstream_api(
incoming_token,
'Graph',
'me/messages?$top=10&$select=subject,from,receivedDateTime'
)
def send_email(incoming_token: str, message: Dict[str, Any]) -> None:
call_downstream_api(
incoming_token,
'Graph',
'me/sendMail',
'POST',
{'message': message}
)
これらの関数を Flask アプリケーションに統合する場合は、次の例を使用できます。
# Flask API example
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/profile')
def profile():
incoming_token = request.headers.get('Authorization')
if not incoming_token:
return jsonify({'error': 'No authorization token'}), 401
try:
profile_data = get_user_profile(incoming_token)
return jsonify(profile_data)
except Exception as e:
print(f"Error: {e}")
return jsonify({'error': 'Failed to fetch profile'}), 500
@app.route('/api/messages')
def messages():
incoming_token = request.headers.get('Authorization')
if not incoming_token:
return jsonify({'error': 'No authorization token'}), 401
try:
messages_data = list_emails(incoming_token)
return jsonify(messages_data)
except Exception as e:
print(f"Error: {e}")
return jsonify({'error': 'Failed to fetch messages'}), 500
@app.route('/api/messages/send', methods=['POST'])
def send_message():
incoming_token = request.headers.get('Authorization')
if not incoming_token:
return jsonify({'error': 'No authorization token'}), 401
try:
message = request.json
send_email(incoming_token, message)
return jsonify({'success': True})
except Exception as e:
print(f"Error: {e}")
return jsonify({'error': 'Failed to send message'}), 500
if __name__ == '__main__':
app.run(port=8080)
POST/PUT/PATCH リクエスト
/DownstreamApi エンドポイントは、HTTP メソッドと要求本文を渡すことによって変更操作をサポートします。 これらのパターンは、ダウンストリーム API でリソースを作成、更新、または削除する必要がある場合に使用します。
リソースの作成
// POST example - Create a calendar event
async function createEvent(incomingToken: string, event: any) {
return await callDownstreamApi(
incomingToken,
'Graph',
'me/events',
'POST',
event
);
}
// Usage
const newEvent = {
subject: "Team Meeting",
start: {
dateTime: "2024-01-15T14:00:00",
timeZone: "Pacific Standard Time"
},
end: {
dateTime: "2024-01-15T15:00:00",
timeZone: "Pacific Standard Time"
}
};
const createdEvent = await createEvent(incomingToken, newEvent);
リソースの更新
// PATCH example - Update user profile
async function updateProfile(incomingToken: string, updates: any) {
return await callDownstreamApi(
incomingToken,
'Graph',
'me',
'PATCH',
updates
);
}
// Usage
await updateProfile(incomingToken, {
mobilePhone: "+1 555 0100",
officeLocation: "Building 2, Room 201"
});
高度なシナリオ
次のシナリオは、特殊なユース ケースの高度な構成を示しています。
カスタム ヘッダー
ダウンストリーム API 要求にカスタム ヘッダーを追加します。
const url = new URL(`${sdkUrl}/DownstreamApi/MyApi`);
url.searchParams.append('optionsOverride.RelativePath', 'items');
url.searchParams.append('optionsOverride.CustomHeader.X-Custom-Header', 'custom-value');
url.searchParams.append('optionsOverride.CustomHeader.X-Request-Id', requestId);
スコープをオーバーライドする
既定で構成されたスコープとは異なるスコープを要求します。
const url = new URL(`${sdkUrl}/DownstreamApi/Graph`);
url.searchParams.append('optionsOverride.RelativePath', 'me');
url.searchParams.append('optionsOverride.Scopes', 'User.ReadWrite');
url.searchParams.append('optionsOverride.Scopes', 'Mail.Send');
エージェント ID を用いた認証
エージェント ID を使用して、アプリケーションのアクセス許可を持つ API を呼び出します。
const url = new URL(`${sdkUrl}/DownstreamApi/Graph`);
url.searchParams.append('optionsOverride.RelativePath', 'users');
url.searchParams.append('AgentIdentity', agentClientId);
url.searchParams.append('AgentUsername', 'admin@contoso.com');
エラー処理
一時的な障害を適切に処理するために、指数バックオフを使用して再試行ロジックを実装します。
async function callDownstreamApiWithRetry(
incomingToken: string,
serviceName: string,
relativePath: string,
method: string = 'GET',
body?: any,
maxRetries: number = 3
): Promise<any> {
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await callDownstreamApi(
incomingToken,
serviceName,
relativePath,
method,
body
);
} catch (error) {
lastError = error as Error;
// Don't retry on client errors (4xx)
if (error.message.includes('API error 4')) {
throw error;
}
// Retry on server errors (5xx) or network errors
if (attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 100;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw new Error(`Failed after ${maxRetries} retries: ${lastError!.message}`);
}
AuthorizationHeader アプローチとの比較
Microsoft Entra SDK for Agent ID には、ダウンストリーム API を呼び出すための 2 つの方法が用意されています。 この比較を使用して、ニーズに最も適したアプローチを決定します。
機能の比較
| 能力 | /DownstreamApi | /AuthorizationHeader |
|---|---|---|
| トークンの取得 | SDK によって処理されます | SDK によって処理されます |
| HTTP 要求 | SDK によって処理されます | お客様の責任 |
| 応答の解析 | JSON 形式で包む | 直接 HTTP 応答 |
| カスタム ヘッダー | クエリ パラメーターを使用する | 完全な HTTP 制御 |
| 要求本文 | 自動的に転送される | フル コントロール |
| エラー処理 | SDK によってラップされたエラー | 標準 HTTP エラー |
各アプローチを使用するタイミング
| 使用事例 | 勧告 | 最適な対象者 |
|---|---|---|
| 従来のパターンを使用した標準 REST API 呼び出し | /DownstreamApi |
GET、POST、PUT、PATCH、DELETE 操作。定型句の削減 |
| カスタム構成を必要とする複雑な HTTP クライアント | /AuthorizationHeader |
特殊な要求/応答処理。きめ細かい制御 |
| 必要な HTTP エラー コードとヘッダーへの直接アクセス | /AuthorizationHeader |
低レベルの HTTP 動作制御を必要とするアプリケーション |
| シンプルで迅速な統合の優先順位付け | /DownstreamApi |
低レベルの制御よりもシンプルさを優先するアプリケーション |
ベスト プラクティス
- HTTP クライアントを再利用する: 接続のオーバーヘッドを回避するために 1 回作成して再利用する
- エラー処理を実装する: 指数バックオフを伴う一時的な障害の再試行ロジックを追加する
- 状態コードの確認: 応答コンテンツを解析する前に、常に状態コードを確認してください
- タイムアウトの設定: 要求のハングを防ぐために適切な要求タイムアウトを構成する
- 関連付け ID を含める: エンド ツー エンドトレースの一意の識別子を持つすべての要求をログに記録する
- 入力の検証: ダウンストリーム API に送信する前にデータをサニタイズして検証する
- パフォーマンスの監視: API 呼び出しの待機時間と失敗率を追跡して監視可能
関連するコンテンツ
- 承認ヘッダーを取得する
- TypeScript からの使用
Python からの使用 - Security