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>())
Related
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;
});
}
}
I'm confused on how the OWIN CreatePerOwinContext method is to be used. As far as I can see it's a poor mans DI mechanism. Yet, I fail to see how to use it.
We can register a type/implementation at the Startup sequence like:
app.CreatePerOwinContext<IUserService>(() => {
return new UserService() as IUserService;
});
Then how do we resolve to that later on. Documentation says it can be retrieved via Get method. But Get<T> expects a string parameter, which is the key to that entry in the Enviornment IDictionary? How can I know the key in this case?
IUserService userService = context.Get<IUserService>(???);
You can use typeof to get the key parameter:
HttpContext.GetOwinContext().Get<ApplicationDbContext>(typeof(ApplicationDbContext).ToString());
Also, Microsoft.AspNet.Identity.Owin assembly contains the parameterless version of Get<T>() method, so you can use it if you already have ASP.NET Identity in your project.
I have a more correct answer after running into this myself, trying to implement the code within this stackoverflow answer: https://stackoverflow.com/a/31918218
So given this initialization code within the conventional Configure method:
static void Configuration(IAppBuilder app)
{
//https://stackoverflow.com/a/31918218
app.CreatePerOwinContext<AppBuilderProvider>(() => new AppBuilderProvider(app));
ConfigureAuth(app); //note implementation for this is typically in separate partial class file ~/App_Start/Startup.Auth.cs
}
One can retrieve the instance created by this code:
public ActionResult SomeAction()
{
//https://stackoverflow.com/a/31918218
var app = HttpContext.GetOwinContext().Get<AppBuilderProvider>("AspNet.Identity.Owin:" + typeof(AppBuilderProvider).AssemblyQualifiedName).Get();
var protector = Microsoft.Owin.Security.DataProtection.AppBuilderExtensions.CreateDataProtector(app, typeof(Microsoft.Owin.Security.OAuth.OAuthAuthorizationServerMiddleware).Namespace, "Access_Token", "v1");
var tdf = new Microsoft.Owin.Security.DataHandler.TicketDataFormat(protector);
var ticket = new AuthenticationTicket(ci, null);
var accessToken = tdf.Protect(ticket);
//you now have an access token that can be used.
}
I have an issue with RegisterHttpRequestMessage not working for me and cannot figure out what I'm doing wrong. This is specifically when I try to manually resolve a service that accepts the HttpMessageRequest as a parameter.
I'm using modules to register components in my builder, and currently my module in the main web project looks like this:
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterHttpRequestMessage(GlobalConfiguration.Configuration);
builder.RegisterType<SourceSystemViewModel>().AsImplementedInterfaces().InstancePerRequest();
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.Where(t => t.Name.EndsWith("ViewModelValidator"))
.AsImplementedInterfaces()
.PropertiesAutowired();
// Etc etc etc
SourceSystemViewModel is currently quite simple and looks like this:
public interface ISourceSystemViewModel
{
SourceSystem Value { get; }
}
public class SourceSystemViewModel : ISourceSystemViewModel
{
public SourceSystemViewModel(HttpRequestMessage request)
{
Value = request.Headers.GetSourceSystem();
}
public SourceSystem Value { get; }
}
GetSourceSystem is just an extension method that pulls out the header value. I have tried both registering SourceSystemViewModel with and without InstancePerRequest but it doesn't make a difference. The moment autofac tries to resolve ISourceSystemViewModal (and ultimately HttpRequestMessage) it throws this:
An exception of type
'Autofac.Core.Registration.ComponentNotRegisteredException' occurred
in Autofac.dll but was not handled in user code
Additional information: The requested service 'System.Net.Http.HttpRequestMessage' 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.
Using autofac 3.5 (webapi dll is quoted as autofac.webapi2 3.4).
Any ideas much appreciated!
Notes
I'll add any findings as I come across them...
Update 1
I took a look at how RegisterHttpRequestMessage works and it does indeed add a message handler called CurrentRequestHandler to HttpConfiguration. When my request comes in I can see that this message handler still exists. So the method seems to do what it's supposed to, it's just not resolving the request message for me...
Update 2
I have noticed that while in the context of a controller and therefore have access to the HttpRequestMessage I can resolve both objects. Like this:
ILifetimeScope requestLifetimeScope = Request.GetDependencyScope().GetRequestLifetimeScope();
var h = requestLifetimeScope.Resolve<HttpRequestMessage>();
var sourceSystemViewModel = requestLifetimeScope.Resolve<ISourceSystemViewModel>();
Update 3
It's important to note that I am manually trying to resolve a service that is expecting the HttpMessageRequest as an injected parameter. For example, this fails for me:
using (var httpRequestScope = IocProxy.Container.Context.BeginLifetimeScope("AutofacWebRequest"))
{
var sourceSystemViewModel = httpRequestScope.Resolve<ISourceSystemViewModel>();
}
I had the same problem and based on SO I was able to get it working with
public class NLoggerTraceWriterModule : Module
{
private HttpConfiguration _config;
public NLoggerTraceWriterModule(HttpConfiguration config)
{
this._config = config;
}
protected override void Load(ContainerBuilder builder)
{
builder.RegisterInstance(this._config).As<HttpConfiguration>();
builder.Register(c =>
c.Resolve<HttpConfiguration>()
.Services
.GetService(typeof(ITraceWriter)) as ITraceWriter)
.As<ITraceWriter>();
}
}
registering this module as
// Call RegisterHttpRequestMessage to add the feature.
builder.RegisterHttpRequestMessage(config);
builder.RegisterModule(new Helper.Logging.NLoggerTraceWriterModule(config));
Even though this wasn't the issue for OP, here's another solution since this is currently the only Google result for "The requested service System.Net.Http.HttpRequestMessage' has not been registered."
If you've created an HttpConfiguration instance, make sure that's what you're passing to the registration function.
In my case, this was resolved by changing this:
builder.RegisterHttpRequestMessage(GlobalConfiguration.Configuration);
To this:
builder.RegisterHttpRequestMessage(Startup.HttpConfiguration);
Attempting to inject data into a FluentValidation validator:
public class MyFormValidator : AbstractValidator<MyForm>
{
private readonly IQueryable<Models.User> _users;
public MyFormValidator(IQueryable<Models.User> users)
{
_users = users;
...
}
}
My validator factory:
public class DependencyResolverValidatorFactory : ValidatorFactoryBase
{
private readonly IContainer container;
public DependencyResolverValidatorFactory(IContainer container)
{
this.container = container;
}
public override IValidator CreateInstance(Type validatorType)
{
return container.ResolveOptionalKeyed<IValidator>(validatorType);
}
}
My Autofac configurator:
public class AutofacConfigurator
{
public static void Configure()
{
var builder = new ContainerBuilder();
...
builder.RegisterType<MyFormValidator>()
.Keyed<IValidator>(typeof(IValidator<MyForm>))
.As<IValidator>()
// 2nd parameter returns IQueryable<User>
.WithParameter("users", new SqlRepository<User>(dataContext))
.InstancePerRequest();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// Register the validator factory with FluentValidation, and register
// FluentValidation as the model validator provider for the MVC framework.
// see http://www.jerriepelser.com/blog/using-fluent-validation-with-asp-net-mvc-part-3-adding-dependency-injection
var fluentValidationModelValidatorProvider =
new FluentValidationModelValidatorProvider(
new DependencyResolverValidatorFactory(container));
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
fluentValidationModelValidatorProvider.AddImplicitRequiredValidator = false;
ModelValidatorProviders.Providers.Add(fluentValidationModelValidatorProvider);
}
}
Getting the following exception:
No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.
I have other validators, most of which will not need data injected into them.
This is largely new ground for me (in both Autofac and FluentValidation) and am still trying to understand what I am doing here. I suspect I'm simply registering my type incorrectly. How do I fix this and properly register my type?
(My apologies if this is too similar to other questions that were already asked.)
I have zero experience with FluentValidation, but I doubt it's the cause of your issues anyway, so I'll plow forward regardless.
The exception you're getting means that Autofac can't resolve your service as 'instance per request'. There's a lot of documentation as to what this means on the Autofac documentation page. To summarize, it means that Autofac will attempt to resolve the service from a lifetime scope that is automatically created for each request sent to the webserver. When you register something as .InstancePerRequestScope() but then attempt to resolve that service outside of that scope, you'll get the DependencyResolutionException you see.
So we've established that your MyFormValidator isn't being resolved from a 'Request' scope. Why?
The custom DependencyResolverValidatorFactory you've written takes the actual IContainer that was built by Autofac, and resolves from that. This is a special type of ILifetimeScope, the 'root scope'. There's no request lifetime scope directly associated with this, so you get your exception. You need to to resolve from an ILifetimeScope that is began from the 'request' scope, or a sub-scope that is contained within the request scope.
The Autofac/MVC integration already automatically hosts a request scope (within the AutofacDependencyResolver, see the source), but your custom DependencyResolverValidatorFactory doesn't resolve from it. If you want to do that, I suppose you could modify your DependencyResolverValidatorFactory to accept the AutofacDependencyResolver instance instead, and use that to resolve.
It would look something like this:
public class DependencyResolverValidatorFactory : ValidatorFactoryBase
{
private readonly AutofacDependencyResolver resolver;
public DependencyResolverValidatorFactory(AutofacDependencyResolver resolver)
{
this.resolver = resolver;
}
public override IValidator CreateInstance(Type validatorType)
{
return resolver.RequestLiftimeScope.ResolveOptionalKeyed<IValidator>(validatorType);
}
}
Note the RequestLifetimeScope stuck in there.
Then you create this in your .Configure() method using
var resolver = new AutofacDependencyResolver(container);
DependencyResolver.SetResolver(resolver);
var fluentValidationModelValidatorProvider =
new FluentValidationModelValidatorProvider(
new DependencyResolverValidatorFactory(resolver));
That should get rid of the exception, assuming that this factory does indeed have a request to work from when creating instances of IValidators. If not, You might need to register using the default behavior (.InstancePerDependency(), where it creates a new instance every time it's requested) or a singleton (.SingleInstance()), depending on how/if validators can or should be shared.
Good luck.
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();