Autofac, Owin, Webapi and injection in AuthorizationServerProvider

After reading questions and articles about using autofac with owin and webapi, I came across a solution for deploying services, but this will not work. Here is my code:

public class StartUp { public void Configuration(IAppBuilder app) { HttpConfiguration config = new HttpConfiguration(); WebApiConfig.Register(config); var builder = new ContainerBuilder(); // Create the container builder. builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); // Register the Web API controllers. var authcontext = new AuthContext(); builder.RegisterInstance(authcontext).AsSelf().SingleInstance(); //Updated //var simpleauth = new SimpleAuthorizationServerProvider(); //Updated // builder.RegisterInstance(simpleauth).SingleInstance().AsSelf().PropertiesAutowired(); builder.Register(x => new UserStore<IdentityUser>(authcontext)).As<IUserStore<IdentityUser>>(); //updated builder.Register(x => { var p = new SimpleAuthorizationServerProvider(); var userStore = x.Resolve<IUserStore<IdentityUser>>(); p.userManager = new UserManager<IdentityUser>(userStore); return p; }).AsSelf().PropertiesAutowired(); builder.RegisterType<AuthRepository>().As<IAuthRepository>().InstancePerRequest().PropertiesAutowired(); var container = builder.Build(); var resolver = new AutofacWebApiDependencyResolver(container); // Create an assign a dependency resolver for Web API to use. config.DependencyResolver = resolver; app.UseAutofacMiddleware(container); app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); app.UseWebApi(config); ConfigureOAuth(app, resolver); } public void ConfigureOAuth(IAppBuilder app, AutofacWebApiDependencyResolver resolver) { OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() { AllowInsecureHttp = true, TokenEndpointPath = new PathString("/token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), //updated Provider = new SimpleAuthorizationServerProvider() //resolver.GetService(typeof(SimpleAuthorizationServerProvider)) as SimpleAuthorizationServerProvider }; // Token Generation app.UseOAuthAuthorizationServer(OAuthServerOptions); app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); } } 

But in the SimpleAuthorizationServerProvider class, when a method is launched, for example ValidateClientAuthentication, all services are zero, here is the code:

  public readonly IAuthRepository repository; public readonly UserManager<IdentityUser> userManager; public readonly AuthContext dbContext; public SimpleAuthorizationServerProvider() { } public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { string clientId; string clientSecret; if (context.TryGetFormCredentials(out clientId, out clientSecret)) { try { Client client = await repository.FindClientById(clientId); } } } 

could you help me?

Update

If the ConfigureOAuth method uses the following approach:

  OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() { AllowInsecureHttp = true, TokenEndpointPath = new PathString("/token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), Provider = resolver.GetService(typeof(SimpleAuthorizationServerProvider)) as SimpleAuthorizationServerProvider }; 

I get an error:

 An exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll but was not handled in user code Additional information: 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. 
+5
source share
1 answer

When you register an instance of an object, not a type, even if you specify PropertiesAutowired , which does not take effect, since Autofac assumes that you have completed all the work you want when creating the instance. If you want properties to be connected, you need to do this in the OnActivated handler.

There are many things in this code example that will not work.

  • The values ​​in SimpleAuthorizationServerProvider are not property fields , so PropertiesAutowired will not work on them.
  • Fields are marked as readonly, and they are never set.
  • Your UserManager<IdentityUser> registered as a lambda, but there is also a PropertiesAutowired that will not work - you can only use PropertiesAutowired for a reflection-based component (e.g. RegisterType<T> ).

Consider registering a lambda for your provider and install everything in a lambda:

 builder.Register(c => { var p = new SimpleAuthorizationServerProvider(); p.repository = c.Resolve<UserManager<IdentityUser>>(); // ...and so on return p; }).AsSelf().SingleInstance(); 

Also, keep in mind that if you register an instance (or register something as SingleInstance , then the properties will be resolved once and that is so. Therefore, if you have some dependencies InstancePerDependency or InstancePerRequest , this is not going to work like that, what do you think - they will be solved once and effectively become singles after that.


Update 1

Based on the source and updated code, it occurs to me that it would be nice if you could check out some of the Autofac docs to better understand how this works. For example, using a field in SimpleAuthorizationServerProvider shows that you cannot fully understand how injection works in Autofac or how to register things correctly so that Autofac can do your job.

For example, viewing updates ...

  • Now you have the lambda registered for SimpleAuthorizationServerProvider , but I do not see where you set the repository field there.
  • You do not need PropertiesAutowired in registering SimpleAuthorizationServerProvider , because you are registering lambda and the properties will not be auto-updated (as noted earlier).
  • The only component that I see as registered InstancePerRequest is AuthRepository , but, as I said, I don’t see where it is allowed or set, and that the only thing that would throw an exception that you noted. There is a FAQ regarding this exact exception that you should study.

In addition, you are showing two different versions of OAuthServerOptions that are being initialized, and it is difficult to determine which one is β€œreal”.

I would recommend fairly large refactoring to allow actions to use DI correctly.

Modify SimpleAuthorizationServerProvider to stop using public fields and add them as constructor parameters so that Autofac can connect them to you.

 public class SimpleAuthorizationServerProvider { public IAuthRepository Repository { get; private set; } public UserManager<IdentityUser> UserManager {get; private set; } public AuthContext Context { get; private set; } public SimpleAuthorizationServerProvider( IAuthRepository repository, UserManager<IdentityUser> userManager, AuthContext context) { this.Repository = repository; this.UserManager = userManager; this.AuthContext = context; } } 

At startup, correct your login information to remove foreign objects and take advantage of Autofac auto-wiring.

 public class StartUp { public void Configuration(IAppBuilder app) { var config = new HttpConfiguration(); WebApiConfig.Register(config); var builder = new ContainerBuilder(); builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); // Register the auth context instance but skip // the extra .AsSelf() and .SingleInstance() because // it implicit. builder.RegisterInstance(new AuthContext()); // Use the lambda to resolve the auth context rather // than making a closure over an instance. builder.Register(c => new UserStore<IdentityUser>(c.Resolve<AuthContext>())) .As<IUserStore<IdentityUser>>(); // Just register the provider type and let Autofac // do the work without all this manual stuff. Skip // the .AsSelf() because it implicit if you don't // specify other interfaces and don't auto-wire properties // because you don't need it. builder.RegisterType<SimpleAuthorizationProvider>(); // This is fine, but I can't tell where it used - if // you are using it at app startup or OUTSIDE a request, // you will get that exception you noted. Also, unless // you're actually using property injection, lose the // .PropertiesAutowired() call. builder.RegisterType<AuthRepository>() .As<IAuthRepository>() .InstancePerRequest() .PropertiesAutowired(); var container = builder.Build(); var resolver = new AutofacWebApiDependencyResolver(container); config.DependencyResolver = resolver; app.UseAutofacMiddleware(container); app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); app.UseWebApi(config); ConfigureOAuth(app, resolver); } public void ConfigureOAuth(IAppBuilder app, AutofacWebApiDependencyResolver resolver) { var options = new OAuthAuthorizationServerOptions() { AllowInsecureHttp = true, TokenEndpointPath = new PathString("/token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), // If you want the values to be wired up, you have // to do a resolve. Note, however, that since you're // doing this wire-up at app startup, there no request // scope, so if something in here is registered `InstancePerRequest` // you will get an exception. Provider = resolver.GetService(typeof(SimpleAuthorizationServerProvider)) as SimpleAuthorizationServerProvider }; app.UseOAuthAuthorizationServer(options); app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); } } 

Assuming all your code should be in order. If everything is not set - as one of the SimpleAuthorizationServerProvider properties is null, or if you get an exception because it is missing a dependency, or if you get an exception because there is no request scope ... then something else happens that you are not asked your question.

Again, please take the time to check the documents and familiarize yourself with Autofac. I think that many of the problems you face are the result of some misunderstanding as to how things connect.

+9
source

Source: https://habr.com/ru/post/1205707/


All Articles