@httpc/kit
Version:
httpc toolbox for building function-based API with minimal code and end-to-end type safety
51 lines (50 loc) • 2.17 kB
JavaScript
import { PassthroughMiddleware, useContext } from "@httpc/server";
import { RESOLVE, useContainer } from "../di";
import { useAuthentication } from "./context";
import { catchLogAndThrowUnauthorized } from "../services";
export function AuthenticationApiKeyMiddleware(options) {
const authenticate = options?.onAuthenticate || onAuthenticate;
const extractKey = options?.extractKey || extractApiKey;
return PassthroughMiddleware(async () => {
const { request, user } = useContext();
// try to authenticate if not already present
if (!user) {
const apiKey = extractKey(request);
if (apiKey) {
useAuthentication(await authenticate(apiKey)
.catch(catchLogAndThrowUnauthorized("ApiKeyMiddleware")));
}
}
});
}
/**
* Look for api key in the request in this order
* 1. apikey or api-key or x-api-key header
* 2. authorization header with schema APIKEY or API_KEY
* 3. query string with param apikey or api_key
*/
function extractApiKey(request) {
const apiKey = (request.headers["apikey"] || request.headers["api_key"] || request.headers["api-key"] || request.headers["x-api-key"]);
if (apiKey) {
return apiKey.trim();
}
if (request.headers.authorization) {
let [schema, token] = request.headers.authorization.split(" ");
schema = schema.toLowerCase();
if (schema === "api-key" || schema === "api_key" || schema === "apikey") {
return token.trim() || undefined;
}
}
if (request.url) {
let qs = new URL(request.url, `https://${request.headers.host}`).searchParams;
// lowercase all param names
qs = new URLSearchParams([...qs.entries()].map(([key, value]) => [key.toLowerCase(), value]));
return (qs.get("api_key") || qs.get("api-key") || qs.get("apikey"))?.trim() || undefined;
}
}
async function onAuthenticate(apiKey) {
const container = useContainer();
const auth = RESOLVE(container, "ApiKeyAuthentication");
return await auth.authenticate(apiKey);
}
//cspell: ignore apikey