Belldandy
Un blog? Que es esto, 2004? Mi nombre es Andrea, y hace muchos años que trabajo en sistemas.
Logo

FastEndPoints: Autenticando con una API Key

Publicado el 23 ago 2025, 14:39:14 —  Categorias: .NET

Desde que integre Grafana, de vez en cuando veo los traces, y ayer me encontre con un par de errores 500 tipo 3 y media de la mañana, que despues no se repitieron. Fui a mirar Clodwatch, y alguien le estaba pegando a la API publica con parametros "raros". Como corcho encontraron la URL de la API publica? Eso sera tarea para otro post, lo mas probable es que simplemente probaron (no era exactamente dificil encontrarlo) pero el tema es que si ya saben que hay algo ahi, a pesar que es publica, a pesar que no tiene nada "peligroso", los bots van a empezar a romper los 🥚🥚

Este humilde blog corre con LAMBDA con unas librerias para armar Minimal API en .NET que se llama FastEndpoints. Usa el patron REPR Design Pattern (Request-Endpoint-Response), viene con clases e implementaciones para facilitar la seguridad, validaciones, integration tests, unit tests, etc. Muy bueno, realmente.

Entonces, decidi meterle autenticacion con una API Key a la API Publica ("Porque odias mis trenes, señor, se suponia que era una API publica, no tengo ganas de tener alertas de 5xx en Cloudwatch porque un bot quiere romper las cosas").

Asi que veamos como decidi resolverlo.

En este caso, implementar JWT o algo mas complejo NI VALIA LA PENA (de nuevo, no es algo que necesite autenticacion, es simplemente meter un freno a los bots). Entonces pense en meterle un header con una key, y listo. Simple, elegante, y "relativamente" seguro. Cuando estaba por empezar a codear, pense: "Y si FastEndpoints tiene soporte para esto? Veamos la documentacion!"). Y efectivamente, en la documentacion, habia un ejemplito para implementarlo!

internal sealed class ApikeyAuth(IOptionsMonitor<AuthenticationSchemeOptions> options,
	ILoggerFactory logger,
	UrlEncoder encoder,
	IOptions<SecuritySettings> settings)
	: AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)
{
	internal const string SCHEME_NAME = "ApiKey";
	internal const string HEADER_NAME = "mi-super-segura-api-key";
	private readonly string _apiKey = settings.Value.PublicKey ?? throw new InvalidOperationException("Api Key not set in Appsettings.json");

	protected override Task<AuthenticateResult> HandleAuthenticateAsync()
	{
		Request.Headers.TryGetValue(HEADER_NAME, out var extractedApiKey);

		if (!IsPublicEndpoint() && !extractedApiKey.Equals(_apiKey))
		{
			return Task.FromResult(AuthenticateResult.Fail("Invalid API credentials!"));
		}

		var identity = new ClaimsIdentity(
			claims: new[] { new Claim("ClientID", "Default") },
			authenticationType: Scheme.Name);
		var principal = new GenericPrincipal(identity, roles: null);
		var ticket = new AuthenticationTicket(principal, Scheme.Name);

		return Task.FromResult(AuthenticateResult.Success(ticket));
	}

	private bool IsPublicEndpoint()
		=> Context.GetEndpoint()?.Metadata.OfType<AllowAnonymousAttribute>().Any() is null or true;
}

Y despues, simplemente, le decimos a FastEndpoints que use autenticacion y autorizacion

services.AddFastEndpoints(o => ...)
    .AddAuthorization()
    .AddAuthentication(ApikeyAuth.SCHEME_NAME)
    .AddScheme<AuthenticationSchemeOptions, ApikeyAuth>(ApikeyAuth.SCHEME_NAME, null);

Y despues al builder:

builder
    .UseAuthentication()
    .UseAuthorization()

Luego sacamos el AllowAnonymous(); del Endpoint, y listo el pollo! Ahora cuando queremos acceder los endpoints, nos devuelve un 401, excepto si le mandamos en el header el key correcto. Despues fue cambiar el frontend (fueron 2-3 cambios) y listo.

Image

PD: Podria haberle puesto esto mismo al API Gateway y dejar la API con AllowAnonymous(), ahora que lo pienso, pero bueno, yasta, "it's good enough" por ahora 😊

Volver

Comentarios Recientes

No hay comentarios, porque no dejás alguno?

¡Comentario agregado con éxito!
Angel

Deja un comentario

(no se publica)