Disable Auth doesn't work with BF nuget package 4.7.1 - botframework

Version
Microsoft.Bot.Streaming and Microsoft.Bot.Builder
"4.7.1"
Describe the bug
For direct line speech local test, set IsAuthenticationDisabledAsync to true in building a BotFrameworkHttpAdapter. SendActivityAsync() fails to send response back with below error
Failed to fetch token before processing outgoing activity. An IIdentity is required in TurnState for this operation.
For webchat test, it failed at Conversation:: ReplyToActivityWithHttpMessagesAsync, I get 401 unauthorized issue
To Reproduce
Update nuget package of BF to 4.7.1
Implement public class DisabledAuthCredentialProvider : ICredentialProvider which set IsAuthenticationDisabledAsync to true
Build BotFrameworkHttpAdapterv with DisabledAuthCredentialProvider
See exception in SendActivitiesAsync
(await this.GetAppCredentialsAsync(this.GetBotAppId(turnContext), (string) null, new CancellationToken()).ConfigureAwait(false)).GetTokenAsync(false);

Seems to work for me (in Emulator). Running 4.7.1. Here's what I have in DisabledAuthCredentialProvider
public class DisabledAuthCredentialProvider : ICredentialProvider
{
public Task<string> GetAppPasswordAsync(string appId)
{
throw new NotImplementedException();
}
public Task<bool> IsAuthenticationDisabledAsync()
{
return Task.FromResult(true);
}
public Task<bool> IsValidAppIdAsync(string appId)
{
throw new NotImplementedException();
}
}
I can share my bot if that helps.

Related

get access to Adapter and Configuration for IBot

I am using Twitter Adapter Sample.
In class TwitterAdapterSampleBot:IBot
I want to get access to IBotFrameworkHttpAdapter adapter, IConfiguration configuration and ILogger logger, which are created in Startup->ConfigureServices method
I tried simple implement constructor :
public class TwitterAdapterSampleBot : IBot
{
public TwitterAdapterSampleBot(IBotFrameworkHttpAdapter adapter, IConfiguration configuration)
But got internal exception on startup:
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.Bot.Builder.Integration.AspNet.Core.IBotFrameworkHttpAdapter' while attempting to activate 'TwitterAdapter_Sample.TwitterAdapterSampleBot'.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type serviceType, Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type serviceType, Type implementationType, CallSiteChain callSiteChai
This "IBotFrameworkHttpAdapter" interface using to express the relationship between an mvc api Controller and a Bot Builder Adapter. So you need to resolve the dependency issue with it's implementation.
IBotFrameworkHttpAdapter is implemented in "BotFrameworkHttpAdapter" ( Bot Builder Adapter implementation ) class.
ConfigureServices in Asp.Net Core Startup Class:
services.AddSingleton<IBotFrameworkHttpAdapter, BotFrameworkHttpAdapter>();
You can implement the above scenario in another way for example create a botframework custom adapter error handler class with the implementation of BotFrameworkHttpAdapter.
Microsoft docs example:
public class AdapterWithErrorHandler : BotFrameworkHttpAdapter
{
private static log4net.ILog logger
= log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public AdapterWithErrorHandler(
ICredentialProvider credentialProvider,
ConversationState conversationState = null)
: base(credentialProvider)
{
OnTurnError = async (turnContext, exception) =>
{
// Log any leaked exception from the application.
logger.Error($"Exception caught : {exception.Message}");
// Send a catch-all apology to the user.
await turnContext.SendActivityAsync("Sorry, it looks like something went wrong.");
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.Error($"Exception caught on attempting to Delete ConversationState : {e.Message}");
}
}
};
}
}
ConfigureServices in Asp.Net Core Startup Class:
// Create the Bot Framework Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();
Reference:
Microsoft V4 docs
BotFrameworkHttpAdapter docs

ShowTypingMiddleware broke my bot when deployed - BotFramework V4

