@usequeek/app-sdk 0.5.0 → 0.6.0

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.
Files changed (74) hide show
  1. package/README.md +172 -16
  2. package/dist/app-auth.d.ts +23 -2
  3. package/dist/app-auth.d.ts.map +1 -1
  4. package/dist/app-auth.js +40 -6
  5. package/dist/app-auth.js.map +1 -1
  6. package/dist/auth-fetch.d.ts +90 -0
  7. package/dist/auth-fetch.d.ts.map +1 -0
  8. package/dist/auth-fetch.js +261 -0
  9. package/dist/auth-fetch.js.map +1 -0
  10. package/dist/client.d.ts +32 -10
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/client.js +6 -2
  13. package/dist/client.js.map +1 -1
  14. package/dist/frame.d.ts +233 -4
  15. package/dist/frame.d.ts.map +1 -1
  16. package/dist/frame.js +694 -19
  17. package/dist/frame.js.map +1 -1
  18. package/dist/handoff.d.ts +32 -2
  19. package/dist/handoff.d.ts.map +1 -1
  20. package/dist/handoff.js +2 -1
  21. package/dist/handoff.js.map +1 -1
  22. package/dist/hono.d.ts +13 -0
  23. package/dist/hono.d.ts.map +1 -1
  24. package/dist/hono.js +15 -0
  25. package/dist/hono.js.map +1 -1
  26. package/dist/index.d.ts +11 -7
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +10 -6
  29. package/dist/index.js.map +1 -1
  30. package/dist/install-handlers.d.ts +33 -8
  31. package/dist/install-handlers.d.ts.map +1 -1
  32. package/dist/install-handlers.js +133 -15
  33. package/dist/install-handlers.js.map +1 -1
  34. package/dist/merchant-schema.d.ts +1550 -68
  35. package/dist/merchant-schema.d.ts.map +1 -1
  36. package/dist/merchant-schema.js +1 -1
  37. package/dist/proxy.d.ts +122 -0
  38. package/dist/proxy.d.ts.map +1 -0
  39. package/dist/proxy.js +204 -0
  40. package/dist/proxy.js.map +1 -0
  41. package/dist/react.d.ts +78 -0
  42. package/dist/react.d.ts.map +1 -0
  43. package/dist/react.js +262 -0
  44. package/dist/react.js.map +1 -0
  45. package/dist/scopes.d.ts +174 -0
  46. package/dist/scopes.d.ts.map +1 -0
  47. package/dist/scopes.js +297 -0
  48. package/dist/scopes.js.map +1 -0
  49. package/dist/server.d.ts +2 -2
  50. package/dist/server.d.ts.map +1 -1
  51. package/dist/server.js +1 -1
  52. package/dist/server.js.map +1 -1
  53. package/dist/session.d.ts +52 -2
  54. package/dist/session.d.ts.map +1 -1
  55. package/dist/session.js +99 -12
  56. package/dist/session.js.map +1 -1
  57. package/dist/store.d.ts +33 -0
  58. package/dist/store.d.ts.map +1 -1
  59. package/dist/store.js +114 -4
  60. package/dist/store.js.map +1 -1
  61. package/dist/theme.d.ts +96 -0
  62. package/dist/theme.d.ts.map +1 -0
  63. package/dist/theme.js +158 -0
  64. package/dist/theme.js.map +1 -0
  65. package/dist/tokens.d.ts +30 -15
  66. package/dist/tokens.d.ts.map +1 -1
  67. package/dist/tokens.js +57 -19
  68. package/dist/tokens.js.map +1 -1
  69. package/dist/webhooks.d.ts.map +1 -1
  70. package/dist/webhooks.js +1 -1
  71. package/dist/webhooks.js.map +1 -1
  72. package/package.json +17 -5
  73. package/CHANGELOG.md +0 -150
  74. package/openapi/merchant.json +0 -21452
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @usequeek/app-sdk
2
2
 
3
+ > In development — not yet released. APIs may change before 1.0.
4
+
3
5
  The SDK for building a Queek app on the public Merchant API and signed webhooks. Framework-agnostic Web-standard handlers for the install handoff and topic webhooks (`Request` in, `Response` out — use them from Next.js route handlers, Express, or any runtime), a typed Merchant API client (types generated from the live contract), GitHub-style app credentials (one asymmetric key per app, short-lived per-installation tokens minted on demand), resync recovery, and an encrypted installation store (SQLite for local/test, Postgres in production). Optional Hono wrappers live under `@usequeek/app-sdk/hono`.
4
6
 
