シングル サインオン (SSO) を有効にするコードを追加する前に、必ず Microsoft Entra 管理センターでアプリとボット リソースを構成してください。
注:
OnTeamsMessagingExtensionQueryAsync
TeamsMessagingExtensionsSearchAuthConfigBot.cs ファイルの SSO ハンドラーは、 ファイルからのOnTeamsAppBasedLinkQueryAsyncのみがサポートされます。 その他の SSO ハンドラーはサポートされません。
この記事の内容:
- 開発環境変数を更新する
- トークンを要求するコードを追加します
- トークンを受け取るコードを追加します
- Bot Framework トークン ストアにトークンを追加する
- アプリ ユーザーのログアウトを処理する
開発環境変数を更新する
Microsoft Entra ID でアプリのクライアント シークレットと OAuth 接続設定を構成しました。 これらの変数を使用してアプリ コードを構成する必要があります。
開発環境変数を更新するには:
アプリ プロジェクトを開きます。
プロジェクトの
./envファイルを開きます。次の変数を更新します。
-
MicrosoftAppIdの場合は、ボット登録 ID を Microsoft Entra ID から更新します。 -
MicrosoftAppPasswordについては、ボット登録のクライアント シークレットを更新します。 -
ConnectionNameについては、Microsoft Entra ID で構成した OAuth 接続の名前を更新します。 -
MicrosoftAppTenantIdについては、テナント ID を更新します。
-
ファイルを保存します。
これで、ボット アプリと SSO に必要な環境変数が構成されました。 次に、トークンを処理するためのコードを追加します。
トークンを要求するコードを追加します
トークンを取得する要求は、既存のメッセージ スキーマを使用した POST メッセージ要求です。 それは、OAuthCard の添付ファイルに含まれています。 OAuthCard クラスのスキーマは、 Microsoft Bot Schema 4.0 で定義されています。 カード に TokenExchangeResource プロパティが設定されている場合、Teams はトークンを更新します。 Microsoft Teams チャンネルの場合、トークンの要求を一意に認識するプロパティ Id だけが引き受けられます。
注:
Microsoft Bot Framework OAuthPrompt または MultiProviderAuthDialog は、SSO 認証でサポートされています。
アプリのコードを更新するには:
TeamsSSOTokenExchangeMiddleware用のコード スニペットを追加します。次のコード スニペットを
AdapterWithErrorHandler.cs(またはアプリのコード内の同等のクラス) に追加します。base.Use(new TeamsSSOTokenExchangeMiddleware(storage, configuration["ConnectionName"]));注:
ユーザーが複数のアクティブなエンドポイントを持っている場合、与えられた要求に対して複数の応答を受け取る場合があります。 トークンを使用して、重複または冗長な応答をすべて排除する必要があります。 signin/tokenExchange の詳細については、「 TeamsSSOTokenExchangeMiddleware クラス」を参照してください。
トークンを要求するには、次のコード スニペットを使用します。
AdapterWithErrorHandler.csを追加した後、次のコードが表示されます。public class AdapterWithErrorHandler : CloudAdapter { public AdapterWithErrorHandler( IConfiguration configuration, IHttpClientFactory httpClientFactory, ILogger<IBotFrameworkHttpAdapter> logger, IStorage storage, ConversationState conversationState) : base(configuration, httpClientFactory, logger) { base.Use(new TeamsSSOTokenExchangeMiddleware(storage, configuration["ConnectionName"])); OnTurnError = async (turnContext, exception) => { // Log any leaked exception from the application. // NOTE: In production environment, you must consider logging this to // Azure Application Insights. Visit https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-telemetry?view=azure-bot-service-4.0&tabs=csharp to see how // to add telemetry capture to your bot. logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}"); // Send a message to the user. await turnContext.SendActivityAsync("The bot encountered an error or bug."); await turnContext.SendActivityAsync("To continue to run this bot, please fix the bot source code."); if (conversationState != null) { try { // Delete the conversationState for the current conversation to prevent the // bot from getting stuck in an error-loop caused by being in a bad state. // ConversationState must be thought of as similar to "cookie-state" in a Web pages. await conversationState.DeleteAsync(turnContext); } catch (Exception e) { logger.LogError(e, $"Exception caught on attempting to Delete ConversationState : {e.Message}"); } } // Send a trace activity, which will be displayed in the Bot Framework Emulator. await turnContext.TraceActivityAsync( "OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError"); }; } }
アクセス トークンを取得するための同意ダイアログ
アプリ ユーザーがアプリを初めて使用する場合は、SSO 認証に同意する必要があります。
アプリ ユーザーがユーザー名を選択すると、アクセス許可が付与され、アプリを使用できます。
表示される同意ダイアログは、Microsoft Entra ID で定義されたオープン ID スコープ用です。 アプリ ユーザーは同意を 1 回だけ行う必要があります。 同意すると、アプリ ユーザーは、付与されたアクセス許可とスコープに対してメッセージ拡張機能アプリにアクセスして使用できます。
重要
同意ダイアログが不要なシナリオ:
- 管理者がテナントに代わって同意を付与した場合、アプリ ユーザーに同意を求めるプロンプトを表示する必要はありません。 つまり、アプリ ユーザーには同意ダイアログが表示されず、シームレスにアプリにアクセスできます。
エラーが発生した場合は、「 Teams での SSO 認証のトラブルシューティング」を参照してください。
トークンを受け取るコードを追加します
トークンを使用した応答は、ボットが今日受け取る他の呼び出しアクティビティと同じスキーマを持つ呼び出しアクティビティを介して送信されます。 唯一の違いは、呼び出し名、サインイン/tokenExchange、 および値 フィールドです。 値フィールドには、ID、トークンを取得するための最初の要求の文字列、トークン フィールド、トークンを含む文字列値が含まれます。
応答を呼び出すには、次のコード スニペットの例を使用します。
public MainDialog(IConfiguration configuration, ILogger<MainDialog> logger)
: base(nameof(MainDialog), configuration["ConnectionName"])
{
AddDialog(new OAuthPrompt(
nameof(OAuthPrompt),
new OAuthPromptSettings
{
ConnectionName = ConnectionName,
Text = "Please Sign In",
Title = "Sign In",
Timeout = 300000, // User has 5 minutes to login (1000 * 60 * 5)
EndOnInvalidMessage = true
}));
AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
PromptStepAsync,
LoginStepAsync,
}));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> PromptStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
return await stepContext.BeginDialogAsync(nameof(OAuthPrompt), null, cancellationToken);
}
private async Task<DialogTurnResult> LoginStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var tokenResponse = (TokenResponse)stepContext.Result;
if (tokenResponse?.Token != null)
{
var token = tokenResponse.Token;
// On successful login, the token contains sign in token.
}
else
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Login was not successful please try again."), cancellationToken);
}
return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
}
注:
コード スニペットでは、ウォーターフォール ダイアログ ボットを使用します。 ウォーターフォール ダイアログの詳細については、「 コンポーネントとウォーターフォール ダイアログについて」を参照してください。
トークン OnTeamsMessagingExtensionQueryAsync SSO を有効にするシナリオに応じて、 turnContext.Activity.Value ペイロードまたは OnTeamsAppBasedLinkQueryAsync内のハンドラーでトークンを受け取ります。
JObject valueObject = JObject.FromObject(turnContext.Activity.Value);
if (valueObject["authentication"] != null)
{
JObject authenticationObject = JObject.FromObject(valueObject["authentication"]);
if (authenticationObject["token"] != null)
{
// Process the token.
}
}
アクセス トークンを検証する
サーバー上の Web API はアクセス トークンをデコードし、クライアントから送信されたかどうかを確認する必要があります。
注:
Bot Framework を使用する場合は、アクセス トークンの検証が処理されます。 Bot Framework を使用しない場合は、このセクションのガイドラインに従ってください。
アクセス トークンの検証の詳細については、「 トークンの検証」を参照してください。
JWT の検証を処理できるライブラリが複数入手可能です。 基本的な検証には、次のものが含まれます。
- トークンが整形式であることを確認します。
- トークンが目的の機関によって発行されたことを確認します。
- トークンが Web API をターゲットにしていることを確認します。
トークンの検証時には、次のガイドラインに注意してください。
- 有効な SSO トークンは Microsoft Entra ID によって発行されます。 トークン内の
issクレームは、この値で始まる必要があります。 - トークンの
aud1パラメーターは、アプリの登録時に生成されるアプリ ID に設定されMicrosoft Entra。 - トークンの
scpパラメーターがaccess_as_userに設定されています。
アクセス トークンの例
次のコード スニペットは、アクセス トークンの典型的なデコードされたペイロードです。
{
aud: "2c3caa80-93f9-425e-8b85-0745f50c0d24",
iss: "https://login.microsoftonline.com/fec4f964-8bc9-4fac-b972-1c1da35adbcd/v2.0",
iat: 1521143967,
nbf: 1521143967,
exp: 1521147867,
aio: "ATQAy/8GAAAA0agfnU4DTJUlEqGLisMtBk5q6z+6DB+sgiRjB/Ni73q83y0B86yBHU/WFJnlMQJ8",
azp: "e4590ed6-62b3-5102-beff-bad2292ab01c",
azpacr: "0",
e_exp: 262800,
name: "Mila Nikolova",
oid: "6467882c-fdfd-4354-a1ed-4e13f064be25",
preferred_username: "milan@contoso.com",
scp: "access_as_user",
sub: "XkjgWjdmaZ-_xDmhgN1BMP2vL2YOfeVxfPT_o8GRWaw",
tid: "fec4f964-8bc9-4fac-b972-1c1da35adbcd",
uti: "MICAQyhrH02ov54bCtIDAA",
ver: "2.0"
}
Bot Framework トークン ストアにトークンを追加する
OAuth 接続を使用している場合は、Bot Framework トークン ストアでトークンを更新または追加する必要があります。 ストアでトークンを更新または追加するために、次のコード スニペット例を TeamsMessagingExtensionsSearchAuthConfigBot.cs (またはアプリのコード内の同等のファイル) に追加します。
注:
タブ、ボット、およびメッセージ拡張機能 (ME) SSO にあるサンプル TeamsMessagingExtensionsSearchAuthConfigBot.csを見つけることができます。
protected override async Task<InvokeResponse> OnInvokeActivityAsync(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
{
JObject valueObject = JObject.FromObject(turnContext.Activity.Value);
if (valueObject["authentication"] != null)
{
JObject authenticationObject = JObject.FromObject(valueObject["authentication"]);
if (authenticationObject["token"] != null)
{
// If the token is NOT exchangeable, then return 412 to require user consent.
if (await TokenIsExchangeable(turnContext, cancellationToken))
{
return await base.OnInvokeActivityAsync(turnContext, cancellationToken).ConfigureAwait(false);
}
var response = new InvokeResponse();
response.Status = 412;
return response;
}
}
return await base.OnInvokeActivityAsync(turnContext, cancellationToken).ConfigureAwait(false);
}
private async Task<bool> TokenIsExchangeable(ITurnContext turnContext, CancellationToken cancellationToken)
{
TokenResponse tokenExchangeResponse = null;
try
{
JObject valueObject = JObject.FromObject(turnContext.Activity.Value);
var tokenExchangeRequest =
((JObject)valueObject["authentication"])?.ToObject<TokenExchangeInvokeRequest>();
var userTokenClient = turnContext.TurnState.Get<UserTokenClient>();
tokenExchangeResponse = await userTokenClient.ExchangeTokenAsync(
turnContext.Activity.From.Id,
_connectionName,
turnContext.Activity.ChannelId,
new TokenExchangeRequest
{
Token = tokenExchangeRequest.Token,
},
cancellationToken).ConfigureAwait(false);
}
#pragma warning disable CA1031 // Do not catch general exception types (ignoring, see comment below)
catch
#pragma warning restore CA1031 // Do not catch general exception types
{
// Ignore exceptions. If token exchange fails for any reason,
// tokenExchangeResponse remains null and a failure invoke response is sent.
}
if (tokenExchangeResponse == null || string.IsNullOrEmpty(tokenExchangeResponse.Token))
{
return false;
}
return true;
}
アプリ ユーザーのログアウトを処理する
次のコード スニペットを使用して、アプリ ユーザーがログアウトした場合にアクセス トークンを処理します。
private async Task<DialogTurnResult> InterruptAsync(
DialogContext innerDc,
CancellationToken cancellationToken = default(CancellationToken))
{
if (innerDc.Context.Activity.Type == ActivityTypes.Message)
{
var text = innerDc.Context.Activity.Text.ToLowerInvariant();
// Allow logout anywhere in the command.
if (text.IndexOf("logout") >= 0)
{
// The UserTokenClient encapsulates the authentication processes.
var userTokenClient = innerDc.Context.TurnState.Get<UserTokenClient>();
await userTokenClient.SignOutUserAsync(
innerDc.Context.Activity.From.Id,
ConnectionName,
innerDc.Context.Activity.ChannelId,
cancellationToken).ConfigureAwait(false);
await innerDc.Context.SendActivityAsync(
MessageFactory.Text("You have been signed out."), cancellationToken);
return await innerDc.CancelAllDialogsAsync(cancellationToken);
}
}
return null;
}
コード サンプル
このセクションでは、ボット認証 v3 SDK のサンプルを提供します。
| サンプルの名前 | 説明 | .NET | Node.js | Python | マニフェスト |
|---|---|---|---|---|---|
| ボット認証 | このサンプル アプリでは、ボットが Teams 認証を使用する方法を示します。 | 表示 | 表示 | 表示 | 該当なし |
| タブ、ボット、メッセージ拡張機能の SSO | このサンプル アプリでは、安全な認証のために C# と Microsoft Entra ID を使用したタブ、ボット、メッセージ拡張機能の Teams SSO 統合を示します。 | 表示 | 表示 | 該当なし | 表示 |
| タブ、ボット、メッセージ拡張機能 | このサンプルでは、Microsoft Teams のボット、タブ、メッセージ拡張機能全体での Microsoft Entra ID と Facebook 認証について説明します。 | 表示 | 表示 | 該当なし | 表示 |
次の手順
Platform Docs