Andrii Litvinov
Andrii Litvinov

Reputation: 13192

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>());

Upvotes: 6

Views: 2596

Answers (3)

jmzagorski
jmzagorski

Reputation: 1155

I was using .Net 5 and the original provided by Steven no longer worked for me after I upgraded from my older .net core app. I had to:

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddAuthentication("MyScheme")
        .AddScheme<ApiKeyOptions, ApiKeyAuthenticationHandler>("MyScheme", o => { });

    var authHandlerDescriptor = services
        .First(s => s.ImplementationType == typeof(ApiKeyAuthenticationHandler));

    services.Remove(authHandlerDescriptor);

    services.AddTransient(c => container.GetInstance<ApiKeyAuthenticationHandler>());
}

Upvotes: 3

Steven
Steven

Reputation: 172835

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);

Upvotes: 6

Edward
Edward

Reputation: 30046

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.

Upvotes: 3

Related Questions