5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Bot Builder v4 でボット開発 : 他サービスに対する認証

Last updated at Posted at 2018-10-27

ボットから 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 (管理者権限が必要のため、試用版で試すことを推奨します)

※持っていない場合、こちら より体験版を作成してください。
image.png

Azure ポータルからアプリケーションの登録

まず Azure AD に対して Microsoft Graph を利用するアプリケーションを登録します。

1. 利用している Office 365 の管理者アカウントで Microsoft Application Registration Portal にログイン。
image.png

2. 「Add an app」ボタンをクリック。
image.png

3. 名前を付けて、「Create」をクリック。
image.png

4. Application Id をメモしてから、Application Secrets で「Generate New Password」をクリック。
image.png

5. パスワードが表示されるのでコピー。※一度しか表示されない。
image.png

6. Platform より「Add Platform」をクリック
image.png

7. 「Web」をクリック。
image.png

8. Redirect URLs に 「https://token.botframework.com/.auth/web/redirect」 を追加。
image.png

9. Microsoft Graph Permission の Delegated Permissions より「Add」をクリック。
image.png

10. Calendars.Read にチェックを入れて「OK」をクリック。
image.png

11. 「Save」をクリックしてアプリケーションの登録完了。
image.png

Bot チャンネルへの登録

次に Bot チャンネルに対して、作成したアプリケーションを構成します。

1. Azure ポータル にログインし、「リソースの作成」をクリック。※ Office 365 ユーザーではなく、開発で利用している Azure アカウントを利用。
image.png

2. 「Bot Channels Registration」を検索して作成。
image.png

3. 任意の名前を付けて「作成」をクリック。※価格レベルは F0 (無料) で今回は十分。
image.png

4. 作成が完了したらリソースに移動して、「設定」をクリック。
image.png

5. 画面一番下にある OAuth 接続設定より「設定の追加」をクリック。
image.png

6. 名前を付けて、サービスプロバイダーより「Azure Active Directory v2」を選択。
image.png

7. 画面の様に設定を追加して「Save」をクリック。
※ Tenant ID は Office 365 のテナント ID を入れるか、複数のテナントで使いたい場合は common を指定。
※スコープは大文字小文字を識別する。複数指定する場合は半角スペース
image.png

8. 作成された接続を開いて「接続テスト」をクリック。
image.png

9. Office 365 管理者権限でログイン。アプリ名、アクセス許可が正しいことを確認。「組織の代理として同意する」にチェックをいれて「承諾」をクリック。これにより他の一般ユーザーではコンセントが出なくて済む。
image.png

10. 成功すると以下の画面が出る。
image.png

11. 次にボットで使うための ApplicationId とパスワードを確認。構成にある「Microsoft App ID」をコピーして保存。
image.png

12. 管理リンクをクリック。
image.png

13. Application Secret セクションで「Generate New Password」をクリック。
image.png

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 に追加。
image.png

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。
image.png

テスト

1. F5 キーを押下してデバッグ実行後、エミュレーターを起動。新しい .bot ファイルを読み込み。
image.png

2. プロファイルダイアログを完了し、「予定を確認」をクリック。
image.png

3. サインインプロンプトが出るので、「サインイン」をクリック。
image.png

4. ログイン画面が出るので、ログインを実行。
image.png

5. サインイン完了後、トークンが取得できることを確認。
image.png

6. 再度「予定の確認」をクリックした際、ログインをしなくてもトークンが取れることを確認。

Bot にログアウト機能を追加

一度ログインが成功すると、トークンの期限が切れるまでは再ログインを聞かれませんが、任意のタイミングでログアウトしたい場合もあるため、ここではログアウトを追加します。

1. LUIS にログアウトインテントを追加。Train 後 Publish。
image.png

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 でデバッグを開始して、エミュレーターより「ログオフ」を送信。
image.png

2. 「予定の確認」を送信。再度サインインできることを確認。

まとめ

今回は Azure AD v2 の認証について、ログイン/ログオフを見ていきました。次回は取得したトークンで Microsoft Graph から予定を取得します。

次の記事へ
目次へ戻る

この記事のサンプルコード

5
4
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?