Openiddict-core: Use alternate DI framework to vNext?

Created on 20 Jan 2016  路  10Comments  路  Source: openiddict/openiddict-core

I have setup a website that uses OpenIddict and Angular based on the Mvc.Server sample. If i use the vNext DI then it all works fine. However, i need to use SimpleInjector.

Is this possible with OpenIddict because everywhere i look it seems to be tied into vnext DI?

I have tried to override everything, but doesnt seem to work. I have followed the code to actually create a OpenIddictProvider, which uses context.HttpContext.RequestServices which seems to always be the vNext DI. Am i missing something??

Here is my startup.cs

using System.Linq;
using CryptoHelper;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.HttpOverrides;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Data.Entity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Mvc.Angular.Models;
using Mvc.Angular.Services;
using OpenIddict;
using OpenIddict.Models;
using SimpleInjector;
using SimpleInjector.Integration.AspNet;
using Microsoft.AspNet.Mvc.Controllers;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using System.Threading;
using Microsoft.AspNet.Identity;
using Microsoft.Extensions.Options;
using System.Collections.Generic;

namespace Mvc.Angular {
    public class Startup {

        public static void Main(string[] args) {
            var application = new WebApplicationBuilder()
                .UseConfiguration(WebApplicationConfiguration.GetDefault(args))
                .UseStartup<Startup>()
                .Build();

            application.Run();
        }

        private IConfigurationRoot _configuration;
        private Container _container = new Container();

        public void ConfigureServices(IServiceCollection services) {
            _configuration = new ConfigurationBuilder()
                .AddJsonFile("config.json")
                .AddEnvironmentVariables()
                .Build();

            services.AddMvc();

            services.AddEntityFramework()
                .AddSqlServer()
                .AddDbContext<ApplicationDbContext>(options =>
                    options.UseSqlServer(_configuration["Data:DefaultConnection:ConnectionString"]));

            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders()
                .AddOpenIddict();

            // TO USE VNEXT DI, UNCOMMENT HERE
            //services.AddTransient<IEmailSender, AuthMessageSender>();
            //services.AddTransient<ISmsSender, AuthMessageSender>();


            // TO USE SIMPLE INJECTOR, UNCOMMENT THIS, AND UNCOMMENT USESIMPLEINJECTOR CALL BELOW

            // So we can use Simple Injector to inject our controllers and other services, but
            // let ASP.NET resolve all system dependencies
            services.AddScoped<IControllerActivator>(e => new SimpleInjectorControllerActivator(_container));

            // Work around for a Identity Framework bug inside the SignInManager<T> class.
            services.Add(ServiceDescriptor.Scoped<IHttpContextAccessor>(e => new NeverNullHttpContextAccessor()));
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            var factory = app.ApplicationServices.GetRequiredService<ILoggerFactory>();
            factory.AddConsole();
            factory.AddDebug();

            app.UseDeveloperExceptionPage();

            app.UseIISPlatformHandler(options => {
                options.AuthenticationDescriptions.Clear();
                options.FlowWindowsAuthentication = false;
            });

            app.UseOverrideHeaders(options => {
                options.ForwardedOptions = ForwardedHeaders.All;
            });

            app.UseStaticFiles();

            // Add a middleware used to validate access
            // tokens and protect the API endpoints.
            app.UseOAuthValidation();

            // comment this out and you get an error saying 
            // InvalidOperationException: No authentication handler is configured to handle the scheme: Microsoft.AspNet.Identity.External
            app.UseIdentity();

            // Note: OpenIddict must be added after
            // ASP.NET Identity and the external providers.
            app.UseOpenIddict(options =>
            {
                // development
                options.Options.AllowInsecureHttp = true;
            });

            app.UseMvcWithDefaultRoute();

            using (var context = new ApplicationDbContext(_configuration["Data:DefaultConnection:ConnectionString"]))
            {
                context.Database.EnsureCreated();

                // Add Mvc.Client to the known applications.
                if (!context.Applications.Any()) {
                    // Note: when using the introspection middleware, your resource server
                    // MUST be registered as an OAuth2 client and have valid credentials.
                    // 
                    // context.Applications.Add(new Application {
                    //     Id = "resource_server",
                    //     DisplayName = "Main resource server",
                    //     Secret = "875sqd4s5d748z78z7ds1ff8zz8814ff88ed8ea4z4zzd"
                    // });

                    context.Applications.Add(new Application {
                        Id = "myClient",
                        DisplayName = "My client application",
                        RedirectUri = "http://localhost:54540/signin-oidc",
                        LogoutRedirectUri = "http://localhost:54540/",
                        Type = OpenIddictConstants.ApplicationTypes.Public
                    });

                    context.SaveChanges();
                }
            }
        }


        public void UseSimpleInjector(IApplicationBuilder app)
        {
            _container.Options.DefaultScopedLifestyle = new AspNetRequestLifestyle();

            app.UseSimpleInjectorAspNetRequestScoping(_container);

            // IOpenIddictStore
            _container.Register<IOpenIddictStore<ApplicationUser, Application>, OpenIddictStore<ApplicationUser, Application, IdentityRole, ApplicationDbContext, string>>(Lifestyle.Scoped);
            _container.Register(() =>
                new OpenIddictStore<ApplicationUser, Application, IdentityRole, ApplicationDbContext, string>(_container.GetRequiredService<ApplicationDbContext>()), Lifestyle.Scoped);

            // OpenIddicManager
            _container.Register(() => new OpenIddictManager<ApplicationUser, Application>(_container), Lifestyle.Scoped);

            // IUserStore
            var connectionString = _configuration["Data:DefaultConnection:ConnectionString"];
            _container.Register(() => new ApplicationDbContext(connectionString), Lifestyle.Scoped);
            _container.Register<IUserStore<ApplicationUser>>(() => _container.GetRequiredService<IOpenIddictStore<ApplicationUser, Application>>(),
                    Lifestyle.Scoped);

            // UserManager
            _container.Register(() => new OpenIddictManager<ApplicationUser, Application>(_container), Lifestyle.Scoped);
            _container.Register<UserManager<ApplicationUser>, OpenIddictManager<ApplicationUser, Application>>(Lifestyle.Scoped);

            _container.CrossWire<IOptions<IdentityOptions>>(app);
            _container.CrossWire<IPasswordHasher<ApplicationUser>>(app);
            _container.CrossWire<IEnumerable<IUserValidator<ApplicationUser>>>(app);
            _container.CrossWire<IEnumerable<IPasswordValidator<ApplicationUser>>>(app);
            _container.CrossWire<ILookupNormalizer>(app);
            _container.CrossWire<IdentityErrorDescriber>(app);
            _container.CrossWire<ILogger<UserManager<ApplicationUser>>>(app);
            _container.CrossWire<IHttpContextAccessor>(app);

            // RoleStore
            _container.CrossWire<IEnumerable<IRoleValidator<IdentityRole>>>(app);
            _container.Register<IRoleStore<IdentityRole>>(() => new RoleStore<IdentityRole>(_container.GetRequiredService<ApplicationDbContext>()), Lifestyle.Scoped);

            // RoleManager
            _container.CrossWire<ILogger<RoleManager<IdentityRole>>>(app);
            _container.Register(() => new RoleManager<IdentityRole>(_container.GetRequiredService<IRoleStore<IdentityRole>>(),
                _container.GetRequiredService<IEnumerable<IRoleValidator<IdentityRole>>>(),
                _container.GetRequiredService<ILookupNormalizer>(),
                _container.GetRequiredService<IdentityErrorDescriber>(),
                _container.GetRequiredService<ILogger<RoleManager<IdentityRole>>>(),
                _container.GetRequiredService<IHttpContextAccessor>()), Lifestyle.Scoped);

            // IUserClaimsPrincipalFactory
            _container.CrossWire<DataProtectorTokenProvider<ApplicationUser>>(app);
            _container.Register<IUserClaimsPrincipalFactory<ApplicationUser>>(() => new UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>(_container.GetRequiredService<UserManager<ApplicationUser>>(),
                _container.GetRequiredService<RoleManager<IdentityRole>>(), _container.GetRequiredService<IOptions<IdentityOptions>>()), Lifestyle.Scoped);

            // SignInManager
            _container.CrossWire<ILogger<SignInManager<ApplicationUser>>>(app);
            _container.Register(() => new SignInManager<ApplicationUser>(_container.GetRequiredService<UserManager<ApplicationUser>>(),
                _container.GetRequiredService<IHttpContextAccessor>(),
                _container.GetRequiredService<IUserClaimsPrincipalFactory<ApplicationUser>>(),
                _container.GetRequiredService<IOptions<IdentityOptions>>(),
                _container.GetRequiredService<ILogger<SignInManager<ApplicationUser>>>()), Lifestyle.Scoped);

            // Senders
            _container.Register<IEmailSender, AuthMessageSender>();
            _container.Register<ISmsSender, AuthMessageSender>();

            // controllers
            _container.RegisterAspNetControllers(app);

            // verify
            _container.Verify();
        }

        private sealed class NeverNullHttpContextAccessor : IHttpContextAccessor
        {
            private readonly AsyncLocal<HttpContext> context = new AsyncLocal<HttpContext>();

