Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inject application service into AuthenticationHandler

I have a custom AuthenticationHandler<> implementation that depends on application service. Is there a way to resolve dependencies of AuthenticationHandler from Simple Injector? Or maybe cross-wire registration so that applications services can be resolved from IServiceCollection?

Sample implementation can look as follows for simplicity:

public class AuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    private readonly ITokenDecryptor tokenDecryptor;

    public SecurityTokenAuthHandler(ITokenDecryptor tokenDecryptor,
        IOptionsMonitor<AuthenticationSchemeOptions> options, 
        ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) :
        base(options, logger, encoder, clock) =>
        this.tokenDecryptor = tokenDecryptor;

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync() =>
        return tokenDecryptor.Decrypt(this);
}

...

services.AddAuthentication("Scheme")
    .AddScheme<AuthenticationSchemeOptions, AuthHandler>("Scheme", options => { });

Current solution is to manually cross-wire application service which is not quite convenient:

services.AddTransient(provider => container.GetInstance<ITokenDecryptor>());
like image 537
Andrii Litvinov Avatar asked Dec 25 '18 12:12

Andrii Litvinov


2 Answers

Tao's answer is right. The easiest way to implement this is to cross wire the AuthHandler to Simple Injector.

This can be done as follows:

// Your original configuration:
services.AddAuthentication("Scheme")
    .AddScheme<AuthenticationSchemeOptions, AuthHandler>("Scheme", options => { });

// Cross wire AuthHandler; let Simple Injector create AuthHandler.
// Note: this must be done after the call to AddScheme. Otherwise it will
// be overridden by ASP.NET.
services.AddTransient(c => container.GetInstance<AuthHandler>());

// Register the handler with its dependencies (in your case ITokenDecryptor) with 
// Simple Injector
container.Register<AuthHandler>();
container.Register<ITokenDecryptor, MyAwesomeTokenDecryptor>(Lifestyle.Singleton);
like image 78
Steven Avatar answered Oct 13 '22 19:10

Steven


maybe cross-wire registration so that applications services can be resolved from IServiceCollection?

No, it is impossible for .Net Core to resolve service from Simple Injector automatically.

Cross-wiring is a one-way process. By using AutoCrossWireAspNetComponents, ASP.NET’s configuration system will not automatically resolve its missing dependencies from Simple Injector. When an application component, composed by Simple Injector, needs to be injected into a framework or third-party component, this has to be set up manually by adding a ServiceDescriptor to the IServiceCollection that requests the dependency from Simple Injector. This practice however should be quite rare.

Reference:Cross-wiring ASP.NET and third-party services.

As the suggestion from above, you need to register the service in IServiceCollection. Which you currently has implemented.

like image 39
Edward Avatar answered Oct 13 '22 20:10

Edward