ボットから Office 365 など他サービスを呼び出す際に、Azure AD など外部 ID サービスに対して認証が必要となります。Bot Service では認証を GUI での設定と簡単なコードで実現できます。
Azure Bot Service が提供する認証支援機能
今までは、他サービスの認証を行う為には開発者がコードを全て用意する必要がありました。またセキュリティ対応のため、認証後にユーザーにマジックコード (6 桁の数字など) を別途送ってもらい認証を完了していましたが、Azure Bot Service ではその処理をより簡単にします。
- マジックコードを使わずに認証を行える
- ポータルから OAuth 対応プロバイダーの追加や削除など構成が可能
- Azure AD や GitHub など複数のプロバイダーをはじめから用意
- Bot Builder SDK でトークンの取得や認証ダイアログの作成などをサポート
Azure AD に対する認証の準備
今回は ScheduleDialog 機能で Microsoft Graph を利用するため、Azure AD v2 の認証を設定します。Azure AD には v1 と v2 がありますが、詳しはMicrosoft Graph を使ってみよう : Azure AD における認証概要 を参照してください。
前提
- Office 365 (管理者権限が必要のため、試用版で試すことを推奨します)
※持っていない場合、こちら より体験版を作成してください。
Azure ポータルからアプリケーションの登録
まず Azure AD に対して Microsoft Graph を利用するアプリケーションを登録します。
1. 利用している Office 365 の管理者アカウントで Microsoft Application Registration Portal にログイン。
4. Application Id をメモしてから、Application Secrets で「Generate New Password」をクリック。
5. パスワードが表示されるのでコピー。※一度しか表示されない。
6. Platform より「Add Platform」をクリック
8. Redirect URLs に 「https://token.botframework.com/.auth/web/redirect」 を追加。
9. Microsoft Graph Permission の Delegated Permissions より「Add」をクリック。
10. Calendars.Read にチェックを入れて「OK」をクリック。
11. 「Save」をクリックしてアプリケーションの登録完了。
Bot チャンネルへの登録
次に Bot チャンネルに対して、作成したアプリケーションを構成します。
1. Azure ポータル にログインし、「リソースの作成」をクリック。※ Office 365 ユーザーではなく、開発で利用している Azure アカウントを利用。
2. 「Bot Channels Registration」を検索して作成。
3. 任意の名前を付けて「作成」をクリック。※価格レベルは F0 (無料) で今回は十分。
4. 作成が完了したらリソースに移動して、「設定」をクリック。
5. 画面一番下にある OAuth 接続設定より「設定の追加」をクリック。
6. 名前を付けて、サービスプロバイダーより「Azure Active Directory v2」を選択。
7. 画面の様に設定を追加して「Save」をクリック。
※ Tenant ID は Office 365 のテナント ID を入れるか、複数のテナントで使いたい場合は common を指定。
※スコープは大文字小文字を識別する。複数指定する場合は半角スペース
9. Office 365 管理者権限でログイン。アプリ名、アクセス許可が正しいことを確認。「組織の代理として同意する」にチェックをいれて「承諾」をクリック。これにより他の一般ユーザーではコンセントが出なくて済む。
11. 次にボットで使うための ApplicationId とパスワードを確認。構成にある「Microsoft App ID」をコピーして保存。
13. Application Secret セクションで「Generate New Password」をクリック。
14. 表示されたパスワードを保存しておく。
Bot にログイン機能を追加
最後にコードを追加します。Microsoft.Bot.Builder.Dialogs モジュールで提供される OAuthPrompt を使って実装します。
1. ScheduleDialog.cs を以下のコードと差し替え。
- OAuthPrompt の利用
- 取得したトークンの表示
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
public class ScheduleDialog : ComponentDialog
{
private const string connectionName = "AzureAdv2";
public ScheduleDialog() : base(nameof(ScheduleDialog))
{
// ウォーターフォールのステップを定義。処理順にメソッドを追加。
var waterfallSteps = new WaterfallStep[]
{
LoginAsync,
LoginStepAsync,
};
// ウォーターフォールダイアログと各種プロンプトを追加
AddDialog(new WaterfallDialog("schedule", waterfallSteps));
AddDialog(new OAuthPrompt(
"login",
new OAuthPromptSettings
{
ConnectionName = connectionName,
Text = "サインインダイアログ",
Title = "サインイン",
Timeout = 300000, // 5分でタイムアウトするように設定
}));
}
private static async Task<DialogTurnResult> LoginAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
return await stepContext.BeginDialogAsync("login", cancellationToken: cancellationToken);
}
private static async Task<DialogTurnResult> LoginStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
// ログインの結果よりトークンを取得
var tokenResponse = (TokenResponse)stepContext.Result;
await stepContext.Context.SendActivityAsync($"Token: {tokenResponse.Token}", cancellationToken: cancellationToken);
return await stepContext.EndDialogAsync();
}
}
2. 上記で取得した AppId とパスワードを MyBot.bot に追加。
3. Startup.cs の AddBot 内で認証情報を使うように設定。
services.AddBot<MyBot>(options =>
{
options.Middleware.Add(new MyLoggingMiddleware());
options.Middleware.Add(new MyMiddleware());
// Endpoint を構成ファイルより取得
EndpointService endpointService = (EndpointService)botConfig.Services.Where(x => x.Type == "endpoint").First();
// 認証として AppId と AppPassword を使うように設定
options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);
// ストレージとしてインメモリを利用
IStorage dataStore = new MemoryStorage();
var userState = new UserState(dataStore);
var conversationState = new ConversationState(dataStore);
options.State.Add(userState);
options.State.Add(conversationState);
});
4. MyBot.cs のコンストラクタで ScheduleDialog を DialogSets に追加。
public MyBot(MyStateAccessors accessors, LuisRecognizer luisRecognizer)
{
this.accessors = accessors;
this.luisRecognizer = luisRecognizer;
this.dialogs = new DialogSet(accessors.ConversationDialogState);
// コンポーネントダイアログを追加
dialogs.Add(new ProfileDialog(accessors));
dialogs.Add(new MenuDialog());
dialogs.Add(new WeatherDialog());
dialogs.Add(new ScheduleDialog());
}
5. OnTurnAsync メソッドで LUIS の結果によって ScheduleDialog へ振り分けるよう、分岐を追加。
else if (topIntent.Value.intent == "Schedule")
{
await dialogContext.BeginDialogAsync(nameof(ScheduleDialog), null, cancellationToken);
}
6. 認証後は OnTurnAsync に対して Activity.Type が Event または Invoke で戻ってくるため、処理を追加。
else if(turnContext.Activity.Type == ActivityTypes.Event || turnContext.Activity.Type == ActivityTypes.Invoke)
{
// Event または Invoke で戻ってきた場合ダイアログを続ける
await dialogContext.ContinueDialogAsync(cancellationToken);
if (!turnContext.Responded)
{
await turnContext.SendActivityAsync("わかりませんでした。全てキャンセルします。", cancellationToken: cancellationToken);
await dialogContext.CancelAllDialogsAsync(cancellationToken);
await dialogContext.BeginDialogAsync(nameof(MenuDialog), null, cancellationToken);
}
}
7. LUIS ポータル より新しいインテントを追加。Train してから Publish。
テスト
1. F5 キーを押下してデバッグ実行後、エミュレーターを起動。新しい .bot ファイルを読み込み。
2. プロファイルダイアログを完了し、「予定を確認」をクリック。
3. サインインプロンプトが出るので、「サインイン」をクリック。
6. 再度「予定の確認」をクリックした際、ログインをしなくてもトークンが取れることを確認。
Bot にログアウト機能を追加
一度ログインが成功すると、トークンの期限が切れるまでは再ログインを聞かれませんが、任意のタイミングでログアウトしたい場合もあるため、ここではログアウトを追加します。
1. LUIS にログアウトインテントを追加。Train 後 Publish。
2. MyBot.cs に新しくログアウトの判定と処理を追加。
※ここでは接続名を直接記述しているが Accessor に設定して取得する方がよい。
else if (topIntent.Value.intent == "Logout")
{
// アダプターを取得
var botAdapter = (BotFrameworkAdapter)turnContext.Adapter;
// 指定した接続をログアウト
await botAdapter.SignOutUserAsync(turnContext, "AzureAdv2", cancellationToken: cancellationToken);
await turnContext.SendActivityAsync("ログアウトしました。", cancellationToken: cancellationToken);
var results = await dialogContext.ContinueDialogAsync(cancellationToken);
// DialogTurnStatus が Complete または Empty の場合、メニューへ。
if (results.Status == DialogTurnStatus.Complete || results.Status == DialogTurnStatus.Empty)
// メニューの表示
await dialogContext.BeginDialogAsync(nameof(MenuDialog), null, cancellationToken);
}
テスト
1. F5 でデバッグを開始して、エミュレーターより「ログオフ」を送信。
2. 「予定の確認」を送信。再度サインインできることを確認。
まとめ
今回は Azure AD v2 の認証について、ログイン/ログオフを見ていきました。次回は取得したトークンで Microsoft Graph から予定を取得します。