5
7
  ```sh
@@ -28,7 +30,7 @@ const store = new SqliteInstallationStore({
28
30
  storeKey: process.env.APP_ENCRYPTION_KEY!,
29
31
  });
30
32
  const tokens = createAppTokenProvider({
31
- credential: loadAppCredential({ appSlug: "hello" }), // APP_SLUG/APP_KEY_ID/APP_PRIVATE_KEY
33
+ credential: loadAppCredential({ appSlug: "hello" }), // APP_SLUG/APP_KEY_ID/APP_PRIVATE_KEY (base64 of the PEM, one line)
32
34
  store,
33
35
  });
34
36
  const installOptions = { appSecret: process.env.QUEEK_APP_SECRET!, store };
@@ -126,7 +128,7 @@ Signature verification covers the exact bytes Queek sent (`{id}.{timestamp}.{bod
126
128
 
127
129
  ### Body-size posture
128
130
 
129
- The SDK itself imposes no maximum body size (`arrayBuffer` / `TextDecoder` / `JSON.parse` are unbounded, as in 0.4.0) — the cap belongs to the framework parser in front of it, which already rejects oversized bodies before the SDK ever sees them. Queek deliveries are small JSON payloads, so the framework defaults are plenty; if you set them explicitly, `express.raw({ type: "application/json", limit: "1mb" })` and Fastify's default `bodyLimit` of 1 MiB are the recommended ceilings. No SDK option is needed unless Queek ever ships large deliveries.
131
+ The SDK itself imposes no maximum body size (`arrayBuffer` / `TextDecoder` / `JSON.parse` are unbounded) — the cap belongs to the framework parser in front of it, which already rejects oversized bodies before the SDK ever sees them. Queek deliveries are small JSON payloads, so the framework defaults are plenty; if you set them explicitly, `express.raw({ type: "application/json", limit: "1mb" })` and Fastify's default `bodyLimit` of 1 MiB are the recommended ceilings. No SDK option is needed unless Queek ever ships large deliveries.
130
132
 
131
133
  (If you already run Hono on Node, `@hono/node-server`'s `getRequestListener` bridges serving for you — but the adapters above need no extra dependency.)
132
134
 
@@ -162,8 +164,6 @@ app.route(
162
164
  export default app;
163
165
  ```
164
166
 
165
- > Migrating from 0.4.x: `import { createInstallHandlers } from "@usequeek/app-sdk/hono"` — the creators moved out of the root entry so non-Hono apps never install `hono`. The options objects are unchanged.
166
-
167
167
  Every Merchant API call goes through `createInstallationClient({ installationId, apiBase, tokens })`,
168
168
  which resolves the installation's token via `acquireToken()` and sends it as `X-Client-Key`.
169
169
 
@@ -187,7 +187,8 @@ One asymmetric credential per app — no per-installation secrets cross the hand
187
187
 
188
188
  Queek prints the app secret (`QUEEK_APP_SECRET`, verifies the handoff) and the key id
189
189
  (`APP_KEY_ID`, rides the JWT `kid` header). Configure the app with `APP_SLUG` (= `iss`),
190
- `APP_KEY_ID`, and `APP_PRIVATE_KEY` (the PEM — never logged, never shipped to clients).
190
+ `APP_KEY_ID`, and `APP_PRIVATE_KEY` (base64 of the private key PEM, one line — the
191
+ Developer page shows it; raw PEM also accepted — never logged, never shipped to clients).
191
192
 
192
193
  3. **Acquire / cache / re-mint.** `acquireToken(installationId)` serves the cached token while
193
194
  its expiry is more than 5 minutes away; otherwise it signs an RS256 app JWT
@@ -246,10 +247,49 @@ settings) does NOT come back from resync: it needs the per-app `pg_dump` backup
246
247
  (RPO ≤ 24 h) plus the `APP_ENCRYPTION_KEY` backup. Events missed beyond Queek's webhook
247
248
  retries (~4 h) are gone; resync cannot backfill them. Full runbook: `docs/deploy.md` + O1.
248
249
 
250
+ ## Data deletion
251
+
252
+ Treat the uninstall handoff (`app/uninstalled`) as the deletion trigger: the default
253
+ `onUninstall` already deletes the installation row (secrets, token cache, settings) —
254
+ keep that delete when you override it, and purge any app-side working data keyed by the
255
+ installation there too.
256
+
257
+ Unknown topics answer 200 without running a handler (forward compatibility, not consent):
258
+ that 200 must not swallow a future privacy topic. If Queek ships mandatory privacy
259
+ topics, register explicit handlers for them — an unhandled topic only reports
260
+ `unhandled: true`, it deletes nothing.
261
+
262
+ ## Storefront app-proxy (signed reads)
263
+
264
+ Queek signs each storefront proxy fetch with the installation's `proxy_secret`
265
+ (`AppProxyService::signQuery`); verify before answering the shopper:
266
+
267
+ ```ts
268
+ import { handleProxyRequest } from "@usequeek/app-sdk";
269
+
270
+ export async function GET(request: Request) {
271
+ return handleProxyRequest(
272
+ request,
273
+ { store, path: "/apps/booking/availability" }, // Queek-side canonical path, not the local route
274
+ ({ installation, params }) =>
275
+ Response.json({ slots: slotsFor(params.date, installation.storePid) }),
276
+ );
277
+ }
278
+ ```
279
+
280
+ The canonical string is `path + "\n" + shop + "\n" + ts + "\n" + sorted(k=v&...)`
281
+ (`sig` excluded), hex HMAC-SHA256 over the FULL `whsec_…` string — no base64 decode
282
+ step. `kid` routes to the installation whose `proxy_secret` verifies (previous-secret
283
+ grace: pass every active secret to `verifyProxyQuery`); timestamps skew at most 5
284
+ minutes, each `jti` is single-use, and only `GET` is served (phase 1 is read-only).
285
+ Hono: mount `createProxyHandler({ store, path, onVerified })` from
286
+ `@usequeek/app-sdk/hono`.
287
+
249
288
  ## API surface
250
289
 
251
- - **app-auth** (`app-auth.ts`): `loadAppCredential` (`APP_SLUG`/`APP_KEY_ID`/`APP_PRIVATE_KEY`,
252
- PEM validated at boot), `signAppJwt` (RS256, `iat` now − 60 s, `exp` window 540 s ≤ 600 s,
290
+ - **app-auth** (`app-auth.ts`): `loadAppCredential` (`APP_SLUG`/`APP_KEY_ID`/`APP_PRIVATE_KEY`
291
+ — base64 of the PEM, raw PEM, or `\n`-escaped one-line PEM; RSA validated at boot),
292
+ `signAppJwt` (RS256, `iat` now − 60 s, `exp` window 540 s ≤ 600 s,
253
293
  `kid` header), the wire-contract error codes in one place (`INVALID_CLIENT_CODE`,
254
294
  `APP_TOKEN_REVOKED_CODE`, `APP_INSTALLATION_GONE_CODE`, `APP_INSTALLATION_PENDING_CODE`,
255
295
  `RESYNC_COOLDOWN_CODE`, `TOO_MANY_REQUESTS_CODE`), `AppMintHaltedError`.
@@ -265,8 +305,9 @@ retries (~4 h) are gone; resync cannot backfill them. Full runbook: `docs/deploy
265
305
  - **verify** (`signatures.ts`): `verifyQueekSignature` — Standard Webhooks verification (`webhook-id`, `webhook-timestamp`, `webhook-signature` over `{id}.{timestamp}.{body}`, keyed by the decoded `whsec_…` bytes), with timestamp-skew enforcement.
266
306
  - **delivery core** (`delivery.ts`): `CoreDelivery` (`rawBody` + `headers`) / `InstallDelivery` (+ `method`/`path`) / `DeliveryResult` (`{ status, body }`) / `CoreHeaders`, plus `readHeader` (case-insensitive, array-tolerant), `decodeBody`, and `toResponse`. Zero request/response types.
267
307
  - **install handlers** (`install-handlers.ts`): layer 1 `handleInstallDelivery(input, { appSecret, store, onInstall?, onUninstall?, onSettings? })` serves the signed install/uninstall/settings handoff from raw bytes + headers (routes on the path's trailing segment); layer 2 `handleInstallRequest(request, …)` adapts `Request` → `Response` onto it. Defaults persist the installation (encrypted) in the store; a redelivered install for an existing installation merges idempotently (`saveResyncedInstallation`). The Hono wrapper `createInstallHandlers` lives under `@usequeek/app-sdk/hono` (`hono.ts`).
268
- - **client** (`client.ts`): `createQueekClient({ apiBase, apiKey })` — the low-level typed fetch client over the Merchant API (`X-Client-Key`), with `Idempotency-Key` on writes, typed `QueekApiError`s, and 429 retry helpers. Types come from `openapi/merchant.json`, the committed snapshot of the live contract. Prefer `createInstallationClient` in apps.
269
- - **webhooks** (`webhooks.ts`): layer 1 `handleWebhookDelivery(input, { store, handlers })` verifies each delivery against the installation's endpoint secret, dedupes on `webhook-id`, and dispatches `topic → handler` at most once; layer 2 `handleWebhookRequest(request, …)` adapts `Request` → `Response` onto it. The Hono wrapper `createWebhookHandler` lives under `@usequeek/app-sdk/hono` (`hono.ts`).
308
+ - **client** (`client.ts`): `createQueekClient({ apiBase, apiKey })` — the low-level typed fetch client over the Merchant API (`X-Client-Key`), with `Idempotency-Key` on writes, typed `QueekApiError`s, and 429 retry helpers. Both it and `createInstallationClient({ installationId, apiBase, tokens })` are generic (`<AppPaths>`, default: the bundled `merchant-schema.ts` compat shim): pass the app's own codegen output for current types with zero SDK publish. The default does NOT auto-update (re-run codegen in the app; sunset signal at 1.0). `openapi/merchant.json` stays in-repo as `gen:merchant`'s reference input and is not shipped in the published package. Prefer `createInstallationClient` in apps.
309
+ - **webhooks** (`webhooks.ts`): layer 1 `handleWebhookDelivery(input, { store, handlers })` verifies each delivery against the installation's endpoint secret, dedupes on `webhook-id`, and dispatches `topic → handler` at most once; layer 2 `handleWebhookRequest(request, …)` adapts `Request` → `Response` onto it. The Hono wrapper `createWebhookHandler` lives under `@usequeek/app-sdk/hono` (`hono.ts`). Unknown topics answer 200 `unhandled` — see Data deletion before relying on that.
310
+ - **proxy** (`proxy.ts`): `verifyProxyQuery` / `verifyProxyQueryDetailed` — app-proxy query verification byte-exact with the backend (`path\nshop\nts\nsorted(k=v&...)`, hex HMAC-SHA256 over the FULL `whsec_…` string, 5-minute skew floored at 60 s, `timingSafeEqual`, previous-secret grace over the secrets list); layer 1 `verifyProxyDelivery(input, { store, … })` resolves the installation from the store by `kid` and claims single-use `jti`; layer 2 `handleProxyRequest(request, { store, path, … }, onVerified)` serves GET only. The Hono wrapper `createProxyHandler` lives under `@usequeek/app-sdk/hono` (`hono.ts`).
270
311
  - **store** (`store.ts`): `SqliteInstallationStore` (local/dev/test) and `PostgresInstallationStore`
271
312
  (`pg`, pool max 2, advisory-locked schema + `schema_version` row so two containers boot
272
313
  safely) — installations encrypted at rest (AES-GCM via `APP_ENCRYPTION_KEY`), plus the
@@ -282,16 +323,128 @@ retries (~4 h) are gone; resync cannot backfill them. Full runbook: `docs/deploy
282
323
  PEM private-key blocks — the app JWT and private key can never reach logs.
283
324
  - **session** (`session.ts`, server-only via `@usequeek/app-sdk/server`):
284
325
  `verifySessionToken` — HS256 dashboard session tokens minted per installation
285
- (`embsec_…` secret, raw UTF-8 key bytes, 20 s clock tolerance, slug audience
286
- + api_base issuer, full installation binding). The secret never enters a
326
+ (`embsec_…` secret, raw UTF-8 key bytes, 20 s clock tolerance, slug audience,
327
+ issuer = the handoff `apiBase` verbatim (`installation.apiBase` — it equals the
328
+ bare `app.url` the backend signs as `iss`), full installation binding). The secret never enters a
287
329
  browser bundle: the main entry does not export the verifier. The install
288
330
  and resync handoffs deliver `embed_secret` + `app_id`; the store keeps
289
331
  them on the installation (`embedSecret` encrypted, `appId`), and
290
332
  `sessionTokenInstallationId` reads the token's `installation_id` as an
291
333
  unverified routing hint so a server can load that row before verifying.
292
- - **frame** (`frame.ts`, browser-safe, no secret): `listenToDashboard`
293
- (accepts only the exact dashboard origin) and `sendReady` (exact target
294
- origin, never `"*"`).
334
+ `verifyLaunchToken` checks the signed-first-load token the same way plus the purpose pin
335
+ (`purpose` must be `launch`, with the `store` p_id and `theme` hints) — a launch token is refused
336
+ as a bridge token and vice versa.
337
+ - **frame** (`frame.ts`, browser-safe, no secret): the embedded-app bridge
338
+ v1 — typed unions both directions (`ready{capabilities,sdkVersion}`,
339
+ `theme`, `title`/`title-action`, `toast`, `save-bar`/`save-bar-action`,
340
+ `navigate`/`navigated`, `open`, `pick-resource`/`resource-picked`/
341
+ `resource-pick-cancelled`), unknown types ignored, every outbound string
342
+ length-capped client-side. `listenToDashboard` (accepts only the exact
343
+ dashboard origin, plus an `expectSource` window check) and typed senders
344
+ (`sendReady`, `sendToast`, `sendSaveBar`, `sendTitle`, `sendNavigated`,
345
+ `sendOpen`, `sendPickResource`, …) to the exact target origin, never `"*"`.
346
+ `parseOutboundMessage` runs the same per-type validation the dashboard
347
+ runs — the dashboard re-validates everything before acting (open targets,
348
+ title/toast/pick content), so a compromised frame cannot smuggle
349
+ `javascript:` URLs or uncapped strings through the bridge. `open` targets
350
+ are a dashboard-relative path or an absolute `https:` URL; backslashes,
351
+ control characters, protocol-relative URLs and every other scheme are
352
+ refused (by `clipOutbound` as well as `isAllowedOpenTarget`).
353
+
354
+ **Bridge handshake.** The app announces `ready{capabilities,sdkVersion}`;
355
+ the dashboard answers with its `theme` message, which also declares the
356
+ DASHBOARD's capabilities: `{ type: "theme", mode, locale?, capabilities?:
357
+ string[], bridge?: "1" }` (same caps as outbound: at most 32 entries of 64
358
+ characters). `listenToDashboard` exposes both on the `BridgeTheme` passed to
359
+ `onTheme`. A dashboard that omits `capabilities` is legacy (ready/token/
360
+ resize only). `<QueekProvider>` stores the declared set: `pickResource`
361
+ rejects at once when it lacks `pick-resource`, waits up to 1.5 s after
362
+ `ready` when the set is still unknown, and rejects if no handshake arrives.
363
+ Each pick carries a `requestId` (on `pick-resource`); a dashboard that
364
+ declares `pick-resource` MUST echo it on `resource-picked` / `resource-pick-cancelled` — answers with a
365
+ different or missing id are dropped as stale.
366
+ - **auth** (`auth-fetch.ts`, browser-safe, no secret): `installAuthFetch({ exchange })`
367
+ — reads the launch token (`queek_token` query param) from the signed first
368
+ load, strips it with `history.replaceState`, exchanges it once for the
369
+ app's own session, attaches `Authorization: Bearer <session>` to
370
+ same-origin fetch (cookies stay out), and re-establishes via the bridge
371
+ `ready→token` flow + one retry on 401. Session recovery runs only when framed (a post target other than the
372
+ window itself) and is not retried per request after a failed attempt — only
373
+ after a later dashboard token arrives. Pass the same `capabilities`/
374
+ `sdkVersion` as the provider so its refresh `ready` announces one consistent set. The app's
375
+ token-exchange endpoint receives the LAUNCH token on first load and a BRIDGE token on every
376
+ 401 refresh through the same `exchange` callback, so it must try
377
+ `verifyLaunchTokenDetailed`/`verifyLaunchToken` first and fall back to `verifySessionToken`
378
+ (both purposes):
379
+
380
+ ```ts
381
+ import { verifyLaunchTokenDetailed, verifySessionTokenDetailed } from "@usequeek/app-sdk/server";
382
+
383
+ async function exchange(token: string): Promise<string> {
384
+ const options = { secret: installation.embedSecret!, audience: "my-app", issuer: installation.apiBase, expected };
385
+ const launch = await verifyLaunchTokenDetailed(token, options);
386
+ if (launch.ok) return mintAppSession(launch.claims);
387
+ const bridge = await verifySessionTokenDetailed(token, options);
388
+ if (bridge.ok) return mintAppSession(bridge.claims);
389
+ throw Object.assign(new Error("unauthorized"), { status: 401 });
390
+ }
391
+ ```
392
+ - **theme** (`theme.ts`, browser-safe, no secret): `applyTheme(mode)` toggles
393
+ `.dark` on `<html>` + `color-scheme` (shadcn's dark-mode mechanism);
394
+ `themeBootstrapScript()` returns the inline `<head>` snippet that reads
395
+ `theme` from the URL before first paint (no flash);
396
+ `installThemeListener` follows live bridge `theme{mode}` messages (only from
397
+ `window.parent` by default; pass `expectSource` to override). The live mode is
398
+ remembered in `sessionStorage`, and the bootstrap script falls back to it when
399
+ the URL carries no `theme` param, so an in-frame reload never flashes light. Every live `theme` message also
400
+ rewrites the `theme` URL param (other params kept) so a stale first-load param
401
+ never beats the current mode on reload, and the first-load mode is remembered
402
+ too (a legacy dashboard that never sends `theme` still reloads flash-free).
403
+ - **react** (`react.ts`, via `@usequeek/app-sdk/react` — `react` is an
404
+ optional peer, same pattern as `./hono`): `<QueekProvider>` owns one
405
+ bridge subscription; `useQueek()` returns `{ toast, saveBar, title,
406
+ navigate, pickResource, theme }` over the framework-free core.
407
+ - **scopes** (`scopes.ts`): `createInstallationScopesClient({ installationId, store, signJwt, … })`
408
+ — the optional-scopes session, wired to the same store + token provider as the
409
+ installation client. `queryScopes` reads the cached grant (no network);
410
+ `requestScopes` builds the dashboard consent link (pure — the SDK never
411
+ renders consent); `revokeScopes` posts the app-authenticated revoke and
412
+ refreshes the cache. The install/settings handlers accept the signed
413
+ `app/scopes_update` handoff on both routes and refresh the cached grant
414
+ (dropping the cached token when it moved).
415
+
416
+ ## Scopes
417
+
418
+ Apps declare required scopes (granted at install) plus optional scopes
419
+ (requested later, revocable). The per-installation session covers the app side:
420
+
421
+ ```ts
422
+ import { createInstallationScopesClient } from "@usequeek/app-sdk";
423
+
424
+ const scopes = createInstallationScopesClient({
425
+ installationId,
426
+ store,
427
+ signJwt: () => provider.signJwt(),
428
+ appSlug: "my-app",
429
+ optionalScopes: ["merchant-orders-read"], // the app's own manifest knowledge
430
+ });
431
+
432
+ await scopes.queryScopes(); // { granted, optional } — cached, no network
433
+ scopes.requestScopes(["merchant-orders-read"]); // dashboard consent link (open via sendOpen)
434
+ await scopes.revokeScopes(["merchant-orders-read"]); // 422 app_scope_required → AppScopeRequiredError
435
+ ```
436
+
437
+ `queryScopes` splits the cached effective grant against the declared-optional
438
+ list you pass (the app surface exposes the grant but no declared list, so the
439
+ split needs your manifest knowledge). `requestScopes` returns
440
+ `/apps?app={slug}&view=scopes&scopes={a},{b}` (absolute under
441
+ `dashboardOrigin` when configured) — open it via `sendOpen` or a redirect: it
442
+ opens the merchant's consent screen in the dashboard (shipped with dashboard
443
+ item 8). The link carries the slug + the scope list only; the dashboard
444
+ resolves the installation from the signed-in store. The grant reaches you as a
445
+ signed `app/scopes_update` handoff, which the install/settings handlers apply
446
+ like a resync (same verifier, same replay claim): cached grant refreshed,
447
+ cached token dropped only when the grant moved.
295
448
 
296
449
  ## Embedded merchant page (S4 stage 2)
297
450
 
@@ -307,8 +460,11 @@ Content-Security-Policy: frame-ancestors https://merchant.queek.com
307
460
  Replace the host with the dashboard origin you registered. `frame-ancestors`
308
461
  is a docs-only control the dashboard cannot enforce for you: without it any
309
462
  site may frame the page, and the token handshake (origin-bound) is your only
310
- remaining gate. Verify every token server-side with `verifySessionToken`
311
- before trusting calls that carry one.
463
+ remaining gate. Verify every token server-side before trusting calls that carry one:
464
+ the token-exchange endpoint receives the LAUNCH token on first load and a BRIDGE token on
465
+ every 401 refresh through the same `installAuthFetch({ exchange })` callback, so it must try
466
+ `verifyLaunchTokenDetailed`/`verifyLaunchToken` first and fall back to `verifySessionToken`
467
+ (both purposes — see the `auth` entry above).
312
468
 
313
469
  ## Local development
314
470
 
@@ -38,6 +38,12 @@ export declare const APP_INSTALLATION_PENDING_CODE = "app_installation_pending";
38
38
  export declare const RESYNC_COOLDOWN_CODE = "resync_cooldown";
39
39
  /** 429 everywhere: honor `Retry-After`, jittered. */
40
40
  export declare const TOO_MANY_REQUESTS_CODE = "too_many_requests";
41
+ /**
42
+ * 403 on a merchant call: the cached installation token was minted for an
43
+ * older grant (missing scope). The grant changed server-side — drop the
44
+ * cached token, re-mint once, retry once.
45
+ */
46
+ export declare const INSUFFICIENT_SCOPE_CODE = "insufficient_scope";
41
47
  /** True when a Queek error code is the kill-switch/disabled refusal. */
42
48
  export declare function isAppTokenRevoked(code: string | undefined): boolean;
43
49
  /** True when a Queek error code is the fatal app-credential refusal. */
@@ -53,7 +59,7 @@ export declare function isResyncCooldown(code: string | undefined): boolean;
53
59
  * is dead — drop it, re-mint once, retry once. `api_key_revoked` covers the
54
60
  * uninstalled store, the removed kid, and the replaced K-slot; the re-mint
55
61
  * then answers 404 `app_installation_gone` (purge) or 401 `invalid_client`
56
- * (halt) where applicable. Every OTHER merchant 403 (scope, plan, mode)
62
+ * (halt) where applicable. Every OTHER merchant 403 (plan, mode)
57
63
  * propagates to the caller without a mint.
58
64
  */
59
65
  export declare const API_KEY_REVOKED_CODE = "api_key_revoked";
@@ -66,6 +72,8 @@ export declare const INVALID_CLIENT_KEY_CODE = "invalid_client_key";
66
72
  * halt), not a single dead token.
67
73
  */
68
74
  export declare function isTokenRefusal(status: number, code: string | undefined): boolean;
75
+ /** True when a merchant-API 403 means the cached token predates a grant change. */
76
+ export declare function isInsufficientScope(status: number, code: string | undefined): boolean;
69
77
  /** App-JWT `iat` is backdated this far (clock skew, GitHub's recommendation). */
70
78
  export declare const APP_JWT_SKEW_SECONDS = 60;
71
79
  /** App-JWT lifetime: `exp = iat + 540`, so `exp − iat` (540 s) stays ≤ the 600 s wire bound. */
@@ -117,9 +125,22 @@ export interface AppCredentialOptions {
117
125
  privateKeyPem?: string;
118
126
  env?: NodeJS.ProcessEnv;
119
127
  }
128
+ /**
129
+ * Accepted `APP_PRIVATE_KEY` forms (env files are one line per variable):
130
+ * raw PEM text, one-line PEM with literal `\n` escapes, or base64 of the
131
+ * full PEM text (one line, no wrapping — what the Developer page shows).
132
+ */
133
+ export declare const APP_PRIVATE_KEY_FORMS = "raw PEM text, one-line PEM with literal \\n escapes, or base64 of the PEM text";
134
+ /**
135
+ * Normalize an `APP_PRIVATE_KEY` value to PEM text. Detection: a trimmed
136
+ * value starting with `-----BEGIN` is PEM (literal `\n` unescaped first);
137
+ * anything else is base64-decoded and must decode to `-----BEGIN…`.
138
+ * Never echoes the value.
139
+ */
140
+ export declare function decodePrivateKeyInput(raw: string): string;
120
141
  /**
121
142
  * Load the app credential: explicit options win, otherwise `APP_SLUG`,
122
- * `APP_KEY_ID`, `APP_PRIVATE_KEY` from env. The PEM must parse as an RSA
143
+ * `APP_KEY_ID`, `APP_PRIVATE_KEY` from env. The key must parse as an RSA
123
144
  * private key here, once, so a broken key fails at boot — never mid-mint.
124
145
  * Throws `InvalidAppCredentialError` before any fetch.
125
146
  */
@@ -1 +1 @@
1
- {"version":3,"file":"app-auth.d.ts","sourceRoot":"","sources":["../src/app-auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app-auth.d.ts","sourceRoot":"","sources":["../src/app-auth.ts"],"names":[],"mappings":"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"}
package/dist/app-auth.js CHANGED
@@ -44,6 +44,12 @@ export const APP_INSTALLATION_PENDING_CODE = "app_installation_pending";
44
44
  export const RESYNC_COOLDOWN_CODE = "resync_cooldown";
45
45
  /** 429 everywhere: honor `Retry-After`, jittered. */
46
46
  export const TOO_MANY_REQUESTS_CODE = "too_many_requests";
47
+ /**
48
+ * 403 on a merchant call: the cached installation token was minted for an
49
+ * older grant (missing scope). The grant changed server-side — drop the
50
+ * cached token, re-mint once, retry once.
51
+ */
52
+ export const INSUFFICIENT_SCOPE_CODE = "insufficient_scope";
47
53
  /** True when a Queek error code is the kill-switch/disabled refusal. */
48
54
  export function isAppTokenRevoked(code) {
49
55
  return code === APP_TOKEN_REVOKED_CODE;
@@ -69,7 +75,7 @@ export function isResyncCooldown(code) {
69
75
  * is dead — drop it, re-mint once, retry once. `api_key_revoked` covers the
70
76
  * uninstalled store, the removed kid, and the replaced K-slot; the re-mint
71
77
  * then answers 404 `app_installation_gone` (purge) or 401 `invalid_client`
72
- * (halt) where applicable. Every OTHER merchant 403 (scope, plan, mode)
78
+ * (halt) where applicable. Every OTHER merchant 403 (plan, mode)
73
79
  * propagates to the caller without a mint.
74
80
  */
75
81
  export const API_KEY_REVOKED_CODE = "api_key_revoked";
@@ -87,6 +93,10 @@ export function isTokenRefusal(status, code) {
87
93
  return (status === 403 &&
88
94
  (code === API_KEY_REVOKED_CODE || code === API_KEY_EXPIRED_CODE || code === INVALID_CLIENT_KEY_CODE));
89
95
  }
96
+ /** True when a merchant-API 403 means the cached token predates a grant change. */
97
+ export function isInsufficientScope(status, code) {
98
+ return status === 403 && code === INSUFFICIENT_SCOPE_CODE;
99
+ }
90
100
  // ---------------------------------------------------------------------------
91
101
  // Timing bounds (all in the open, all injectable in tests).
92
102
  // ---------------------------------------------------------------------------
@@ -140,9 +150,32 @@ export class UnknownInstallationError extends Error {
140
150
  this.name = "UnknownInstallationError";
141
151
  }
142
152
  }
153
+ /**
154
+ * Accepted `APP_PRIVATE_KEY` forms (env files are one line per variable):
155
+ * raw PEM text, one-line PEM with literal `\n` escapes, or base64 of the
156
+ * full PEM text (one line, no wrapping — what the Developer page shows).
157
+ */
158
+ export const APP_PRIVATE_KEY_FORMS = "raw PEM text, one-line PEM with literal \\n escapes, or base64 of the PEM text";
159
+ /**
160
+ * Normalize an `APP_PRIVATE_KEY` value to PEM text. Detection: a trimmed
161
+ * value starting with `-----BEGIN` is PEM (literal `\n` unescaped first);
162
+ * anything else is base64-decoded and must decode to `-----BEGIN…`.
163
+ * Never echoes the value.
164
+ */
165
+ export function decodePrivateKeyInput(raw) {
166
+ const trimmed = raw.trim();
167
+ if (trimmed.startsWith("-----BEGIN")) {
168
+ return trimmed.replace(/\\n/g, "\n").trim();
169
+ }
170
+ const decoded = Buffer.from(trimmed, "base64").toString("utf8");
171
+ if (!decoded.trimStart().startsWith("-----BEGIN")) {
172
+ throw new InvalidAppCredentialError(`Invalid APP_PRIVATE_KEY: not a PEM and not base64 of a PEM. Accepted forms: ${APP_PRIVATE_KEY_FORMS}.`);
173
+ }
174
+ return decoded.trim();
175
+ }
143
176
  /**
144
177
  * Load the app credential: explicit options win, otherwise `APP_SLUG`,
145
- * `APP_KEY_ID`, `APP_PRIVATE_KEY` from env. The PEM must parse as an RSA
178
+ * `APP_KEY_ID`, `APP_PRIVATE_KEY` from env. The key must parse as an RSA
146
179
  * private key here, once, so a broken key fails at boot — never mid-mint.
147
180
  * Throws `InvalidAppCredentialError` before any fetch.
148
181
  */
@@ -156,10 +189,11 @@ export function loadAppCredential(options = {}) {
156
189
  if (keyId === "") {
157
190
  throw new InvalidAppCredentialError("Missing APP_KEY_ID: set it to the kid registered with Queek (`php artisan app:register --public-key=…`).");
158
191
  }
159
- const privateKeyPem = (options.privateKeyPem ?? env.APP_PRIVATE_KEY ?? "").trim();
160
- if (privateKeyPem === "") {
161
- throw new InvalidAppCredentialError("Missing APP_PRIVATE_KEY: set it to the app's PEM-encoded RSA private key.");
192
+ const privateKeyInput = (options.privateKeyPem ?? env.APP_PRIVATE_KEY ?? "").trim();
193
+ if (privateKeyInput === "") {
194
+ throw new InvalidAppCredentialError(`Missing APP_PRIVATE_KEY: set it to base64 of the app's RSA private key PEM (one line). Accepted forms: ${APP_PRIVATE_KEY_FORMS}.`);
162
195
  }
196
+ const privateKeyPem = decodePrivateKeyInput(privateKeyInput);
163
197
  try {
164
198
  const key = createPrivateKey(privateKeyPem);
165
199
  if (key.asymmetricKeyType !== "rsa") {
@@ -169,7 +203,7 @@ export function loadAppCredential(options = {}) {
169
203
  catch (error) {
170
204
  if (error instanceof InvalidAppCredentialError)
171
205
  throw error;
172
- throw new InvalidAppCredentialError(`Invalid APP_PRIVATE_KEY: not a parseable PEM private key (${error instanceof Error ? error.message : "undecodable"}).`);
206
+ throw new InvalidAppCredentialError(`Invalid APP_PRIVATE_KEY: not a parseable PEM private key (${error instanceof Error ? error.message : "undecodable"}). Accepted forms: ${APP_PRIVATE_KEY_FORMS}.`);
173
207
  }
174
208
  // The PEM stays in this object only; callers must never log it (see
175
209
  // `signAppJwt`: the only consumer, and it never emits the key).
@@ -1 +1 @@
1
- {"version":3,"file":"app-auth.js","sourceRoot":"","sources":["../src/app-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE3D;;;;;;;;;;;GAWG;AAEH,8EAA8E;AAC9E,0EAA0E;AAC1E,2EAA2E;AAC3E,4BAA4B;AAC5B,8EAA8E;AAE9E,wFAAwF;AACxF,MAAM,CAAC,MAAM,mBAAmB,GAAG,gBAAgB,CAAC;AACpD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAC1D,+EAA+E;AAC/E,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAC;AAClE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,0BAA0B,CAAC;AACxE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AACtD,qDAAqD;AACrD,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAE1D,wEAAwE;AACxE,MAAM,UAAU,iBAAiB,CAAC,IAAwB;IACxD,OAAO,IAAI,KAAK,sBAAsB,CAAC;AACzC,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,eAAe,CAAC,IAAwB;IACtD,OAAO,IAAI,KAAK,mBAAmB,CAAC;AACtC,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,kBAAkB,CAAC,IAAwB;IACzD,OAAO,IAAI,KAAK,0BAA0B,CAAC;AAC7C,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,qBAAqB,CAAC,IAAwB;IAC5D,OAAO,IAAI,KAAK,6BAA6B,CAAC;AAChD,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,gBAAgB,CAAC,IAAwB;IACvD,OAAO,IAAI,KAAK,oBAAoB,CAAC;AACvC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AACtD,MAAM,CAAC,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AACtD,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAC;AAE5D;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,IAAwB;IACrE,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAChC,OAAO,CACL,MAAM,KAAK,GAAG;QACd,CAAC,IAAI,KAAK,oBAAoB,IAAI,IAAI,KAAK,oBAAoB,IAAI,IAAI,KAAK,uBAAuB,CAAC,CACrG,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,4DAA4D;AAC5D,8EAA8E;AAE9E,iFAAiF;AACjF,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AACvC,gGAAgG;AAChG,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AACvC,oGAAoG;AACpG,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAC/C,yFAAyF;AACzF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AACnC,mGAAmG;AACnG,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AACxC,wFAAwF;AACxF,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,CAAC;AACzC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAEzC,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,0FAA0F;AAC1F,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IACzC,IAAI,GAAG,wBAAwB,CAAC;IAEzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;IAC1C,CAAC;CACF;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAClC,IAAI,GAAG,iBAAiB,CAAC;IACzB,MAAM,CAA6D;IAE5E,YAAY,MAAoC,EAAE,MAAc;QAC9D,KAAK,CAAC,mBAAmB,MAAM,MAAM,MAAM,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED,gEAAgE;AAChE,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACxC,IAAI,GAAG,sBAAsB,CAAC;IAEvC,YAAY,cAAsB;QAChC,KAAK,CAAC,wBAAwB,cAAc,6CAA6C,CAAC,CAAC;QAC3F,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AAkBD;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAgC,EAAE;IAClE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/D,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QACnB,MAAM,IAAI,yBAAyB,CACjC,6FAA6F,CAC9F,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7D,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,yBAAyB,CACjC,0GAA0G,CAC3G,CAAC;IACJ,CAAC;IACD,MAAM,aAAa,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAClF,IAAI,aAAa,KAAK,EAAE,EAAE,CAAC;QACzB,MAAM,IAAI,yBAAyB,CACjC,2EAA2E,CAC5E,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,gBAAgB,CAAC,aAAa,CAAC,CAAC;QAC5C,IAAI,GAAG,CAAC,iBAAiB,KAAK,KAAK,EAAE,CAAC;YACpC,MAAM,IAAI,yBAAyB,CACjC,mDAAmD,GAAG,CAAC,iBAAiB,IAAI,SAAS,aAAa,CACnG,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,yBAAyB;YAAE,MAAM,KAAK,CAAC;QAC5D,MAAM,IAAI,yBAAyB,CACjC,6DAA6D,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,IAAI,CACxH,CAAC;IACJ,CAAC;IACD,oEAAoE;IACpE,gEAAgE;IAChE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;AAC3C,CAAC;AAED,SAAS,SAAS,CAAC,KAAsB;IACvC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC5D,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,OAA0B;IACnD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAChE,MAAM,GAAG,GAAG,GAAG,GAAG,oBAAoB,CAAC;IACvC,MAAM,GAAG,GAAG,GAAG,GAAG,mBAAmB,CAAC;IACtC,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACtG,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IACzF,MAAM,YAAY,GAAG,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC;IAC5C,IAAI,SAAiB,CAAC;IACtB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QACpC,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IAC5D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,yBAAyB,CACjC,+EAA+E,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAC1L,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,YAAY,IAAI,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;AAC9D,CAAC"}
1
+ {"version":3,"file":"app-auth.js","sourceRoot":"","sources":["../src/app-auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Same-origin session carriage for embedded apps (U-validation "Auth
3
+ * carriage"): `installAuthFetch()` reads the launch token from the signed
4
+ * first load, exchanges it ONCE for the app's own session, and attaches
5
+ * `Authorization: Bearer <session>` to every same-origin fetch. A 401
6
+ * re-establishes the session via the bridge `ready→token` flow and retries
7
+ * once. Cookies stay out (`credentials: "omit"` on handled requests —
8
+ * the cross-site iframe cannot rely on them anyway).
9
+ *
10
+ * The caller supplies `exchange(token) => session`: the app's own endpoint
11
+ * that verifies the dashboard token server-side and mints the app
12
+ * session — the exchange runs once on the signed first load so the first
13
+ * paint needs no bridge round-trip, and again (same callback) with a
14
+ * bridge token after every 401 refresh. The endpoint therefore receives
15
+ * the LAUNCH token on first load and a BRIDGE token on every refresh, so
16
+ * it must try `verifyLaunchTokenDetailed`/`verifyLaunchToken` first and
17
+ * fall back to `verifySessionToken` (both purposes). This module never
18
+ * sees secrets — it only carries opaque tokens.
19
+ *
20
+ * DOM-free by design (like `frame.ts`): location/history access is
21
+ * injectable and defaults to the global window when present.
22
+ */
23
+ import { type EmbedEventTarget, type EmbedPostTarget } from "./frame.js";
24
+ /**
25
+ * Launch token query param on the signed first load. Matches the dashboard
26
+ * frame src contract (`launchFrameUrl` in queek-merchant: the ONLY token
27
+ * that may ride a URL, namespaced so it cannot collide with an app's own
28
+ * `token` param).
29
+ */
30
+ export declare const LAUNCH_TOKEN_PARAM = "queek_token";
31
+ /** How long to wait for the dashboard's token reply after a `ready`. */
32
+ export declare const BRIDGE_TOKEN_TIMEOUT_MS = 8000;
33
+ export interface AuthFetchOptions {
34
+ /**
35
+ * Exchange a dashboard token for the app's own session (caller's endpoint).
36
+ * The endpoint receives the LAUNCH token on first load and a BRIDGE token
37
+ * on every 401 refresh through this same callback, so it must try
38
+ * `verifyLaunchTokenDetailed`/`verifyLaunchToken` first and fall back to
39
+ * `verifySessionToken` (both purposes).
40
+ */
41
+ exchange: (token: string) => Promise<string>;
42
+ /** Exact dashboard origin for the `ready→token` refresh flow. */
43
+ dashboardOrigin: string;
44
+ /** Launch-token query param (default `queek_token`). */
45
+ param?: string;
46
+ /** postMessage target for `ready` (defaults to the global window's parent). */
47
+ postTarget?: EmbedPostTarget;
48
+ /**
49
+ * Capabilities announced in the refresh `ready` — pass the SAME list as the
50
+ * React provider / theme listener so the dashboard sees one consistent set.
51
+ */
52
+ capabilities?: string[];
53
+ /** SDK version announced in the refresh `ready`. */
54
+ sdkVersion?: string;
55
+ /** message-event target for the token reply (defaults to the global window). */
56
+ listenTarget?: EmbedEventTarget;
57
+ /**
58
+ * Required `event.source` for bridge messages. Defaults to the global
59
+ * window's parent when present (the embedding dashboard); pass `null` to
60
+ * disable the source check.
61
+ */
62
+ expectSource?: unknown;
63
+ /** fetch implementation (defaults to the global fetch). */
64
+ fetchImpl?: typeof fetch;
65
+ /** Current href (defaults to the global window location). */
66
+ getHref?: () => string;
67
+ /** Replace the URL without navigating (defaults to history.replaceState). */
68
+ replaceUrl?: (url: string) => void;
69
+ }
70
+ export interface InstalledAuth {
71
+ /** Session-carrying fetch: same-origin requests gain the session as a Bearer token, and a 401 retries once after a refresh. */
72
+ fetch: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
73
+ /** Current app session, or null before the exchange settles / without a token. */
74
+ getSession: () => string | null;
75
+ /** Settles to the session, or null when there is no launch token to exchange. */
76
+ ready: Promise<string | null>;
77
+ /** Re-establish the session via the bridge `ready→token` flow. */
78
+ refresh: () => Promise<string>;
79
+ /** Unsubscribe the bridge token listener. */
80
+ dispose: () => void;
81
+ }
82
+ /** Read the launch token from a URL without touching it. */
83
+ export declare function readLaunchToken(href: string, param?: string): string | null;
84
+ /**
85
+ * Strip the launch token from a URL (every other param, plus the hash, is
86
+ * preserved). Returns the stripped href, or null when no token is present.
87
+ */
88
+ export declare function stripLaunchToken(href: string, param?: string): string | null;
89
+ export declare function installAuthFetch(options: AuthFetchOptions): InstalledAuth;
90
+ //# sourceMappingURL=auth-fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-fetch.d.ts","sourceRoot":"","sources":["../src/auth-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAgC,MAAM,YAAY,CAAC;AAEvG;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,gBAAgB,CAAC;AAEhD,wEAAwE;AACxE,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,MAAM,WAAW,gBAAgB;IAC/B;;;;;;OAMG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,iEAAiE;IACjE,eAAe,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,MAAM,CAAC;IACvB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,+HAA+H;IAC/H,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChF,kFAAkF;IAClF,UAAU,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAChC,iFAAiF;IACjF,KAAK,EAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9B,kEAAkE;IAClE,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/B,6CAA6C;IAC7C,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAeD,4DAA4D;AAC5D,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,MAA2B,GAAG,MAAM,GAAG,IAAI,CAO/F;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,MAA2B,GAAG,MAAM,GAAG,IAAI,CAWhG;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,aAAa,CAqNzE"}