Bot-docs: Incorrect OAuthInput sample

Created on 14 Aug 2020  Â·  9Comments  Â·  Source: MicrosoftDocs/bot-docs

I tried implementing the sample code for OAuthInput, but the TokenProperty property doesn't seem to exist on class OAuthInput. It would also be nice to have some documentation on how OAuthInput should be implemented in broader sense. As in: how do I ge the token in later steps, is this token automatically refreshed when it expires? What happens when the token in time is not valid any more? I was unable to find up-to-date documentation on this for the Adaptive Dialog.


Document Details

⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

tracking

All 9 comments

@jsiegmund Please, look at this article: Authentication. It might help in handling the token expiration.

@jsiegmund As far as OAuthInput is concerned , waiting for an official sample, you can look at this proof of concept below to get an idea.

using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Adaptive;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Actions;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Conditions;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Generators;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Input;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Templates;
using Microsoft.Bot.Builder.LanguageGeneration;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using System.IO;

namespace AdaptiveOAuthBot.Dialogs
{
    public class RootDialog : AdaptiveDialog
    {
        private OAuthInput MyOAuthInput { get; }

        public RootDialog(IConfiguration configuration) : base(nameof(RootDialog))
        {
            MyOAuthInput = new OAuthInput
            {
                ConnectionName = configuration["ConnectionName"],
                Title = "Please log in",
                Text = "This will give you access!",
                InvalidPrompt = new ActivityTemplate("Login was not successful please try again."),
                Timeout = 300000,
                MaxTurnCount = 3,
                Property = "turn.oauth",
            };


            string[] paths = { ".", "Dialogs", $"RootDialog.lg" };
            string fullPath = Path.Combine(paths);

            // These steps are executed when this Adaptive Dialog begins
            Triggers = new List<OnCondition>
                {
                    // Add a rule to welcome user
                    new OnConversationUpdateActivity
                    {
                        Actions = WelcomeUserSteps(),
                    },

                    // Respond to user on message activity
                    new OnUnknownIntent
                    {
                        Actions = LoginSteps(),
                    },
                };
            Generator = new TemplateEngineLanguageGenerator(Templates.ParseFile(fullPath));
        }

        private static List<Dialog> WelcomeUserSteps()
        {
            return new List<Dialog>
            {
                // Iterate through membersAdded list and greet user added to the conversation.
                new Foreach()
                {
                    ItemsProperty = "turn.activity.membersAdded",
                    Actions =
                    {
                        // Note: Some channels send two conversation update events - one for the Bot added to the conversation and another for user.
                        // Filter cases where the bot itself is the recipient of the message. 
                        new IfCondition()
                        {
                            Condition = "$foreach.value.name != turn.activity.recipient.name",
                            Actions =
                            {
                                new SendActivity("Hello, I'm the multi-turn prompt bot. Please send a message to get started!")
                            }
                        }
                    }
                }
            };
        }

        private List<Dialog> LoginSteps()
        {
            return new List<Dialog>
            {
                MyOAuthInput,
                //new SendActivity("\\{turn.token.token} = `${turn.token.token}`."),
                new IfCondition
                {
                    Condition = "turn.token.token && length(turn.token.token)",
                    Actions = LoginSuccessSteps(),
                    ElseActions =
                    {
                        new SendActivity("Sorry, we were unable to log you in."),
                    },
                },
                new EndDialog(),
            };
        }

        private List<Dialog> LoginSuccessSteps()
        {
            return new List<Dialog>
            {
                new SendActivity("You are now logged in."),
                new ConfirmInput
                {
                    Prompt = new ActivityTemplate("Would you like to view your token?"),
                    InvalidPrompt = new ActivityTemplate("Oops, I didn't understand. Would you like to view your token?"),
                    MaxTurnCount = 3,
                },
                new IfCondition
                {
                    Condition = "turn.lastResult == true",
                    ElseActions =
                    {
                        new SendActivity("Great. Type anything to continue."),
                    },
                    Actions =
                    {
                        MyOAuthInput,
                        new SendActivity("Here is your token `${turn.token.token}`."),
                    },
                },
            };
        }
    }
}

Yeah I had it working in the meantime, but ran into another problem in MS Teams (see https://github.com/microsoft/botbuilder-dotnet/issues/4477). So I got it 'working', but still the docs should be checked I think :)

@jsiegmund @Jeffders A couple of remarks to conclude this phase of the discussion.

  • The following code
    MyOAuthInput = new OAuthInput
         {
                ConnectionName = configuration["ConnectionName"],
                Title = "Please log in",
                Text = "This will give you access!",
                InvalidPrompt = 
        new ActivityTemplate("Login was not successful please try again."),
                Timeout = 300000,
                MaxTurnCount = 3,
                Property = "turn.oauth",
         };

defines the OAuthInput object that contains OAuth information. Property contains the memory path where to store the TokenResponse object.
The value shown in the example turn.oauth is arbitrary, you can use any value. It is a good practice to record the token response to a property on the turn scope, since the token can expire between turns.

  • Also, the token must be ephemeral, that is short-lived. The bot cannot hold on a token because it expires after a certain amount of time. It is good practice to check for token expiration and if the token is expired ask the user to login again. If you do not do this, you will get an error down the line, when the bot tries to access user’s protected resources.
    Notice, also that the adaptive dialog manager has its own expiration for a conversation. This means if a conversation has been idle for a while, when the users attempt to continue and the conversation has expired, they will be asked to login again.

Understood. In my current code I'm starting off the dialog with the OAuthInput, so I guess that when the dialog itself expires this would mean the user will have to instruct the bot to start the process again and would be faced with the OAuthInput again, right? That's fine with me.

As for the expired token itself, what I'm doing right now:

  • I have a reference to the OAuthInput dialog stored in my main dialog.
  • I have a method which gets the authentication token by calling OAuthDialog.GetUserTokenAsync(context);
  • I am not sure whether that simply returns me the same token each time or whether it goes out to the Bot Service?
  • I am also not sure if the Bot Service now 'stand alone' will utilize the refresh token to get a new access token once it expires? I have configured the URL for this in the OAuth connection settings for the bot itself, but this is all very much behind the scenes stuff so I have a hard time finding out what actually happens.

In my debug sessions this works fine and I can do stuff for several minutes without having any expiry issues. But I've yet to test what happens when it starts taking too long. I'll need to check the expiry time of the token I'm getting back, otherwise a simple solution might be to have the same expiry time for the dialog itself so that it always expires even before the token does. The process I'm implementing is not something the user will be pauzing in most likely. Then again, it's still chat and chat is async of course.

@jsiegmund Can we close this issue? Thanks

My issue is fixed, but the error in the docs (TokenPropery instead of Property) is still there. So be my guest if you want to close it but I think it should be corrected nevertheless.

@jsiegmund That is what I need to know. Let me keep this open, for now. I will pass the info to the writer that is working on the specific documentation.

@jsiegmund The problem has been fixed and the updated documentation will be available in the next upcoming release.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

v-kydela picture v-kydela  Â·  7Comments

montacerdk picture montacerdk  Â·  5Comments

amthomas46 picture amthomas46  Â·  4Comments

DanteNahuel picture DanteNahuel  Â·  3Comments

SarahSexton picture SarahSexton  Â·  3Comments