@usequeek/app-sdk 0.5.1 → 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 +166 -12
  2. package/dist/app-auth.d.ts +9 -1
  3. package/dist/app-auth.d.ts.map +1 -1
  4. package/dist/app-auth.js +11 -1
  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 -162
  74. package/openapi/merchant.json +0 -21452
package/dist/scopes.js ADDED
@@ -0,0 +1,297 @@
1
+ import { isInstallationGone, UnknownInstallationError } from "./app-auth.js";
2
+ import { newIdempotencyKey, QueekApiError, queekApiErrorFromResponse } from "./client.js";
3
+ import { isAllowedOpenTarget } from "./frame.js";
4
+ import { installationScopesEqual } from "./install-handlers.js";
5
+ import { resolveAppApiBase } from "./tokens.js";
6
+ /**
7
+ * Optional scopes (plan item 6, Shopify `scopes.query / request / revoke`
8
+ * parity — symbol names cited only).
9
+ *
10
+ * - `queryScopes`: the cached effective grant split against the app's own
11
+ * declared-optional list. Store-read only, never a fetch: the app-credential
12
+ * surface exposes the effective grant but no declared-optional list, so
13
+ * the split needs the app's manifest knowledge (passed at client
14
+ * construction). No network, no guessing.
15
+ * - `requestScopes`: builds the dashboard deep link that opens the
16
+ * merchant's consent screen in the dashboard. The SDK never renders
17
+ * consent — it hands the app a link shaped like the bridge
18
+ * `open`/`navigate` targets (dashboard-relative path, or an absolute URL
19
+ * under a given dashboard origin) for the app to open via `sendOpen` or
20
+ * redirect to. The consent screen itself ships with the dashboard (plan
21
+ * item 8), which honours this link shape.
22
+ * - `revokeScopes`: `POST
23
+ * /api/v1/apps/installations/{installation}/scopes/revoke` with the app
24
+ * JWT (same credential as mint/resync — never auth:sanctum, never the
25
+ * installation token). Body `{scopes[]}` (1–50, backend
26
+ * `RevokeAppScopesRequest`); an `Idempotency-Key` rides the write for
27
+ * discipline (generated when the caller does not supply one — the client
28
+ * write discipline), though the route is naturally idempotent: revoking
29
+ * a never-granted optional scope is 200 with no change. Required scopes
30
+ * refuse 422 `app_scope_required` (surfaced as `AppScopeRequiredError`).
31
+ * On 200 the cached grant is refreshed from the response and the cached
32
+ * token dropped only when the grant moved (same compare-and-clear as the
33
+ * scopes_update handler — the backend rewrites live token rows in place,
34
+ * so the drop is backstop, not mechanism).
35
+ *
36
+ * Types here are hand-written: the revoke endpoint and the scopes_update
37
+ * handoff live on the app-credential surface, which the frozen
38
+ * `openapi/merchant.json` does not cover (types are generated app-side by
39
+ * `queek app codegen`; the merchant spec file is never hand-edited).
40
+ */
41
+ /** 422 on the revoke endpoint: required scopes are never revocable (uninstall is the remedy). */
42
+ export const APP_SCOPE_REQUIRED_CODE = "app_scope_required";
43
+ /** 422 on the approve path: the scope is not declared by the live manifest version. */
44
+ export const APP_SCOPE_UNDECLARED_CODE = "app_scope_undeclared";
45
+ /** True for a required-scope refusal on the revoke endpoint (422 `app_scope_required`). */
46
+ export function isAppScopeRequired(status, code) {
47
+ return status === 422 && code === APP_SCOPE_REQUIRED_CODE;
48
+ }
49
+ /**
50
+ * A required-scope refusal, typed so callers switch on the class (or
51
+ * `code`), never on `message`. Carries the offending scopes when the
52
+ * backend named them.
53
+ */
54
+ export class AppScopeRequiredError extends QueekApiError {
55
+ scopes;
56
+ constructor(status, message, scopes) {
57
+ super({ status, code: APP_SCOPE_REQUIRED_CODE, message });
58
+ this.name = "AppScopeRequiredError";
59
+ this.scopes = scopes;
60
+ }
61
+ }
62
+ /** Build an `AppScopeRequiredError` from a parsed 422, tolerantly reading the named scopes. */
63
+ export function appScopeRequiredFromError(error) {
64
+ return new AppScopeRequiredError(error.status, error.message, readErrorScopes(error.errors));
65
+ }
66
+ function readErrorScopes(errors) {
67
+ if (typeof errors !== "object" || errors === null)
68
+ return [];
69
+ const scopes = errors.scopes;
70
+ if (!Array.isArray(scopes))
71
+ return [];
72
+ return scopes.filter((entry) => typeof entry === "string" && entry !== "");
73
+ }
74
+ /** Thrown when a scope list fails client-side validation — before any fetch. */
75
+ export class InvalidScopesError extends Error {
76
+ code = "invalid_scopes";
77
+ constructor(message) {
78
+ super(message);
79
+ this.name = "InvalidScopesError";
80
+ }
81
+ }
82
+ /** Backend `RevokeAppScopesRequest` bounds, mirrored client-side. */
83
+ export const MAX_SCOPE_LIST = 50;
84
+ export const MAX_SCOPE_LENGTH = 120;
85
+ /**
86
+ * Validate + normalise a scope list (trim, drop empties, dedupe keeping
87
+ * first order). Mirrors the backend contract (`required array min:1
88
+ * max:50`, string items) so a malformed call fails before any fetch.
89
+ */
90
+ export function normaliseScopeList(scopes) {
91
+ if (!Array.isArray(scopes)) {
92
+ throw new InvalidScopesError("Expected a scopes array.");
93
+ }
94
+ const cleaned = [];
95
+ for (const entry of scopes) {
96
+ if (typeof entry !== "string") {
97
+ throw new InvalidScopesError("Every scope must be a string.");
98
+ }
99
+ const trimmed = entry.trim();
100
+ if (trimmed === "" || cleaned.includes(trimmed))
101
+ continue;
102
+ if (trimmed.length > MAX_SCOPE_LENGTH) {
103
+ throw new InvalidScopesError(`Scope ${JSON.stringify(trimmed.slice(0, 40))} exceeds 120 characters.`);
104
+ }
105
+ cleaned.push(trimmed);
106
+ }
107
+ if (cleaned.length === 0) {
108
+ throw new InvalidScopesError("Name at least one scope.");
109
+ }
110
+ if (cleaned.length > MAX_SCOPE_LIST) {
111
+ throw new InvalidScopesError(`Too many scopes (max ${MAX_SCOPE_LIST}).`);
112
+ }
113
+ return cleaned;
114
+ }
115
+ /**
116
+ * Split a cached grant against the app's declared-optional list (the app's
117
+ * own manifest knowledge). Pure — no store, no network.
118
+ */
119
+ export function splitInstallationScopes(granted, declaredOptional = []) {
120
+ const declared = new Set(declaredOptional);
121
+ return {
122
+ granted: [...granted],
123
+ optional: granted.filter((scope) => declared.has(scope)),
124
+ };
125
+ }
126
+ /** App-API path of the revoke endpoint below the app base (sibling of the mint path). */
127
+ export function revokeScopesPath(installationId) {
128
+ return `/installations/${encodeURIComponent(installationId)}/scopes/revoke`;
129
+ }
130
+ /**
131
+ * Build the dashboard deep link that opens the merchant's consent screen
132
+ * in the dashboard. Pure — no store, no network. Shaped like the bridge
133
+ * `open` targets: a dashboard-relative path, or an absolute https URL
134
+ * under `dashboardOrigin` (refused exactly like `sendOpen` refuses a bad
135
+ * target).
136
+ *
137
+ * Link shape (the SDK↔dashboard contract the consent screen honours —
138
+ * the only query route the dashboard `AppsPage` serves):
139
+ * `/apps?app={slug}&view=scopes&scopes={a},{b}` (each scope
140
+ * URL-encoded, comma-separated). The link carries the slug + the scope
141
+ * list only: the dashboard resolves the installation from the signed-in
142
+ * store.
143
+ */
144
+ export function buildScopeRequestLink(options) {
145
+ const slug = options.appSlug.trim();
146
+ if (slug === "" || slug.includes("/") || slug.includes("\\")) {
147
+ throw new InvalidScopesError("A dashboard app slug is required to build the scope-request link.");
148
+ }
149
+ const requested = normaliseScopeList(options.scopes);
150
+ const path = `/apps?app=${encodeURIComponent(slug)}&view=scopes&scopes=${requested.map((scope) => encodeURIComponent(scope)).join(",")}`;
151
+ const origin = options.dashboardOrigin?.trim();
152
+ if (!origin)
153
+ return path;
154
+ let url;
155
+ try {
156
+ url = new URL(path, origin);
157
+ }
158
+ catch {
159
+ throw new InvalidScopesError("The dashboard origin is not a valid URL.");
160
+ }
161
+ const link = url.toString();
162
+ if (!isAllowedOpenTarget(link, origin)) {
163
+ throw new InvalidScopesError("The dashboard origin refuses the scope-request link.");
164
+ }
165
+ return link;
166
+ }
167
+ export function createInstallationScopesClient(options) {
168
+ if (!options.installationId || options.installationId.trim() === "") {
169
+ throw new InvalidScopesError("An installation id is required.");
170
+ }
171
+ if (!options.store) {
172
+ throw new InvalidScopesError("An InstallationStore is required.");
173
+ }
174
+ if (typeof options.signJwt !== "function") {
175
+ throw new InvalidScopesError("An app-JWT signer is required (e.g. `() => provider.signJwt()`).");
176
+ }
177
+ const fetchImpl = options.fetchImpl ?? fetch;
178
+ const userAgent = options.userAgent ?? "queek-app/1.0";
179
+ const allowedApiHosts = options.allowedApiHosts ?? [];
180
+ /**
181
+ * The cached grant split — store-read only, never a fetch. The optional
182
+ * half needs the app's declared-optional list (the app's own manifest
183
+ * knowledge: the app surface exposes the effective grant but no declared
184
+ * list), from the per-call option, the client option, or empty.
185
+ */
186
+ async function queryScopes(queryOptions = {}) {
187
+ const row = await options.store.getInstallation(options.installationId);
188
+ if (!row)
189
+ throw new UnknownInstallationError(options.installationId);
190
+ return splitInstallationScopes(row.scopes, queryOptions.optionalScopes ?? options.optionalScopes ?? []);
191
+ }
192
+ /**
193
+ * The dashboard deep link that opens the merchant's consent screen in
194
+ * the dashboard. Pure (no store, no network): the app opens it via
195
+ * `sendOpen` or a redirect and the merchant consents in the dashboard
196
+ * (shipped with dashboard item 8). Shaped like the bridge `open`
197
+ * targets — dashboard-relative, or absolute under the dashboard origin
198
+ * when one is configured.
199
+ */
200
+ function requestScopes(scopes, linkOptions = {}) {
201
+ const appSlug = linkOptions.appSlug ?? options.appSlug;
202
+ if (!appSlug) {
203
+ throw new InvalidScopesError("requestScopes needs the app slug: pass `appSlug` or per-call `appSlug`.");
204
+ }
205
+ return buildScopeRequestLink({
206
+ appSlug,
207
+ scopes,
208
+ dashboardOrigin: linkOptions.dashboardOrigin ?? options.dashboardOrigin,
209
+ });
210
+ }
211
+ /**
212
+ * App-initiated revoke of optional scopes (backend
213
+ * `AppInstallationController::revokeScopes`): the app JWT signs the call
214
+ * (same credential as mint/resync), an `Idempotency-Key` rides the write
215
+ * for discipline only (the route is naturally idempotent), and on 200
216
+ * the cached grant is refreshed from the response with the cached token
217
+ * dropped only when the grant moved (same compare-and-clear as the
218
+ * scopes_update handler — backstop, since the backend rewrites live
219
+ * token rows in place). A required scope refuses 422
220
+ * `app_scope_required` as `AppScopeRequiredError`; a gone installation
221
+ * purges the local row and rethrows; anything else propagates untouched.
222
+ * A 401 never halts here — the mint path owns that mapping and halts on
223
+ * its next call.
224
+ */
225
+ async function revokeScopes(scopes, revokeOptions = {}) {
226
+ const requested = normaliseScopeList(scopes);
227
+ const row = await options.store.getInstallation(options.installationId);
228
+ if (!row)
229
+ throw new UnknownInstallationError(options.installationId);
230
+ const appBase = resolveAppApiBase(row.apiBase, allowedApiHosts);
231
+ // Signed per attempt (never reused past its ≤10-min window, never logged).
232
+ const jwt = options.signJwt();
233
+ const url = `${appBase}${revokeScopesPath(options.installationId)}`;
234
+ const headers = new Headers();
235
+ headers.set("Authorization", `Bearer ${jwt}`);
236
+ headers.set("Accept", "application/json");
237
+ headers.set("Content-Type", "application/json");
238
+ headers.set("User-Agent", userAgent);
239
+ headers.set("Idempotency-Key", revokeOptions.idempotencyKey ?? newIdempotencyKey());
240
+ let response;
241
+ try {
242
+ response = await fetchImpl(url, {
243
+ method: "POST",
244
+ headers,
245
+ body: JSON.stringify({ scopes: requested }),
246
+ signal: revokeOptions.signal,
247
+ });
248
+ }
249
+ catch (cause) {
250
+ throw new QueekApiError({
251
+ status: 0,
252
+ code: "network_error",
253
+ message: cause instanceof Error ? `Could not reach Queek: ${cause.message}` : "Could not reach Queek.",
254
+ });
255
+ }
256
+ const text = await response.text();
257
+ const parsed = text === "" ? null : tryParseJson(text);
258
+ if (!response.ok) {
259
+ const error = queekApiErrorFromResponse(response.status, parsed, response.headers);
260
+ if (isAppScopeRequired(error.status, error.code))
261
+ throw appScopeRequiredFromError(error);
262
+ if (error.status === 404 && isInstallationGone(error.code)) {
263
+ await options.store.deleteInstallation(options.installationId);
264
+ }
265
+ throw error;
266
+ }
267
+ const updated = parseRevokeBody(parsed);
268
+ // Same compare-and-clear as the scopes_update handler: an unchanged
269
+ // grant (e.g. revoking a never-granted scope — idempotent 200) keeps
270
+ // the cached token, since it still matches the live grant.
271
+ const grantChanged = !installationScopesEqual(row.scopes, updated);
272
+ await options.store.saveInstallation({ ...row, scopes: updated, updatedAt: new Date().toISOString() });
273
+ if (grantChanged) {
274
+ await options.store.clearCachedToken(options.installationId);
275
+ }
276
+ return updated;
277
+ }
278
+ return { queryScopes, requestScopes, revokeScopes };
279
+ }
280
+ /** Read the 200 revoke response (`{installation: {p_id}, scopes}`) — the refreshed effective grant. */
281
+ function parseRevokeBody(body) {
282
+ const record = (typeof body === "object" && body !== null ? body : {});
283
+ const scopes = record.scopes;
284
+ if (!Array.isArray(scopes) || !scopes.every((scope) => typeof scope === "string")) {
285
+ throw new Error("Malformed revoke response: expected {scopes: string[]}.");
286
+ }
287
+ return [...scopes];
288
+ }
289
+ function tryParseJson(text) {
290
+ try {
291
+ return JSON.parse(text);
292
+ }
293
+ catch {
294
+ return { message: text.slice(0, 500) };
295
+ }
296
+ }
297
+ //# sourceMappingURL=scopes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scopes.js","sourceRoot":"","sources":["../src/scopes.ts"],"names":[],"mappings":"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"}
package/dist/server.d.ts CHANGED
@@ -4,6 +4,6 @@
4
4
  * bundle. Import the verifier from `@usequeek/app-sdk/server`, never from
5
5
  * the main entry.
6
6
  */
7
- export type { SessionTokenBinding, SessionTokenClaims, SessionTokenFailure, VerifySessionTokenOptions, } from "./session.js";
8
- export { EMBED_SECRET_PREFIX, SESSION_CLOCK_TOLERANCE_SECONDS, sessionTokenInstallationId, verifySessionToken, verifySessionTokenDetailed, } from "./session.js";
7
+ export type { LaunchTokenClaims, SessionTokenBinding, SessionTokenClaims, SessionTokenFailure, VerifySessionTokenOptions, } from "./session.js";
8
+ export { EMBED_SECRET_PREFIX, isBridgePurpose, SESSION_CLOCK_TOLERANCE_SECONDS, sessionTokenInstallationId, verifyLaunchToken, verifyLaunchTokenDetailed, verifySessionToken, verifySessionTokenDetailed, } from "./session.js";
9
9
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,mBAAmB,EACnB,+BAA+B,EAC/B,0BAA0B,EAC1B,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,+BAA+B,EAC/B,0BAA0B,EAC1B,iBAAiB,EACjB,yBAAyB,EACzB,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,cAAc,CAAC"}
package/dist/server.js CHANGED
@@ -4,5 +4,5 @@
4
4
  * bundle. Import the verifier from `@usequeek/app-sdk/server`, never from
