@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.
- package/README.md +166 -12
- package/dist/app-auth.d.ts +9 -1
- package/dist/app-auth.d.ts.map +1 -1
- package/dist/app-auth.js +11 -1
- package/dist/app-auth.js.map +1 -1
- package/dist/auth-fetch.d.ts +90 -0
- package/dist/auth-fetch.d.ts.map +1 -0
- package/dist/auth-fetch.js +261 -0
- package/dist/auth-fetch.js.map +1 -0
- package/dist/client.d.ts +32 -10
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +6 -2
- package/dist/client.js.map +1 -1
- package/dist/frame.d.ts +233 -4
- package/dist/frame.d.ts.map +1 -1
- package/dist/frame.js +694 -19
- package/dist/frame.js.map +1 -1
- package/dist/handoff.d.ts +32 -2
- package/dist/handoff.d.ts.map +1 -1
- package/dist/handoff.js +2 -1
- package/dist/handoff.js.map +1 -1
- package/dist/hono.d.ts +13 -0
- package/dist/hono.d.ts.map +1 -1
- package/dist/hono.js +15 -0
- package/dist/hono.js.map +1 -1
- package/dist/index.d.ts +11 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -6
- package/dist/index.js.map +1 -1
- package/dist/install-handlers.d.ts +33 -8
- package/dist/install-handlers.d.ts.map +1 -1
- package/dist/install-handlers.js +133 -15
- package/dist/install-handlers.js.map +1 -1
- package/dist/merchant-schema.d.ts +1550 -68
- package/dist/merchant-schema.d.ts.map +1 -1
- package/dist/merchant-schema.js +1 -1
- package/dist/proxy.d.ts +122 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +204 -0
- package/dist/proxy.js.map +1 -0
- package/dist/react.d.ts +78 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +262 -0
- package/dist/react.js.map +1 -0
- package/dist/scopes.d.ts +174 -0
- package/dist/scopes.d.ts.map +1 -0
- package/dist/scopes.js +297 -0
- package/dist/scopes.js.map +1 -0
- package/dist/server.d.ts +2 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/session.d.ts +52 -2
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +99 -12
- package/dist/session.js.map +1 -1
- package/dist/store.d.ts +33 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +114 -4
- package/dist/store.js.map +1 -1
- package/dist/theme.d.ts +96 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +158 -0
- package/dist/theme.js.map +1 -0
- package/dist/tokens.d.ts +30 -15
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +57 -19
- package/dist/tokens.js.map +1 -1
- package/dist/webhooks.d.ts.map +1 -1
- package/dist/webhooks.js +1 -1
- package/dist/webhooks.js.map +1 -1
- package/package.json +17 -5
- package/CHANGELOG.md +0 -162
- 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
|
package/dist/server.d.ts.map
CHANGED
|
@@ -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
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
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
|
-
/**
|
|
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
|
package/dist/session.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAEA
|
|
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
|
|
111
|
+
return { ok: true, payload: verified.payload };
|
|
48
112
|
}
|
|
49
113
|
catch (error) {
|
|
50
114
|
return { ok: false, reason: classifyJoseError(error) };
|
|
51
115
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
claims.
|
|
56
|
-
claims.
|
|
57
|
-
claims.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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");
|
package/dist/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAEnE
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|