Search code examples
c#asp.net-core

AddScheme() doesn't configure options


I'm trying to set up a custom authentication scheme in ASP.NET Core 8. I have created AuthenticationOptions and an AuthenticationHandler.

However the issue happens on startup, the line where options are configured is apparently never reached.

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = ApiKeyAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = ApiKeyAuthenticationDefaults.AuthenticationScheme;
})
    .AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(ApiKeyAuthenticationDefaults.AuthenticationScheme, options =>
    {
        options.ApiKey = configuration["ApiKey"] ?? throw new Exception("No API key was configured");
    });

I'm sure it is not executing because if I put a breakpoint on that line it is never hit and if ApiKey is null the exception on the right side of the null coalescing operator is not thrown.

namespace API.Authentication.ApiKeyAuthenticaiton
{
    public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
    {
        public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder) : base(options, logger, encoder)
        {
            ApiKeyAuthenticationOptions opts = options.CurrentValue;

            if (opts.ApiKey == null) throw new ArgumentNullException(nameof(opts.ApiKey));
        }

        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            // Validate API key
            
            if (!Request.Headers.TryGetValue("x-api-key", out var key)) return AuthenticateResult.Fail("Missing API key");
            if (key != Options.ApiKey) return AuthenticateResult.Fail("Invalid API key");

            return AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(), Scheme.Name));
        }
    }
}
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
{
    public string ApiKey { get; set; } = null!;
}

Solution

  • According to this issue

    The AuthenticationHandler constructor doesn't initialize Options because it doesn't have the scheme name yet. It's initialized after the authentication middleware calls InitializeAsync.