@openkey/sdk 0.10.0 → 0.10.2

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/dist/index.d.mts CHANGED
@@ -211,12 +211,23 @@ interface CapabilityPresentationEnvelopeV1 {
211
211
  /**
212
212
  * Sol final continuation contract requirement 5: pure validation of an
213
213
  * incoming iframe resize message envelope. Exported so unit tests can
214
- * exercise every rejection branch — wrong requestId, wrong version, no
215
- * active request, malformed height — without booting the browser
216
- * runtime that `IframeModal` requires.
214
+ * exercise every rejection branch — wrong requestId, wrong version,
215
+ * malformed height — without booting the browser runtime that
216
+ * `IframeModal` requires.
217
217
  *
218
- * Returns the sanitized height when acceptable, or `null` when the
219
- * message must be dropped.
218
+ * Two modes, chosen by the modal's bound correlation:
219
+ * - Versioned request bound (`requestId` AND `protocolVersion` set): the
220
+ * resize MUST carry that exact pair.
221
+ * - No versioned request (both null — `connect`, `signMessage`, and the
222
+ * other unversioned flows): the widget sends a plain
223
+ * `{ type, height }`. It is accepted only when it carries no
224
+ * `requestId`. The caller has already checked that the message came
225
+ * from this modal's iframe (`event.source`) on the OpenKey origin
226
+ * (TC-647).
227
+ * A half-bound correlation (only one of the two set) drops everything.
228
+ *
229
+ * Returns the height clamped to 85% of the viewport when acceptable, or
230
+ * `null` when the message must be dropped.
220
231
  */
221
232
  declare function validateIframeResize(incoming: unknown, expected: {
222
233
  requestId: string | null;
@@ -318,14 +329,13 @@ declare class OpenKey {
318
329
  private popup;
319
330
  private lastAuth;
320
331
  private discoveredProviders;
321
- private sessionToken;
322
332
  /** Cancels widget flows that could outlive a sign-out on this instance. */
323
333
  private activeFlowCancellations;
324
334
  /**
325
335
  * Nostr identity custody + signing (secp256k1 Schnorr / BIP-340). Kept
326
336
  * fully separate from the Ethereum flows above: it never reuses
327
- * `lastAuth`/`sessionToken`, and its widget messages never carry a
328
- * sessionToken - the OpenKey session and any signing grants stay inside
337
+ * `lastAuth`, and its widget messages never carry a session token -
338
+ * the OpenKey session and any signing grants stay inside
329
339
  * the OpenKey-origin iframe for the whole lifetime of this client.
330
340
  */
331
341
  readonly nostr: OpenKeyNostr;
@@ -360,24 +370,28 @@ declare class OpenKey {
360
370
  /**
361
371
  * Signs the active OpenKey account out through the OpenKey-owned widget.
362
372
  *
363
- * The SDK passes its Better Auth bearer through the OpenKey-owned widget
364
- * protocol so applications do not have to implement revocation themselves.
365
- * Local SDK session state is cleared before opening the widget, including
366
- * when remote revocation is unavailable.
373
+ * The OpenKey-owned widget revokes the OpenKey session it holds itself;
374
+ * the SDK never holds an OpenKey session token. Local SDK state is cleared
375
+ * before opening the widget, including when remote revocation is
376
+ * unavailable.
367
377
  */
368
378
  signOut(opts?: {
369
379
  mode?: OpenKeyMode;
370
380
  }): Promise<SignOutAcknowledgement>;
371
381
  /**
372
- * Better-auth session token relayed during connect(), or null if the
373
- * flow did not provide one. Use as a bearer token for API calls made
374
- * on the user's behalf (e.g. TinyCloud auto-sign bootstrap signing).
382
+ * Always null. OpenKey no longer gives its session token to the embedding
383
+ * page (TC-688): the token unlocked OpenKey's key and account APIs, not
384
+ * just the requesting app's flows.
385
+ *
386
+ * @deprecated Kept so existing callers compile. Third-party server-side
387
+ * signing uses an OAuth access token from the OpenKey OAuth flow.
375
388
  */
376
389
  getSessionToken(): string | null;
377
390
  /**
378
- * Endpoint + bearer token for a TinyCloud auto-sign signing strategy.
379
- * The endpoint is on the OpenKey API host and only signs requests the
380
- * server-side bootstrap allowlist permits.
391
+ * Endpoint for a TinyCloud auto-sign signing strategy. `token` is always
392
+ * null; see {@link OpenKey.getSessionToken}.
393
+ *
394
+ * @deprecated The endpoint accepts an OAuth access token as its bearer.
381
395
  */
382
396
  tinycloudSigningOptions(): {
383
397
  endpoint: string;
package/dist/index.d.ts CHANGED
@@ -211,12 +211,23 @@ interface CapabilityPresentationEnvelopeV1 {
211
211
  /**
212
212
  * Sol final continuation contract requirement 5: pure validation of an
213
213
  * incoming iframe resize message envelope. Exported so unit tests can
214
- * exercise every rejection branch — wrong requestId, wrong version, no
215
- * active request, malformed height — without booting the browser
216
- * runtime that `IframeModal` requires.
214
+ * exercise every rejection branch — wrong requestId, wrong version,
215
+ * malformed height — without booting the browser runtime that
216
+ * `IframeModal` requires.
217
217
  *
218
- * Returns the sanitized height when acceptable, or `null` when the
219
- * message must be dropped.
218
+ * Two modes, chosen by the modal's bound correlation:
219
+ * - Versioned request bound (`requestId` AND `protocolVersion` set): the
220
+ * resize MUST carry that exact pair.
221
+ * - No versioned request (both null — `connect`, `signMessage`, and the
222
+ * other unversioned flows): the widget sends a plain
223
+ * `{ type, height }`. It is accepted only when it carries no
224
+ * `requestId`. The caller has already checked that the message came
225
+ * from this modal's iframe (`event.source`) on the OpenKey origin
226
+ * (TC-647).
227
+ * A half-bound correlation (only one of the two set) drops everything.
228
+ *
229
+ * Returns the height clamped to 85% of the viewport when acceptable, or
230
+ * `null` when the message must be dropped.
220
231
  */
221
232
  declare function validateIframeResize(incoming: unknown, expected: {
222
233
  requestId: string | null;
@@ -318,14 +329,13 @@ declare class OpenKey {
318
329
  private popup;
319
330
  private lastAuth;
320
331
  private discoveredProviders;
321
- private sessionToken;
322
332
  /** Cancels widget flows that could outlive a sign-out on this instance. */
323
333
  private activeFlowCancellations;
324
334
  /**
325
335
  * Nostr identity custody + signing (secp256k1 Schnorr / BIP-340). Kept
326
336
  * fully separate from the Ethereum flows above: it never reuses
327
- * `lastAuth`/`sessionToken`, and its widget messages never carry a
328
- * sessionToken - the OpenKey session and any signing grants stay inside
337
+ * `lastAuth`, and its widget messages never carry a session token -
338
+ * the OpenKey session and any signing grants stay inside
329
339
  * the OpenKey-origin iframe for the whole lifetime of this client.
330
340
  */
331
341
  readonly nostr: OpenKeyNostr;
@@ -360,24 +370,28 @@ declare class OpenKey {
360
370
  /**
361
371
  * Signs the active OpenKey account out through the OpenKey-owned widget.
362
372
  *
363
- * The SDK passes its Better Auth bearer through the OpenKey-owned widget
364
- * protocol so applications do not have to implement revocation themselves.
365
- * Local SDK session state is cleared before opening the widget, including
366
- * when remote revocation is unavailable.
373
+ * The OpenKey-owned widget revokes the OpenKey session it holds itself;
374
+ * the SDK never holds an OpenKey session token. Local SDK state is cleared
375
+ * before opening the widget, including when remote revocation is
376
+ * unavailable.
367
377
  */
368
378
  signOut(opts?: {
369
379
  mode?: OpenKeyMode;
370
380
  }): Promise<SignOutAcknowledgement>;
371
381
  /**
372
- * Better-auth session token relayed during connect(), or null if the
373
- * flow did not provide one. Use as a bearer token for API calls made
374
- * on the user's behalf (e.g. TinyCloud auto-sign bootstrap signing).
382
+ * Always null. OpenKey no longer gives its session token to the embedding
383
+ * page (TC-688): the token unlocked OpenKey's key and account APIs, not
384
+ * just the requesting app's flows.
385
+ *
386
+ * @deprecated Kept so existing callers compile. Third-party server-side
387
+ * signing uses an OAuth access token from the OpenKey OAuth flow.
375
388
  */
376
389
  getSessionToken(): string | null;
377
390
  /**
378
- * Endpoint + bearer token for a TinyCloud auto-sign signing strategy.
379
- * The endpoint is on the OpenKey API host and only signs requests the
380
- * server-side bootstrap allowlist permits.
391
+ * Endpoint for a TinyCloud auto-sign signing strategy. `token` is always
392
+ * null; see {@link OpenKey.getSessionToken}.
393
+ *
394
+ * @deprecated The endpoint accepts an OAuth access token as its bearer.
381
395
  */
382
396
  tinycloudSigningOptions(): {
383
397
  endpoint: string;
package/dist/index.js CHANGED
@@ -273,12 +273,16 @@ function validateIframeResize(incoming, expected) {
273
273
  if (!incoming || typeof incoming !== "object") return null;
274
274
  const data = incoming;
275
275
  if (data.type !== "openkey:resize") return null;
276
- if (expected.requestId === null || expected.protocolVersion === null) return null;
277
- if (typeof data.protocolVersion !== "number" || data.protocolVersion !== expected.protocolVersion) {
278
- return null;
279
- }
280
- if (typeof data.requestId !== "string" || data.requestId !== expected.requestId) {
281
- return null;
276
+ if (expected.requestId === null && expected.protocolVersion === null) {
277
+ if (data.requestId !== void 0) return null;
278
+ } else {
279
+ if (expected.requestId === null || expected.protocolVersion === null) return null;
280
+ if (typeof data.protocolVersion !== "number" || data.protocolVersion !== expected.protocolVersion) {
281
+ return null;
282
+ }
283
+ if (typeof data.requestId !== "string" || data.requestId !== expected.requestId) {
284
+ return null;
285
+ }
282
286
  }
283
287
  if (typeof data.height !== "number" || !Number.isFinite(data.height) || data.height <= 0) {
284
288
  return null;
@@ -354,11 +358,11 @@ var IframeModal = class {
354
358
  onMessage;
355
359
  messageHandler;
356
360
  host;
357
- // Sol final continuation contract requirement 5: iframe resize traffic
358
- // MUST correlate to the active request's requestId AND protocolVersion.
359
- // Set by `setExpectedCorrelation` after the outer flow decides which
360
- // request this modal is bound to. When both are null, resize messages
361
- // are dropped (no active request → no resize authority).
361
+ // Sol final continuation contract requirement 5: when the modal carries a
362
+ // versioned request, iframe resize traffic MUST correlate to its
363
+ // requestId AND protocolVersion. Set by `setExpectedCorrelation` as soon
364
+ // as the modal is created. When both are null the flow is unversioned and
365
+ // only uncorrelated resizes from this iframe are accepted (TC-647).
362
366
  expectedRequestId = null;
363
367
  expectedProtocolVersion = null;
364
368
  constructor(opts) {
@@ -440,10 +444,11 @@ var IframeModal = class {
440
444
  * request correlation so subsequent resize (and future correlated)
441
445
  * messages can be verified against `requestId` + `protocolVersion`.
442
446
  *
443
- * The outer flow calls this immediately BEFORE posting the sign
444
- * request into the iframe, so a resize that arrives synchronously
445
- * after ready is correlated correctly. Passing `null` for either
446
- * argument disables correlation (resize is dropped).
447
+ * The outer flow calls this right after creating the modal for a
448
+ * versioned request, before the iframe can send anything, so no
449
+ * uncorrelated resize is ever accepted for that request. Passing `null`
450
+ * for both arguments means the flow is unversioned; passing `null` for
451
+ * only one drops every resize.
447
452
  */
448
453
  setExpectedCorrelation(requestId, protocolVersion) {
449
454
  this.expectedRequestId = requestId;
@@ -581,14 +586,13 @@ var OpenKey = class {
581
586
  popup = null;
582
587
  lastAuth = null;
583
588
  discoveredProviders = [];
584
- sessionToken = null;
585
589
  /** Cancels widget flows that could outlive a sign-out on this instance. */
586
590
  activeFlowCancellations = /* @__PURE__ */ new Set();
587
591
  /**
588
592
  * Nostr identity custody + signing (secp256k1 Schnorr / BIP-340). Kept
589
593
  * fully separate from the Ethereum flows above: it never reuses
590
- * `lastAuth`/`sessionToken`, and its widget messages never carry a
591
- * sessionToken - the OpenKey session and any signing grants stay inside
594
+ * `lastAuth`, and its widget messages never carry a session token -
595
+ * the OpenKey session and any signing grants stay inside
592
596
  * the OpenKey-origin iframe for the whole lifetime of this client.
593
597
  */
594
598
  nostr;
@@ -670,25 +674,22 @@ var OpenKey = class {
670
674
  /**
671
675
  * Signs the active OpenKey account out through the OpenKey-owned widget.
672
676
  *
673
- * The SDK passes its Better Auth bearer through the OpenKey-owned widget
674
- * protocol so applications do not have to implement revocation themselves.
675
- * Local SDK session state is cleared before opening the widget, including
676
- * when remote revocation is unavailable.
677
+ * The OpenKey-owned widget revokes the OpenKey session it holds itself;
678
+ * the SDK never holds an OpenKey session token. Local SDK state is cleared
679
+ * before opening the widget, including when remote revocation is
680
+ * unavailable.
677
681
  */
678
682
  async signOut(opts) {
679
683
  const requestId = `ok-signout-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
680
- const sessionToken = this.sessionToken || void 0;
681
684
  const mode = this.resolveMode(opts?.mode) === "redirect" ? "popup" : opts?.mode;
682
685
  this.cancelActiveFlows();
683
686
  this.lastAuth = null;
684
- this.sessionToken = null;
685
687
  const acknowledgement = await this.openFlow(
686
688
  "sign-out",
687
689
  {
688
690
  type: "openkey:sign-out:request",
689
691
  requestId,
690
- protocolVersion: 1,
691
- sessionToken
692
+ protocolVersion: 1
692
693
  },
693
694
  mode
694
695
  );
@@ -698,22 +699,26 @@ var OpenKey = class {
698
699
  return acknowledgement;
699
700
  }
700
701
  /**
701
- * Better-auth session token relayed during connect(), or null if the
702
- * flow did not provide one. Use as a bearer token for API calls made
703
- * on the user's behalf (e.g. TinyCloud auto-sign bootstrap signing).
702
+ * Always null. OpenKey no longer gives its session token to the embedding
703
+ * page (TC-688): the token unlocked OpenKey's key and account APIs, not
704
+ * just the requesting app's flows.
705
+ *
706
+ * @deprecated Kept so existing callers compile. Third-party server-side
707
+ * signing uses an OAuth access token from the OpenKey OAuth flow.
704
708
  */
705
709
  getSessionToken() {
706
- return this.sessionToken;
710
+ return null;
707
711
  }
708
712
  /**
709
- * Endpoint + bearer token for a TinyCloud auto-sign signing strategy.
710
- * The endpoint is on the OpenKey API host and only signs requests the
711
- * server-side bootstrap allowlist permits.
713
+ * Endpoint for a TinyCloud auto-sign signing strategy. `token` is always
714
+ * null; see {@link OpenKey.getSessionToken}.
715
+ *
716
+ * @deprecated The endpoint accepts an OAuth access token as its bearer.
712
717
  */
713
718
  tinycloudSigningOptions() {
714
719
  return {
715
720
  endpoint: `${this.oauthHost}/api/delegate/sign`,
716
- token: this.sessionToken
721
+ token: null
717
722
  };
718
723
  }
719
724
  /**
@@ -798,8 +803,7 @@ var OpenKey = class {
798
803
  // context. The envelope is display-only; the widget transport
799
804
  // validates and size-bounds it before use and NEVER treats it as
800
805
  // verified unless the server actually origin-binds a manifest.
801
- presentation: request.presentation,
802
- sessionToken: this.sessionToken || void 0
806
+ presentation: request.presentation
803
807
  },
804
808
  opts?.mode
805
809
  );
@@ -876,7 +880,6 @@ var OpenKey = class {
876
880
  // bounded at the transport, never treated as verified without
877
881
  // server-side origin-binding.
878
882
  presentation: request.presentation,
879
- sessionToken: this.sessionToken || void 0,
880
883
  // Sol MAJOR-3 (continuation): tell the widget to hand us back
881
884
  // the previewApproval instead of asking OpenKey to sign
882
885
  // server-side with a managed key. The widget will still render
@@ -1030,15 +1033,13 @@ var OpenKey = class {
1030
1033
  return this.openFlow("sign", {
1031
1034
  type: "openkey:sign:request",
1032
1035
  message: request.message,
1033
- keyId: request.keyId,
1034
- sessionToken: this.sessionToken || void 0
1036
+ keyId: request.keyId
1035
1037
  }, mode);
1036
1038
  }
1037
1039
  signTypedDataWithOpenKey(request, mode) {
1038
1040
  return this.openFlow("sign-typed-data", {
1039
1041
  type: "openkey:signTypedData:request",
1040
- data: request,
1041
- sessionToken: this.sessionToken || void 0
1042
+ data: request
1042
1043
  }, mode);
1043
1044
  }
1044
1045
  toHex(str) {
@@ -1294,12 +1295,6 @@ var OpenKey = class {
1294
1295
  onMessage: (data) => {
1295
1296
  if (data.type === "openkey:ready") {
1296
1297
  readyReceived = true;
1297
- if (isVersionedRequest && modal) {
1298
- modal.setExpectedCorrelation(
1299
- outgoingRequestId,
1300
- outgoingProtocolVersion
1301
- );
1302
- }
1303
1298
  modal?.postMessage(message);
1304
1299
  return;
1305
1300
  }
@@ -1330,7 +1325,6 @@ var OpenKey = class {
1330
1325
  cleanup();
1331
1326
  if (data.success) {
1332
1327
  if (data.type === "openkey:auth:response") {
1333
- if (data.sessionToken) this.sessionToken = data.sessionToken;
1334
1328
  resolve({ address: data.address, keyId: data.keyId, keyType: data.keyType || "MANAGED" });
1335
1329
  } else if (data.type === "openkey:link-wallet:response") {
1336
1330
  resolve({ address: data.address, keyId: data.keyId });
@@ -1360,6 +1354,12 @@ var OpenKey = class {
1360
1354
  }
1361
1355
  }
1362
1356
  });
1357
+ if (isVersionedRequest) {
1358
+ modal.setExpectedCorrelation(
1359
+ outgoingRequestId,
1360
+ outgoingProtocolVersion
1361
+ );
1362
+ }
1363
1363
  const readyTimeout = setTimeout(() => {
1364
1364
  if (readyReceived || settled) return;
1365
1365
  cleanup();
@@ -1547,7 +1547,6 @@ var OpenKey = class {
1547
1547
  popup.close();
1548
1548
  if (data.success) {
1549
1549
  if (data.type === "openkey:auth:response") {
1550
- if (data.sessionToken) this.sessionToken = data.sessionToken;
1551
1550
  resolve({ address: data.address, keyId: data.keyId, keyType: data.keyType || "MANAGED" });
1552
1551
  } else if (data.type === "openkey:link-wallet:response") {
1553
1552
  resolve({ address: data.address, keyId: data.keyId });