Skip to content

Support of multiple schemas and IApiKeyProvider #12

Closed
@maartenmensink

Description

@maartenmensink

Hi,

Thanks for creating this library. I was testing the library and came across the following.

I am trying to add a second schema but due to a registration issue in the ApiKeyExtension the second provider overrides the first provider.

services.AddAuthentication(ApiKeyDefaults.AuthenticationScheme)
	.AddApiKeyInHeader<ProviderOne>(options =>
	{
		options.Realm = "Realm 1";
		options.KeyName = "X-API-KEY";
	})
	.AddApiKeyInHeader<ProviderTwo>("SCHEMA2", options =>
	{
		options.Realm = "Realm 2";
		options.KeyName = "X-API-KEY";
	});

ApiKeyExtensions on line 384

private static AuthenticationBuilder AddApiKey<TApiKeyProvider, TApiKeyHandler>(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<ApiKeyOptions> configureOptions)
	where TApiKeyProvider : class, IApiKeyProvider
	where TApiKeyHandler : AuthenticationHandler<ApiKeyOptions>
{
	// Adds implementation of IApiKeyProvider to the dependency container.
	builder.Services.AddTransient<IApiKeyProvider, TApiKeyProvider>(); //I think that this overrides the registration make 
	builder.Services.Configure<ApiKeyOptions>(
		authenticationScheme,
		o => o.ApiKeyProviderType = typeof(TApiKeyProvider)
	);

	// Adds post configure options to the pipeline.
	builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<ApiKeyOptions>, ApiKeyPostConfigureOptions>());

	// Adds api key authentication scheme to the pipeline.
	return builder.AddScheme<ApiKeyOptions, TApiKeyHandler>(authenticationScheme, displayName, configureOptions);

}

This will cause ApiKeyHandlerBase:L193 to always return a instance to return. This will prevent it from reaching if (apiKeyProvider == null) and resolving a provider based on the type in the options
apiKeyProvider = Context.RequestServices.GetService(Options.ApiKeyProviderType) as IApiKeyProvider;

private async Task<IApiKey> ValidateUsingApiKeyProviderAsync(string apiKey)
{
	var apiKeyProvider = Context.RequestServices.GetService<IApiKeyProvider>();
	if (apiKeyProvider == null)
	{
		if (Options.ApiKeyProviderType != null)
		{
			apiKeyProvider = Context.RequestServices.GetService(Options.ApiKeyProviderType) as IApiKeyProvider;
		}

		if (apiKeyProvider == null)
		{
			throw new InvalidOperationException($"Either {nameof(Options.Events.OnValidateKey)} delegate on configure options {nameof(Options.Events)} should be set or an implementaion of {nameof(IApiKeyProvider)} should be registered in the dependency container.");
		}
	}

	return await apiKeyProvider.ProvideAsync(apiKey).ConfigureAwait(false);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions