Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: Possibility to have different keys for query and header #20

Closed
cjblomqvist opened this issue Feb 8, 2021 · 4 comments
Closed

Comments

@cjblomqvist
Copy link

What the title says, it would nice to have different key name for the query parameter and the header. Headers often have certain considerations, which query params don't, which make a name that works in both scenario rather ugly and unintuitive (e.g. 'X-API-KEY' is nice as a header key, but quite unexpected as a query param.

Anyway, not critical. Thanks for your lib - great work!

@mihirdilip
Copy link
Owner

Hi @cjblomqvist ,

Thanks for a very good suggestion. Glad to know that you like the library. I had a quick look and to solve your suggestion I can suggest 2 solutions as below. I have tried the below on .NET 5 so it may be little bit different on older .NET Core versions. I hope this helps. Let me know your thoughts. :)

Solution 1

It would try to use the default scheme first and then fallback to next scheme. Please notice the scheme names on AddAuthentication as well as AddAuthorization (FallbackPolicy).

        public void ConfigureServices(IServiceCollection services)
        {
            // Add User repository to the dependency container.
            services.AddTransient<IApiKeyRepository, InMemoryApiKeyRepository>();

            services.AddAuthentication("InHeader")
                .AddApiKeyInHeader<ApiKeyProvider>("InHeader", options =>
                {
                    options.Realm = "Sample Web API";
                    options.KeyName = "X-API-KEY";
                })
                .AddApiKeyInQueryParams<ApiKeyProvider>("InQueryParams", options =>
                {
                    options.Realm = "Sample Web API";
                    options.KeyName = "apikey";
                });

            // All the requests will need to be authorized. 
            // Alternatively, add [Authorize] attribute to Controller or Action Method where necessary.
            services.AddAuthorization(options =>
            {
                options.FallbackPolicy = new AuthorizationPolicyBuilder("InHeader", "InQueryParams")    // SCHEME NAMES HERE!!
                    .RequireAuthenticatedUser()
                    .Build();
            });

            services.AddControllers(options =>
            {
                // ALWAYS USE HTTPS (SSL) protocol in production when using ApiKey authentication.
                //options.Filters.Add<RequireHttpsAttribute>();
            }); //.AddXmlSerializerFormatters()   // To enable XML along with JSON;
        }

Solution 2

It would resolve the default scheme from the selector so performance wise might be better than 1st solution above.

        public void ConfigureServices(IServiceCollection services)
        {
            // Add User repository to the dependency container.
            services.AddTransient<IApiKeyRepository, InMemoryApiKeyRepository>();

            services.AddAuthentication(ApiKeyDefaults.AuthenticationScheme)
                .AddPolicyScheme(ApiKeyDefaults.AuthenticationScheme, ApiKeyDefaults.AuthenticationScheme, options =>
                {
                    options.ForwardDefaultSelector = context =>
                    {
                        return context.Request.Headers.ContainsKey("X-API-KEY")
                            ? "InHeader"
                            : "InQueryParams";
                    };
                })
                .AddApiKeyInHeader<ApiKeyProvider>("InHeader", options =>
                {
                    options.Realm = "Sample Web API";
                    options.KeyName = "X-API-KEY";
                })
                .AddApiKeyInQueryParams<ApiKeyProvider>("InQueryParams", options =>
                {
                    options.Realm = "Sample Web API";
                    options.KeyName = "apikey";
                });

            // All the requests will need to be authorized. 
            // Alternatively, add [Authorize] attribute to Controller or Action Method where necessary.
            services.AddAuthorization(options =>
            {
                options.FallbackPolicy = new AuthorizationPolicyBuilder()    // NO scheme names required. It will pick up default.
                    .RequireAuthenticatedUser()
                    .Build();
            });

            services.AddControllers(options =>
            {
                // ALWAYS USE HTTPS (SSL) protocol in production when using ApiKey authentication.
                //options.Filters.Add<RequireHttpsAttribute>();
            }); //.AddXmlSerializerFormatters()   // To enable XML along with JSON;
        }

@mihirdilip
Copy link
Owner

Hi @cjblomqvist, did the above solve the problem?

@cjblomqvist
Copy link
Author

Sorry I haven't had the time to test this yet. It looks reasonable though!
It would be awesome to have a simple built in API for it, but I can understand you want to limit complexity as well.

@mihirdilip
Copy link
Owner

I have tested both suggested solutions and they both works. I shall close this issue. Feel free to reopen if you have any queries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants