Rikardo
Rikardo

Reputation: 79

MS teams Microsoft Bot Authentication V4

I am working on the example Bot Authentication MS GRAPH

I have desired output in the emulator channel as on the photo below:

Emulator output

When user joins the channel he is presented with welcome message and Login prompt. After authentication dialog is finished I want to return to the onMessageActivity to carry on with my code. The teams channel seems not to respond to the onMembersAdded at all.

    protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            foreach (var member in turnContext.Activity.MembersAdded)
            {
                if (member.Id != turnContext.Activity.Recipient.Id)
                {
                    // First message and action that will happen when user joins the channel
                    await turnContext.SendActivityAsync(MessageFactory.Text("Welcome to Chat Bot. Please login."), cancellationToken);
                    // Call OAuthDialog
                    //await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
                    //  await turnContext.SendActivityAsync(MessageFactory.Text($"Welcome to Audit Bot! I am happy to help!. Type 'Login' anytime to sign-in."), cancellationToken);


                }
            }
        }
        public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (turnContext?.Activity?.Type == ActivityTypes.Invoke && turnContext.Activity.ChannelId == "msteams")
            {
                await turnContext.SendActivityAsync("You are using MS Teams.");
                await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
            }
            else
            {
                await base.OnTurnAsync(turnContext, cancellationToken);
                // Save any state changes that might have occured during the turn.
                await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
                await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
            }

        }
        protected override async Task OnTokenResponseEventAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
        {
            Logger.LogInformation("Running dialog with Token Response Event Activity.");
            // Run the Dialog with the new Token Response Event Activity.
            // Create Token globally and assigned here
             token = turnContext.Activity.Value.ToString();
            //var myJsonString = "{token: {Id: \"aaakkj98898983\"}}";
            //var jo = JObject.Parse(token);
            // cToken = jo["token"].ToString();
            //  await turnContext.SendActivityAsync(cToken);
            await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);

        }
     protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
        {
                // Call the MainDialog to display OAuth
                await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
                // Proceed with rest of the code
                Logger.LogInformation("Running dialog with Message Activity.");
                //  First, we use the dispatch model to determine which cognitive service(LUIS or QnA) to use.
                var recognizerResult = await _botServices.Dispatch.RecognizeAsync(turnContext, cancellationToken);
                // Top intent tells us which cognitive service to use. LUIS or QnA Maker
                var topIntent = recognizerResult.GetTopScoringIntent();
                // Next, we call the dispatcher with the top intent.
                await DispatchToTopIntentAsync(turnContext, topIntent.intent, recognizerResult, cancellationToken);      

        }

Dialog Class ( I want to display my Graph call only once)

 AddDialog(new OAuthPrompt(
            nameof(OAuthPrompt),
            new OAuthPromptSettings
            {
                ConnectionName = "my connection", // HARD CODED
                Text = "Please login",
                Title = "Login",
                Timeout = 300000, // User has 5 minutes to login
            }));

        AddDialog(new TextPrompt(nameof(TextPrompt)));
        //  AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));

        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)
    {
        var tokenResponse = (TokenResponse)stepContext.Result;
        //Send token to state
        // stepContext.Context.TurnState.Add("tokenResponse", tokenResponse);

        if (tokenResponse != null)
        {
            ////
            await OAuthHelpers.ListMeAsync(stepContext.Context, tokenResponse);
            return await stepContext.EndDialogAsync();

        }
        else
        {
            return await stepContext.BeginDialogAsync(nameof(OAuthPrompt), null, cancellationToken);

        }

    }

    private async Task<DialogTurnResult> LoginStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        // Get the token from the previous step. Note that we could also have gotten the
        // token directly from the prompt itself. There is an example of this in the next method.
        var tokenResponse = (TokenResponse)stepContext.Result;

        if (tokenResponse != null)
        {

            await stepContext.Context.SendActivityAsync(MessageFactory.Text("You are now logged in."), cancellationToken);
            // Display my name 
            await OAuthHelpers.ListMeAsync(stepContext.Context, tokenResponse);
            // End and return to the bot onMessageActivity
            return await stepContext.EndDialogAsync();

        }

        await stepContext.Context.SendActivityAsync(MessageFactory.Text("Login was not successful please try again."), cancellationToken);
        return await stepContext.EndDialogAsync();


    }

Upvotes: 1

Views: 728

Answers (1)

mdrichardson
mdrichardson

Reputation: 7241

Teams will only fire the OnMembersAdded event one time -- when the user first adds the bot. The user can delete or uninstall the bot and it will still never fire again more than just that initial time. Note: Facebook Channel is like this, too

What you want to do, instead, is to fire the OAuthPrompt dialog on both:

  1. OnMembersAdded (you already do), and again in
  2. OnMessage (need to add this here await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(OAuthPrompt)), cancellationToken);)

If the user is already authenticated, OAuthPrompt will return the token without requesting it again. You just need to make sure that your OAuthPrompt dialog appropriately handles users that are already authenticated. It's a good idea to have this in OnMessage anyway, to make sure that you're always authenticated with your bot.

This can make it difficult to debug, I know. You can work around this through my answer here, which was for a similar question I answered today.

Upvotes: 1

Related Questions