Round #706
10 May 2026, 15:09· csharp· Meta-Llama-3.3-70B-Instruct· r13-clean-hints
- Complexity
- band 0
- ZP / UO / OBS
- 5 / 5 / 0
- Journey weight
- 621
- Lightshift ΔL
- +0.00
Prompt
Implement a custom AuthenticationHandler<TOptions> for a header-based API key, including ChallengeAsync and AuthenticateResult.
Zero-point index (what the engine surfaced)
ZPI-ZPI-0005 | 2026-05-10T15:09:29Z
Implement a custom AuthenticationHandler<TOptions> for a header-based API key, including ChallengeAsync and AuthenticateResult.
ZERO POINT — 5 entries (structural invariants)
● hsl(228.20, 100%, 30%) IAuthenticationHandler [contract]
● hsl(61.59, 100%, 30%) AuthenticationHandler<TOptions> [contract]
● hsl(257.20, 100%, 30%) AuthenticationSchemeOptions [contract]
● hsl(161.43, 100%, 30%) JwtBearerOptions [contract]
● hsl(89.40, 100%, 30%) OpenIdConnectOptions [contract]
UNOBSERVED — 5 entries (implied truths)
○ hsl(27.69, 100%, 50%) async [feature]
○ hsl(9.23, 100%, 50%) interface [feature]
○ hsl(77.54, 100%, 42%) Routing middleware [function]
○ hsl(83.08, 100%, 42%) ILogger of T injection [function]
○ hsl(31.38, 100%, 42%) Scoped registration [function]
OBSERVED — 0 entries (explicit in request)
COMPLEXITY │ 10 entries │ spread: 74° │ band: LOW
Baseline response (no ZPI)
**Custom Authentication Handler for Header-Based API Key**
===========================================================
Below is an example implementation of a custom `AuthenticationHandler<TOptions>` for a header-based API key in C#.
### ApiKeyAuthenticationHandler.cs
```csharp
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using System;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
{
public string HeaderName { get; set; } = "X-Api-Key";
public string ApiKey { get; set; }
}
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
{
public ApiKeyAuthenticationHandler(
IOptionsMonitor<ApiKeyAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue(Options.HeaderName, out var apiKeyHeaderValues))
{
return AuthenticateResult.NoResult();
}
var providedApiKey = apiKeyHeaderValues.FirstOrDefault();
if (string.IsNullOrEmpty(providedApiKey) || providedApiKey != Options.ApiKey)
{
return AuthenticateResult.Fail("Invalid API key");
}
var claims = new[] { new Claim(ClaimTypes.Name, "API Key") };
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
await Response.WriteAsync("Unauthorized: Invalid API key");
}
}
```
### Startup.cs (or Program.cs in .NET 6 and later)
```csharp
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.DefaultScheme = "ApiKey";
})
.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>("ApiKey", _ => { });
services.Configure<ApiKeyAuthenticationOptions>(options =>
{
options.ApiKey = "your_secret_api_key";
});
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
```
### Example Usage
To use this custom authentication handler, you can decorate your controllers or actions with the `[Authorize]` attribute. The `ApiKeyAuthenticationHandler` will automatically check for the presence and validity of the API key in the `X-Api-Key` header.
```csharp
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class MyController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok("Hello, authenticated user!");
}
}
```
When making requests to this controller, you must include a valid API key in the `X-Api-Key` header. For example, using `curl`:
```bash
curl -X GET \
http://localhost:5000/api/my \
-H 'X-Api-Key: your_secret_api_key'
```
Replace `your_secret_api_key` with the actual API key configured in your application.
Enhanced response (with ZPI)
```csharp
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
{
public string HeaderName { get; set; } = "X-Api-Key";
public string ApiKey { get; set; }
}
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
{
private readonly ILogger _logger;
public ApiKeyAuthenticationHandler(
IOptionsMonitor<ApiKeyAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock)
{
_logger = logger.CreateLogger<ApiKeyAuthenticationHandler>();
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue(Options.HeaderName, out var apiKeyHeaderValues))
{
return AuthenticateResult.NoResult();
}
var providedApiKey = apiKeyHeaderValues.FirstOrDefault();
if (string.IsNullOrEmpty(providedApiKey))
{
return AuthenticateResult.NoResult();
}
if (providedApiKey != Options.ApiKey)
{
_logger.LogWarning("Invalid API key provided.");
return AuthenticateResult.Fail("Invalid API key");
}
var claims = new[] { new Claim(ClaimTypes.Name, "API Key") };
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
Response.StatusCode = 401;
Response.Headers.Append("WWW-Authenticate", $"Bearer realm=\"{Options.HeaderName}\"");
await base.HandleChallengeAsync(properties);
}
}
public static class ApiKeyAuthenticationExtensions
{
public static AuthenticationBuilder AddApiKeyAuthentication(
this AuthenticationBuilder builder,
Action<ApiKeyAuthenticationOptions> configureOptions)
{
builder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(
"ApiKey", configureOptions);
return builder;
}
}
```
This implementation provides a custom `AuthenticationHandler<TOptions>` for a header-based API key. The `ApiKeyAuthenticationOptions` class defines the configuration options for the authentication scheme, including the header name and the expected API key.
The `ApiKeyAuthenticationHandler` class handles the authentication process. It checks for the presence of the API key header in the request and verifies its value against the expected API key. If the API key is valid, it creates a claims identity and returns an authentication ticket.
The `HandleChallengeAsync` method is used to handle challenges, such as when an invalid API key is provided. It sets the response status code to 401 and includes a WWW-Authenticate header with the expected header name.
The `ApiKeyAuthenticationExtensions` class provides an extension method for adding the API key authentication scheme to the authentication builder.
To use this implementation, you can add the API key authentication scheme to the authentication builder in the `Startup.cs` file:
```csharp
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.DefaultScheme = "ApiKey";
})
.AddApiKeyAuthentication(options =>
{
options.HeaderName = "X-Api-Key";
options.ApiKey = "your_api_key_here";
});
}
```
You can then use the `[Authorize]` attribute to protect controllers or actions that require API key authentication.
Grader chain
Each grader reads the same baseline + enhanced pair and casts an independent verdict. Disagreement is the framework's own signal — not noise to be averaged away.
V1samba/Meta-Llama-3.3-70B-Instruct
BetterV4qwen2.5-coder-3b-instruct
BetterV5openrouter/qwen/qwen3-235b-a22b-2507
BetterV7qwen2.5-coder-7b-instruct
BetterV8openai/gpt-4.1
BetterV9anthropic/claude-opus-4-7
SameV12openai/gpt-4o
BetterComments
Our grader said what it said. What do you say? Comment as a guest below.
No comments yet. Be the first to say what you make of this round.