Autofac or the way Delegates work is loading the wrong dependency inside ASP.Net Web API Bot Framework - c#

Since the Microsoft Bot Framework is using Autofac as a dependency, I figured I'd use it in the rest of my project (using the Bot Framework project scaffold). I'm trying to load the correct Bot Framework IDialog using DI. My Global.asax.cs looks like this:
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
var builder = new ContainerBuilder();
// Get your HttpConfiguration.
var config = GlobalConfiguration.Configuration;
// Register your Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// OPTIONAL: Register the Autofac filter provider.
builder.RegisterWebApiFilterProvider(config);
builder.RegisterType<EchoDialogState>().Named<IDialog<object>>(ActivityTypes.Message);
builder.RegisterType<WelcomeDialog1>().Named<IDialog<object>>(ActivityTypes.ConversationUpdate);
builder.RegisterType<UnknownDialog>().Named<IDialog<object>>(string.Empty).PreserveExistingDefaults();
// Set the dependency resolver to be Autofac.
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
The ActivityTypes are string constants that equate to message, conversationUpdate, etc. For my controllers, I have a base that looks like this to resolve the Autofac DI Container:
public class BaseApiController : ApiController
{
public IContainer Container { get; private set; } = ((IContainer)((AutofacWebApiDependencyResolver)GlobalConfiguration.Configuration.DependencyResolver).Container);
}
In my MessagesController that catches all bot interactions, my code looks like this:
[BotAuthentication]
public class MessagesController : BaseApiController
{
public virtual async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
using (var scope = Container.BeginLifetimeScope())
{
var dialog = scope.ResolveNamed<IDialog<object>>(activity.GetActivityType());
await Conversation.SendAsync(activity, () => dialog);
}
return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted);
}
}
The activity.GetActivityType() simply returns back a string with equates with the ActivityTypes referred to above. When I breakpoint on scope.ResolveNamed, it looks like the correct IDialog has been returned to dialog. However, when the Conversation.SendAsync runs, the wrong IDialog is running. For example, I see EchoDialogState being injected into dialog, but then the code inside WelcomeDialog1 is always being called instead.
Am I doing this wrong? What would cause the incorrect IDialog being run inside the delegate function?
If it helps, this is an example of the IDialog that's running:
[Serializable]
public class WelcomeDialog1 : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
var message = await argument;
var name = message.From.Name;
await context.PostAsync($"Hi {name}. Welcome to SMEBot! First time here stuff.");
context.Wait(MessageReceivedAsync);
}
}

Instead of doing a context.Wait in the MessageReceivedAsync method of your WelcomeDialog1, try doing a context.Done.
In the way you are doing it now, the WelcomeDialog1 is still listening to your messages. Usually the welcome message is done using the ConnectorClient in the MessageController as it is a simple message. You don't need all the dialog infrastructure.

Related

How to inject a ClaimsPrincipal in a Blazor Server application

Here are some artifacts to help understand the issue:
Sample Code - Github repo
Deployed Application - no longer available
Update: I have followed this YouTube video which I now believe to be the correct way of accessing information about the authenticated user in dependent services for a Blazor Server application: https://www.youtube.com/watch?v=Eh4xPgP5PsM.
I've updated the Github code to reflect that solution.
I have the following classes that I register using dependency injection in my ASP.NET MVC Core application.
public class UserContext
{
ClaimsPrincipal _principal;
public UserContext(ClaimsPrincipal principal) => _principal = principal;
public bool IsAuthenticated => _principal.Identity.IsAuthenticated;
}
public class WrapperService
{
UserContext _userContext;
public WrapperService(UserContext context) => _userContext = context;
public bool UserHasSpecialAccess()
{
return _userContext.IsAuthenticated;
}
}
The IoC dependency registrations are configured in Startup.cs
services.AddScoped<ClaimsPrincipal>(x =>
{
var context = x.GetRequiredService<IHttpContextAccessor>();
return context.HttpContext.User;
});
services.AddScoped<UserContext>();
services.AddScoped<WrapperService>();
I recently enabled Blazor in the MVC application and wanted to use my DI registered services from within my Blazor components.
I injected the service in a Blazor component in order to use it like so:
#inject WrapperService _Wrapper
However, when I attempt to use the service from a server side handler, the request fails with an exception complaining that the services could not be constructed - due to IHttpContext not existing on subsequent calls to the server.
<button #onclick="HandleClick">Check Access</button>
async Task HandleClick()
{
var hasPermission = _Wrapper.UserHasSpecialAccess(); // fails 😔
}
I think I understand why the use of IHttpContextAccessor is not working/recommended in Blazor Server apps. My question is, how can I access the claims I need in my services without it?
The odd thing to me is that this all works when I run it under IIS Express in my development environment, but fails when I deploy and attempt to run it from within an Azure AppService.
This is what work for me, writing a derived class for AuthenticationStateProvider.
public class AppAuthenticationStateProvider : AuthenticationStateProvider
{
private ClaimsPrincipal principal;
// Constructor, only needed when injections required
public AppAuthenticationStateProvider(/* INJECTIONS HERE */)
: base()
{
principal ??= new();
}
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
return Task.FromResult(new AuthenticationState(principal));
}
// Method called from login form view
public async Task LogIn(/* USER AND PASSWORD */)
{
// Create session
principal = new ClaimsPrincipal(...);
var task = Task.FromResult(new AuthenticationState(principal));
NotifyAuthenticationStateChanged(task);
}
// Method called from logout form view
public async Task LogOut()
{
// Close session
principal = new();
var task = Task.FromResult(new AuthenticationState(principal));
NotifyAuthenticationStateChanged(task);
}
Then, at program/startup you add these lines:
// Example for .Net 6
builder.Services.AddScoped<AuthenticationStateProvider, AppAuthenticationStateProvider>();
builder.Services.AddScoped<ClaimsPrincipal>(s =>
{
var stateprovider = s.GetRequiredService<AuthenticationStateProvider>();
var state = stateprovider.GetAuthenticationStateAsync().Result;
return state.User;
});
That's it. Now you can inject ClaimsPrincipal wherever you want.
You can inject AuthenticationStateProvider into your Service constructor and then use
var principal = await _authenticationStateProvider.GetAuthenticationStateAsync();
AuthenticationStateProvider is a Scoped service so yours has to be too.
Use CascadingAuthenticationState to access the claims principal
https://learn.microsoft.com/en-us/aspnet/core/blazor/security/?view=aspnetcore-5.0#expose-the-authentication-state-as-a-cascading-parameter-1
If you need to use your own logic, you will need to implement your own authentication state provider.
If you want to use a service to use ClaimsPrincipal you can do the following:
ClaimsPrincipalUserService.cs
ClaimsPrincipal claimsPrincipal;
void SetClaimsPrincipal(ClaimsPrincipal cp)
{
claimsPrincipal = cp;
// any logic + notifications which need to be raised when
// ClaimsPrincipal has changes
}
Inject this service as scoped in the startup.
In the layout
MainLayout.razor
#inject ClaimsPrincipalUserService cpus;
[CascadingParameter]
public Task<AuthenticationState> State {get;set;}
protected override async Task OnInitializedAsync()
{
var state = await State;
var user = state.User; // Get claims principal.
cpus.SetClaimsPrincipal(user);
}

Adding middleware to bot framework

I am trying to add middleware into echo bot, that converts message into lower cases.
I have created Middleware class that inherits from IMiddleware
public class MiddlewareOne : IMiddleware
{
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default)
{
if(turnContext.Activity.Type == ActivityTypes.Message)
{
Debug.WriteLine(turnContext.Activity.Text);
turnContext.Activity.Text = turnContext.Activity.Text.ToLower();
await next(cancellationToken);
Debug.WriteLine(turnContext.Activity.Text);
}
else
{
await next(cancellationToken);
}
}
}
}
Now I am trying to add it into Startup.cs file. I found somewhere it should be added as Transient.
services.AddTransient<MiddlewareOne>();
Still, it's not working. I think MiddlewareOne class is okay, but how should I configure it in Startup.cs file?
Thank you
You have to register the middleware in your BotFrameworkAdapter descendant (e.g. BotFrameworkHttpAdapter) by calling the Use method in constructor. You can pass the middleware as a constructor parameter and DI will take care of activation.
An example (made without VS assistance)
public class MyAdapter : BotFrameworkHttpAdapter
{
public MyAdapter(MiddlewareOne mw1, IConfiguration configuration, ILogger<BotFrameworkHttpAdapter> logger)
: base(configuration, logger)
{
Use(mw1);
// other code..
}
}

How to initialize scoped dependencies for consumers using MassTransit filters?

