diff --git a/AuthBot.sln b/AuthBot.sln index aa180de..3dc1f39 100644 --- a/AuthBot.sln +++ b/AuthBot.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AuthBot", "AuthBot\AuthBot.csproj", "{B8AD59D3-C36D-4E18-B504-06871001BC8D}" EndProject @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OneDriveBot", "OneDriveBot\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleAADv1Bot", "SampleAADv1Bot\SampleAADv1Bot.csproj", "{D2253234-1279-486E-8A63-D8C3424E0525}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleB2CBot", "SampleB2CBot\SampleB2CBot.csproj", "{D1B2B925-D8E2-44F9-A8A1-D07B789AFB34}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,8 +35,15 @@ Global {D2253234-1279-486E-8A63-D8C3424E0525}.Debug|Any CPU.Build.0 = Debug|Any CPU {D2253234-1279-486E-8A63-D8C3424E0525}.Release|Any CPU.ActiveCfg = Release|Any CPU {D2253234-1279-486E-8A63-D8C3424E0525}.Release|Any CPU.Build.0 = Release|Any CPU + {D1B2B925-D8E2-44F9-A8A1-D07B789AFB34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1B2B925-D8E2-44F9-A8A1-D07B789AFB34}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1B2B925-D8E2-44F9-A8A1-D07B789AFB34}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1B2B925-D8E2-44F9-A8A1-D07B789AFB34}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9607E4D9-B5D4-4868-96E0-864AD81E3EE0} + EndGlobalSection EndGlobal diff --git a/AuthBot/ContextExtensions.cs b/AuthBot/ContextExtensions.cs index 08ed006..afe8495 100644 --- a/AuthBot/ContextExtensions.cs +++ b/AuthBot/ContextExtensions.cs @@ -58,7 +58,12 @@ public static async Task GetAccessToken(this IBotContext context, string } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { - throw new NotImplementedException(); + InMemoryTokenCacheMSAL tokenCache = new InMemoryTokenCacheMSAL(authResult.TokenCache); + var result = await AzureActiveDirectoryHelper.GetB2CToken(authResult.UserUniqueId, tokenCache, scopes); + authResult.AccessToken = result.AccessToken; + authResult.ExpiresOnUtcTicks = result.ExpiresOnUtcTicks; + authResult.TokenCache = tokenCache.Serialize(); + context.StoreAuthResult(authResult); } } catch (Exception ex) diff --git a/AuthBot/Controllers/OAuthCallbackController.cs b/AuthBot/Controllers/OAuthCallbackController.cs index 2aef868..a15d7f7 100644 --- a/AuthBot/Controllers/OAuthCallbackController.cs +++ b/AuthBot/Controllers/OAuthCallbackController.cs @@ -44,8 +44,9 @@ public async Task OAuthCallback() [HttpGet] [Route("api/OAuthCallback")] public async Task OAuthCallback( - [FromUri] string code, - [FromUri] string state, + + [FromUri] string state, + [FromUri] string code, CancellationToken cancellationToken) { try @@ -63,6 +64,7 @@ public async Task OAuthCallback( } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { + tokenCache = new Microsoft.Identity.Client.TokenCache(); } var resumptionCookie = UrlToken.Decode(queryParams); @@ -87,6 +89,8 @@ public async Task OAuthCallback( } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { + var token = await AzureActiveDirectoryHelper.GetB2cTokenByAuthCodeAsync(code, (Microsoft.Identity.Client.TokenCache)tokenCache, Models.AuthSettings.Scopes); + authResult = token; } IStateClient sc = scope.Resolve(); @@ -138,6 +142,7 @@ public async Task OAuthCallback( } } + private int GenerateRandomNumber() { int number = 0; diff --git a/AuthBot/Helpers/AzureActiveDirectoryHelper.cs b/AuthBot/Helpers/AzureActiveDirectoryHelper.cs index 781c0ce..4866c88 100644 --- a/AuthBot/Helpers/AzureActiveDirectoryHelper.cs +++ b/AuthBot/Helpers/AzureActiveDirectoryHelper.cs @@ -7,6 +7,7 @@ namespace AuthBot.Helpers using System.Web; using Microsoft.Bot.Builder.Dialogs; using Models; + using Microsoft.Identity.Client; public static class AzureActiveDirectoryHelper { @@ -54,7 +55,23 @@ public static async Task GetAuthUrlAsync(ResumptionCookie resumptionCook } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { - return null; + InMemoryTokenCacheMSAL tokenCache = new InMemoryTokenCacheMSAL(); + Microsoft.Identity.Client.ConfidentialClientApplication client = new Microsoft.Identity.Client.ConfidentialClientApplication("https://login.microsoftonline.com/" + AuthSettings.Tenant + "/oauth2/v2.0", + AuthSettings.ClientId, redirectUri.ToString(), + new Microsoft.Identity.Client.ClientCredential(AuthSettings.ClientSecret), + tokenCache); + + + var uri = "https://login.microsoftonline.com/" + AuthSettings.Tenant + "/oauth2/v2.0/authorize?response_type=code&&response_mode=query" + + "&client_id=" + AuthSettings.ClientId + + "&p=" + AuthSettings.Policy + + "&redirect_uri=" + HttpUtility.UrlEncode(AuthSettings.RedirectUrl) + + "&scope=" + HttpUtility.UrlEncode("openid profile offline_access") + + "&state=" + extraParameters; + + + + return uri.ToString(); } return null; } @@ -76,6 +93,15 @@ public static async Task GetTokenByAuthCodeAsync(string authorizatio return authResult; } + public static async Task GetB2cTokenByAuthCodeAsync(string authorizationCode, Microsoft.Identity.Client.TokenCache tokenCache, string[] scopes) + { + Microsoft.Identity.Client.ConfidentialClientApplication client = new Microsoft.Identity.Client.ConfidentialClientApplication("https://login.microsoftonline.com/" + AuthSettings.Tenant + "/oauth2/v2.0", AuthSettings.ClientId, AuthSettings.RedirectUrl, new Microsoft.Identity.Client.ClientCredential(AuthSettings.ClientSecret), tokenCache); + Uri redirectUri = new Uri(AuthSettings.RedirectUrl); + var result = await client.AcquireTokenByAuthorizationCodeAsync(scopes, authorizationCode,AuthSettings.Policy); + AuthResult authResult = AuthResult.FromMSALAuthenticationResult(result, tokenCache); + return authResult; + } + public static async Task GetToken(string userUniqueId, Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache tokenCache, string resourceId) { Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext context = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(AuthSettings.EndpointUrl + "/" + AuthSettings.Tenant, tokenCache); @@ -91,6 +117,13 @@ public static async Task GetToken(string userUniqueId, Microsoft.Ide AuthResult authResult = AuthResult.FromMSALAuthenticationResult(result, tokenCache); return authResult; } + public static async Task GetB2CToken(string userUniqueId, Microsoft.Identity.Client.TokenCache tokenCache, string[] scopes) + { + Microsoft.Identity.Client.ConfidentialClientApplication client = new Microsoft.Identity.Client.ConfidentialClientApplication("https://login.microsoftonline.com/" + AuthSettings.Tenant + "/oauth2/v2.0",AuthSettings.ClientId, AuthSettings.RedirectUrl, new Microsoft.Identity.Client.ClientCredential(AuthSettings.ClientSecret), tokenCache); + var result = await client.AcquireTokenSilentAsync(scopes, userUniqueId, "https://login.microsoftonline.com/" + AuthSettings.Tenant + "/oauth2/v2.0",AuthSettings.Policy,false); + AuthResult authResult = AuthResult.FromMSALAuthenticationResult(result, tokenCache); + return authResult; + } public static string TokenEncoder(string token) { diff --git a/AuthBot/Models/AuthResult.cs b/AuthBot/Models/AuthResult.cs index b96c53f..5e739ad 100644 --- a/AuthBot/Models/AuthResult.cs +++ b/AuthBot/Models/AuthResult.cs @@ -39,6 +39,18 @@ public static AuthResult FromMSALAuthenticationResult(Microsoft.Identity.Client. return result; } + + public static AuthResult FromMSALAuthenticationResult(string authResult, Microsoft.Identity.Client.TokenCache tokenCache) + { + var result = new AuthResult + { + AccessToken = authResult, + ExpiresOnUtcTicks = DateTime.UtcNow.AddDays(1).Ticks, + TokenCache = tokenCache.Serialize() + }; + + return result; + } } } //********************************************************* diff --git a/AuthBot/Models/AuthSettings.cs b/AuthBot/Models/AuthSettings.cs index af7182e..5304152 100644 --- a/AuthBot/Models/AuthSettings.cs +++ b/AuthBot/Models/AuthSettings.cs @@ -16,7 +16,7 @@ public class AuthSettings public static string RedirectUrl { get; set; } public static string Mode { get; set; } public static string[] Scopes { get; set; } - + public static string Policy { get; set; } } } diff --git a/SampleB2CBot/App_Start/WebApiConfig.cs b/SampleB2CBot/App_Start/WebApiConfig.cs new file mode 100644 index 0000000..e0f09bd --- /dev/null +++ b/SampleB2CBot/App_Start/WebApiConfig.cs @@ -0,0 +1,37 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Http; + +namespace SampleB2CBot +{ + public static class WebApiConfig + { + public static void Register(HttpConfiguration config) + { + // Json settings + config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; + JsonConvert.DefaultSettings = () => new JsonSerializerSettings() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + Formatting = Newtonsoft.Json.Formatting.Indented, + NullValueHandling = NullValueHandling.Ignore, + }; + + // Web API configuration and services + + // Web API routes + config.MapHttpAttributeRoutes(); + + config.Routes.MapHttpRoute( + name: "DefaultApi", + routeTemplate: "api/{controller}/{id}", + defaults: new { id = RouteParameter.Optional } + ); + } + } +} diff --git a/SampleB2CBot/Controllers/MessagesController.cs b/SampleB2CBot/Controllers/MessagesController.cs new file mode 100644 index 0000000..237a428 --- /dev/null +++ b/SampleB2CBot/Controllers/MessagesController.cs @@ -0,0 +1,60 @@ +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using System.Web.Http; +using Microsoft.Bot.Builder.Dialogs; +using Microsoft.Bot.Connector; +using SampleB2CBot.Dialogs; + +namespace SampleB2CBot +{ + [BotAuthentication] + public class MessagesController : ApiController + { + /// + /// POST: api/Messages + /// Receive a message from a user and reply to it + /// + public async Task Post([FromBody]Activity activity) + { + if (activity != null && activity.GetActivityType() == ActivityTypes.Message) + { + await Conversation.SendAsync(activity, () => new ActionDialog()); + } + else + { + this.HandleSystemMessage(activity); + } + return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted); + } + + private Activity HandleSystemMessage(Activity message) + { + if (message.Type == ActivityTypes.DeleteUserData) + { + // Implement user deletion here + // If we handle user deletion, return a real message + } + else if (message.Type == ActivityTypes.ConversationUpdate) + { + // Handle conversation state changes, like members being added and removed + // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info + // Not available in all channels + } + else if (message.Type == ActivityTypes.ContactRelationUpdate) + { + // Handle add/remove from contact lists + // Activity.From + Activity.Action represent what happened + } + else if (message.Type == ActivityTypes.Typing) + { + // Handle knowing tha the user is typing + } + else if (message.Type == ActivityTypes.Ping) + { + } + + return null; + } + } +} \ No newline at end of file diff --git a/SampleB2CBot/Dialogs/ActionDialog.cs b/SampleB2CBot/Dialogs/ActionDialog.cs new file mode 100644 index 0000000..c736cb2 --- /dev/null +++ b/SampleB2CBot/Dialogs/ActionDialog.cs @@ -0,0 +1,83 @@ + +namespace SampleB2CBot.Dialogs +{ + using System; + using System.Threading; + using System.Threading.Tasks; + using AuthBot; + using AuthBot.Dialogs; + using AuthBot.Models; + using Microsoft.Bot.Builder.Dialogs; + using Microsoft.Bot.Connector; + + [Serializable] + public class ActionDialog : IDialog + { + public async Task StartAsync(IDialogContext context) + { + context.Wait(MessageReceivedAsync); + } + + public async Task TokenSample(IDialogContext context) + { + //endpoint b2c + var accessToken = await context.GetAccessToken(AuthSettings.Scopes); + + if (string.IsNullOrEmpty(accessToken)) + { + return; + } + + await context.PostAsync($"Your access token is: {accessToken}"); + + context.Wait(MessageReceivedAsync); + } + + public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable item) + { + var message = await item; + + if (message.Text == "logon") + { + //endpoint v2 + if (string.IsNullOrEmpty(await context.GetAccessToken(AuthSettings.Scopes))) + { + await context.Forward(new AzureAuthDialog(AuthSettings.Scopes), this.ResumeAfterAuth, message, CancellationToken.None); + } + else + { + context.Wait(MessageReceivedAsync); + } + } + else if (message.Text == "echo") + { + await context.PostAsync("echo"); + + context.Wait(this.MessageReceivedAsync); + } + else if (message.Text == "token") + { + await TokenSample(context); + } + else if (message.Text == "logout") + { + await context.Logout(); + context.Wait(this.MessageReceivedAsync); + } + else + { + context.Wait(MessageReceivedAsync); + } + } + + private async Task ResumeAfterAuth(IDialogContext context, IAwaitable result) + { + var message = await result; + + await context.PostAsync(message); + context.Wait(MessageReceivedAsync); + } + } +} + + diff --git a/SampleB2CBot/Dialogs/DialogExtensions.cs b/SampleB2CBot/Dialogs/DialogExtensions.cs new file mode 100644 index 0000000..8ffb885 --- /dev/null +++ b/SampleB2CBot/Dialogs/DialogExtensions.cs @@ -0,0 +1,59 @@ + +namespace SampleB2CBot.Dialogs +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using Autofac; + using Microsoft.Bot.Builder.Dialogs; + using Microsoft.Bot.Builder.Dialogs.Internals; + using Microsoft.Bot.Builder.Luis.Models; + using Microsoft.Bot.Connector; + + public static class DialogExtensions + { + public static void NotifyLongRunningOperation(this Task operation, IDialogContext context, Func handler) + { + operation.ContinueWith( + async (t, ctx) => + { + var messageText = handler(t.Result); + await NotifyUser((IDialogContext)ctx, messageText); + }, + context); + } + + public static void NotifyLongRunningOperation(this Task operation, IDialogContext context, Func handler) + { + operation.ContinueWith( + async (t, ctx) => + { + var messageText = handler(t.Result, (IDialogContext)ctx); + await NotifyUser((IDialogContext)ctx, messageText); + }, + context); + } + + public static string GetEntityOriginalText(this EntityRecommendation recommendation, string query) + { + if (recommendation.StartIndex.HasValue && recommendation.EndIndex.HasValue) + { + return query.Substring(recommendation.StartIndex.Value, recommendation.EndIndex.Value - recommendation.StartIndex.Value + 1); + } + + return null; + } + + public static async Task NotifyUser(this IDialogContext context, string messageText) + { + if (!string.IsNullOrEmpty(messageText)) + { + await context.PostAsync(messageText); + } + } + + } +} + + diff --git a/SampleB2CBot/Global.asax b/SampleB2CBot/Global.asax new file mode 100644 index 0000000..0b65d07 --- /dev/null +++ b/SampleB2CBot/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.cs" Inherits="SampleB2CBot.WebApiApplication" Language="C#" %> diff --git a/SampleB2CBot/Global.asax.cs b/SampleB2CBot/Global.asax.cs new file mode 100644 index 0000000..e724f3f --- /dev/null +++ b/SampleB2CBot/Global.asax.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Linq; +using System.Web; +using System.Web.Http; +using System.Web.Routing; + +namespace SampleB2CBot +{ + public class WebApiApplication : System.Web.HttpApplication + { + protected void Application_Start() + { + GlobalConfiguration.Configure(WebApiConfig.Register); + AuthBot.Models.AuthSettings.Mode = ConfigurationManager.AppSettings["ActiveDirectory.Mode"]; + AuthBot.Models.AuthSettings.EndpointUrl = ConfigurationManager.AppSettings["ActiveDirectory.EndpointUrl"]; + AuthBot.Models.AuthSettings.Tenant = ConfigurationManager.AppSettings["ActiveDirectory.Tenant"]; + AuthBot.Models.AuthSettings.RedirectUrl = ConfigurationManager.AppSettings["ActiveDirectory.RedirectUrl"]; + AuthBot.Models.AuthSettings.ClientId = ConfigurationManager.AppSettings["ActiveDirectory.ClientId"]; + AuthBot.Models.AuthSettings.ClientSecret = ConfigurationManager.AppSettings["ActiveDirectory.ClientSecret"]; + AuthBot.Models.AuthSettings.Scopes = ConfigurationManager.AppSettings["ActiveDirectory.Scopes"].Split(','); + AuthBot.Models.AuthSettings.Policy = ConfigurationManager.AppSettings["ActiveDirectory.Policy"]; + } + } +} diff --git a/SampleB2CBot/Properties/AssemblyInfo.cs b/SampleB2CBot/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e0a2b0a --- /dev/null +++ b/SampleB2CBot/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SampleB2CBot")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SampleB2CBot")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d1b2b925-d8e2-44f9-a8a1-d07b789afb34")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SampleB2CBot/SampleB2CBot.csproj b/SampleB2CBot/SampleB2CBot.csproj new file mode 100644 index 0000000..5852bfd --- /dev/null +++ b/SampleB2CBot/SampleB2CBot.csproj @@ -0,0 +1,178 @@ + + + + + Debug + AnyCPU + + + 2.0 + {D1B2B925-D8E2-44F9-A8A1-D07B789AFB34} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + SampleB2CBot + Bot Application + v4.6 + true + + + + + + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + ..\packages\Autofac.4.6.1\lib\net45\Autofac.dll + + + $(SolutionDir)\packages\Chronic.Signed.0.3.2\lib\net40\Chronic.dll + + + False + ..\packages\Microsoft.Bot.Builder.3.8.1.0\lib\net46\Microsoft.Bot.Builder.dll + + + False + ..\packages\Microsoft.Bot.Builder.3.8.1.0\lib\net46\Microsoft.Bot.Builder.Autofac.dll + + + False + ..\packages\Microsoft.Bot.Builder.3.8.1.0\lib\net46\Microsoft.Bot.Connector.dll + + + + $(SolutionDir)\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.4.403061554\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll + + + ..\packages\Microsoft.Rest.ClientRuntime.2.3.9\lib\net452\Microsoft.Rest.ClientRuntime.dll + + + ..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll + + + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + + + + $(SolutionDir)\packages\System.IdentityModel.Tokens.Jwt.4.0.4.403061554\lib\net45\System.IdentityModel.Tokens.Jwt.dll + + + + + $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll + + + + + + + + + + + + + + $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll + + + $(SolutionDir)\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll + + + + + + + + + + + + Designer + + + + + + + + + Global.asax + + + + + + Designer + + + Web.config + + + Web.config + + + + + {b8ad59d3-c36d-4e18-b504-06871001bc8d} + AuthBot + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + true + + + + + + + + + True + True + 3979 + / + http://localhost:3979/ + False + False + + + False + + + + + + \ No newline at end of file diff --git a/SampleB2CBot/Web.Debug.config b/SampleB2CBot/Web.Debug.config new file mode 100644 index 0000000..2e302f9 --- /dev/null +++ b/SampleB2CBot/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/SampleB2CBot/Web.Release.config b/SampleB2CBot/Web.Release.config new file mode 100644 index 0000000..c358444 --- /dev/null +++ b/SampleB2CBot/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/SampleB2CBot/Web.config b/SampleB2CBot/Web.config new file mode 100644 index 0000000..642affe --- /dev/null +++ b/SampleB2CBot/Web.config @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SampleB2CBot/default.htm b/SampleB2CBot/default.htm new file mode 100644 index 0000000..be6fbcb --- /dev/null +++ b/SampleB2CBot/default.htm @@ -0,0 +1,12 @@ + + + + + + + +

SampleB2CBot

+

Describe your bot here and your terms of use etc.

+

Visit Bot Framework to register your bot. When you register it, remember to set your bot's endpoint to

https://your_bots_hostname/api/messages

+ + diff --git a/SampleB2CBot/packages.config b/SampleB2CBot/packages.config new file mode 100644 index 0000000..01b8e0b --- /dev/null +++ b/SampleB2CBot/packages.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file