            public HttpContext HttpContext
            {
                get { return this.context.Value ?? new DefaultHttpContext(); }
                set { this.context.Value = value; }
            }
        }

    }
}
invalid question

Most helpful comment

I have been looking at @Gillardo's example more closely now and now understand where things go wrong. Gillardo is trying to move all of Openiddict's registrations into Simple Injector, but this is wrong, because:

  • This is a very cumbersome practice,
  • It can lead to hard to fix problems, since Openiddict classes might not be designed to work well with Simple Injector.
  • Which might break any time a new version of Openiddict is released

Instead, you should always let Openiddict register itself in the default DI system and resolve one of its types when some application component needs to use it. You should refrain from trying to re-register every type of Openiddict into Simple Injector.

There are typically two practices you can apply. Either you 'cross-wire' the required type into Simple Injector -or- you create an adapter to an application-specified abstraction that resolves the OpenID type from the built-in DI container at runtime.

Cross-wiring is the concept of registering a delegate in one container so that it can resolve a type that is built-up by another container. We already see this concept of cross-wiring in Gillardo's code, but unfortunately Gillardo tries to register everything, instead of just cross-wiring that single type that needs to be injected by Simple Injector. So instead, we just want to do something like this:

``` c#
container.RegisterSingleton>>(
() => app.GetRequestService>());


The idea here is that if your application components only requires `DataProtectorTokenProvider<ApplicationUser>` directly, this should be the only cross-wired registration you make. Since you typically have no idea about the lifestyle of such cross-wired service, it's best to register it as a `Func<T>` factory; this way you won't get into any nasty problems like captive dependencies a.k.a. [lifestyle mismatches](https://simpleinjector.readthedocs.io/en/latest/LifestyleMismatches.html). While captive dependencies are typically detected by Simple Injector, this is impossible to detect mismatches with cross-wired dependencies, since the Core DI doesn't contain the same safety nets as Simple Injector does and Simple Injector (nor you) doesn鈥檛 know about the actual lifestyle of the cross-wired component.

The `GetRequestService` method is an extension method that is defined in the Missing-Core-DI-Extensions repository [here](https://github.com/dotnetjunkie/Missing-Core-DI-Extensions/blob/master/src/MissingDIExtensions/AspNetCoreExtensions.cs#L28). The Missing-Core-DI-Extensions is a repository I created to [discuss](https://github.com/aspnet/Mvc/issues/5403) some missing functionality in .NET Core with Microsoft. Hopefully these extension methods will end up in a future version of .NET Core, but for now you can copy paste this into your own application. The `GetRequestService` method preserves the lifestyle of the registered instance, while calling `app.ApplicationServices.GetService` does not (and can cause you major headackes because of that).

Instead of injecting a library type directly into your application components, I usually advice defining an application-specific abstraction. This way your application code stays oblivious to the library. This allows you to define an adapter implementation for this abstraction that wraps the `IApplicationBuilder` and calls `app.GetRequestService` when one of the adapter's methods is called. Such adapter can be registered as follows:

``` c#
container.RegisterSingleton<IMyAppTokenProvider>(new AspNetTokenProvider(app));

Such adapter can be specified as follows:

``` c#
public class AspNetTokenProvider : IMyAppTokenProvider
{
IApplicationBuilder app;
public AspNetTokenProvider(IApplicationBuilder app) { this.app = app; }

public string GetToken() {
    var provider = this.app.GetRequestService<DataProtectorTokenProvider<ApplicationUser>>();
    return provider. // etc
}

}
```

When using this practice, you can completely separate all the framework and library registrations (such as Openiddict鈥檚) from your application registrations in Simple Injector and keep your Simple Injector registrations very clean, with just a few adapters or cross-wired registrations.

All 10 comments

I have tried to override everything, but doesnt seem to work. I have followed the code to actually create a OpenIddictProvider, which uses context.HttpContext.RequestServices which seems to always be the vNext DI. Am i missing something??

If you want to replace the default container offered by ASP.NET Core 1.0 (ex-ASP.NET 5), you need to update your ConfigureServices method to return an IServiceProvider instance calling your SimpleInjector container. Simply replacing specific services like the controller activator won't work (context.HttpContext.RequestServices won't return your own container but the default one).

This is not recommended by SimpleInjector, but i will ask them also and see what they say. Thanks

Curious: why not using the built-in DI system?

Alternatively, you could use a DI provider that fully supports the new abstractions, like the great Autofac.

I was using simpleinjector before going to vnext as it is far faster than autofac. The new aspnet DI i aimt convinced fully yet. I believe i have fixed this problem now as well without returning IServiceProvider and i will post here later encase others need to do it

