Skip to content

Commit

Permalink
[#3910][DotNet] Add Bot Authentication SNI sample bot (#3917)
Browse files Browse the repository at this point in the history
* add SNI authentication .net sample

* change bot name

* update SDK version

---------

Co-authored-by: tracyboehrer <tracyboehrer@users.noreply.github.com>
  • Loading branch information
JhontSouth and tracyboehrer authored Oct 24, 2023
1 parent 6fe1a7c commit c6904bd
Show file tree
Hide file tree
Showing 23 changed files with 1,852 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ Samples are designed to illustrate functionality you'll need to implement to bui
|24|MSGraph&nbsp;authentication | Demonstrates bot authentication capabilities of Azure Bot Service. Demonstrates utilizing the Microsoft Graph API to retrieve data about the user.|[.NET&nbsp;Core][cs#24] |[JavaScript][js#24] |[Python][py#24]|[Java][java#24]
|46|Teams authentication | Demonstrates how to use authentication for a bot running in Microsoft Teams. | [.NET&nbsp;Core](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-teams-authentication/csharp) | [JavaScript](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-conversation-sso-quickstart/js) |[Python](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-teams-authentication/python)|[Java](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-teams-authentication/java)
|84|Certificate authentication | Demonstrates how to use Certificates to authenticate the bot | [.NET&nbsp;Core][cs#84] |[JavaScript][js#84] | |
|85|Subject name/issuer authentication | Demonstrates how to use the subject name/issuer authentication in a bot | [.NET&nbsp;Core][cs#85] | | |

### Custom question answering samples

Expand Down Expand Up @@ -142,6 +143,7 @@ A [collection of **experimental** samples](./experimental) exist, intended to pr
[cs#81]:samples/csharp_dotnetcore/81.skills-skilldialog
[cs#82]:samples/csharp_dotnetcore/82.skills-sso-cloudadapter
[cs#84]:samples/csharp_dotnetcore/84.bot-authentication-certificate
[cs#85]:samples/csharp_dotnetcore/85.bot-authentication-sni

[wa#13]:samples/csharp_webapi/13.core-bot

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.TraceExtensions;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Logging;

namespace Microsoft.BotBuilderSamples
{
public class AdapterWithErrorHandler : CloudAdapter
{
public AdapterWithErrorHandler(BotFrameworkAuthentication auth, ILogger<IBotFrameworkHttpAdapter> logger, ConversationState conversationState = default)
: base(auth, logger)
{
OnTurnError = async (turnContext, exception) =>
{
// Log any leaked exception from the application.
// NOTE: In production environment, you should consider logging this to
// Azure Application Insights. Visit https://aka.ms/bottelemetry to see how
// to add telemetry capture to your bot.
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");

// Send a message to the user
await turnContext.SendActivityAsync("The bot encountered an error or bug.");
await turnContext.SendActivityAsync("To continue to run this bot, please fix the bot source code.");

if (conversationState != null)
{
try
{
// 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" in a Web pages.
await conversationState.DeleteAsync(turnContext);
}
catch (Exception e)
{
logger.LogError(e, $"Exception caught on attempting to Delete ConversationState : {e.Message}");
}
}

// Send a trace activity, which will be displayed in the Bot Framework Emulator
await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError");
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.10.2" />
<PackageReference Include="Azure.Security.KeyVault.Certificates" Version="4.5.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.7" />
<PackageReference Include="Microsoft.Bot.Builder.Dialogs" Version="4.21.1" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="4.21.1" />
</ItemGroup>

<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;

namespace Microsoft.BotBuilderSamples
{
public class AuthSNIBot : ActivityHandler
{
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)
{
await turnContext.SendActivityAsync(MessageFactory.Text("Welcome to the Bot with Subject Name/Issuer Authentication."), cancellationToken);
}
}
}

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var replyText = "Running dialog with bot authenticated";
await turnContext.SendActivityAsync(MessageFactory.Text(replyText, replyText), cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;

namespace Microsoft.BotBuilderSamples
{
// This IBot implementation can run any type of Dialog. The use of type parameterization is to allows multiple different bots
// to be run at different endpoints within the same project. This can be achieved by defining distinct Controller types
// each with dependency on distinct IBot types, this way ASP Dependency Injection can glue everything together without ambiguity.
// The ConversationState is used by the Dialog system. The UserState isn't, however, it might have been used in a Dialog implementation,
// and the requirement is that all BotState objects are saved at the end of a turn.
public class DialogBot<T> : ActivityHandler where T : Dialog
{
protected readonly BotState ConversationState;
protected readonly Dialog Dialog;
protected readonly ILogger Logger;
protected readonly BotState UserState;

public DialogBot(ConversationState conversationState, UserState userState, T dialog, ILogger<DialogBot<T>> logger)
{
ConversationState = conversationState;
UserState = userState;
Dialog = dialog;
Logger = logger;
}

public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
await base.OnTurnAsync(turnContext, cancellationToken);

// Save any state changes that might have occurred during the turn.
await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
}

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
Logger.LogInformation("Running dialog with Message Activity.");

// Run the Dialog with the new message Activity.
await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;

namespace Microsoft.BotBuilderSamples
{
// This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot
// implementation at runtime. Multiple different IBot implementations running at different endpoints can be
// achieved by specifying a more specific type for the bot constructor argument.
[Route("api/messages")]
[ApiController]
public class BotController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter _adapter;
private readonly IBot _bot;

public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
{
_adapter = adapter;
_bot = bot;
}

[HttpPost]
public async Task PostAsync()
{
// Delegate the processing of the HTTP POST to the adapter.
// The adapter will invoke the bot.
await _adapter.ProcessAsync(Request, Response, _bot);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"azureBotId": {
"value": ""
},
"azureBotSku": {
"value": "S1"
},
"azureBotRegion": {
"value": "global"
},
"botEndpoint": {
"value": ""
},
"appType": {
"value": "MultiTenant"
},
"appId": {
"value": ""
},
"UMSIName": {
"value": ""
},
"UMSIResourceGroupName": {
"value": ""
},
"tenantId": {
"value": ""
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"appServiceName": {
"value": ""
},
"existingAppServicePlanName": {
"value": ""
},
"existingAppServicePlanLocation": {
"value": ""
},
"newAppServicePlanName": {
"value": ""
},
"newAppServicePlanLocation": {
"value": ""
},
"newAppServicePlanSku": {
"value": {
"name": "S1",
"tier": "Standard",
"size": "S1",
"family": "S",
"capacity": 1
}
},
"appType": {
"value": "MultiTenant"
},
"appId": {
"value": ""
},
"appSecret": {
"value": ""
},
"UMSIName": {
"value": ""
},
"UMSIResourceGroupName": {
"value": ""
},
"tenantId": {
"value": ""
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Usage
The BotApp must be deployed prior to AzureBot.

Command line:
- az login
- az deployment group create --resource-group <group-name> --template-file <template-file> --parameters @<parameters-file>

# parameters-for-template-BotApp-with-rg:

- **appServiceName**:(required) The Name of the Bot App Service.

- (choose an existingAppServicePlan or create a new AppServicePlan)
- **existingAppServicePlanName**: The name of the App Service Plan.
- **existingAppServicePlanLocation**: The location of the App Service Plan.
- **newAppServicePlanName**: The name of the App Service Plan.
- **newAppServicePlanLocation**: The location of the App Service Plan.
- **newAppServicePlanSku**: The SKU of the App Service Plan. Defaults to Standard values.

- **appType**: Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. **Allowed values are: MultiTenant(default), SingleTenant, UserAssignedMSI.**

- **appId**:(required) Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings.

- **appSecret**:(required for MultiTenant and SingleTenant) Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings.

- **UMSIName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource used for the Bot's Authentication.

- **UMSIResourceGroupName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource Group used for the Bot's Authentication.

- **tenantId**: The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to <Subscription Tenant ID>.

MoreInfo: https://docs.microsoft.com/en-us/azure/bot-service/tutorial-provision-a-bot?view=azure-bot-service-4.0&tabs=userassigned%2Cnewgroup#create-an-identity-resource



# parameters-for-template-AzureBot-with-rg:

- **azureBotId**:(required) The globally unique and immutable bot ID.
- **azureBotSku**: The pricing tier of the Bot Service Registration. **Allowed values are: F0, S1(default)**.
- **azureBotRegion**: Specifies the location of the new AzureBot. **Allowed values are: global(default), westeurope**.
- **botEndpoint**: Use to handle client messages, Such as https://<botappServiceName>.azurewebsites.net/api/messages.

- **appType**: Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. **Allowed values are: MultiTenant(default), SingleTenant, UserAssignedMSI.**
- **appId**:(required) Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings.
- **UMSIName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource used for the Bot's Authentication.
- **UMSIResourceGroupName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource Group used for the Bot's Authentication.
- **tenantId**: The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to <Subscription Tenant ID>.

MoreInfo: https://docs.microsoft.com/en-us/azure/bot-service/tutorial-provision-a-bot?view=azure-bot-service-4.0&tabs=userassigned%2Cnewgroup#create-an-identity-resource
Loading

0 comments on commit c6904bd

Please sign in to comment.