エージェントはOAuthを使ってユーザーにサインインし、認証情報を直接処理せずに下流リソース (たとえばMicrosoft Graph) のトークンを取得します。 Azure Bot Serviceはトークン交換を管理し、エージェントはターン中に生成されたユーザートークンを取得します。
概要
エージェントでOAuthを使用するには、次の3つのアクティビティが必要です。
- Azure Bot での OAuth の構成とアプリ登録: Azure Bot リソース上に 1 つ以上の OAuth 接続を作成します。各接続は、Microsoft Entra ID によるアプリ登録によってサポートされます。 フェデレーションID認証情報を使用したユーザー認証の追加は、最も一般的なアプローチです。 クライアントシークレットや証明書など、他の認証情報タイプもサポートされています。 オプションの全セットについては、Bot Service 認証の基本を参照してください。
- エージェントで対応する設定を構成する: Azure Bot 上の各 OAuth 接続は、エージェント構成内の 1 つの OAuth ハンドラーに対応付けられます。 「設定」を参照してください。 一般的なエージェント設定については、Microsoft 365 エージェント SDK とは何かをご覧ください。
- コード内でトークンを使用する: ターン中に、エージェントのユーザー認証APIを介してユーザートークンを取得するか、または代理(OBO)交換を実行します。 コードでのトークンの使用 (非 OBO) およびコードでのトークンの使用 (OBO) を参照してください。
この記事の続きを読む際には、以下の概念を念頭に置いてください。
- Azure Bot は、複数の OAuth 接続を持つことができます。 たとえば、Microsoft Graph用の接続とGitHub用の接続です。 各接続はAzure Bot上で独立して構成されています。
- Azure BotのOAuth接続とエージェント内のOAuthハンドラの間には1対1の関係があります。 ハンドラーの
AzureBotOAuthConnectionName設定は、使用するAzure Bot接続の名前を付けます。 2つの接続を使うには、2つのハンドラを定義します。 - エージェントのユーザー認証APIは、コード内で呼び出すインターフェイスです。 .NET では、これは
AgentApplication.UserAuthorizationとなります。たとえば、トークンを読み取るにはGetTurnTokenAsync、OBO交換を実行するにはExchangeTurnTokenAsyncを使用します。 同等のサーフェスは、JavaScriptではauthorization、Pythonではauthです。
動作中のサンプルについては、自動サインインおよびOBOサンプルを参照してください:
OAuth のサポート言語
エージェントSDKは.NET、JavaScript、Python向けのOAuthをサポートしています。 すべての言語で基本的な概念は同じです (OAuthハンドラ、ルートへのハンドラの接続、OBO交換)。 異なるのは設定形式とハンドラーAPI名のみです。
| Language | 構成する場所 | API サーフェス |
|---|---|---|
| .NET |
appsettings.json (または Program.cs内のコード) |
AgentApplication.UserAuthorization |
| JavaScript |
.env 環境変数 |
AgentApplication.authorization |
| Python |
.env 環境変数 |
AgentApplication.auth |
JavaScriptやPythonの場合、 .env キーは.NET appsettings.json 構造と同じ階層名を使用し、各レベルはダブルアンダースコア (__) で区切られています。 JavaScript キーは、テーブルに表示されるキャメルケースのリーフ名を保持します (例: azureBotOAuthConnectionName)。 Pythonキーは大文字(例: AZUREBOTOAUTHCONNECTIONNAME)で表記されています。
重要
グローバル自動サインイン (AutoSignIn) と DefaultHandlerName は.NETでのみサポートされています。 JavaScriptとPythonでは、ルートごとの構成に示すように、OAuthハンドラーを特定のルートにアタッチします。
設定
AgentApplication 内部のユーザー認証オブジェクトは、エージェントがユーザートークンを取得する方法を制御します。 少なくとも、各ハンドラーは使用するAzure Bot OAuth接続名を付けます。 以下の例は各言語における最小構造を示しています。
以下の表では、利用可能なその他の物件について説明し、OBOのセクションではOBOConnectionNameとOBOScopesの設定について説明します。
.NET では、appsettings.json の AgentApplication の下でユーザー認証を構成します。
"AgentApplication": {
"UserAuthorization": {
"DefaultHandlerName": "{{handler-name}}",
"AutoSignIn": true | false,
"Handlers": {
"{{handler-name}}": {
"Settings": {
"AzureBotOAuthConnectionName": "{{azure-bot-connection-name}}"
}
}
}
}
}
JavaScriptでは、.env ファイルの環境変数でユーザー認可を設定します。
# Connection used to authenticate the agent itself
connections__serviceConnection__settings__clientId=
connections__serviceConnection__settings__clientSecret=
connections__serviceConnection__settings__tenantId=
connectionsMap__0__connection=serviceConnection
connectionsMap__0__serviceUrl=*
# OAuth handler named "{{handler-name}}"
AgentApplication__UserAuthorization__Handlers__{{handler-name}}__Settings__azureBotOAuthConnectionName={{azure-bot-connection-name}}
Pythonでは、.env ファイルの環境変数でユーザー認可を設定します。
# Connection used to authenticate the agent itself
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=
# OAuth handler named "{{handler-name}}"
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__{{handler-name}}__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME={{azure-bot-connection-name}}
UserAuthorization のプロパティ
次の表は、各着信活動に対して、ハンドラの選択方法およびトークンの取得方法を決定するトップレベルの UserAuthorization プロパティを一覧にしたものです。
| プロパティ | 必須 | 型 | 内容 |
|---|---|---|---|
DefaultHandlerName |
いいえ (非推奨) | string | .NET のみ。
AutoSignIn の評価結果が true であり、ルートごとの上書きが指定されていない場合に使用されるハンドラーの名前。 |
AutoSignIn |
いいえ | ブールまたは代理人 | .NET のみ。 true (既定) の場合、エージェントは受信するすべての活動に対してトークンの取得を試みます。 活動の種類をフィルターするには、実行時に Options.AutoSignIn を使用して上書きしてください。 |
Handlers |
はい (1 つ以上) | オブジェクト (辞書) | ハンドラー名とその構成のマッピング 各キーは一意である必要があります。 |
.NET では、自動サインインが適用されるアクティビティを制限するには、次のような述語を設定します: Options.AutoSignIn = (context, ct) => Task.FromResult(context.Activity.IsType(ActivityTypes.Message));。 JavaScriptとPythonはグローバル自動サインインをサポートしていません。 代わりに、ルートごとの例に示されているように、個々のルートに特定のハンドラ名を付与してサインインします。
設定のプロパティ
次の表は、個々の OAuth ハンドラに適用されるネストされた Settings オブジェクトについて説明したもので、サインインカードの表示、再試行の動作、タイムアウト、オプションの OBO 交換構成を制御します。
| プロパティ | 必須 | 型 | 説明 |
|---|---|---|---|
AzureBotOAuthConnectionName |
イエス | string | Azure Bot リソースで定義された OAuth 接続名。 |
OBOConnectionName |
いいえ (OBO のみ) | string | On‑Behalf‑Of トークンの交換を実行するために使用される、エージェント SDK 接続の名前。 |
OBOScopes |
いいえ (OBO のみ) | string[] | OBO 交換時に要求されるスコープ
OBOConnectionName で省略した場合は、ExchangeTurnTokenAsync を手動で呼び出すことができます。 |
Title |
いいえ | string | カスタム サインイン カードのタイトル。 既定は サインイン です。 |
Text |
いいえ | string | サインインカードのボタンのテキスト。 既定値は、サインインしてくださいです。 |
InvalidSignInRetryMax |
いいえ | int | ユーザーが無効なコードを入力した場合に許容される最大再試行回数。 既定値は 2 です。 |
InvalidSignInRetryMessage |
いいえ | string | 無効なコード入力後の表示メッセージです。 既定値: 無効なサインインコードです。6桁のコードを入力してください。 |
Timeout |
いいえ | int (ms) | 進行中のサインイン試行が期限切れになるまでのミリ秒数。 既定は 900000 (15 分) です。 |
紙幣
AzureBotOAuthConnectionName、OBOConnectionName、OBOScopes、Title、およびTextは、3つの言語すべてに適用されます(OAuthの言語サポートで説明されている言語ごとのキーの大文字小文字を使用します)。
InvalidSignInRetryMax、InvalidSignInRetryMessage、Timeout は.NETの設定です。
どの種類を使用すべきですか?
以下の表を使って、どのアプローチが状況に合っているかを判断してください。
| 選択肢 | 使用する場合 |
|---|---|
| 自動サインイン (.NET のみ) | すべての受信活動に対して自動的にトークンを取得させたい場合、あるいは UserAuthorizationOptions.AutoSignIn に述語を指定して、フィルターによって絞り込まれたサブセット (たとえば、メッセージのみ、またはイベントを除くすべて) を対象にしたい場合。 .NETでのみサポートされています。 |
| ルートごと | トークンが必要なのは特定のルートハンドラーのみであり、それ以外のルートでは異なる OAuth 接続 (まり異なるトークン) を使用する必要があります。 このオプションはJavaScriptとPythonで唯一の選択肢です。 .NETでは、グローバル自動サインインと連携して動作します。 .NETで両方が有効な場合、そのターンはそれぞれのトークンにアクセスできます。 |
コード内でトークンを使用 (OBO なし)
このセクションでは、 On-Behalf-Of の交換を実行せずに、Azure Bot の OAuth 接続から直接返されるユーザートークンを取得し、使用する方法について説明します。 .NETでは、グローバル自動サインインやルートごとのハンドラを使用できます。 JavaScriptとPythonはルートごとのハンドラのみを使用します。 アクティビティ ハンドラー内で、トークン (.NET では GetTurnTokenAsync、JavaScript では authorization.getToken、Python では auth.get_token) をできるだけ遅く取得してください。そうすることで、SDK がトークンの有効期限が近い場合にトークンを更新できます。 以下の例は、両方のパターンを示しています。
自動サインイン (.NET のみ)
紙幣
グローバル自動サインインと DefaultHandlerName は.NETでのみ使用可能です。 JavaScriptとPythonの場合は、ルートごとの構成を使用してください。
グローバル自動サインインによって、ルートごとのハンドラーを指定する必要なく、すべての受信アクティビティに対してトークンを取得する場合は、この設定を使用してください。
"AgentApplication": {
"UserAuthorization": {
"DefaultHandlerName": "auto",
"Handlers": {
"auto": {
"Settings": {
"AzureBotOAuthConnectionName": "teams_sso",
}
}
}
}
},
エージェント コードは、次のような形になります:
public class MyAgent : AgentApplication
{
[MessageRoute]
public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
{
var token = await UserAuthorization.GetTurnTokenAsync(turnContext);
// use the token
}
}
ルートごとの構成
きめ細やかな制御を行いたい場合は、ルートごとの構成を使用してください。トークンを取得するのは、明示的に指定したルートのみです。 ルートごとの設定には、次の利点があります。
- 不要なトークン取得を削減します。
- 異なるOAuth接続(したがって異なるリソースやスコープ)をターゲットにする異なるルートを可能にします。
- これにより、認証済みルートと認証なしルートを同じエージェント内で混在させることができます。
以下の例では、単一の graph ハンドラがメッセージ経路にのみ付加されます。
.NETではグローバル自動サインインが無効化され、graph ハンドラは autoSignInHandlersを使ってルートに紐づけられています。
"AgentApplication": {
"UserAuthorization": {
"AutoSignIn": false,
"Handlers": {
"graph": {
"Settings": {
"AzureBotOAuthConnectionName": "teams_sso",
}
}
}
}
},
エージェント コードは、次のような形になります:
public class MyAgent : AgentApplication
{
[MessageRoute(autoSignInHandlers: "graph")]
public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
{
var token = await UserAuthorization.GetTurnTokenAsync(turnContext, "graph");
// use the token
}
}
JavaScriptでは、ルート登録の最後の引数としてハンドラ名の配列を渡します。 そのルートのみが graph ハンドラーのサインインをトリガーします。
AgentApplication__UserAuthorization__Handlers__graph__Settings__azureBotOAuthConnectionName=teams_sso
AgentApplication__UserAuthorization__Handlers__graph__Settings__title=Graph Sign In
AgentApplication__UserAuthorization__Handlers__graph__Settings__text=Sign in with Microsoft Graph
エージェント コードは、次のような形になります:
class MyAgent extends AgentApplication {
constructor () {
super({ storage: new MemoryStorage() })
// the `graph` handler runs only for this route
this.onMessage('-me', this._profileRequest, ['graph'])
}
private _profileRequest = async (context, state) => {
const tokenResponse = await this.authorization.getToken(context, 'graph')
// use tokenResponse.token
}
}
Pythonでは、auth_handlers をルートデコレーターに渡します。 そのルートのみが GRAPH ハンドラーのサインインをトリガーします。
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=teams_sso
エージェント コードは、次のような形になります:
@AGENT_APP.message(re.compile(r"^/(me|profile)$", re.IGNORECASE), auth_handlers=["GRAPH"])
async def profile_request(context: TurnContext, state: TurnState) -> None:
token_response = await AGENT_APP.auth.get_token(context, "GRAPH")
# use token_response.token
ターン中にトークンを回収する
ターン中に必要なときにユーザートークンを取り戻します。 複数回呼び出すことができます。 利用直前に呼び出すことで、必要に応じて更新ロジックが透過的に処理されます。
| Language | 通話 |
|---|---|
| .NET |
GetTurnTokenAsync(turnContext, handlerName) |
| JavaScript | authorization.getToken(context, handlerName) |
| Python | auth.get_token(context, handler_name) |
コードでトークンを使用する (OBO)
On-Behalf-Of (OBO) は、ユーザーが最初にサインインした際に交換可能なトークンが返されることを前提としています。 そのためには、OAuth 接続のスコープの中に、ダウンストリーム API が公開しているスコープに対応するものが含まれている必要があります (たとえば、公開されているスコープが defaultScopes 場合、構成するスコープは api://botid-{{clientId}}/defaultScopes となる可能性があります)。 その後、Agents SDK は、OBOConnectionName で識別される構成済みの接続と OBOScopes のリストを使用して、Microsoft Authentication Library (MSAL) による認証交換を使用します。
OBOConnectionName と OBOScopes の両方が設定に含まれている場合、交換は自動的に行われ、標準のトークン呼び出し(GetTurnTokenAsync / getToken / get_token)を通じて最終トークンを取得します。 どちらかが未設定の場合は、実行時に ExchangeTurnTokenAsync を使用して明示的にトークン交換を実行でき (.NET では ExchangeTurnTokenAsync、JavaScript では authorization.exchangeToken、Python ではauth.exchange_token) 、接続やスコープリストを動的に解決できます。
構成における OBO
このパターンは、構成時に必要な下流のリソースとスコープが分かっている場合に使用します。
OBOConnectionName と OBOScopes の両方を指定すると、SDK はサインイン時に自動的に「On‑Behalf‑Of」の交換を行います。 これは、標準トークン取得関数へのその後の呼び出しで、追加のランタイムコードを必要とせずに、OBOトークンが直接返されることを意味します。
"AgentApplication": {
"UserAuthorization": {
"AutoSignIn": false,
"Handlers": {
"graph": {
"Settings": {
"AzureBotOAuthConnectionName": "teams_sso",
"OBOConnectionName": "ServiceConnection",
"OBOScopes": [
"https://graph.microsoft.com/.default"
]
}
}
}
}
},
"Connections": {
"ServiceConnection": {
"Settings": {
"AuthType": "FederatedCredentials",
"AuthorityEndpoint": "https://login.microsoftonline.com/{{TenantId}}",
"ClientId": "{{ClientId}}",
"FederatedClientId": "{{ManagedIdentityClientId}}",
"Scopes": [
"https://api.botframework.com/.default"
]
}
}
},
エージェント コードは、次のような形になります:
public class MyAgent : AgentApplication
{
[MessageRoute(autoSignInHandlers: "graph")]
public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
{
// returns the OBO token because OBOConnectionName and OBOScopes are configured
var token = await UserAuthorization.GetTurnTokenAsync(turnContext, "graph");
// use the token
}
}
JavaScriptでは、接続マップにOBO接続を定義し、ハンドラーから oboConnectionName と oboScopesを使用して参照します。
# Agent's own connection
connections__serviceConnection__settings__clientId=
connections__serviceConnection__settings__clientSecret=
connections__serviceConnection__settings__tenantId=
# OBO connection
connections__oboConnection__settings__clientId=
connections__oboConnection__settings__clientSecret=
connections__oboConnection__settings__tenantId=
connectionsMap__0__connection=serviceConnection
connectionsMap__0__serviceUrl=*
connectionsMap__1__connection=oboConnection
connectionsMap__1__serviceUrl=obo
AgentApplication__UserAuthorization__Handlers__graph__Settings__azureBotOAuthConnectionName=teams_sso
AgentApplication__UserAuthorization__Handlers__graph__Settings__oboConnectionName=oboConnection
AgentApplication__UserAuthorization__Handlers__graph__Settings__oboScopes=https://graph.microsoft.com/.default
エージェント コードは、次のような形になります:
class MyAgent extends AgentApplication {
constructor () {
super({ storage: new MemoryStorage() })
this.onActivity('message', this._onMessage, ['graph'])
}
private _onMessage = async (context, state) => {
// returns the OBO token because oboConnectionName and oboScopes are configured
const tokenResponse = await this.authorization.getToken(context, 'graph')
// use tokenResponse.token
}
}
Pythonでは、CONNECTIONSの下にOBO接続を定義し、ハンドラからOBOCONNECTIONNAMEとOBOSCOPESを使用してそれを参照します。
# Agent's own connection
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=
# OBO connection
CONNECTIONS__OBO__SETTINGS__CLIENTID=
CONNECTIONS__OBO__SETTINGS__CLIENTSECRET=
CONNECTIONS__OBO__SETTINGS__TENANTID=
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=teams_sso
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__OBOCONNECTIONNAME=OBO
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__OBOSCOPES=https://graph.microsoft.com/.default
エージェント コードは、次のような形になります:
@AGENT_APP.message(re.compile(r".*"), auth_handlers=["GRAPH"])
async def on_message(context: TurnContext, state: TurnState) -> None:
# returns the OBO token because OBOCONNECTIONNAME and OBOSCOPES are configured
token_response = await AGENT_APP.auth.get_token(context, "GRAPH")
# use token_response.token
実行時の OBO 交換
構成で下流のリソース、スコープ、または接続を修正できない場合は、ランタイム交換を使用してください。 この状況は、たとえば、スコープがテナント、ユーザーロール、または機能フラグに依存する場合に発生します。 このモデルでは、オプションでOBO接続を構成し、ターン時に決めたスコープで交換メソッドを呼び出します。 交換済みトークンを受け取り、すぐに適用できます。
ターン時に決めたスコープで ExchangeTurnTokenAsync を呼び出します。
"AgentApplication": {
"UserAuthorization": {
"AutoSignIn": false,
"Handlers": {
"graph": {
"Settings": {
"AzureBotOAuthConnectionName": "teams_sso",
"OBOConnectionName": "ServiceConnection"
}
}
}
}
},
"Connections": {
"ServiceConnection": {
"Settings": {
"AuthType": "FederatedCredentials",
"AuthorityEndpoint": "https://login.microsoftonline.com/{{TenantId}}",
"ClientId": "{{ClientId}}",
"FederatedClientId": "{{ManagedIdentityClientId}}",
"Scopes": [
"https://api.botframework.com/.default"
]
}
}
},
エージェント コードは、次のような形になります:
public class MyAgent : AgentApplication
{
[MessageRoute(autoSignInHandlers: "graph")]
public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
{
var scopes = GetScopes();
var exchangedToken = await UserAuthorization.ExchangeTurnTokenAsync(turnContext, "graph", exchangeScopes: scopes);
// use the token
}
}
ハンドラー名と、ターン時に決定したスコープを authorization.exchangeToken に伝えてください。
connections__oboConnection__settings__clientId=
connections__oboConnection__settings__clientSecret=
connections__oboConnection__settings__tenantId=
connectionsMap__1__connection=oboConnection
connectionsMap__1__serviceUrl=obo
AgentApplication__UserAuthorization__Handlers__graph__Settings__azureBotOAuthConnectionName=teams_sso
AgentApplication__UserAuthorization__Handlers__graph__Settings__oboConnectionName=oboConnection
エージェント コードは、次のような形になります:
class MyAgent extends AgentApplication {
constructor () {
super({ storage: new MemoryStorage() })
this.onActivity('message', this._onMessage, ['graph'])
}
private _onMessage = async (context, state) => {
const scopes = getScopes()
const exchangedToken = await this.authorization.exchangeToken(context, 'graph', { scopes })
// use exchangedToken.token
}
}
ターンタイム時に決定したスコープとハンドラー名を添えて、auth.exchange_token に伝えてください。
CONNECTIONS__MCS__SETTINGS__CLIENTID=
CONNECTIONS__MCS__SETTINGS__CLIENTSECRET=
CONNECTIONS__MCS__SETTINGS__TENANTID=
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__MCS__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=teams_sso
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__MCS__SETTINGS__OBOCONNECTIONNAME=MCS
エージェント コードは、次のような形になります:
@AGENT_APP.message(re.compile(r".*"), auth_handlers=["MCS"])
async def on_message(context: TurnContext, state: TurnState) -> None:
scopes = get_scopes()
token_response = await AGENT_APP.auth.exchange_token(context, scopes, "MCS")
# use token_response.token
地域ごとの OAuth 設定
米国以外の地域については、エージェントが使用するトークン サービスのエンドポイントを更新してください。
次の例は、.NET 構成の例を示しています。
appsettings.json に追加します。
"RestChannelServiceClientFactory": {
"TokenServiceEndpoint": "{{service-endpoint-uri}}"
}
service-endpoint-url については、指定されたリージョンにデータ所在地を持つパブリック クラウドのボットの場合、以下の表から適切な値を使用してください。
| URI | Region |
|---|---|
https://europe.api.botframework.com |
ヨーロッパ |
https://unitedstates.api.botframework.com |
米国 |
https://india.api.botframework.com |
インド |