I'm trying to use the ShowTypingMiddleware, a custom middleware already given by BotFramework (see here: https://github.com/microsoft/botbuilder-dotnet/blob/master/libraries/Microsoft.Bot.Builder/ShowTypingMiddleware.cs) to send an typing message to my user while the bot is processing his request. I'm using the BotFramework V4.
It all works locally, but not when I publish it on Azure's WebChat.
I've followed the example in Microsoft's samples, where they create an adapter that adds the desired middleware to the bot pipeline (the sample I've used is here: https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/17.multilingual-bot. The custom adapter I'm referring to is AdapterWithErrorHandler.cs, and it adds the TranslationMiddleware to the pipeline).
Running locally, everything works as planned. The problem is: when I'm publishing it to Azure, the webchat stop working. It throws the following exception:
7‎/‎21‎/‎2019‎ ‎1‎:‎07‎:‎33‎ ‎PM There was an error sending this message to your bot: HTTP status code Unauthorized
‎7‎/‎21‎/‎2019‎ ‎1‎:‎07‎:‎33‎ ‎PM There was an error sending this message to your bot:
HTTP status code Unauthorized
In my StartUp.cs's ConfigureServices, I've injected my custom adapter and my middleware:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// Create the credential provider to be used with the Bot Framework Adapter.
services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();
// Create the Bot Framework Adapter.
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithTypingAndErrorHandler>();
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, MyBot>();
services.AddSingleton<ShowTypingMiddleware>();
}
My AdapterWithTypingAndErrorHandler is as follows:
public class AdapterWithTypingAndErrorHandler : BotFrameworkHttpAdapter
{
public AdapterWithTypingAndErrorHandler(
IConfiguration configuration,
ILogger<BotFrameworkHttpAdapter> logger,
ShowTypingMiddleware showTypingMiddleware) : base(logger: logger)
{
if (showTypingMiddleware == null)
throw new NullReferenceException($"Could not load '{nameof(showTypingMiddleware)}' in custom adapter.");
AddAdapterToPipeline(showTypingMiddleware);
OnTurnError = async (turnContext, exception) =>
{
logger.LogError($"Exception caught : {exception.Message}");
await turnContext.SendActivityAsync("Sorry, something went wrong :/");
};
}
private void AddAdapterToPipeline(ShowTypingMiddleware showTypingMiddleware)
=> Use(showTypingMiddleware);
}
And I'm using it in my controller:
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()
{
await Adapter.ProcessAsync(Request, Response, Bot);
}
}
As I said, everything works fine, locally, but when I publish it, the WebChat throws the Unauthorized exception. If I use the default Adapter (the BotFrameworkHttpAdapter.cs one), instead of my customized, it all works fine too.
What should I do?

Alternative to HttpRequestMessage SetContext method

