How to inject application service into AuthenticationHandler - c#

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

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

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.

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

Related

How to add service dependancies in ASP.NET CORE API

I am new to ASP.NET CORE API so please bare with my limited understand. I am learning about dependancy injection and I am trying to register my services in the start up class. However I noticed that one of my services has an dependancy to another service via constructor. How do I pass this within the configure services method.
Do I just 'new up' a the dependancy class and add it within the constructor.
Here is my implementation in the start up class:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "TestAPI", Version = "v2" });
});
services.AddSingleton<IAdminRepository>(new AdminRepository());
services.AddSingleton<IAdminService>(new AdminService(need to add dependancy here));
}
Here is implementation in my service class:
private IAdminRepository _adminRepository;
public AdminService(IAdminRepository adminRepository)
{
_adminRepository = adminRepository;
}
You don't need to add the dependency explicitly, you just have to register your service with
services.AddSingleton<IAdminService, AdminService>();
The dependency injection framework will automatically detect the required services for your AdminService.
You don't need to add the dependency explicitly, add a theme like this pattern.
services.AddSingleton<IAdminRepository, AdminRepository>();
services.AddSingleton<IAdminService, AdminService>();
This way, the DI framework will do it wherever needed to create a new instance.
moreover, you can use Addscope or Addtransient if you need it, for example:
services.Addscope<IAdminRepository, AdminRepository>();
services.Addscope<IAdminService, AdminService>();

Asp.Net Core Dependency Injection ValidateOnBuild not works properly

I have a project in .NET 5 with RazorPages, I set this code to validate the Dependecy Injection in the Progam.cs file:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseDefaultServiceProvider(options =>
{
options.ValidateOnBuild = true;
options.ValidateScopes = true;
})....
I forgot to register the service that is injected into my page, so I would have expected that when I try to start the app an error page would show this kind of problem, but I don't understand why it doesn't happen, because for example in case I don't register ILocalizerService this happens:
This is my RazorPage:
public class SignupModel : IdentityPageModel
{
[BindProperty]
public Models.Account.Signup Signup { get; set; }
private readonly CustomUserManager _userManager;
private readonly ILogger<SignupModel> _logger;
private readonly INcsService _ncsService;
public SignupModel(CustomUserManager userManager,
ILogger<SignupModel> logger,
INcsService ncsService) : base(localizerService)
{
Guard.Against.Null(userManager, nameof(userManager));
Guard.Against.Null(logger, nameof(logger));
Guard.Against.Null(ncsService, nameof(ncsService));
_userManager = userManager;
_logger = logger;
_ncsService = ncsService;
}
// Other code....
}
This is my service:
[PublicAPI]
public class NcsService : INcsService
{
private readonly IHttpClientFactory _httpClientFactory;
public NcsService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
// Other code...
}
I have only registered IHttpClientFactory but not INcsService interface and implementation:
services.AddHttpClient(nameof(NcsService), client =>
{
client.BaseAddress = new Uri(ncsSettings.BaseUri);
client.DefaultRequestHeaders.Add("x-functions-key", ncsSettings.ApiKey);
client.DefaultRequestHeaders.Add("x-app-name", "TSID");
}).AddHeaderPropagation(options =>
{
options.Headers.Add("x-request-id");
options.Headers.Add("x-correlation-id");
})
.AddPolicyHandler(GetRetryPolicy());
I hope I was clear.
Thank you
The root of the issue is Microsoft's default IComponentActivator implementation (the DefaultComponentActivator). The Component Activator is in control of creating your Razor Pages, but the built-in behavior does not request those pages from the built-in container. Instead, it just creates them using Activator.CreateInstance.
This means that Blazor does not register your pages in the built-in container and because of that, the page will not be part of the container's verification process.
This is, IMO, a design flaw in Blazor, because it well known, and well understood that, in case you are using a DI Container, you should let all your application components go through the container pipeline. That's the only way that the container can give you a reasonable amount of certainty about the validity of your application components.
Blazor, however, is not the only part of the ASP.NET Core framework where this happens. ASP.NET MVC Controllers, for instance, by default aren't registered in the container, and aren't resolved from the container. This is configurable though, but since this is not the default behavior, the ValidateOnBuild gives a false sense of security.
Other containers might have a more sensible default. Simple Injector, for instance, (the container that I maintain) contains extension methods that always register all MVC controllers up front. And with the Blazor integration, similar things happen.
If you stick with the built-in container, it would be good to ensure all components are resolved from the container. With MVC this is easy, because you can simply call AddControllersAsServices. With Blazor, unfortunately, this is much more difficult, because there exists no such method as AddComponentsAsServices. This means that you have to create a custom IComponentActivator that calls back into the container. But still, you'll likely have to fallback to the original behavior using Activator.CreateInstance for all Blazor Components that are created by Microsoft, because it might be much harder to find and register them using reflection. For inspiration on how to create such custom Component Activator and register your application Blazor components, take a look at the code presented here.

Net Core 3 - Accessing DBContext outside of the web api

i'm building a small webapi to work in conjunction with additional functionalities running in the background.
In the specific case I have a class called TelegramBot:
public class TelegramBot
{
static ITelegramBotClient botClient;
private readonly BotManagerContext _botManagerContext;
public TelegramBot(BotManagerContext botManagerContext)
{
_botManagerContext = botManagerContext;
botClient = new TelegramBotClient("xxxx:yyyyy");
botClient.OnMessage += Bot_OnMessage;
botClient.StartReceiving();
}
That I'm trying to run together with the web api. BotManagerContext is a DbContext initialized in the web api, i'm trying to retrieve it using dependency injection - so i'm trying to add the TelegramBot class into the Startup.cs file so that it starts as a Singleton and can retrieve the dbcontext
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<BotManagerContext>(opt =>
opt.UseSqlite("Data Source=BotManager.db"));
services.AddControllers();
services.AddSingleton<TelegramBot>();
}
Question is - how do I implement this? using an interface? I'm fairly new to this and I don't know how to implement it :)
Thanks
Implementing your own IHostedService would be the best way to go about this. For getting the dbcontext in the service you can use IserviceProvider as your dependency. Serviceprovider will give you the dbcontext.
You can configure your custom hosted service to be added as a singleton then. Check this documentation for details:
https://learn.microsoft.com/en-us/dotnet/architecture/microservices/multi-container-microservice-net-applications/background-tasks-with-ihostedservice#implementing-ihostedservice-with-a-custom-hosted-service-class-deriving-from-the-backgroundservice-base-class

How to pass dependencies to a custom .NET Core ILoggerProvider

