Skip to content

Instantly share code, notes, and snippets.

@code-atom
Created May 18, 2024 17:24
Show Gist options
  • Save code-atom/2bebe459c756f15a519c3136b5be1a89 to your computer and use it in GitHub Desktop.
Save code-atom/2bebe459c756f15a519c3136b5be1a89 to your computer and use it in GitHub Desktop.
Shared API Key Authentication Handler
using Microsoft.AspNetCore.Authorization;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthentication()
.AddSharedApiKey(option => option.SharedApiKey = "Test");
builder.Services.AddAuthorization();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
}).RequireAuthorization(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
app.Run();
internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.Options;
using SharedApiKeyAuthentication;
using System.Security.Claims;
using System.Text.Encodings.Web;
namespace SharedApiKeyAuthentication
{
public class SharedApiKeyAuthenticationHandler : AuthenticationHandler<SharedApiKeyAuthenticationOptions>
{
public SharedApiKeyAuthenticationHandler(
IOptionsMonitor<SharedApiKeyAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder) : base(options, logger, encoder)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
ProviderApiKeyResult? providerApiKeyResult = null;
if (Options.RequestApiKeyProviders != null)
{
foreach (var provider in Options.RequestApiKeyProviders)
{
providerApiKeyResult = await provider.DetermineProviderApiKeyResult(Context);
if (providerApiKeyResult != null)
{
break;
}
}
}
if (providerApiKeyResult == null)
{
return AuthenticateResult.NoResult();
}
if (providerApiKeyResult.ApiKey != Options.SharedApiKey)
{
return AuthenticateResult.Fail("API Key does not match");
}
var claimIdentity = new ClaimsIdentity("SharedAPIKey");
claimIdentity.AddClaim(new Claim("Key", providerApiKeyResult.ApiKey));
var claimPrincipal = new ClaimsPrincipal(claimIdentity);
return AuthenticateResult.Success(new AuthenticationTicket(claimPrincipal, Scheme.Name));
}
}
public class ProviderApiKeyResult
{
public string ApiKey { get; set; }
public ProviderApiKeyResult(string apiKey)
{
ApiKey = apiKey;
}
}
public interface IRequestApiKeyProvider
{
Task<ProviderApiKeyResult?> DetermineProviderApiKeyResult(HttpContext httpContext);
}
public class RequestHeaderApiKeyProvider : IRequestApiKeyProvider
{
public string HeaderKey { get; set; } = "X-API-KEY";
public async Task<ProviderApiKeyResult?> DetermineProviderApiKeyResult(HttpContext httpContext)
{
if (!httpContext.Request.Headers.ContainsKey(HeaderKey))
{
return default;
}
var apiKey = httpContext.Request.Headers[HeaderKey];
return new ProviderApiKeyResult(apiKey);
}
}
public class SharedApiKeyAuthenticationOptions : AuthenticationSchemeOptions
{
/// <summary>
/// Shared API KEY
/// </summary>
public string? SharedApiKey { get; set; }
public ICollection<IRequestApiKeyProvider> RequestApiKeyProviders { get; set; } = new HashSet<IRequestApiKeyProvider>();
public SharedApiKeyAuthenticationOptions()
{
RequestApiKeyProviders.Add(new RequestHeaderApiKeyProvider());
}
}
}
namespace Microsoft.AspNetCore.Builder
{
public static class SharedApiKeyAuthenticationExtensions
{
public static AuthenticationBuilder AddSharedApiKey(this AuthenticationBuilder builder, Action<SharedApiKeyAuthenticationOptions> configureOptions)
{
return builder.AddScheme<SharedApiKeyAuthenticationOptions, SharedApiKeyAuthenticationHandler>("SharedAPIKey", configureOptions);
}
public static AuthenticationBuilder AddSharedApiKey(this AuthenticationBuilder builder, string name, Action<SharedApiKeyAuthenticationOptions> configureOptions)
{
return builder.AddScheme<SharedApiKeyAuthenticationOptions, SharedApiKeyAuthenticationHandler>(name, configureOptions);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment