import { timingSafeEqual } from 'node:crypto' import type { HttpContext } from '@adonisjs/core/http' import type { NextFn } from '@adonisjs/core/types/http' import env from '#start/env' /** * ApiKeyMiddleware * * Protects REST API routes by validating a fixed API key provided in the * X-Api-Key request header. The expected key is configured via: * API_KEY (required env variable) * * Security: * - Constant-time comparison (timingSafeEqual) to prevent timing attacks * * Responds with 401 if the header is missing or the key does not match. */ export default class ApiKeyMiddleware { async handle({ request, response }: HttpContext, next: NextFn) { const apiKey = request.header('x-api-key') ?? '' const expectedKey = env.get('API_KEY')! const provided = Buffer.from(apiKey) const expected = Buffer.from(expectedKey) // Lengths must match and content must match in constant time const valid = provided.length === expected.length && timingSafeEqual(provided, expected) if (!valid) { return response.status(401).send('Unauthorized: invalid or missing X-Api-Key header') } return next() } }