1

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();


    }

Rikardo
  • 79
  • 1
  • 10
  • So your problem is? The event is never thrown and you don't get the login asked in Teams channel, right? – Nicolas R Aug 01 '19 at 18:50
  • 1
    I've reviewed the code you added and nothing sticks out. It could have something to do with the "stripped down" login prompt. Can you add that code? Honestly, if you can link to a repo of your code or zip it up and email it, I can help much easier. vDAHmicricATmicrosoftDOTcom (replace uppercase with appropriate character) – mdrichardson Aug 06 '19 at 21:06

1 Answers1

1

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.

mdrichardson
  • 7,141
  • 1
  • 7
  • 21
  • Hi, thanks for the reply. I have added this line of code in the onMessageActivity but the problem I have now is that the method keeps displaying the dialog message along with my own code each time I ask the bot question. This probably refers to the OAuthPrompt handling authenticated users. I have tried some if else statements checking if the token exists etc but I am still geting the same error. – Rikardo Aug 06 '19 at 11:40
  • @Rikardo Tough to say exactly what's causing it as it can happen because of code in a lot of different places, mostly `OnMessage`, `OnTurn`, `OnMembersAdded`, and your whole Dialog. Can you include a link to your code or edit ALL of those into your question? – mdrichardson Aug 06 '19 at 14:26
  • I have updated the code in the first post with code. I have manage to get the onMembers working the issue here was as you said it only happens once. The problem I am having now is the checking if user is authenticated. If it is dont show the message "You are now logged in" but instead go and do my own code in the onMessageActivity. The problem I am having now is that my own code from bot class and from dialog class runs all the time I send the message. Any help ? – Rikardo Aug 12 '19 at 13:35
  • @Rikardo The Auth Dialog is *supposed* to run with every message to ensure that the user is authenticated. To stop it from displaying the message, just remove that line on subsequent calls to the dialog. Try adjusting your dialog code flows and if you run into issues, please open a new Stack Overflow question. – mdrichardson Aug 12 '19 at 15:20
  • @Rikardo any updates, I'm facing the same issue. When I run the OAuthPrompt Dialog on every message sent to the bot, it runs the OAuthPrompt Dialog + the code that I want to execute for a OnMessageActivityAsync. And couldn't find a workaround to check if the user is logged in and continue my execution without having the OAuthPrompt dialog running – othman.Da Jun 28 '20 at 18:50
  • @othman.Da Hi, I cant remember what I have done it was over year ago but I think we I have followed mdrichardson advice and keep it in both. I remember it was being displayed once and then in case token expired it would show the dialog again. – Rikardo Jul 07 '20 at 06:43