Search code examples
c#nancysimple-injector

How to inject ClaimsPrincipal in Nancy


I have a Nancy module and a decorator class that needs to know the ClaimsPrincipal in order to retrieve the user's email address. So I have declared a constructor as

public EmailDecorator(IDbConnectionFactory connectionFactory,
                      IPrincipal principal,
                      IEmailClient emailClient) : base(connectionFactory)

What I'm struggling to figure out is how to inject the principal into the constructor. I'm using Simple Injector for DI and it has been very effective. But if I override the ConfigureRequestContainer() method, which has as a parameter for the NancyContext, to instantiate IPrincipal I get an exception

protected override void ConfigureRequestContainer(
    TinyIoCContainer container, NancyContext context)
{
    container.Register<IPrincipal>((c, o) => context.CurrentUser); 
    base.ConfigureRequestContainer(container, context);
}

The exception indicates Simple Injector doesn't know about IPrincipal which is registered in the default TinyIOC container.

The configuration is invalid. Creating the instance for type InvoiceModule failed. The constructor of type EmailDecorator contains the parameter with name 'principal' and type IPrincipal that is not registered. Please ensure IPrincipal is registered, or change the constructor of EmailDecorator.

Edit

I've spent way too long trying to resolve this and I suspect the answer is to stop trying to do it this way. In my original example, I'm trying to inject IPrincipal into the decorator constructor which is incorrect. Instead I need to inject some form of service that allows me to derive IPrincipal when one of the methods in the decorator is called, e.g. Func<IPrincipal>. Nancy provides an override-able method ConfigureRequestContainer() that is called for each request, so could potentially be used:

protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
{
    container.Register<Func<IPrincipal>>((c, o) =>
    {
        return () => context.CurrentUser;
    });
}

Except that the request container is a TinyIoCContainer and I'm using SimpleInjector. So, maybe I can add a SimpleInjector registration that offloads resolution to the request container?

_container.RegisterSingleton<Func<IPrincipal>>(() => nancy.Resolve<Func<IPrincipal>>());

Actually no, nancy is not the same container as the request container. So maybe somewhere there is a container that can successfully resolve IPrincipal but I do not know how to access it. The objective of the exercise was to avoid having to modify the method signatures of the repo code to include the ClaimsPrincipal, kind of like the old days when it was possible to call Thread.CurrentPrincipal. Still looking for a clue but will start modifying the method signature to include a ClaimsPrincipal.


Solution

  • The answer is to use IHttpContextAccessor. In Startup.cs add a new service definition in the ConfigureServices() method:

    public void ConfigureServices(IServiceCollection services)
    {           
        services.AddSingleton<IAppSettings>(settings);
        services.AddSingleton<IHttpContextAccessor>(new HttpContextAccessor());
        …
     }
    

    Then pass app.ApplicationServices as a parameter to the bootstrapper:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAppSettings settings)
    {
        var log = ConfigureLogger(settings);
        app.UseOwin(buildFunc => 
        { 
            buildFunc.UseMonitoringAndLogging(log, HealthCheckAsync);
            buildFunc.UseNancy(cfg => 
                cfg.Bootstrapper = new Bootstrapper(env, log, app.ApplicationServices));
        });
    }
    

    In the Bootstrapper class ApplicationStartup() method, register the service in the Simple Injector container:

        public Bootstrapper(IHostingEnvironment env, ILogger log, IServiceProvider services)
        {
            _env = env;
            _log = log;
            _services = services;
        }
    
        …
    
        _container.Register<IHttpContextAccessor>(() =>
            (IHttpContextAccessor) _services.GetService(typeof(IHttpContextAccessor))); 
    

    Then in the decorator class, add IHttpContextAccessor to the constructor:

    public EmailDecorator(IDbConnectionFactory connectionFactory,
                          ILogger log,
                          IHttpContextAccessor httpContextAccessor,
                          IEmailClient emailClient) : base(connectionFactory, log)
    {
        _emailClient = emailClient;
        _httpContextAccessor = httpContextAccessor;
    }
    

    The ClaimsPrincipal can be accessed from the _httpContextAccessor.HttpContext property.