5
5
  * the main entry.
6
6
  */
7
- export { EMBED_SECRET_PREFIX, SESSION_CLOCK_TOLERANCE_SECONDS, sessionTokenInstallationId, verifySessionToken, verifySessionTokenDetailed, } from "./session.js";
7
+ export { EMBED_SECRET_PREFIX, isBridgePurpose, SESSION_CLOCK_TOLERANCE_SECONDS, sessionTokenInstallationId, verifyLaunchToken, verifyLaunchTokenDetailed, verifySessionToken, verifySessionTokenDetailed, } from "./session.js";
8
8
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,OAAO,EACL,mBAAmB,EACnB,+BAA+B,EAC/B,0BAA0B,EAC1B,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,+BAA+B,EAC/B,0BAA0B,EAC1B,iBAAiB,EACjB,yBAAyB,EACzB,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,cAAc,CAAC"}
package/dist/session.d.ts CHANGED
@@ -4,6 +4,16 @@
4
4
  * verifies them with the per-installation `embsec_…` secret before trusting
5
5
  * any call that carries one.
6
6
  *
7
+ * Purpose binding (backend contract, mirrored from
8
+ * `AppSessionTokenService::verify()`): absent or "session" is a bridge
9
+ * token, verified by `verifySessionToken`; "launch" is the signed
10
+ * first-load token, verified ONLY by `verifyLaunchToken` as the input to
11
+ * the app's launch exchange (exchange it once for the app's own session so
12
+ * the first paint needs no bridge round-trip). Each verifier refuses the
13
+ * other's purpose with the same failure path callers map to 401, so a
14
+ * launch token that rode a URL can never be replayed as a bridge token
15
+ * and vice versa.
16
+ *
7
17
  * Server-only: the secret must never enter a browser bundle, so this module
