Skip to content
This repository has been archived by the owner on Jun 30, 2022. It is now read-only.

Tedlee/separate dependency auth #1094

Merged
merged 4 commits into from
Apr 12, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ namespace Microsoft.Bot.Builder.Skills
public class SkillDialog : ComponentDialog
{
private static readonly HttpClient _httpClient = new HttpClient();
private readonly MultiProviderAuthDialog _authDialog;
private MicrosoftAppCredentialsEx _microsoftAppCredentialsEx;
private IBotTelemetryClient _telemetryClient;
private JsonSerializerSettings _serializationSettings = new JsonSerializerSettings
Expand All @@ -48,47 +49,32 @@ public class SkillDialog : ComponentDialog
/// Initializes a new instance of the <see cref="SkillDialog"/> class.
/// SkillDialog constructor that accepts the manifest description of a Skill along with TelemetryClient for end to end telemetry.
/// </summary>
/// <param name="skillManifest"></param>
/// <param name="proactiveState"></param>
/// <param name="endpointService"></param>
/// <param name="telemetryClient"></param>
/// <param name="backgroundTaskQueue"></param>
/// <param name="useCachedTokens"></param>
public SkillDialog(SkillManifest skillManifest, ResponseManager responseManager, MicrosoftAppCredentialsEx microsoftAppCredentialsEx, IBotTelemetryClient telemetryClient)
/// <param name="skillManifest">skill manifest object.</param>
/// <param name="responseManager">response manager.</param>
/// <param name="microsoftAppCredentialsEx">MicrosoftAppCredentialsEx object that customizes scope and authority.</param>
/// <param name="telemetryClient">telemetry client.</param>
/// <param name="authDialog">optional: auth dialog.</param>
public SkillDialog(SkillManifest skillManifest, ResponseManager responseManager, MicrosoftAppCredentialsEx microsoftAppCredentialsEx, IBotTelemetryClient telemetryClient, MultiProviderAuthDialog authDialog = null)
: base(skillManifest.Id)
{
_skillManifest = skillManifest;
_microsoftAppCredentialsEx = microsoftAppCredentialsEx;
_telemetryClient = telemetryClient;

if (_skillManifest.AuthenticationConnections != null)
if (authDialog != null)
{
// hack for oauth connection for now
var list = new List<OAuthConnection>();
foreach (var provider in _skillManifest.AuthenticationConnections)
{
if (provider.ServiceProviderId.Contains("Azure"))
{
list.Add(new OAuthConnection { Name = "office365", Provider = provider.ServiceProviderId });
break;
}
else
{
continue;
}
}

AddDialog(new MultiProviderAuthDialog(responseManager, list));
_authDialog = authDialog;
AddDialog(authDialog);
}
}

/// <summary>
/// When a SkillDialog is started, a skillBegin event is sent which firstly indicates the Skill is being invoked in Skill mode, also slots are also provided where the information exists in the parent Bot.
/// </summary>
/// <param name="innerDc"></param>
/// <param name="options"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <param name="innerDc">inner dialog context.</param>
/// <param name="options">options.</param>
/// <param name="cancellationToken">cancellation token.</param>
/// <returns>dialog turn result.</returns>
protected override async Task<DialogTurnResult> OnBeginDialogAsync(DialogContext innerDc, object options, CancellationToken cancellationToken = default(CancellationToken))
{
// TODO - The SkillDialog Orchestration should try to fill slots defined in the manifest and pass through this event.
Expand All @@ -112,18 +98,20 @@ public SkillDialog(SkillManifest skillManifest, ResponseManager responseManager,
/// <summary>
/// All subsequent messages are forwarded on to the skill.
/// </summary>
/// <param name="innerDc"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <param name="innerDc">inner dialog context.</param>
/// <param name="cancellationToken">cancellation token.</param>
/// <returns>dialog turn result.</returns>
protected override async Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken))
{
var activity = innerDc.Context.Activity;

if (innerDc.ActiveDialog?.Id == nameof(MultiProviderAuthDialog))
if (_authDialog != null && innerDc.ActiveDialog?.Id == _authDialog.Id)
{
// Handle magic code auth
var result = await innerDc.ContinueDialogAsync(cancellationToken);

// this is dependent on a specific type coming out from the MultiProviderAuthDialog which is wrong
// TODO: refactor this
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider a stylecop rule for todo comments so we can be held accountable later 😏 (example)

// forward the token response to the skill
if (result.Status == DialogTurnStatus.Complete && result.Result is ProviderTokenResponse)
{
Expand All @@ -143,10 +131,10 @@ public SkillDialog(SkillManifest skillManifest, ResponseManager responseManager,
/// <summary>
/// End the Skill dialog.
/// </summary>
/// <param name="outerDc"></param>
/// <param name="result"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <param name="outerDc">outer dialog context.</param>
/// <param name="result">dialog result.</param>
/// <param name="cancellationToken">cancellation token.</param>
/// <returns>dialog turn result.</returns>
protected override Task<DialogTurnResult> EndComponentAsync(DialogContext outerDc, object result, CancellationToken cancellationToken)
{
return outerDc.EndDialogAsync(result, cancellationToken);
Expand All @@ -155,8 +143,8 @@ protected override Task<DialogTurnResult> EndComponentAsync(DialogContext outerD
/// <summary>
/// Forward an inbound activity on to the Skill. This is a synchronous operation whereby all response activities are aggregated and returned in one batch.
/// </summary>
/// <param name="innerDc"></param>
/// <param name="activity"></param>
/// <param name="innerDc">inner dialog context.</param>
/// <param name="activity">activity to forward.</param>
/// <returns>DialogTurnResult</returns>
private async Task<DialogTurnResult> ForwardToSkill(DialogContext innerDc, Activity activity)
{
Expand Down Expand Up @@ -195,11 +183,18 @@ private async Task<DialogTurnResult> ForwardToSkill(DialogContext innerDc, Activ
}
else if (skillResponse?.Name == TokenEvents.TokenRequestEventName)
{
if (_authDialog == null)
{
throw new Exception($"Skill {_skillManifest.Id} is asking for a token but the skill doesn't have an auth dialog to handle it!");
}

// Send trace to emulator
await innerDc.Context.SendActivityAsync(new Activity(type: ActivityTypes.Trace, text: $"<--Received a Token Request from a skill"));

var authResult = await innerDc.BeginDialogAsync(nameof(MultiProviderAuthDialog));
var authResult = await innerDc.BeginDialogAsync(_authDialog.Id);

// this is dependent on a specific type coming out from the MultiProviderAuthDialog which is wrong
// TODO: refactor this
if (authResult.Result?.GetType() == typeof(ProviderTokenResponse))
{
var tokenEvent = skillResponse.CreateReply();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,25 @@
// Licensed under the MIT License.

using System;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Luis;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using System.Globalization;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Builder.Skills.Auth;
using VirtualAssistantTemplate.Responses.Main;
using VirtualAssistantTemplate.Models;
using VirtualAssistantTemplate.Services;
using Microsoft.Bot.Schema;
using Microsoft.Bot.Builder.Solutions;
using Microsoft.Bot.Builder.Solutions.Authentication;
using Microsoft.Bot.Builder.Solutions.Dialogs;
using Microsoft.Bot.Builder.Solutions.Responses;
using Microsoft.Bot.Builder.Solutions.Authentication;
using Microsoft.Bot.Schema;
using VirtualAssistantTemplate.Models;
using VirtualAssistantTemplate.Responses.Main;
using VirtualAssistantTemplate.Services;
using System.Collections.Generic;

namespace VirtualAssistantTemplate.Dialogs
{
Expand Down Expand Up @@ -54,10 +55,7 @@ public MainDialog(
AddDialog(new OnboardingDialog(_services, _userState.CreateProperty<OnboardingState>(nameof(OnboardingState)), telemetryClient));
AddDialog(new EscalateDialog(_services, telemetryClient));

foreach (var skill in settings.Skills)
{
AddDialog(new SkillDialog(skill, _responseManager, new MicrosoftAppCredentialsEx(_microsoftAppCredentials.MicrosoftAppId, _microsoftAppCredentials.MicrosoftAppPassword, skill.MSAappId), telemetryClient));
}
AddSkillDialogs();
}

protected override async Task OnStartAsync(DialogContext dc, CancellationToken cancellationToken = default(CancellationToken))
Expand Down Expand Up @@ -303,5 +301,40 @@ private async Task<InterruptionAction> LogoutAsync(DialogContext dc)

return InterruptionAction.StartedDialog;
}

private void AddSkillDialogs()
{
foreach (var skill in _settings.Skills)
{
MultiProviderAuthDialog authDialog = null;
if (skill.AuthenticationConnections != null && skill.AuthenticationConnections.Count() > 0)
{
List<OAuthConnection> oauthConnections = new List<OAuthConnection>();

if (_settings.OAuthConnections != null && _settings.OAuthConnections.Count > 0)
{
foreach (var authConnection in skill.AuthenticationConnections)
{
var connection = _settings.OAuthConnections.FirstOrDefault(o => o.Provider.Equals(authConnection.ServiceProviderId, StringComparison.InvariantCultureIgnoreCase));
if (connection != null)
{
oauthConnections.Add(connection);
}
}
}

if (oauthConnections.Count > 0)
{
authDialog = new MultiProviderAuthDialog(_responseManager, oauthConnections);
}
else
{
throw new Exception($"None of the oauth types that the skill {skill.Name} requires is supported by the bot!");
}
}

AddDialog(new SkillDialog(skill, _responseManager, new MicrosoftAppCredentialsEx(_microsoftAppCredentials.MicrosoftAppId, _microsoftAppCredentials.MicrosoftAppPassword, skill.MSAappId), TelemetryClient, authDialog));
}
}
}
}