I would like to initialize some dependencies resolved from the MassTransit serviceProvider in the same way Asp.Net Core does with the pipeline's middlewares.
In particular I would like to inspect the incoming message before the consumer is called and extract the tenant from it (I'm currently working on a multitenant web application with single database per tenant).
With this informations I need to initialize some scoped instances (Ef Core DbContext for example).
I know that I can inject them in the Consumer through constructor but this means that I must do that everytime I write a new one, so I suppose that a filter should be the right place (correct me if I'm wrong).
The problem raises when I need to access the current consumer scope to resolve the dependencies that I need. I was thinking that the behavior of the MassTransit' pipeline was similar to the Asp.Net one regarding middleware injection but I was probably wrong.
I haven't found any documentation on how to do that clearly without cluttering the code of the filter, so any suggestion is going to be really appreciated.
This is the filter that I need to modify:
public class TenantContextInitializerFilter<T> : IFilter<T> where T : class, ConsumeContext
{
public void Probe(ProbeContext context) { }
public async Task Send(T context, IPipe<T> next)
{
//Resolve scoped instance here and do something before Consumer is called
var connectionStringProvider = scope.GetService<IConnectionStringProvider>();
await next.Send(context);
}
}
public class RegistrationsDeliveredEventConsumer : IConsumer<IRegistrationsDelivered>
{
private readonly IConnectionStringProvider _connectionStringProvider;
public RegistrationsDeliveredEventConsumer(IConnectionStringProvider connectionStringProvider)
{
//This should be the same instance that has been resolved in the filter' Send() method
_connectionStringProvider = connectionStringProvider;
}
public async Task Consume(ConsumeContext<IRegistrationsDelivered> context)
{
}
}
This is a simplified example of my code but this should be enough
There's two facets to consider: 1) are filters registered as services/pulled from the service collection when using the ASP.NET Core integration and 2) what lifetime do the filters have if they are. I'm not familiar with the MassTransit ASP.NET Core integration, but it looks like you should be good based on a cursory review. You'll need to confirm that both of those requirements are met.
For dependency injection, in general, constructor injection is the way to go unless there's a very specific need to do something different, which does not seem to be the case here. In short, you need a constructor for your filter.
What exactly you need to inject is a function of the lifetime of the filter. If it has a transient lifetime, then you can inject your scoped dependencies directly. If it has a singleton lifetime, then you'll need to inject IServiceProvider instead, and do the following whenever you need to use one of those dependencies:
using (var scope = _serviceProvider.CreateScope())
{
var dep = scope.ServiceProvider.GetRequiredService<MyDependency>();
// do something with `dep`
}
Here's a draft... I'm sure there are missing pieces, so let me know if you have questions.
public class TenantContextInitializerFilter<T> : IFilter<T> where T : class, ConsumeContext
{
private readonly Func<string, IDbConnection> _dbContextAccessor;
public void Probe(ProbeContext context) { }
public TenantContextInitializerFilter(Func<string, IDbConnection> dbContextAccessor)
{
_dbContextAccessor = dbContextAccessor;
}
public async Task Send(T context, IPipe<T> next)
{
var tenantId = ""; // place holder
using (var dbContext = _dbContextAccessor(tenantId))
{
//... do db logic
}
await next.Send(context);
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IConnectionStringProvider>(
provider => null /* TODO figure out how to fetch scoped instance from a cache or some storage mechanism*/);
services.AddScoped(provider =>
{
IDbConnection Accessor(string tenantId)
{
if (provider.GetService<IConnectionStringProvider>()
.TryGetConnectionString(tenantId, out var connectionString, out var providerName))
return new SqlConnection(connectionString);
throw new Exception();
}
return (Func<string, IDbConnection>)Accessor;
});
}
}

IdentityServer3, Autofac, IdentityFramework in existing project

I have been playing around with IdentityServer3 with the hopes to replace our current authentication process.
Currently we use a custom identity framework process using code first entity framework.
I managed to install IdentityServer3 and get the "in memory" stuff working. Now I want to hook it up to our already customised UserProvider (UserManager if you like).
We already use Autofac and have our UserProvider registered like this:
builder.RegisterType<UserProvider>().As<IUserProvider>().InstancePerDependency();
I found some documentation that states that IdentityServer uses Autofac itself.
They recommend creating a factory and then using IdentityServerOptions to register the user service like this:
options.Factory.UserService = new Registration<IUserService>(UserServiceFactory.Create())
The problem I have with that, is the factory looks something like this:
public class UserServiceFactory
{
public static AspNetIdentityUserService<User, string> Create()
{
var context = new IdentityDbContext();
var userStore = new UserStore<User>(context);
var userManager = new UserManager<User>(userStore);
return new AspNetIdentityUserService<User, string>(userManager);
}
}
Which is using the normal UserManager rather than our customised version and it isn't using DI because you create it all in the static method.
Surely it would be better to use Autofac as we already have our UserProvider registered.
So, I didn't use their IdentityServerOptions to invoke the static method. So I changed my factory to this:
public class IdentityServerUserService : UserServiceBase
{
private readonly IUserProvider _userProvider;
public IdentityServerUserService(IUserProvider userProvider)
{
_userProvider = userProvider;
}
public override async Task AuthenticateLocalAsync(LocalAuthenticationContext context)
{
var user = await _userProvider.FindAsync(context.UserName, context.Password);
if (user != null && !user.Disabled)
{
// Get the UserClaims
// Add the user to our context
context.AuthenticateResult = new AuthenticateResult(user.Id, user.UserName, new List<Claim>());
}
}
}
Which I registered in autofac like this:
builder.RegisterType<IdentityServerUserService>()
.As<IdentityServer3.Core.Services.IUserService>()
.InstancePerDependency();
And then I assigned to the IdentityServerOptions.Factory.UserService like this:
private static void SetupServices(IdentityServerOptions options, ILifetimeScope scope)
{
options.Factory.UserService = new Registration<IUserService>(scope.Resolve<IdentityServerUserService>());
}
And the scope I get like this:
var config = new HttpConfiguration();
var scope = config.DependencyResolver.GetRootLifetimeScope();
I believe this should work, but I get an error when I try to use postman to authenticate:
Autofac.Core.Registration.ComponentNotRegisteredException: The requested service 'Business.IdentityServerUserService' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.
I tried to change from InstancePerDependency to InstancePerLifetimeScope but still got the same error.
So, I have a couple of questions:
Is this the right way to assign the UserService?
Will this allow my existing users to authenticate?
Has anyone done this before? If so, did they get it to work?
If anyone can help me with these questions, I would be eternally grateful.
You resolve IdentityServerUserService but you register IdentityServerUserService as IUserService. Autofac doesn't automatically register the type as itself.
To fix the error you can register the type as itself
builder.RegisterType<IdentityServerUserService>()
.As<IdentityServer3.Core.Services.IUserService>()
.InstancePerDependency();
or resolve IUserService
options.Factory.UserService = new Registration<IUserService>(scope.Resolve<IUserService>())

How to pass Owin context to a Repo being injected into Api controller

I've got a MVC WebApi owin (soft hosted) project, that uses Unity for resolving controller dependencies
which look like this
public class PacientaiController : ODataController
{
private readonly IEntityRepo<Ent.Pacientas> repo;
public PacientaiController(IEntityRepo<Ent.Pacientas> repo)
{
this.repo = repo;
}
the problem I'm trying to solve - is how do I pass 'OwinContex' into a Repo.
public class PacientasEntityRepo:IEntityRepo<Pacientas>,IDisposable
{
public PacientasEntityRepo(IOwinContext ctx)
{
.........
If I try to register it like this in the Startup.cs
Container.RegisterType<IOwinContext>(new InjectionFactory(o => HttpContext.Current.GetOwinContext()));
I get a null ref, saying that HttpContext.Current is NULL
The main idea here, is to pass the currently authenticated user to the repo, because Repo host the Logic for querying the Database, depending on the user. (say if the user is Admin, then return this data, if the user is guest - return this data)
The point being - that this is a self Host !
Lets put aside why you have this design and concentrate to the problem: injecting the IOwinContext:
you can also get it from a HttpRequestMessage instance with the GetOwinContext method, however you also need to get a HttpRequestMessage somehow.
Unity does not support injection of the HttpRequestMessage out of the box but you can use a custom DelegatingHandler which stores the current HttpRequestMessage in the container as described here: Inject WebAPI UrlHelper into service using Autofac
The linked question is about Autofac but you can transfer it for work with Unity:
The CurrentRequest and the CurrentRequestHandler can be used from Andrew Davey's answer as it is:
public class CurrentRequest
{
public HttpRequestMessage Value { get; set; }
}
public class CurrentRequestHandler : DelegatingHandler
{
protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
var scope = request.GetDependencyScope();
var currentRequest = (CurrentRequest)scope.GetService(typeof(CurrentRequest));
currentRequest.Value = request;
return await base.SendAsync(request, cancellationToken);
}
}
Then you just need to register the DelegatingHandler with:
httpConfiguration.MessageHandlers.Insert(0, new CurrentRequestHandler());
And register the CurrentRequest and IOwinContext in the container
container.RegisterType<CurrentRequest>(
new HierarchicalLifetimeManager());
container.RegisterType<IOwinContext>(
new HierarchicalLifetimeManager(),
new InjectionFactory(c => c.Resolve<CurrentRequest>().Value.GetOwinContext()));
httpConfiguration.DependencyResolver = new UnityHierarchicalDependencyResolver(container);
Beside the custom delegation handler there are other places to hook into Web.API to capture the HttpRequestMessage for example you can create your own IHttpControllerActivator and use the ExecuteAsync method as described here: Dependency Injection in ASP.NET Web API 2
In a selfhosted application you do not have a HttpContext. You need an other way to move the state around. An option is to use a self implemented HttpContext like:
https://github.com/danielcrenna/graveyard/tree/master/httpcontext-shim
I think the problem is that HttpContext does not exist at the time Startup is called, so what you probably need, is to have a Func instead, like this:
public class PacientasEntityRepo:IEntityRepo<Pacientas>,IDisposable
{
public PacientasEntityRepo(Func<IOwinContext> ctx)
{
.........
and then change the code in Startup to this:
Container.RegisterType<IOwinContext>(new InjectionFactory(() => HttpContext.Current.GetOwinContext()));
In my Asp.Net Mvc (AutoFac) project (not core) i have used below registeration andthat was successed
builder.RegisterType<OwinContext>().AsImplementedInterfaces().InstancePerRequest();

Categories