private async Task <bool> AuthenticateUser(ITurnContext turnContext, CancellationToken cancellationToken) { bool waitingForLogin = false; // perform authorization check ... if already authorized, nothing happens, if not auth process is started if (_azureAdEnabled) { var botAdapter = (BotFrameworkAdapter)turnContext.Adapter; var token = await botAdapter.GetUserTokenAsync(turnContext, _connectionName, null, cancellationToken); if (token == null) { var promptSettings = new OAuthPromptSettings { ConnectionName = _configuration["AzureAd:ConnectionName"], Text = _configuration["AzureAd:LoginText"], Title = _configuration["AzureAd:ButtonText"], Timeout = 300000, // User has 5 minutes to login }; var authPrompt = new OAuthPrompt(nameof(OAuthPrompt), promptSettings); await authPrompt.Run(turnContext, _conversationState.CreateProperty <DialogState>("DialogState"), cancellationToken); waitingForLogin = true; // Save any state changes that might have occured during the turn. await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken); await _userState.SaveChangesAsync(turnContext, false, cancellationToken); } } return(waitingForLogin); }
public async Task OAuthPromptBeginDialogWithWrongOptions() { await Assert.ThrowsAsync <ArgumentException>(async() => { var prompt = new OAuthPrompt("abc", new OAuthPromptSettings()); var convoState = new ConversationState(new MemoryStorage()); var dialogState = convoState.CreateProperty <DialogState>("dialogState"); var adapter = new TestAdapter() .Use(new AutoSaveStateMiddleware(convoState)); // Create new DialogSet. var dialogs = new DialogSet(dialogState); dialogs.Add(prompt); var tc = new TurnContext(adapter, new Activity() { Type = ActivityTypes.Message, Conversation = new ConversationAccount() { Id = "123" }, ChannelId = "test" }); var dc = await dialogs.CreateContextAsync(tc); await prompt.BeginDialogAsync(dc, CancellationToken.None); }); }
public async Task GetUserTokenShouldReturnToken() { var oauthPromptSettings = new OAuthPromptSettings { ConnectionName = ConnectionName, Text = "Please sign in", Title = "Sign in", }; var prompt = new OAuthPrompt("OAuthPrompt", oauthPromptSettings); var convoState = new ConversationState(new MemoryStorage()); var dialogState = convoState.CreateProperty <DialogState>("dialogState"); var adapter = new TestAdapter() .Use(new AutoSaveStateMiddleware(convoState)); adapter.AddUserToken(ConnectionName, ChannelId, UserId, Token); // Create new DialogSet. var dialogs = new DialogSet(dialogState); dialogs.Add(prompt); var activity = new Activity { ChannelId = ChannelId, From = new ChannelAccount { Id = UserId } }; var turnContext = new TurnContext(adapter, activity); var userToken = await prompt.GetUserTokenAsync(turnContext, CancellationToken.None); Assert.Equal(Token, userToken.Token); }
private async Task <DialogTurnResult> DisplayTokenAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { var result = (bool)stepContext.Result; if (result) { const string showTokenMessage = "Here is your token:"; await stepContext.Context.SendActivityAsync(MessageFactory.Text($"{showTokenMessage} {stepContext.Values["Token"]}", showTokenMessage, InputHints.IgnoringInput), cancellationToken); } // Sign out const string signOutMessage = "I have signed you out."; var oauthPrompt = new OAuthPrompt( "SignOut", new OAuthPromptSettings { ConnectionName = _connectionName, Text = signOutMessage, Title = "Sign Out" }); await oauthPrompt.SignOutUserAsync(stepContext.Context, cancellationToken); await stepContext.Context.SendActivityAsync(MessageFactory.Text(signOutMessage, signOutMessage, inputHint: InputHints.IgnoringInput), cancellationToken); return(await stepContext.EndDialogAsync(cancellationToken : cancellationToken)); }
private Task SendOAuthCardAsync(DialogContext dc, IMessageActivity prompt, CancellationToken cancellationToken) { var settings = new OAuthPromptSettings { ConnectionName = ConnectionName?.GetValue(dc.State), Title = Title?.GetValue(dc.State), Text = Text?.GetValue(dc.State) }; return(OAuthPrompt.SendOAuthCardAsync(settings, dc.Context, prompt, cancellationToken)); }
private Task <PromptRecognizerResult <TokenResponse> > RecognizeTokenAsync(DialogContext dc, CancellationToken cancellationToken) { var settings = new OAuthPromptSettings { ConnectionName = ConnectionName.GetValue(dc.State) }; return(OAuthPrompt.RecognizeTokenAsync(settings, dc, cancellationToken)); }
public async Task OAuthPromptBeginDialogWithNoDialogContext() { await Assert.ThrowsAsync <ArgumentNullException>(async() => { var prompt = new OAuthPrompt("abc", new OAuthPromptSettings()); await prompt.BeginDialogAsync(null); }); }
private async Task SendOAuthCardAsync(DialogContext dc, IMessageActivity prompt, CancellationToken cancellationToken) { var title = await Title.GetValueAsync(dc, cancellationToken).ConfigureAwait(false); var text = await Text.GetValueAsync(dc, cancellationToken).ConfigureAwait(false); var settings = new OAuthPromptSettings { ConnectionName = ConnectionName?.GetValue(dc.State), Title = title, Text = text }; await OAuthPrompt.SendOAuthCardAsync(settings, dc.Context, prompt, cancellationToken).ConfigureAwait(false); }
public async Task OAuthPromptContinueDialogWithNullDialogContext() { await Assert.ThrowsAsync <ArgumentNullException>(async() => { var prompt = new OAuthPrompt("abc", new OAuthPromptSettings()); var convoState = new ConversationState(new MemoryStorage()); var dialogState = convoState.CreateProperty <DialogState>("dialogState"); var dialogs = new DialogSet(dialogState); dialogs.Add(prompt); await prompt.ContinueDialogAsync(null, CancellationToken.None); }); }
public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken)) { var skillDialogOptions = (SkillDialogOptions)options; _skill = skillDialogOptions.MatchedSkill; // Set our active Skill so later methods know which Skill to use. dc.ActiveDialog.State[ActiveSkillStateKey] = skillDialogOptions.MatchedSkill; _authPrompt = new OAuthPrompt(nameof(OAuthPrompt), new OAuthPromptSettings() { ConnectionName = skillDialogOptions.MatchedSkill.AuthConnectionName, Title = "Skill Authentication", Text = $"Please login to access this feature.", }); var parameters = new Dictionary <string, object>(); if (skillDialogOptions.MatchedSkill.Parameters != null) { foreach (var parameter in skillDialogOptions.MatchedSkill.Parameters) { if (skillDialogOptions.Parameters.TryGetValue(parameter, out var paramValue)) { parameters.Add(parameter, paramValue); } } } var skillMetadata = new SkillMetadata( skillDialogOptions.LuisResult, skillDialogOptions.LuisService, skillDialogOptions.MatchedSkill.Configuration, parameters); var dialogBeginEvent = new Activity( type: ActivityTypes.Event, channelId: dc.Context.Activity.ChannelId, from: new ChannelAccount(id: dc.Context.Activity.From.Id, name: dc.Context.Activity.From.Name), recipient: new ChannelAccount(id: dc.Context.Activity.Recipient.Id, name: dc.Context.Activity.Recipient.Name), conversation: new ConversationAccount(id: dc.Context.Activity.Conversation.Id), name: SkillBeginEventName, value: skillMetadata); return(await ForwardToSkill(dc, dialogBeginEvent)); }
public LoginDialog( IServiceProvider serviceProvider, IBotTelemetryClient telemetryClient) : base(nameof(LoginDialog)) { _serviceProvider = serviceProvider; _oauthPrompt = serviceProvider.GetService <OAuthPrompt>(); AddDialog(_oauthPrompt); AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[] { PromptStepAsync, LoginStepAsync })); InitialDialogId = nameof(WaterfallDialog); }
private async Task SendOAuthCardAsync(DialogContext dc, IMessageActivity prompt, CancellationToken cancellationToken) { // Save state prior to sending OAuthCard: the invoke response for a token exchange from the root bot could come in // before this method ends or could land in another instance in scale-out scenarios, which means that if the state is not saved, // the OAuthInput would not be at the top of the stack, and the token exchange invoke would get discarded. await dc.Context.TurnState.Get <ConversationState>().SaveChangesAsync(dc.Context, false, cancellationToken).ConfigureAwait(false); // Prepare OAuthCard var title = Title == null ? null : await Title.GetValueAsync(dc, cancellationToken).ConfigureAwait(false); var text = Text == null ? null : await Text.GetValueAsync(dc, cancellationToken).ConfigureAwait(false); var settings = new OAuthPromptSettings { ConnectionName = ConnectionName?.GetValue(dc.State), Title = title, Text = text }; // Send OAuthCard to root bot. The root bot could attempt to do a token exchange or if it cannot do token exchange for this connection // it will let the card get to the user to allow them to sign in. await OAuthPrompt.SendOAuthCardAsync(settings, dc.Context, prompt, cancellationToken).ConfigureAwait(false); }
protected override async Task OnUnrecognizedActivityTypeAsync(ITurnContext turnContext, CancellationToken cancellationToken) { if (turnContext.Activity.Type == "invoke") { var promptSettings = new OAuthPromptSettings { ConnectionName = _configuration["AzureAd:ConnectionName"], Text = _configuration["AzureAd:LoginText"], Title = _configuration["AzureAd:ButtonText"], Timeout = 300000, // User has 5 minutes to login }; var authPrompt = new OAuthPrompt(nameof(OAuthPrompt), promptSettings); await authPrompt.Run(turnContext, _conversationState.CreateProperty <DialogState>("DialogState"), cancellationToken); await CompleteSignIn(turnContext, cancellationToken); } else { await base.OnUnrecognizedActivityTypeAsync(turnContext, cancellationToken); } }
public MainDialog( IServiceProvider serviceProvider, IBotTelemetryClient telemetryClient) : base(nameof(MainDialog), serviceProvider, telemetryClient) { _services = serviceProvider.GetService <BotServices>(); _settings = serviceProvider.GetService <BotSettings>(); _templateEngine = serviceProvider.GetService <LocaleTemplateEngineManager>(); _skillsConfig = serviceProvider.GetService <SkillsConfiguration>(); var userState = serviceProvider.GetService <UserState>(); _userProfileState = userState.CreateProperty <UserProfileState>(nameof(UserProfileState)); var conversationState = serviceProvider.GetService <ConversationState>(); _previousResponseAccessor = conversationState.CreateProperty <List <Activity> >(StateProperties.PreviousBotResponse); WaterfallStep[] steps = SetupWaterfallSteps(); AddDialog(new WaterfallDialog(nameof(MainDialog), steps)); AddDialog(new TextPrompt(nameof(TextPrompt))); AddDialog(new ChoicePrompt(nameof(ChoicePrompt)) { Style = ListStyle.HeroCard }); InitialDialogId = nameof(MainDialog); // Register dialogs _oauthPrompt = serviceProvider.GetService <OAuthPrompt>(); _loginDialog = serviceProvider.GetService <LoginDialog>(); _onboardingDialog = serviceProvider.GetService <OnboardingDialog>(); _switchSkillDialog = serviceProvider.GetService <SwitchSkillDialog>(); _escalateDialog = serviceProvider.GetService <EscalateDialog>(); _cancelDialog = serviceProvider.GetService <CancelDialog>(); _entertainDialog = serviceProvider.GetService <EntertainDialog>(); _chitchatdialog = serviceProvider.GetService <ChitchatDialog>(); _stressDialog = serviceProvider.GetService <StressDialog>(); _highstresshandlingDialog = serviceProvider.GetService <HighStressHandlingDialog>(); _stresshandlingDialog = serviceProvider.GetService <StressHandlingDialog>(); _journalingDialog = serviceProvider.GetService <JournalingDialog>(); AddDialog(_oauthPrompt); AddDialog(_loginDialog); AddDialog(_onboardingDialog); AddDialog(_switchSkillDialog); AddDialog(_escalateDialog); AddDialog(_cancelDialog); AddDialog(_entertainDialog); AddDialog(_chitchatdialog); AddDialog(_stressDialog); AddDialog(_highstresshandlingDialog); AddDialog(_stresshandlingDialog); AddDialog(_journalingDialog); // Register a QnAMakerDialog for each registered knowledgebase and ensure localised responses are provided. var localizedServices = _services.GetCognitiveModels(); foreach (var knowledgebase in localizedServices.QnAConfiguration) { var qnaDialog = new QnAMakerDialog( knowledgeBaseId: knowledgebase.Value.KnowledgeBaseId, endpointKey: knowledgebase.Value.EndpointKey, hostName: knowledgebase.Value.Host, noAnswer: _templateEngine.GenerateActivityForLocale("UnsupportedMessage"), activeLearningCardTitle: _templateEngine.GenerateActivityForLocale("QnaMakerAdaptiveLearningCardTitle").Text, cardNoMatchText: _templateEngine.GenerateActivityForLocale("QnaMakerNoMatchText").Text) { Id = knowledgebase.Key }; AddDialog(qnaDialog); } // Register skill dialogs var skillDialogs = serviceProvider.GetServices <SkillDialog>(); foreach (var dialog in skillDialogs) { AddDialog(dialog); } }
public void OAuthPromptWithEmptyIdShouldFail() { var emptyId = string.Empty; var confirmPrompt = new OAuthPrompt(emptyId, new OAuthPromptSettings()); }
public void OAuthPromptWithEmptySettingsShouldFail() { var confirmPrompt = new OAuthPrompt("abc", null); }
/// <summary> /// Called when a prompt dialog is pushed onto the dialog stack and is being activated. /// </summary> /// <param name="dc">The dialog context for the current turn of the conversation.</param> /// <param name="options">Optional, additional information to pass to the prompt being started.</param> /// <param name="cancellationToken">A cancellation token that can be used by other objects /// or threads to receive notice of cancellation.</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> /// <remarks>If the task is successful, the result indicates whether the prompt is still /// active after the turn has been processed by the prompt.</remarks> public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken)) { if (dc == null) { throw new ArgumentNullException(nameof(dc)); } if (options is CancellationToken) { throw new ArgumentException($"{nameof(options)} cannot be a cancellation token"); } if (this.Disabled != null && this.Disabled.GetValue(dc.State)) { return(await dc.EndDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false)); } PromptOptions opt = null; if (options != null) { if (options is PromptOptions) { // Ensure prompts have input hint set opt = options as PromptOptions; if (opt.Prompt != null && string.IsNullOrEmpty(opt.Prompt.InputHint)) { opt.Prompt.InputHint = InputHints.AcceptingInput; } if (opt.RetryPrompt != null && string.IsNullOrEmpty(opt.RetryPrompt.InputHint)) { opt.RetryPrompt.InputHint = InputHints.AcceptingInput; } } } var op = OnInitializeOptions(dc, options); dc.State.SetValue(ThisPath.Options, op); dc.State.SetValue(TURN_COUNT_PROPERTY, 0); // If AlwaysPrompt is set to true, then clear Property value for turn 0. if (this.Property != null && this.AlwaysPrompt != null && this.AlwaysPrompt.GetValue(dc.State)) { dc.State.SetValue(this.Property.GetValue(dc.State), null); } // Initialize state var state = dc.ActiveDialog.State; state[PersistedOptions] = opt; state[PersistedState] = new Dictionary <string, object> { { AttemptCountKey, 0 }, }; state[PersistedExpires] = DateTime.UtcNow.AddMilliseconds(Timeout.GetValue(dc.State)); OAuthPrompt.SetCallerInfoInDialogState(state, dc.Context); // Attempt to get the users token if (!(dc.Context.Adapter is IUserTokenProvider adapter)) { throw new InvalidOperationException("OAuthPrompt.Recognize(): not supported by the current adapter"); } var output = await adapter.GetUserTokenAsync(dc.Context, ConnectionName.GetValue(dc.State), null, cancellationToken).ConfigureAwait(false); if (output != null) { if (this.Property != null) { dc.State.SetValue(this.Property.GetValue(dc.State), output); } // Return token return(await dc.EndDialogAsync(output, cancellationToken).ConfigureAwait(false)); } else { dc.State.SetValue(TURN_COUNT_PROPERTY, 1); // Prompt user to login await SendOAuthCardAsync(dc, opt?.Prompt, cancellationToken).ConfigureAwait(false); return(Dialog.EndOfTurn); } }
public async Task OAuthPromptBeginDialogWithNoDialogContext() { var prompt = new OAuthPrompt("abc", new OAuthPromptSettings()); await prompt.BeginDialogAsync(null); }
private async Task <DialogTurnResult> ForwardActivity(DialogContext dc, Activity activity) { if (!skillInitialized) { InititializeSkill(dc); } // Process the activity (pass through middleware) and then perform Skill processing inProcAdapter.ProcessActivity(activity, async(skillContext, cancellationToken) => { await activatedSkill.OnTurnAsync(skillContext); }).Wait(); // Incurs a lock each time but given we need to inspect each one for EOC and filter them out this saves another collection of activities. Swings and roundabouts var filteredActivities = new List <Activity>(); var endOfConversation = false; var replyActivity = inProcAdapter.GetNextReply(); while (replyActivity != null) { if (replyActivity.Type == ActivityTypes.EndOfConversation) { endOfConversation = true; } else if (replyActivity.Type == ActivityTypes.Event && replyActivity.Name == TokenRequestEventName) { // Send trace to emulator await dc.Context.SendActivityAsync(new Activity(type : ActivityTypes.Trace, text : $"<--Received a Token Request from a skill")); var skill = dc.ActiveDialog.State[ActiveSkillStateKey] as SkillRegistration; // If you want to force signin interactively and not cached uncomment this // var a = dialogContext.Context.Adapter as BotFrameworkAdapter; // await a.SignOutUserAsync(dialogContext.Context, skill.AuthConnectionName, default(CancellationToken)); // Skills could support multiple token types, only allow one through config for now. var prompt = new OAuthPrompt( "SkillAuth", new OAuthPromptSettings() { ConnectionName = skill.AuthConnectionName, Text = $"Please signin to provide an authentication token for the {skill.Name} skill", Title = "Skill Authentication", Timeout = 300000, // User has 5 minutes to login }, AuthPromptValidator); var tokenResponse = await prompt.GetUserTokenAsync(dc.Context); if (tokenResponse != null) { var response = replyActivity.CreateReply(); response.Type = ActivityTypes.Event; response.Name = TokenResponseEventName; response.Value = tokenResponse; var result = await ForwardActivity(dc, response); if (result.Status == DialogTurnStatus.Complete) { endOfConversation = true; } } else { var dtr = await prompt.BeginDialogAsync(dc); } } else { filteredActivities.Add(replyActivity); } replyActivity = inProcAdapter.GetNextReply(); } if (filteredActivities.Count > 0) { await dc.Context.SendActivitiesAsync(filteredActivities.ToArray()); } // If we got an End of Conversation then close this skill dialog down if (endOfConversation) { var state = dc.Context.TurnState; state[ActiveSkillStateKey] = null; skillInitialized = false; // Send trace to emulator await dc.Context.SendActivityAsync(new Activity(type : ActivityTypes.Trace, text : $"<--Ending the skill conversation")); return(await dc.EndDialogAsync()); } return(EndOfTurn); }
public async Task OAuthPromptSignOutUser() { // Arrange var userId = "user-id"; var connectionName = "connection-name"; var channelId = "channel-id"; // Arrange the Adapter. var mockConnectorFactory = new Mock <ConnectorFactory>(); mockConnectorFactory.Setup( x => x.CreateAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <CancellationToken>())) .ReturnsAsync(new ConnectorClient(new Uri("http://tempuri/"))); var mockUserTokenClient = new Mock <UserTokenClient>(); mockUserTokenClient.Setup( x => x.SignOutUserAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <CancellationToken>())); var authenticateRequestResult = new AuthenticateRequestResult { CallerId = "callerId", ClaimsIdentity = new ClaimsIdentity(), ConnectorFactory = mockConnectorFactory.Object, }; var mockBotFrameworkAuthentication = new Mock <BotFrameworkAuthentication>(); mockBotFrameworkAuthentication.Setup( x => x.AuthenticateRequestAsync(It.IsAny <Activity>(), It.IsAny <string>(), It.IsAny <CancellationToken>())) .ReturnsAsync(authenticateRequestResult); mockBotFrameworkAuthentication.Setup( x => x.CreateUserTokenClientAsync(It.IsAny <ClaimsIdentity>(), It.IsAny <CancellationToken>())) .ReturnsAsync(mockUserTokenClient.Object); var adapter = new TestCloudAdapter(mockBotFrameworkAuthentication.Object); // Add the OAuthPrompt. var oauthPromptSettings = new OAuthPromptSettings { Text = "Please sign in", ConnectionName = connectionName, Title = "Sign in", }; // The on-turn callback. BotCallbackHandler callback = async(turnContext, cancellationToken) => { var oauthPrompt = new OAuthPrompt("OAuthPrompt", oauthPromptSettings); await oauthPrompt.SignOutUserAsync(turnContext, cancellationToken); }; // The Activity for the turn. var activity = new Activity { Type = ActivityTypes.Message, Name = SignInConstants.TokenExchangeOperationName, From = new ChannelAccount { Id = userId }, Conversation = new ConversationAccount { Id = "conversation-id" }, ChannelId = channelId, Text = "logout", }; // Act var invokeResponse = await adapter.ProcessAsync(string.Empty, activity, callback); // Assert mockUserTokenClient.Verify( x => x.SignOutUserAsync(It.Is <string>(s => s == userId), It.Is <string>(s => s == connectionName), It.Is <string>(s => s == channelId), It.IsAny <CancellationToken>()), Times.Once()); }