I have this code from codeplex
private async Task ExecuteChangeSet(
ChangeSetRequestItem changeSet,
IList<ODataBatchResponseItem> responses,
CancellationToken cancellation)
{
ChangeSetResponseItem changeSetResponse;
// Create a new ShoppingContext instance, associate it with each of the requests, start a new
// transaction, execute the changeset and then commit or rollback the transaction depending on
// whether the responses were all successful or not.
using (ShoppingContext context = new ShoppingContext())
{
foreach (HttpRequestMessage request in changeSet.Requests)
{
request.SetContext(context);
}
The complete sample code can be found here.
I downloaded the project and it is using .net framework 4.5
but in .NET Framework 4.6.1 the SetContext method is no longer present
I want to know how can I achieve the same in framework version 4.6.1?
I am basically creating a OData V3 Service which will be hosted in IIS.
You can go with creating your own functionality for setting the context and retrieve it where needed, with HttpRequestMessage extensions like:
Example class:
public static class HttpRequestMessageExtensions
{
private const string Context = "ShoppingContext";
public static void SetContext(this HttpRequestMessage request, ShoppingContext context)
{
request.Properties[Context] = context;
}
public static ShoppingContext GetContext(this HttpRequestMessage request)
{
object context;
if (request.Properties.TryGetValue(Context, out context))
{
return (ShoppingContext) context;
}
return null;
}
}
Usage:
//Setting context
request.SetContext(context);
//reading context
var context = request.GetContext();

A system.net.webException when using httpclient on xamarin android

error: NameResolutionFailure
public class JsonConverter
{
public async Task<string> GetStringbyJson(string link)
{
HttpClient client = new HttpClient ();
HttpResponseMessage message = await client.GetAsync (link);
return await message.Content.ReadAsStringAsync ();
}
}
public async override void OnActivityCreated (Bundle savedInstanceState)
{
base.OnActivityCreated (savedInstanceState);
lst = View.FindViewById<ListView> (Resource.Id.lstHome);
var result = await json.GetStringbyJson ("https://api-v2.soundcloud.com/explore/Popular+Music?tag=out-of-experiment&limit=30&linked_partitioning=1&client_id=9ac2b330e1b3&offset="+offset);
}
App just run when I removed "var result". I test app on device
The error is exactly what it says - it is unable to resolve the domain name you've given it.
The most likely cause is neglecting to enable INTERNET_PERMISSION in the app's manifest. But it could also be a general networking issue on the device. Can you resolve the domain from the device's browser?

.net 5 MVC 6 web api using existing identityDb for authentication

I am working on having native app be able to authenticate to a web api which uses an existing identity db database created from MVC6. I understand this is not a secure way of doing things as per this post. However, until I can figure out how to get IdentityServer3 working with a database I thought I would try a simple web api that authenticates to a database I already created when I built a standard MVC 6 web app. Here is what I did:
Created an asp.net 5 web api from the template and added the following:
Settings:
appsettings.json I added:
"Data": {
"DefaultConnection": {
"ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet5-TestUsers-eaf0c85f-23e4-4603-97ce-b9f49ee1d167;Trusted_Connection=True;MultipleActiveResultSets=true"
}
},
Startup:
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApiDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
Models:
public class AppUser : IdentityUser
{
}
DBContext:
public class ApiDbContext : IdentityDbContext<AppUser>
{
}
Controller:
private readonly UserManager<AppUser> _userManager;
private readonly SignInManager<AppUser> _signInManager;
private readonly ILogger _logger;
...
public async Task<IEnumerable<string>> Post([FromBody]LoginModel model)
{
if (ModelState.IsValid) {
string user = model.userid;
string passwd = model.password;
var result = await _signInManager.PasswordSignInAsync(model.userid, model.password, false, lockoutOnFailure: false);
if (result.Succeeded)
{
_logger.LogInformation(1, "User logged in.");
return new string[] { user };
}
else
{
return new string[] { "Failed" };
}
}
else
{
return new string[] { "Incorrect format received"};
}
}
However, it bombs at the _signInManager line with the error:
System.NullReferenceException: Object reference not set to an instance
of an object.
So apparently _signInManager is Null because I know the model is fine because I am printing the userid and password and they are there.
What am I missing so I can use the signInManager in a web api?
I went back yet another time to see what was different between the web api and the web app, since the web app auth was working fine. Here is what I added to get it working:
controller needed a constructor:
public AuthController(
SignInManager<AppUser> signInManager,
ILoggerFactory loggerFactory)
{
_signInManager = signInManager;
_logger = loggerFactory.CreateLogger<AuthController>();
}
Which got rid of my other error but produced the following error:
System.InvalidOperationException: No authentication handler is
configured to handle the scheme: Microsoft.AspNet.Identity.Application
So after researching that I needed to add to startup:
configureservices:
services.AddIdentity<AppUser, IdentityRole>()
.AddEntityFrameworkStores<ApiDbContext>()
.AddDefaultTokenProviders();
configure:
app.UseIdentity();
Adding these to the above allowed me to post JSON with userid and password.

Resources