8
18
  * ships behind the `./server` export, not the main entry. Style mirrors
9
19
  * `signatures.ts` — a boolean fast path plus a detailed variant returning
@@ -14,7 +24,7 @@ export declare const EMBED_SECRET_PREFIX = "embsec_";
14
24
  export declare const SESSION_TOKEN_ALG = "HS256";
15
25
  /** Backend skew the verifier tolerates (matches the mint side). */
16
26
  export declare const SESSION_CLOCK_TOLERANCE_SECONDS = 20;
17
- export type SessionTokenFailure = "missing_token" | "missing_secret" | "wrong_algorithm" | "invalid_signature" | "expired" | "not_yet_valid" | "wrong_audience" | "wrong_issuer" | "binding_mismatch";
27
+ export type SessionTokenFailure = "missing_token" | "missing_secret" | "wrong_algorithm" | "invalid_signature" | "expired" | "not_yet_valid" | "wrong_audience" | "wrong_issuer" | "wrong_purpose" | "binding_mismatch";
18
28
  export interface SessionTokenClaims {
19
29
  installationId: string;
20
30
  vendorId: string;
@@ -31,12 +41,27 @@ export interface SessionTokenBinding {
31
41
  appSlug: string;
32
42
  appId: string;
33
43
  }
44
+ /**
45
+ * Launch-token claims: the bridge binding plus the launch-only hints the
46
+ * backend mints for the signed first load — the store p_id (the URL-safe
47
+ * store id) and the dashboard theme for a flash-free first paint. Both are
48
+ * hints, not auth: `storePid` is null when the mint carried none, `theme`
49
+ * is null unless the mint carried exactly `light` or `dark`.
50
+ */
51
+ export interface LaunchTokenClaims extends SessionTokenClaims {
52
+ storePid: number | null;
53
+ theme: "light" | "dark" | null;
54
+ }
34
55
  export interface VerifySessionTokenOptions {
35
56
  /** The per-installation `embsec_…` secret, raw UTF-8 bytes (never decoded). */
36
57
  secret: string;
37
58
  /** Single expected audience: the app slug. */
38
59
  audience: string;
39
- /** Expected issuer: the Queek api_base. */
60
+ /**
61
+ * Expected issuer: the handoff `api_base` VERBATIM
62
+ * (`installation.apiBase` — it equals the bare `app.url` the backend
63
+ * signs as `iss`). Never a prefix or a suffix of it.
64
+ */
40
65
  issuer: string;
41
66
  /**
42
67
  * The installation row the token must belong to (the app backend's own
@@ -55,6 +80,24 @@ export declare function verifySessionTokenDetailed(token: string, options: Verif
55
80
  ok: false;
56
81
  reason: SessionTokenFailure;
57
82
  }>;
83
+ /**
84
+ * Server-side launch exchange input (signed first load): verify a
85
+ * purpose=launch token with the SAME checks as the bridge verifier
86
+ * (signature, audience, issuer, installation binding, expiry) plus the
87
+ * purpose pin — anything but exactly `"launch"` is refused with
88
+ * `wrong_purpose`, so a bridge token can never open the exchange. On
89
+ * success the app mints its own session from the claims (subject, store,
90
+ * theme) and the first paint needs no bridge round-trip. Callers map every
91
+ * `{ ok: false }` to 401, mirroring the backend `verify()`.
92
+ */
93
+ export declare function verifyLaunchToken(token: string, options: VerifySessionTokenOptions): Promise<boolean>;
94
+ export declare function verifyLaunchTokenDetailed(token: string, options: VerifySessionTokenOptions): Promise<{
95
+ ok: true;
96
+ claims: LaunchTokenClaims;
97
+ } | {
98
+ ok: false;
99
+ reason: SessionTokenFailure;
100
+ }>;
58
101
  /**
59
102
  * ROUTING HINT ONLY — the `installation_id` claim read WITHOUT verifying
60
103
  * anything, so a server can load the installation whose embed secret then
@@ -63,4 +106,11 @@ export declare function verifySessionTokenDetailed(token: string, options: Verif
63
106
  * Null for anything that is not a decodable JWT carrying the claim.
64
107
  */
65
108
  export declare function sessionTokenInstallationId(token: string): string | null;
109
+ /**
110
+ * True for the purposes the bridge verifier accepts: absent (legacy
111
+ * tokens) or "session". "launch" is the signed first-load token for the
112
+ * launch exchange (`verifyLaunchToken`) only, so the bridge verifier
113
+ * refuses it. Any other present value fails closed.
114
+ */
115
+ export declare function isBridgePurpose(purpose: unknown): boolean;
66
116
  //# sourceMappingURL=session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAE7C,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,UAAU,CAAC;AAEzC,mEAAmE;AACnE,eAAO,MAAM,+BAA+B,KAAK,CAAC;AAElD,MAAM,MAAM,mBAAmB,GAC3B,eAAe,GACf,gBAAgB,GAChB,iBAAiB,GACjB,mBAAmB,GACnB,SAAS,GACT,eAAe,GACf,gBAAgB,GAChB,cAAc,GACd,kBAAkB,CAAC;AAEvB,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,yBAAyB;IACxC,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAC,CA2ChG;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOvE"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAE7C,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,UAAU,CAAC;AAEzC,mEAAmE;AACnE,eAAO,MAAM,+BAA+B,KAAK,CAAC;AAElD,MAAM,MAAM,mBAAmB,GAC3B,eAAe,GACf,gBAAgB,GAChB,iBAAiB,GACjB,mBAAmB,GACnB,SAAS,GACT,eAAe,GACf,gBAAgB,GAChB,cAAc,GACd,eAAe,GACf,kBAAkB,CAAC;AAEvB,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IAC3D,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,yBAAyB;IACxC,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAoBhG;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,OAAO,CAAC,CAG3G;AAED,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAe/F;AAsED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOvE;AAgBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEzD"}
package/dist/session.js CHANGED
@@ -5,6 +5,16 @@ import { decodeJwt, decodeProtectedHeader, jwtVerify } from "jose";
5
5
  * verifies them with the per-installation `embsec_…` secret before trusting
6
6
  * any call that carries one.
7
7
  *
8
+ * Purpose binding (backend contract, mirrored from
9
+ * `AppSessionTokenService::verify()`): absent or "session" is a bridge
10
+ * token, verified by `verifySessionToken`; "launch" is the signed
11
+ * first-load token, verified ONLY by `verifyLaunchToken` as the input to
12
+ * the app's launch exchange (exchange it once for the app's own session so
13
+ * the first paint needs no bridge round-trip). Each verifier refuses the
14
+ * other's purpose with the same failure path callers map to 401, so a
15
+ * launch token that rode a URL can never be replayed as a bridge token
16
+ * and vice versa.
17
+ *
8
18
  * Server-only: the secret must never enter a browser bundle, so this module
9
19
  * ships behind the `./server` export, not the main entry. Style mirrors
10
20
  * `signatures.ts` — a boolean fast path plus a detailed variant returning
@@ -20,6 +30,61 @@ export async function verifySessionToken(token, options) {
20
30
  return result.ok;
21
31
  }
22
32
  export async function verifySessionTokenDetailed(token, options) {
33
+ const envelope = await verifyTokenEnvelope(token, options);
34
+ if (!envelope.ok)
35
+ return envelope;
36
+ const { payload } = envelope;
37
+ // Purpose binding (backend contract): absent or "session" is a bridge
38
+ // token; "launch" belongs to the signed first-load exchange only and is
39
+ // refused here with the same failure path callers map to 401. Any other
40
+ // present purpose fails closed as well — the bridge verifier accepts
41
+ // exactly the bridge purposes, never a foreign one.
42
+ if (!isBridgePurpose(payload.purpose)) {
43
+ return { ok: false, reason: "wrong_purpose" };
44
+ }
45
+ const claims = readSessionClaims(payload);
46
+ const { expected } = options;
47
+ if (!claims || !bindingMatches(claims, expected)) {
48
+ return { ok: false, reason: "binding_mismatch" };
49
+ }
50
+ return { ok: true, claims };
51
+ }
52
+ /**
53
+ * Server-side launch exchange input (signed first load): verify a
54
+ * purpose=launch token with the SAME checks as the bridge verifier
55
+ * (signature, audience, issuer, installation binding, expiry) plus the
56
+ * purpose pin — anything but exactly `"launch"` is refused with
57
+ * `wrong_purpose`, so a bridge token can never open the exchange. On
58
+ * success the app mints its own session from the claims (subject, store,
59
+ * theme) and the first paint needs no bridge round-trip. Callers map every
60
+ * `{ ok: false }` to 401, mirroring the backend `verify()`.
61
+ */
62
+ export async function verifyLaunchToken(token, options) {
63
+ const result = await verifyLaunchTokenDetailed(token, options);
64
+ return result.ok;
65
+ }
66
+ export async function verifyLaunchTokenDetailed(token, options) {
67
+ const envelope = await verifyTokenEnvelope(token, options);
68
+ if (!envelope.ok)
69
+ return envelope;
70
+ const { payload } = envelope;
71
+ if (payload.purpose !== "launch") {
72
+ return { ok: false, reason: "wrong_purpose" };
73
+ }
74
+ const claims = readSessionClaims(payload);
75
+ const { expected } = options;
76
+ if (!claims || !bindingMatches(claims, expected)) {
77
+ return { ok: false, reason: "binding_mismatch" };
78
+ }
79
+ return { ok: true, claims: { ...claims, ...readLaunchHints(payload) } };
80
+ }
81
+ /**
82
+ * Shared envelope verification for both purposes: presence, secret shape,
83
+ * algorithm pin, then signature + audience + issuer + expiry via jose.
84
+ * Purpose and binding checks stay with the caller — the only thing that
85
+ * differs between the bridge and launch verifiers.
86
+ */
87
+ async function verifyTokenEnvelope(token, options) {
23
88
  if (!token) {
24
89
  return { ok: false, reason: "missing_token" };
25
90
  }
@@ -36,7 +101,6 @@ export async function verifySessionTokenDetailed(token, options) {
36
101
  if (headerAlg !== null && headerAlg !== SESSION_TOKEN_ALG) {
37
102
  return { ok: false, reason: "wrong_algorithm" };
38
103
  }
39
- let payload;
40
104
  try {
41
105
  const verified = await jwtVerify(token, new TextEncoder().encode(secret), {
42
106
  algorithms: ["HS256"],
@@ -44,21 +108,35 @@ export async function verifySessionTokenDetailed(token, options) {
44
108
  issuer,
45
109
  clockTolerance: options.clockToleranceSeconds ?? SESSION_CLOCK_TOLERANCE_SECONDS,
46
110
  });
47
- payload = verified.payload;
111
+ return { ok: true, payload: verified.payload };
48
112
  }
49
113
  catch (error) {
50
114
  return { ok: false, reason: classifyJoseError(error) };
51
115
  }
52
- const claims = readSessionClaims(payload);
53
- const { expected } = options;
54
- if (!claims ||
55
- claims.installationId !== expected.installationId ||
56
- claims.vendorId !== expected.vendorId ||
57
- claims.appSlug !== expected.appSlug ||
58
- claims.appId !== expected.appId) {
59
- return { ok: false, reason: "binding_mismatch" };
60
- }
61
- return { ok: true, claims };
116
+ }
117
+ function bindingMatches(claims, expected) {
118
+ return (claims.installationId === expected.installationId &&
119
+ claims.vendorId === expected.vendorId &&
120
+ claims.appSlug === expected.appSlug &&
121
+ claims.appId === expected.appId);
122
+ }
123
+ /**
124
+ * Launch-only hints off a verified payload. Tolerant by design (they steer
125
+ * first paint, never auth): `store` is the integer p_id or null, `theme`
126
+ * survives only as exactly `light` or `dark`.
127
+ *
128
+ * Boundary (deliberate, pinned both sides): this `store` claim is an INT,
129
+ * while install-handoff p_ids are STRINGS per the handoff contract — never
130
+ * accept a string here to "match" the handoff; a string claim nulls the
131
+ * hint (session.test.ts) exactly as the backend pins int (AppUiKitTest).
132
+ */
133
+ function readLaunchHints(payload) {
134
+ const store = payload.store;
135
+ const theme = payload.theme;
136
+ return {
137
+ storePid: typeof store === "number" && Number.isInteger(store) && store >= 0 ? store : null,
138
+ theme: theme === "light" || theme === "dark" ? theme : null,
139
+ };
62
140
  }
63
141
  /**
64
142
  * ROUTING HINT ONLY — the `installation_id` claim read WITHOUT verifying
@@ -90,6 +168,15 @@ function readTokenAlg(token) {
90
168
  return null;
91
169
  }
92
170
  }
171
+ /**
172
+ * True for the purposes the bridge verifier accepts: absent (legacy
173
+ * tokens) or "session". "launch" is the signed first-load token for the
174
+ * launch exchange (`verifyLaunchToken`) only, so the bridge verifier
175
+ * refuses it. Any other present value fails closed.
176
+ */
177
+ export function isBridgePurpose(purpose) {
178
+ return purpose === undefined || purpose === null || purpose === "session";
179
+ }
93
180
  function readSessionClaims(payload) {
94
181
  const pick = (key) => typeof payload[key] === "string" && payload[key].length > 0 ? payload[key] : null;
95
182
  const installationId = pick("installation_id");
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}