Fixed logout issue.
Ported skillbot to cloud adapter
This commit is contained in:
Родитель
b3cee90a37
Коммит
52feafa2b6
|
@ -127,6 +127,8 @@ namespace Microsoft.BotBuilderSamples.RootBot.Dialogs
|
|||
private async Task<DialogTurnResult> HandleActionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
|
||||
{
|
||||
var action = ((FoundChoice)stepContext.Result).Value.ToLowerInvariant();
|
||||
var userId = stepContext.Context.Activity?.From?.Id;
|
||||
var userTokenClient = stepContext.Context.TurnState.Get<UserTokenClient>();
|
||||
|
||||
switch (action)
|
||||
{
|
||||
|
@ -134,14 +136,12 @@ namespace Microsoft.BotBuilderSamples.RootBot.Dialogs
|
|||
return await stepContext.BeginDialogAsync(nameof(SsoSignInDialog), null, cancellationToken);
|
||||
|
||||
case "logout from the root bot":
|
||||
var adapter = (IUserTokenProvider)stepContext.Context.Adapter;
|
||||
await adapter.SignOutUserAsync(stepContext.Context, _connectionName, cancellationToken: cancellationToken);
|
||||
await userTokenClient.SignOutUserAsync(userId, _connectionName, stepContext.Context.Activity?.ChannelId, cancellationToken);
|
||||
await stepContext.Context.SendActivityAsync("You have been signed out.", cancellationToken: cancellationToken);
|
||||
return await stepContext.NextAsync(cancellationToken: cancellationToken);
|
||||
|
||||
case "show token":
|
||||
var tokenProvider = (IUserTokenProvider)stepContext.Context.Adapter;
|
||||
var token = await tokenProvider.GetUserTokenAsync(stepContext.Context, _connectionName, null, cancellationToken);
|
||||
var token = await userTokenClient.GetUserTokenAsync(userId, _connectionName, stepContext.Context.Activity?.ChannelId, null, cancellationToken);
|
||||
if (token == null)
|
||||
{
|
||||
await stepContext.Context.SendActivityAsync("User has no cached token.", cancellationToken: cancellationToken);
|
||||
|
|
|
@ -5,25 +5,24 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.Bot.Builder;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Schema;
|
||||
|
||||
namespace Microsoft.BotBuilderSamples.SkillBot.Bots
|
||||
{
|
||||
public class ChildBot<T> : ActivityHandler
|
||||
public class SkillBot<T> : ActivityHandler
|
||||
where T : Dialog
|
||||
{
|
||||
private readonly ConversationState _conversationState;
|
||||
private readonly Dialog _dialog;
|
||||
private readonly Dialog _mainDialog;
|
||||
|
||||
public ChildBot(Dialog dialog, ConversationState conversationState)
|
||||
public SkillBot(ConversationState conversationState, T mainDialog)
|
||||
{
|
||||
_dialog = dialog;
|
||||
_conversationState = conversationState;
|
||||
_mainDialog = mainDialog;
|
||||
}
|
||||
|
||||
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _dialog.RunAsync(turnContext, _conversationState.CreateProperty<DialogState>("DialogState"), cancellationToken);
|
||||
await _mainDialog.RunAsync(turnContext, _conversationState.CreateProperty<DialogState>("DialogState"), cancellationToken);
|
||||
|
||||
// Save any state changes that might have occurred during the turn.
|
||||
await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
|
|
@ -19,8 +19,8 @@ namespace Microsoft.BotBuilderSamples.SkillBot.Dialogs
|
|||
public ActivityRouterDialog(IConfiguration configuration)
|
||||
: base(nameof(ActivityRouterDialog))
|
||||
{
|
||||
AddDialog(new SsoSkillDialog(configuration));
|
||||
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[] { ProcessActivityAsync }));
|
||||
AddDialog(new SsoSkillDialog(configuration.GetSection("ConnectionName")?.Value));
|
||||
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[] {ProcessActivityAsync}));
|
||||
|
||||
// The initial child Dialog to run.
|
||||
InitialDialogId = nameof(WaterfallDialog);
|
||||
|
|
|
@ -8,8 +8,8 @@ using System.Threading.Tasks;
|
|||
using Microsoft.Bot.Builder;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Builder.Dialogs.Choices;
|
||||
using Microsoft.Bot.Connector.Authentication;
|
||||
using Microsoft.Bot.Schema;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Microsoft.BotBuilderSamples.SkillBot.Dialogs
|
||||
{
|
||||
|
@ -17,10 +17,10 @@ namespace Microsoft.BotBuilderSamples.SkillBot.Dialogs
|
|||
{
|
||||
private readonly string _connectionName;
|
||||
|
||||
public SsoSkillDialog(IConfiguration configuration)
|
||||
public SsoSkillDialog(string connectionName)
|
||||
: base(nameof(SsoSkillDialog))
|
||||
{
|
||||
_connectionName = configuration.GetSection("ConnectionName")?.Value;
|
||||
_connectionName = connectionName;
|
||||
if (string.IsNullOrWhiteSpace(_connectionName))
|
||||
{
|
||||
throw new ArgumentException("\"ConnectionName\" is not set in configuration");
|
||||
|
@ -58,11 +58,13 @@ namespace Microsoft.BotBuilderSamples.SkillBot.Dialogs
|
|||
|
||||
private async Task<List<Choice>> GetPromptChoicesAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
|
||||
{
|
||||
var promptChoices = new List<Choice>();
|
||||
var adapter = (IUserTokenProvider)stepContext.Context.Adapter;
|
||||
// Try to get the token for the current user to determine if it is logged in or not.
|
||||
var userId = stepContext.Context.Activity?.From?.Id;
|
||||
var userTokenClient = stepContext.Context.TurnState.Get<UserTokenClient>();
|
||||
var token = await userTokenClient.GetUserTokenAsync(userId, _connectionName, stepContext.Context.Activity?.ChannelId, null, cancellationToken);
|
||||
|
||||
// Present different choices depending on the user's sign in status.
|
||||
var token = await adapter.GetUserTokenAsync(stepContext.Context, _connectionName, null, cancellationToken);
|
||||
var promptChoices = new List<Choice>();
|
||||
if (token == null)
|
||||
{
|
||||
promptChoices.Add(new Choice("Login to the skill"));
|
||||
|
@ -81,6 +83,8 @@ namespace Microsoft.BotBuilderSamples.SkillBot.Dialogs
|
|||
private async Task<DialogTurnResult> HandleActionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
|
||||
{
|
||||
var action = ((FoundChoice)stepContext.Result).Value.ToLowerInvariant();
|
||||
var userId = stepContext.Context.Activity?.From?.Id;
|
||||
var userTokenClient = stepContext.Context.TurnState.Get<UserTokenClient>();
|
||||
|
||||
switch (action)
|
||||
{
|
||||
|
@ -90,14 +94,12 @@ namespace Microsoft.BotBuilderSamples.SkillBot.Dialogs
|
|||
|
||||
case "logout from the skill":
|
||||
// This will just clear the token from the skill.
|
||||
var adapter = (IUserTokenProvider)stepContext.Context.Adapter;
|
||||
await adapter.SignOutUserAsync(stepContext.Context, _connectionName, cancellationToken: cancellationToken);
|
||||
await userTokenClient.SignOutUserAsync(userId, _connectionName, stepContext.Context.Activity?.ChannelId, cancellationToken);
|
||||
await stepContext.Context.SendActivityAsync("You have been signed out.", cancellationToken: cancellationToken);
|
||||
return await stepContext.NextAsync(cancellationToken: cancellationToken);
|
||||
|
||||
case "show token":
|
||||
var tokenProvider = (IUserTokenProvider)stepContext.Context.Adapter;
|
||||
var token = await tokenProvider.GetUserTokenAsync(stepContext.Context, _connectionName, null, cancellationToken);
|
||||
var token = await userTokenClient.GetUserTokenAsync(userId, _connectionName, stepContext.Context.Activity?.ChannelId, null, cancellationToken);
|
||||
if (token == null)
|
||||
{
|
||||
await stepContext.Context.SendActivityAsync("User has no cached token.", cancellationToken: cancellationToken);
|
||||
|
@ -110,6 +112,7 @@ namespace Microsoft.BotBuilderSamples.SkillBot.Dialogs
|
|||
return await stepContext.NextAsync(cancellationToken: cancellationToken);
|
||||
|
||||
case "end":
|
||||
// Ends the interaction with the skill.
|
||||
return new DialogTurnResult(DialogTurnStatus.Complete);
|
||||
|
||||
default:
|
||||
|
|
|
@ -10,7 +10,6 @@ using Microsoft.Bot.Builder.Integration.AspNet.Core;
|
|||
using Microsoft.Bot.Builder.TraceExtensions;
|
||||
using Microsoft.Bot.Connector.Authentication;
|
||||
using Microsoft.Bot.Schema;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.BotBuilderSamples.SkillBot
|
||||
|
@ -23,13 +22,13 @@ namespace Microsoft.BotBuilderSamples.SkillBot
|
|||
/// <see cref="SendActivitiesAsync"/> to save the state of the conversation when a OAuthPrompt is sent to the parent.
|
||||
/// This prepares the bot to receive the activity send by the TokenExchangeSkillHandler.
|
||||
/// </remarks>
|
||||
public class SsoSkillAdapterWithErrorHandler : BotFrameworkHttpAdapter
|
||||
public class SsoSkillAdapterWithErrorHandler : CloudAdapter
|
||||
{
|
||||
private readonly ConversationState _conversationState;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public SsoSkillAdapterWithErrorHandler(IConfiguration configuration, ICredentialProvider credentialProvider, AuthenticationConfiguration authConfig, ILogger<BotFrameworkHttpAdapter> logger, ConversationState conversationState)
|
||||
: base(configuration, credentialProvider, authConfig, logger: logger)
|
||||
public SsoSkillAdapterWithErrorHandler(BotFrameworkAuthentication auth, ConversationState conversationState, ILogger<IBotFrameworkHttpAdapter> logger)
|
||||
: base(auth, logger)
|
||||
{
|
||||
_conversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
@ -109,7 +108,7 @@ namespace Microsoft.BotBuilderSamples.SkillBot
|
|||
{
|
||||
// Delete the conversationState for the current conversation to prevent the
|
||||
// bot from getting stuck in a error-loop caused by being in a bad state.
|
||||
// ConversationState should be thought of as similar to "cookie-state" for a web page.
|
||||
// ConversationState should be thought of as similar to "cookie-state" for a Web page.
|
||||
await _conversationState.DeleteAsync(turnContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
@ -2,11 +2,10 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Bot.Builder;
|
||||
using Microsoft.Bot.Builder.BotFramework;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Builder.Integration.AspNet.Core;
|
||||
using Microsoft.Bot.Connector.Authentication;
|
||||
using Microsoft.BotBuilderSamples.SkillBot.Bots;
|
||||
|
@ -32,12 +31,37 @@ namespace Microsoft.BotBuilderSamples.SkillBot
|
|||
services.AddControllers()
|
||||
.AddNewtonsoftJson();
|
||||
|
||||
// Configure credentials.
|
||||
services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();
|
||||
|
||||
// Register AuthConfiguration to enable custom claim validation.
|
||||
var allowedCallers = new List<string>(Configuration.GetSection("AllowedCallers").Get<string[]>());
|
||||
services.AddSingleton(sp => new AuthenticationConfiguration {ClaimsValidator = new AllowedCallersClaimsValidator(allowedCallers)});
|
||||
services.AddSingleton(sp =>
|
||||
{
|
||||
var allowedCallers = new List<string>(sp.GetService<IConfiguration>().GetSection("AllowedCallers").Get<string[]>());
|
||||
|
||||
var claimsValidator = new AllowedCallersClaimsValidator(allowedCallers);
|
||||
|
||||
// If TenantId is specified in config, add the tenant as a valid JWT token issuer for Bot to Skill conversation.
|
||||
// The token issuer for MSI and single tenant scenarios will be the tenant where the bot is registered.
|
||||
var validTokenIssuers = new List<string>();
|
||||
var tenantId = sp.GetService<IConfiguration>().GetSection(MicrosoftAppCredentials.MicrosoftAppTenantIdKey)?.Value;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tenantId))
|
||||
{
|
||||
// For SingleTenant/MSI auth, the JWT tokens will be issued from the bot's home tenant.
|
||||
// Therefore, these issuers need to be added to the list of valid token issuers for authenticating activity requests.
|
||||
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidTokenIssuerUrlTemplateV1, tenantId));
|
||||
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidTokenIssuerUrlTemplateV2, tenantId));
|
||||
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV1, tenantId));
|
||||
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV2, tenantId));
|
||||
}
|
||||
|
||||
return new AuthenticationConfiguration
|
||||
{
|
||||
ClaimsValidator = claimsValidator,
|
||||
ValidTokenIssuers = validTokenIssuers
|
||||
};
|
||||
});
|
||||
|
||||
// Create the Bot Framework Authentication to be used with the Bot Adapter.
|
||||
services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();
|
||||
|
||||
// Create the Bot Framework Adapter with error handling enabled.
|
||||
services.AddSingleton<IBotFrameworkHttpAdapter, SsoSkillAdapterWithErrorHandler>();
|
||||
|
@ -47,10 +71,12 @@ namespace Microsoft.BotBuilderSamples.SkillBot
|
|||
|
||||
// Create the Conversation state. (Used by the Dialog system itself.)
|
||||
services.AddSingleton<ConversationState>();
|
||||
services.AddSingleton<Dialog, ActivityRouterDialog>();
|
||||
|
||||
// The Dialog that will be run by the bot.
|
||||
services.AddSingleton<ActivityRouterDialog>();
|
||||
|
||||
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
|
||||
services.AddTransient<IBot, ChildBot<ActivityRouterDialog>>();
|
||||
services.AddTransient<IBot, SkillBot<ActivityRouterDialog>>();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
|
Загрузка…
Ссылка в новой задаче