I am creating a custom .NET Core ILoggerProvider that requires some dependencies to be passed into its constructor.
I believe I am using a fairly common pattern to initialize my logging implementation; it looks something like this:
var services = new ServiceCollection();
// Register some services here
services.AddLogging(builder =>
{
builder.AddProvider(new DebugLoggerProvider());
});
var provider = services.BuildServiceProvider();
I want to add my new provider within the AddLogging block, in the same way that the DebugLoggerProvider is currently added.
My custom provider requires some other services to be passed into its constructor and since these are already registered with the ServiceCollection, I assume that I should be able to reference them. However, unlike methods such as AddSingleton, which have an overload that exposes the IServiceProvider, AddLogging doesn't seem to offer an equivalent.
Is there a simple way to achieve this, or am I attempting to do something that contradicts the way .NET Core logging was designed to be deployed?
UPDATE:
After experimenting with the suggestions proposed by #Nkosi, I can confirm that it is possible to get this to work by bypassing AddLogging and directly implementing what it does internally, as follows:
var services = new ServiceCollection();
// Register some services
services.AddSingleton<IMyService, MyService>();
// Initialize logging
services.AddOptions();
services.AddSingleton<ILoggerFactory, LoggerFactory>();
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
services.AddSingleton<ILoggerProvider>(p => new DebugLoggerProvider());
services.AddSingleton<ILoggerProvider>(p => new MyLoggerProvider("Constant value", p.GetService<IMyService>()));
var provider = services.BuildServiceProvider();
Now I am not sure if an extension already exists to do this but I see potential here.
First this is how AddProvider is defined in the source code repo.
public static ILoggingBuilder AddProvider(this ILoggingBuilder builder, ILoggerProvider provider) {
builder.Services.AddSingleton(provider);
return builder;
}
You could build up on that by making your own generic version
public static class MyLoggingBuilderExtensions {
public static ILoggingBuilder AddProvider<T>(this ILoggingBuilder builder)
where T: class, ILoggerProvider{
builder.Services.AddSingleton<ILoggerProvider, T>();
return builder;
}
}
which should allow the DI container to build up the object graph when resolved
services.AddLogging(builder =>
{
builder.AddProvider<CustomLoggerProvider>();
});
And there is room to extend this functionality, like adding your own overload that exposes the IServiceProvider and passing that on to the AddSingleton within the extension.
public static ILoggingBuilder AddProvider<T>(this ILoggingBuilder builder, Func<IServiceProvider, T> factory)
where T: class, ILoggerProvider {
builder.Services.AddSingleton<ILoggerProvider, T>(factory);
return builder;
}
And used
services.AddLogging(builder => {
builder.AddProvider<CustomLoggerProvider>(p => new CustomLoggerProvider("Constant value", p.GetService<IMyService>()));
});
Apologies for being a bit late to the party on this one, but I ran into exactly the same problem after having searched high and low. Inspired by the excellent entries in this page, I ended up with the solution below.
services.AddTransient<IMyLogRepository, LogRepository>();
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole()
.AddDbLoggerProvider(services.BuildServiceProvider().GetService<IMyLogRepository>());
});
services.AddSingleton(loggerFactory.CreateLogger("MyLogging"));
The key to this being:
services.BuildServiceProvider().GetService<IMyLogRepository>())
Which allowed me to link my database repository to the dbLogger object I created in a single extra line. In essence, it gives me the ability to pluck my DI database object an send it to the Logging service via standard ILoggerProvider and ILogger interfaces
I got a simple solution to work which is kinda lighter.
serviceCollection.AddLogging(logBuilder =>
{
logBuilder.AddConfiguration(theConfigRoot.GetSection("Logging"));
});
serviceCollection.AddSingleton<ILoggerProvider, MyLogProvider>();
However.... Instanciating the Provider keeps you from running in circular dependency problems--> The service you may want to inject soon want´s a logger himself^^

ASP.NET Core MvcJsonOptions dependency injection without modified closure?

(This question is similar to ASP.NET Core MvcOptions dependency injection without modified closure? However, I struggle to apply the solution of that question to this one.)
In my ASP.NET Core 1.1.3 project, I currently inject an ITraceWriter dependency into MvcJsonOptions in the ConfigureServices method in the following way:
public override IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddProjectSpecificStuff();
ITraceWriter traceWriter = null;
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.TraceWriter = traceWriter;
});
var provider = base.ConfigureServices(services);
traceWriter = provider.GetService<ITraceWriter>();
return provider;
}
This works, but causes code analyzers such as ReSharper to complain about access to modified closure.
Is there an alternative to achieve the same dependency injection without using modified closure?
You can likely postpone the assignment to .Configure method, it should work.
public override IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddProjectSpecificStuff();
ITraceWriter traceWriter = null;
services.AddMvc().AddJsonOptions(options =>
{
...
// other options here
});
return base.ConfigureServices(services);
}
public void Configure(IApplicationBuilder app, ITraceWriter traceWriter, IOptions<MvcJsonOptions> jsonOptions)
{
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.TraceWriter = provider.GetService<ITraceWriter>();
});
}
This will work for as long as your dependencies (like IOptions<MvcJsonOptions> and ITraceWriter are singletons.
Word of warning
However, this behavior may change in future, as some plans/ideas were on the ASP.NET Core GitHub issues, that the ASP.NET Core may change this to create an implicitly scoped context which is used during .Configure, it may or may not break the above code (if it ever comes).
But you may want consider to directly instantiate the ITraceWriter and pass it as instance to the IoC container, such as
ITraceWriter traceWriter = new TraceWriter(/* other dependencies */);
services.AddSingleton<ITraceWriter>(traceWriter);
It's okay to compose it in the composition root, as long as it's a singleton.
Or alternatively you choose this brute and somewhat ugly solution and instantiate MvcJsonOptions via factory method:
services.AddSingleton<IOptions<MvcJsonOptions>>(provider => Options.Create(new MvcJsonOptions
{
SerializerSettings.TraceWriter = provider.GetService<ITraceWriter>()
}));
Then it's pretty ugly (though you could hide that behind an extension method) and you may override defaults set by the mddleware/extension method.

Categories

Resources