@mathismeadows/roamer-device-auth 1.5.4 → 1.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mathismeadows/roamer-device-auth",
3
- "version": "1.5.4",
3
+ "version": "1.5.6",
4
4
  "private": false,
5
5
  "mcpName": "com.mathismeadows/roamer-mcp",
6
6
  "type": "module",
@@ -355,6 +355,13 @@ async function readCachedTokens(clientSlug, identityKey = null) {
355
355
  // A cache from an incompatible prior format must never be trusted just because it
356
356
  // happens to have the right field names — see CACHE_VERSION's comment.
357
357
  if (tokens?.cacheVersion !== CACHE_VERSION) return null;
358
+ // AUTH-38/AUTH-device-code-cache-issuer-blind: same protection readLoopbackTokens already
359
+ // has — a token cached against a prior authorization server (e.g. Cloudflare Access
360
+ // Managed OAuth, before AUTH-38) must never be trusted just because cacheVersion matches.
361
+ // No live discovery needed here (unlike the loopback cache): RoamerMcp declares itself as
362
+ // its own AS in its .well-known/oauth-protected-resource document, so the fixed
363
+ // ROAMER_MCP_ORIGIN constant is exactly as authoritative as a freshly-discovered value.
364
+ if (tokens?.issuerUrl !== ROAMER_MCP_ORIGIN) return null;
358
365
  return tokens;
359
366
  } catch {
360
367
  return null;
@@ -368,7 +375,12 @@ async function writeCachedTokens(clientSlug, tokens, identityKey = null) {
368
375
  const path = cacheFilePath("roamer_tokens", clientSlug, identityKey);
369
376
  if (!path) return; // AUTH-51: no usable client identity — never persisted.
370
377
  await mkdir(CACHE_DIR, { recursive: true, mode: 0o700 });
371
- const stamped = { ...tokens, cacheVersion: CACHE_VERSION, label: identityLabelFromTokens(tokens) };
378
+ const stamped = {
379
+ ...tokens,
380
+ cacheVersion: CACHE_VERSION,
381
+ issuerUrl: ROAMER_MCP_ORIGIN,
382
+ label: identityLabelFromTokens(tokens),
383
+ };
372
384
  await writeFile(path, JSON.stringify(stamped, null, 2), { mode: 0o600 });
373
385
  }
374
386
 
@@ -639,10 +651,12 @@ async function doGetValidTokens(clientSlug, forceRefresh, forceFreshLogin = fals
639
651
  }
640
652
 
641
653
  log(`Go to ${device.verification_uri} and enter code: ${device.user_code}`);
642
- // Fire-and-forget: the dialog is how the human actually sees this on macOS; polling below
643
- // must not wait on it being dismissed. Skip re-showing it on a resume — the human already
644
- // saw it (or is mid-flow in the browser) from the process that started this same code.
645
- if (!resuming) showDeviceCodeDialog(device.verification_uri, device.user_code);
654
+ // AUTH-61: launch the browser first, then show the fallback dialog both are best-effort
655
+ // OS-level UI activations, and requesting them in the same instant can leave Safari
656
+ // intermittently stuck showing "Application Not Responding" on launch (macOS-only; see
657
+ // thread AUTH-safari-device-flow-hang). The dialog stays fire-and-forget: polling below
658
+ // must not wait on it being dismissed. Skip both on a resume — the human already saw them
659
+ // (or is mid-flow in the browser) from the process that started this same code.
646
660
  if (!resuming) {
647
661
  try {
648
662
  const { default: open } = await import("open");
@@ -651,6 +665,7 @@ async function doGetValidTokens(clientSlug, forceRefresh, forceFreshLogin = fals
651
665
  // Best-effort only — the dialog and QR code below already carry the URL and code.
652
666
  }
653
667
  }
668
+ if (!resuming) showDeviceCodeDialog(device.verification_uri, device.user_code);
654
669
  // Terminal-visible for any client where a human sees this process's stderr/log output
655
670
  // (Claude Code CLI, VS Code's integrated terminal) — QR codes are a real usability win
656
671
  // for CLI auth over typing an 8-character code by hand.