Plus i want to handle the creation of my services. I will let vnext resolve asp.net services, but not mine. Vnext DI also had limitations last time i checked

Plus i want to handle the creation of my services. I will let vnext resolve asp.net services, but not mine. Vnext DI also had limitations last time i checked

This kind of hybrid thingy is not something we support (nor the rest of ASP.NET Core 1.0 does): if you don't want to use the recommended approach, you're sadly pretty much on your own.

Curious: why not using the built-in DI system?

We (the Simple Injector team) advice against the use of an adapter on top of the new DI system, because this is an implementation of the Conforming Container anti-pattern and because such adapter is highly incompatible with Simple Injector. You can see an interesting discussion about me and Fowler here about why such adapter won't work for Simple Injector.

@Gillardo did you ever find a working solution with OpenIddict and SimpleInjector?

No I didn't. I ended up using the native DI instead

I have been looking at @Gillardo's example more closely now and now understand where things go wrong. Gillardo is trying to move all of Openiddict's registrations into Simple Injector, but this is wrong, because:

  • This is a very cumbersome practice,
  • It can lead to hard to fix problems, since Openiddict classes might not be designed to work well with Simple Injector.
  • Which might break any time a new version of Openiddict is released

Instead, you should always let Openiddict register itself in the default DI system and resolve one of its types when some application component needs to use it. You should refrain from trying to re-register every type of Openiddict into Simple Injector.

There are typically two practices you can apply. Either you 'cross-wire' the required type into Simple Injector -or- you create an adapter to an application-specified abstraction that resolves the OpenID type from the built-in DI container at runtime.

Cross-wiring is the concept of registering a delegate in one container so that it can resolve a type that is built-up by another container. We already see this concept of cross-wiring in Gillardo's code, but unfortunately Gillardo tries to register everything, instead of just cross-wiring that single type that needs to be injected by Simple Injector. So instead, we just want to do something like this:

``` c#
container.RegisterSingleton>>(
() => app.GetRequestService>());


The idea here is that if your application components only requires `DataProtectorTokenProvider<ApplicationUser>` directly, this should be the only cross-wired registration you make. Since you typically have no idea about the lifestyle of such cross-wired service, it's best to register it as a `Func<T>` factory; this way you won't get into any nasty problems like captive dependencies a.k.a. [lifestyle mismatches](https://simpleinjector.readthedocs.io/en/latest/LifestyleMismatches.html). While captive dependencies are typically detected by Simple Injector, this is impossible to detect mismatches with cross-wired dependencies, since the Core DI doesn't contain the same safety nets as Simple Injector does and Simple Injector (nor you) doesn鈥檛 know about the actual lifestyle of the cross-wired component.

The `GetRequestService` method is an extension method that is defined in the Missing-Core-DI-Extensions repository [here](https://github.com/dotnetjunkie/Missing-Core-DI-Extensions/blob/master/src/MissingDIExtensions/AspNetCoreExtensions.cs#L28). The Missing-Core-DI-Extensions is a repository I created to [discuss](https://github.com/aspnet/Mvc/issues/5403) some missing functionality in .NET Core with Microsoft. Hopefully these extension methods will end up in a future version of .NET Core, but for now you can copy paste this into your own application. The `GetRequestService` method preserves the lifestyle of the registered instance, while calling `app.ApplicationServices.GetService` does not (and can cause you major headackes because of that).

Instead of injecting a library type directly into your application components, I usually advice defining an application-specific abstraction. This way your application code stays oblivious to the library. This allows you to define an adapter implementation for this abstraction that wraps the `IApplicationBuilder` and calls `app.GetRequestService` when one of the adapter's methods is called. Such adapter can be registered as follows:

``` c#
container.RegisterSingleton<IMyAppTokenProvider>(new AspNetTokenProvider(app));

Such adapter can be specified as follows:

``` c#
public class AspNetTokenProvider : IMyAppTokenProvider
{
IApplicationBuilder app;
public AspNetTokenProvider(IApplicationBuilder app) { this.app = app; }

public string GetToken() {
    var provider = this.app.GetRequestService<DataProtectorTokenProvider<ApplicationUser>>();
    return provider. // etc
}

}
```

When using this practice, you can completely separate all the framework and library registrations (such as Openiddict鈥檚) from your application registrations in Simple Injector and keep your Simple Injector registrations very clean, with just a few adapters or cross-wired registrations.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

levitatejay picture levitatejay  路  3Comments

AlphaCreativeDev picture AlphaCreativeDev  路  3Comments

masakura picture masakura  路  3Comments

ivanmariychuk picture ivanmariychuk  路  6Comments

jedielson picture jedielson  路  5Comments