@crvouga/mockingbird-service-oauth 3.11.5 → 3.11.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog — @crvouga/mockingbird-service-oauth
2
2
 
3
+ ## 3.11.6 (2026-11-02)
4
+
5
+ ### Fixes and improvements
6
+
7
+ - compare the admin key in constant time ([2a43141](https://github.com/crvouga/mockingbird/commit/2a43141f50dab41e698c3bbce0c69bf65b40e24e))
8
+
3
9
  ## 3.11.5 (2026-11-02)
4
10
 
5
11
  ### Fixes and improvements
@@ -2461,6 +2461,17 @@ var createNamespaceControls = (options) => {
2461
2461
  };
2462
2462
  };
2463
2463
 
2464
+ // ../core/dist/signing.js
2465
+ var encoder = new TextEncoder();
2466
+ var timingSafeEqual = (a, b) => {
2467
+ if (a.length !== b.length)
2468
+ return false;
2469
+ let diff = 0;
2470
+ for (let i = 0; i < a.length; i++)
2471
+ diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
2472
+ return diff === 0;
2473
+ };
2474
+
2464
2475
  // ../core/dist/surface.js
2465
2476
  var defineMock = (runtime) => runtime;
2466
2477
  var ADMIN_ROUTE_REFERENCE = {
@@ -2579,6 +2590,10 @@ var assertAdminPrefixAvailable = (prefix, paths) => {
2579
2590
  }
2580
2591
  };
2581
2592
  var ADMIN_KEY_HEADER = CONTROL_HEADERS.adminKey;
2593
+ var hasAdminKey = (headers, adminKey) => {
2594
+ const sent = headers.get(ADMIN_KEY_HEADER);
2595
+ return sent !== null && timingSafeEqual(sent, adminKey);
2596
+ };
2582
2597
  var NAMESPACE_HEADER = CONTROL_HEADERS.namespace;
2583
2598
  var ADMIN_DOCS_URL = "https://mockingbird.chrisvouga.dev/docs/admin-controls";
2584
2599
  var ADMIN_CONTROL_PAGES = [
@@ -3052,7 +3067,7 @@ var createControlPlane = (context) => {
3052
3067
  }
3053
3068
  const path = url.pathname.slice(adminPrefix.length) || "/";
3054
3069
  const shell = request.method === "GET" && (path === "/ui" || path === "/ui/");
3055
- if (!shell && context.adminKey !== void 0 && request.headers.get(ADMIN_KEY_HEADER) !== context.adminKey) {
3070
+ if (!shell && context.adminKey !== void 0 && !hasAdminKey(request.headers, context.adminKey)) {
3056
3071
  return adminError(401, `missing or wrong ${ADMIN_KEY_HEADER}`);
3057
3072
  }
3058
3073
  for (const route of routes) {
@@ -3807,7 +3822,7 @@ var stateAdminRoutes = (routes) => {
3807
3822
  };
3808
3823
 
3809
3824
  // ../core/dist/version.js
3810
- var PACKAGE_VERSION = true ? "3.11.5" : UNRELEASED_VERSION;
3825
+ var PACKAGE_VERSION = true ? "3.11.6" : UNRELEASED_VERSION;
3811
3826
 
3812
3827
  // ../core/dist/webhooks.js
3813
3828
  var json4 = (status, body) => new Response(JSON.stringify(body), {
@@ -4786,7 +4801,7 @@ var createRuntime = (options) => {
4786
4801
  throw error;
4787
4802
  }
4788
4803
  const rewinds = at !== void 0 && selectedBranch === "main" && MUTATING_METHODS.has(request.method);
4789
- if ((requested || rewinds) && options.adminKey !== void 0 && request.headers.get(ADMIN_KEY_HEADER) !== options.adminKey)
4804
+ if ((requested || rewinds) && options.adminKey !== void 0 && !hasAdminKey(request.headers, options.adminKey))
4790
4805
  return refuse(401, `control headers need ${ADMIN_KEY_HEADER}`);
4791
4806
  controls.ensure(namespace);
4792
4807
  if (requested?.restore !== void 0 && !snapshotStore.get(namespace, requested.restore))
@@ -5278,10 +5293,10 @@ var BehaviorState = class {
5278
5293
  };
5279
5294
 
5280
5295
  // src/crypto.ts
5281
- var encoder = new TextEncoder();
5296
+ var encoder2 = new TextEncoder();
5282
5297
  var base64url = (bytes) => btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
5283
5298
  var random = () => base64url(crypto.getRandomValues(new Uint8Array(32)));
5284
- var hash = async (value) => base64url(new Uint8Array(await crypto.subtle.digest("SHA-256", encoder.encode(value))));
5299
+ var hash = async (value) => base64url(new Uint8Array(await crypto.subtle.digest("SHA-256", encoder2.encode(value))));
5285
5300
  var RSA = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
5286
5301
  var Signer = class {
5287
5302
  pair;
@@ -5313,20 +5328,20 @@ var Signer = class {
5313
5328
  }
5314
5329
  async sign(claims) {
5315
5330
  const header = base64url(
5316
- encoder.encode(JSON.stringify({ alg: "RS256", kid: this.kid, typ: "JWT" }))
5331
+ encoder2.encode(JSON.stringify({ alg: "RS256", kid: this.kid, typ: "JWT" }))
5317
5332
  );
5318
- const payload = base64url(encoder.encode(JSON.stringify(claims)));
5333
+ const payload = base64url(encoder2.encode(JSON.stringify(claims)));
5319
5334
  const input = `${header}.${payload}`;
5320
5335
  const signature = await crypto.subtle.sign(
5321
5336
  "RSASSA-PKCS1-v1_5",
5322
5337
  (await this.pair).privateKey,
5323
- encoder.encode(input)
5338
+ encoder2.encode(input)
5324
5339
  );
5325
5340
  return `${input}.${base64url(new Uint8Array(signature))}`;
5326
5341
  }
5327
5342
  };
5328
5343
  var halfHash = async (value) => base64url(
5329
- new Uint8Array(await crypto.subtle.digest("SHA-256", encoder.encode(value))).slice(0, 16)
5344
+ new Uint8Array(await crypto.subtle.digest("SHA-256", encoder2.encode(value))).slice(0, 16)
5330
5345
  );
5331
5346
  async function verifyAppleSecret(token, clientId, apple, now) {
5332
5347
  try {
@@ -5350,7 +5365,7 @@ async function verifyAppleSecret(token, clientId, apple, now) {
5350
5365
  { name: "ECDSA", hash: "SHA-256" },
5351
5366
  key,
5352
5367
  decode(signature),
5353
- encoder.encode(`${header}.${payload}`)
5368
+ encoder2.encode(`${header}.${payload}`)
5354
5369
  );
5355
5370
  } catch {
5356
5371
  return false;
@@ -7473,4 +7488,4 @@ export {
7473
7488
  createMultiRuntime,
7474
7489
  OAuthAPI
7475
7490
  };
7476
- //# sourceMappingURL=chunk-EFBIR4YL.js.map
7491
+ //# sourceMappingURL=chunk-BWGDZLMA.js.map