@rhinestone/1auth 0.1.2 → 0.4.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/dist/index.mjs CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  isTestnet,
20
20
  isTokenAddressSupported,
21
21
  resolveTokenAddress
22
- } from "./chunk-TACK3LJN.mjs";
22
+ } from "./chunk-X73ALCGW.mjs";
23
23
 
24
24
  // src/client.ts
25
25
  import { parseUnits, hashTypedData } from "viem";
@@ -27,7 +27,7 @@ var POPUP_WIDTH = 450;
27
27
  var POPUP_HEIGHT = 600;
28
28
  var DEFAULT_EMBED_WIDTH = "400px";
29
29
  var DEFAULT_EMBED_HEIGHT = "500px";
30
- var MODAL_WIDTH = 360;
30
+ var MODAL_WIDTH = 340;
31
31
  var DEFAULT_PROVIDER_URL = "https://passkey.1auth.box";
32
32
  var OneAuthClient = class {
33
33
  constructor(config) {
@@ -35,6 +35,12 @@ var OneAuthClient = class {
35
35
  const dialogUrl = config.dialogUrl || providerUrl;
36
36
  this.config = { ...config, providerUrl, dialogUrl };
37
37
  this.theme = this.config.theme || {};
38
+ if (typeof document !== "undefined") {
39
+ this.injectPreconnect(providerUrl);
40
+ if (dialogUrl !== providerUrl) {
41
+ this.injectPreconnect(dialogUrl);
42
+ }
43
+ }
38
44
  }
39
45
  /**
40
46
  * Update the theme configuration at runtime
@@ -189,6 +195,133 @@ var OneAuthClient = class {
189
195
  }
190
196
  return this.waitForConnectResponse(dialog, iframe, cleanup);
191
197
  }
198
+ /**
199
+ * Check if a user has already granted consent for the requested fields.
200
+ * This is a read-only check — no dialog is shown.
201
+ *
202
+ * @example
203
+ * ```typescript
204
+ * const result = await client.checkConsent({
205
+ * username: "alice",
206
+ * fields: ["email"],
207
+ * });
208
+ * if (result.hasConsent) {
209
+ * console.log(result.data?.email);
210
+ * }
211
+ * ```
212
+ */
213
+ async checkConsent(options) {
214
+ const clientId = options.clientId ?? this.config.clientId;
215
+ if (!clientId) {
216
+ return {
217
+ hasConsent: false
218
+ };
219
+ }
220
+ const username = options.username ?? options.accountAddress;
221
+ if (!username) {
222
+ return {
223
+ hasConsent: false
224
+ };
225
+ }
226
+ try {
227
+ const res = await fetch(`${this.config.providerUrl || "https://passkey.1auth.box"}/api/consent`, {
228
+ method: "POST",
229
+ headers: {
230
+ "Content-Type": "application/json",
231
+ ...clientId ? { "x-client-id": clientId } : {}
232
+ },
233
+ body: JSON.stringify({
234
+ username,
235
+ requestedFields: options.fields,
236
+ clientId
237
+ })
238
+ });
239
+ if (!res.ok) {
240
+ return { hasConsent: false };
241
+ }
242
+ const data = await res.json();
243
+ return {
244
+ hasConsent: data.hasConsent ?? false,
245
+ data: data.data,
246
+ grantedAt: data.grantedAt
247
+ };
248
+ } catch {
249
+ return { hasConsent: false };
250
+ }
251
+ }
252
+ /**
253
+ * Request consent from the user to share their data.
254
+ *
255
+ * First checks if consent was already granted (returns cached data immediately).
256
+ * If not, opens the consent dialog where the user can review and approve sharing.
257
+ *
258
+ * @example
259
+ * ```typescript
260
+ * const result = await client.requestConsent({
261
+ * username: "alice",
262
+ * fields: ["email", "deviceNames"],
263
+ * });
264
+ * if (result.success) {
265
+ * console.log(result.data?.email);
266
+ * console.log(result.cached); // true if no dialog was shown
267
+ * }
268
+ * ```
269
+ */
270
+ async requestConsent(options) {
271
+ const clientId = options.clientId ?? this.config.clientId;
272
+ if (!clientId) {
273
+ return {
274
+ success: false,
275
+ error: { code: "INVALID_REQUEST", message: "clientId is required (set in config or options)" }
276
+ };
277
+ }
278
+ const username = options.username ?? options.accountAddress;
279
+ if (!username) {
280
+ return {
281
+ success: false,
282
+ error: { code: "INVALID_REQUEST", message: "username or accountAddress is required" }
283
+ };
284
+ }
285
+ if (!options.fields || options.fields.length === 0) {
286
+ return {
287
+ success: false,
288
+ error: { code: "INVALID_REQUEST", message: "At least one field is required" }
289
+ };
290
+ }
291
+ const existing = await this.checkConsent({ ...options, clientId });
292
+ if (existing.hasConsent && existing.data) {
293
+ return {
294
+ success: true,
295
+ data: existing.data,
296
+ grantedAt: existing.grantedAt,
297
+ cached: true
298
+ };
299
+ }
300
+ const dialogUrl = this.getDialogUrl();
301
+ const params = new URLSearchParams({
302
+ mode: "iframe",
303
+ username,
304
+ clientId,
305
+ fields: options.fields.join(",")
306
+ });
307
+ const themeParams = this.getThemeParams(options.theme);
308
+ if (themeParams) {
309
+ const themeParsed = new URLSearchParams(themeParams);
310
+ themeParsed.forEach((value, key) => params.set(key, value));
311
+ }
312
+ const url = `${dialogUrl}/dialog/consent?${params.toString()}`;
313
+ const { dialog, iframe, cleanup } = this.createModalDialog(url);
314
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
315
+ mode: "iframe"
316
+ });
317
+ if (!ready) {
318
+ return {
319
+ success: false,
320
+ error: { code: "USER_CANCELLED", message: "User closed the dialog" }
321
+ };
322
+ }
323
+ return this.waitForConsentResponse(dialog, iframe, cleanup);
324
+ }
192
325
  /**
193
326
  * Authenticate a user with an optional challenge to sign.
194
327
  *
@@ -316,7 +449,8 @@ var OneAuthClient = class {
316
449
  }
317
450
  };
318
451
  }
319
- if (!username && !signedIntent?.accountAddress) {
452
+ const accountAddress = signedIntent?.accountAddress || options.accountAddress;
453
+ if (!username && !accountAddress) {
320
454
  return {
321
455
  success: false,
322
456
  intentId: "",
@@ -345,6 +479,7 @@ var OneAuthClient = class {
345
479
  let prepareResponse;
346
480
  const requestBody = signedIntent || {
347
481
  username: options.username,
482
+ accountAddress: options.accountAddress,
348
483
  targetChain: options.targetChain,
349
484
  calls: options.calls,
350
485
  tokenRequests: serializedTokenRequests,
@@ -352,48 +487,35 @@ var OneAuthClient = class {
352
487
  sourceChainId: options.sourceChainId,
353
488
  ...this.config.clientId && { clientId: this.config.clientId }
354
489
  };
355
- try {
356
- const response = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
357
- method: "POST",
358
- headers: {
359
- "Content-Type": "application/json"
360
- },
361
- body: JSON.stringify(requestBody)
362
- });
363
- if (!response.ok) {
364
- const errorData = await response.json().catch(() => ({}));
365
- const errorMessage = errorData.error || "Failed to prepare intent";
366
- if (errorMessage.includes("User not found")) {
367
- localStorage.removeItem("1auth-user");
368
- }
369
- return {
370
- success: false,
371
- intentId: "",
372
- status: "failed",
373
- error: {
374
- code: errorMessage.includes("User not found") ? "USER_NOT_FOUND" : "PREPARE_FAILED",
375
- message: errorMessage
376
- }
377
- };
378
- }
379
- prepareResponse = await response.json();
380
- } catch (error) {
490
+ const dialogUrl = this.getDialogUrl();
491
+ const themeParams = this.getThemeParams();
492
+ const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
493
+ const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
494
+ const [prepareResult, dialogResult] = await Promise.all([
495
+ this.prepareIntent(requestBody),
496
+ this.waitForDialogReadyDeferred(dialog, iframe, cleanup)
497
+ ]);
498
+ if (!dialogResult.ready) {
381
499
  return {
382
500
  success: false,
383
501
  intentId: "",
384
502
  status: "failed",
385
- error: {
386
- code: "NETWORK_ERROR",
387
- message: error instanceof Error ? error.message : "Network error"
388
- }
503
+ error: { code: "USER_CANCELLED", message: "User closed the dialog" }
389
504
  };
390
505
  }
391
- const dialogUrl = this.getDialogUrl();
392
- const themeParams = this.getThemeParams();
393
- const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
394
- const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
506
+ if (!prepareResult.success) {
507
+ this.sendPrepareError(iframe, prepareResult.error.message);
508
+ await this.waitForDialogClose(dialog, cleanup);
509
+ return {
510
+ success: false,
511
+ intentId: "",
512
+ status: "failed",
513
+ error: prepareResult.error
514
+ };
515
+ }
516
+ prepareResponse = prepareResult.data;
395
517
  const dialogOrigin = this.getDialogOrigin();
396
- const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
518
+ const initPayload = {
397
519
  mode: "iframe",
398
520
  calls,
399
521
  chainId: targetChain,
@@ -405,16 +527,21 @@ var OneAuthClient = class {
405
527
  tokenRequests: serializedTokenRequests,
406
528
  expiresAt: prepareResponse.expiresAt,
407
529
  userId: prepareResponse.userId,
408
- intentOp: prepareResponse.intentOp
409
- });
410
- if (!ready) {
411
- return {
412
- success: false,
413
- intentId: "",
414
- status: "failed",
415
- error: { code: "USER_CANCELLED", message: "User closed the dialog" }
416
- };
417
- }
530
+ intentOp: prepareResponse.intentOp,
531
+ digestResult: prepareResponse.digestResult,
532
+ tier: prepareResult.tier
533
+ };
534
+ dialogResult.sendInit(initPayload);
535
+ const handleReReady = (event) => {
536
+ if (event.origin !== dialogOrigin) return;
537
+ if (event.data?.type === "PASSKEY_READY") {
538
+ iframe.contentWindow?.postMessage(
539
+ { type: "PASSKEY_INIT", ...initPayload },
540
+ dialogOrigin
541
+ );
542
+ }
543
+ };
544
+ window.addEventListener("message", handleReReady);
418
545
  const signingResult = await this.waitForSigningWithRefresh(
419
546
  dialog,
420
547
  iframe,
@@ -441,7 +568,8 @@ var OneAuthClient = class {
441
568
  expiresAt: refreshedData.expiresAt,
442
569
  challenge: refreshedData.challenge,
443
570
  originMessages: refreshedData.originMessages,
444
- transaction: refreshedData.transaction
571
+ transaction: refreshedData.transaction,
572
+ digestResult: refreshedData.digestResult
445
573
  };
446
574
  } catch (error) {
447
575
  console.error("[SDK] Quote refresh error:", error);
@@ -449,6 +577,7 @@ var OneAuthClient = class {
449
577
  }
450
578
  }
451
579
  );
580
+ window.removeEventListener("message", handleReReady);
452
581
  if (!signingResult.success) {
453
582
  return {
454
583
  success: false,
@@ -480,6 +609,7 @@ var OneAuthClient = class {
480
609
  targetChain: prepareResponse.targetChain,
481
610
  calls: prepareResponse.calls,
482
611
  expiresAt: prepareResponse.expiresAt,
612
+ digestResult: prepareResponse.digestResult,
483
613
  // Signature from dialog
484
614
  signature: signingResult.signature,
485
615
  passkey: signingResult.passkey
@@ -521,10 +651,21 @@ var OneAuthClient = class {
521
651
  let finalTxHash = executeResponse.transactionHash;
522
652
  if (finalStatus === "pending") {
523
653
  this.sendTransactionStatus(iframe, "pending");
654
+ let userClosedEarly = false;
655
+ const dialogOrigin2 = this.getDialogOrigin();
656
+ const earlyCloseHandler = (event) => {
657
+ if (event.origin !== dialogOrigin2) return;
658
+ if (event.data?.type === "PASSKEY_CLOSE") {
659
+ userClosedEarly = true;
660
+ cleanup();
661
+ }
662
+ };
663
+ window.addEventListener("message", earlyCloseHandler);
524
664
  const maxAttempts = 120;
525
665
  const pollIntervalMs = 1500;
526
666
  let lastStatus = "pending";
527
667
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
668
+ if (userClosedEarly) break;
528
669
  try {
529
670
  const statusResponse = await fetch(
530
671
  `${this.config.providerUrl}/api/intent/status/${executeResponse.intentId}`,
@@ -559,6 +700,21 @@ var OneAuthClient = class {
559
700
  }
560
701
  await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
561
702
  }
703
+ window.removeEventListener("message", earlyCloseHandler);
704
+ if (userClosedEarly) {
705
+ cleanup();
706
+ return {
707
+ success: false,
708
+ intentId: executeResponse.intentId,
709
+ status: finalStatus,
710
+ transactionHash: finalTxHash,
711
+ operationId: executeResponse.operationId,
712
+ error: {
713
+ code: "USER_CANCELLED",
714
+ message: "User closed the dialog"
715
+ }
716
+ };
717
+ }
562
718
  }
563
719
  const closeOn = options.closeOn || "preconfirmed";
564
720
  const successStatuses = {
@@ -569,8 +725,9 @@ var OneAuthClient = class {
569
725
  };
570
726
  const isSuccessStatus = successStatuses[closeOn]?.includes(finalStatus) ?? false;
571
727
  const displayStatus = isSuccessStatus ? "confirmed" : finalStatus;
728
+ const closePromise = this.waitForDialogClose(dialog, cleanup);
572
729
  this.sendTransactionStatus(iframe, displayStatus, finalTxHash);
573
- await this.waitForDialogClose(dialog, cleanup);
730
+ await closePromise;
574
731
  if (options.waitForHash && !finalTxHash) {
575
732
  const hash = await this.waitForTransactionHash(executeResponse.intentId, {
576
733
  timeoutMs: options.hashTimeoutMs,
@@ -631,7 +788,7 @@ var OneAuthClient = class {
631
788
  * ```
632
789
  */
633
790
  async sendBatchIntent(options) {
634
- if (!options.username) {
791
+ if (!options.username && !options.accountAddress) {
635
792
  return {
636
793
  success: false,
637
794
  results: [],
@@ -655,35 +812,24 @@ var OneAuthClient = class {
655
812
  amount: r.amount.toString()
656
813
  })),
657
814
  sourceAssets: intent.sourceAssets,
658
- sourceChainId: intent.sourceChainId
815
+ sourceChainId: intent.sourceChainId,
816
+ moduleInstall: intent.moduleInstall
659
817
  }));
660
818
  const requestBody = {
661
- username: options.username,
819
+ ...options.username && { username: options.username },
820
+ ...options.accountAddress && { accountAddress: options.accountAddress },
662
821
  intents: serializedIntents,
663
822
  ...this.config.clientId && { clientId: this.config.clientId }
664
823
  };
665
- let prepareResponse;
666
- try {
667
- const response = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
668
- method: "POST",
669
- headers: { "Content-Type": "application/json" },
670
- body: JSON.stringify(requestBody)
671
- });
672
- if (!response.ok) {
673
- const errorData = await response.json().catch(() => ({}));
674
- const errorMessage = errorData.error || "Failed to prepare batch intent";
675
- if (errorMessage.includes("User not found")) {
676
- localStorage.removeItem("1auth-user");
677
- }
678
- return {
679
- success: false,
680
- results: [],
681
- successCount: 0,
682
- failureCount: 0
683
- };
684
- }
685
- prepareResponse = await response.json();
686
- } catch {
824
+ const dialogUrl = this.getDialogUrl();
825
+ const themeParams = this.getThemeParams();
826
+ const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
827
+ const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
828
+ const [prepareResult, dialogResult] = await Promise.all([
829
+ this.prepareBatchIntent(requestBody),
830
+ this.waitForDialogReadyDeferred(dialog, iframe, cleanup)
831
+ ]);
832
+ if (!dialogResult.ready) {
687
833
  return {
688
834
  success: false,
689
835
  results: [],
@@ -691,29 +837,50 @@ var OneAuthClient = class {
691
837
  failureCount: 0
692
838
  };
693
839
  }
694
- const dialogUrl = this.getDialogUrl();
695
- const themeParams = this.getThemeParams();
696
- const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
697
- const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
840
+ if (!prepareResult.success) {
841
+ const failedIntents = prepareResult.failedIntents;
842
+ const failureResults = failedIntents?.map((f) => ({
843
+ index: f.index,
844
+ success: false,
845
+ intentId: "",
846
+ status: "failed",
847
+ error: { message: f.error, code: "PREPARE_FAILED" }
848
+ })) ?? [];
849
+ this.sendPrepareError(iframe, prepareResult.error);
850
+ await this.waitForDialogClose(dialog, cleanup);
851
+ return {
852
+ success: false,
853
+ results: failureResults,
854
+ successCount: 0,
855
+ failureCount: failureResults.length,
856
+ error: prepareResult.error
857
+ };
858
+ }
859
+ let prepareResponse = prepareResult.data;
698
860
  const dialogOrigin = this.getDialogOrigin();
699
- const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
861
+ const batchInitPayload = {
700
862
  mode: "iframe",
701
863
  batchMode: true,
702
864
  batchIntents: prepareResponse.intents,
865
+ batchFailedIntents: prepareResponse.failedIntents,
703
866
  challenge: prepareResponse.challenge,
704
867
  username: options.username,
705
868
  accountAddress: prepareResponse.accountAddress,
706
869
  userId: prepareResponse.userId,
707
- expiresAt: prepareResponse.expiresAt
708
- });
709
- if (!ready) {
710
- return {
711
- success: false,
712
- results: [],
713
- successCount: 0,
714
- failureCount: 0
715
- };
716
- }
870
+ expiresAt: prepareResponse.expiresAt,
871
+ tier: prepareResult.tier
872
+ };
873
+ dialogResult.sendInit(batchInitPayload);
874
+ const handleBatchReReady = (event) => {
875
+ if (event.origin !== dialogOrigin) return;
876
+ if (event.data?.type === "PASSKEY_READY") {
877
+ iframe.contentWindow?.postMessage(
878
+ { type: "PASSKEY_INIT", ...batchInitPayload },
879
+ dialogOrigin
880
+ );
881
+ }
882
+ };
883
+ window.addEventListener("message", handleBatchReReady);
717
884
  const batchResult = await new Promise((resolve) => {
718
885
  const handleMessage = async (event) => {
719
886
  if (event.origin !== dialogOrigin) return;
@@ -760,13 +927,21 @@ var OneAuthClient = class {
760
927
  status: r.status === "FAILED" ? "failed" : "pending",
761
928
  error: r.error ? { code: "EXECUTE_FAILED", message: r.error } : void 0
762
929
  }));
763
- const successCount = results.filter((r) => r.success).length;
930
+ const prepareFailures = (prepareResponse.failedIntents ?? []).map((f) => ({
931
+ index: f.index,
932
+ success: false,
933
+ intentId: "",
934
+ status: "failed",
935
+ error: { code: "PREPARE_FAILED", message: f.error }
936
+ }));
937
+ const allResults = [...results, ...prepareFailures].sort((a, b) => a.index - b.index);
938
+ const successCount = allResults.filter((r) => r.success).length;
764
939
  await this.waitForDialogClose(dialog, cleanup);
765
940
  resolve({
766
- success: successCount === results.length,
767
- results,
941
+ success: successCount === allResults.length,
942
+ results: allResults,
768
943
  successCount,
769
- failureCount: results.length - successCount
944
+ failureCount: allResults.length - successCount
770
945
  });
771
946
  } else {
772
947
  cleanup();
@@ -791,6 +966,7 @@ var OneAuthClient = class {
791
966
  };
792
967
  window.addEventListener("message", handleMessage);
793
968
  });
969
+ window.removeEventListener("message", handleBatchReReady);
794
970
  return batchResult;
795
971
  }
796
972
  /**
@@ -994,6 +1170,143 @@ var OneAuthClient = class {
994
1170
  dialog.addEventListener("close", handleClose);
995
1171
  });
996
1172
  }
1173
+ /**
1174
+ * Inject a preconnect link tag to pre-warm DNS + TLS for a given URL.
1175
+ */
1176
+ injectPreconnect(url) {
1177
+ try {
1178
+ const origin = new URL(url).origin;
1179
+ if (document.querySelector(`link[rel="preconnect"][href="${origin}"]`)) return;
1180
+ const link = document.createElement("link");
1181
+ link.rel = "preconnect";
1182
+ link.href = origin;
1183
+ link.crossOrigin = "anonymous";
1184
+ document.head.appendChild(link);
1185
+ } catch {
1186
+ }
1187
+ }
1188
+ /**
1189
+ * Wait for the dialog iframe to signal ready without sending init data.
1190
+ * Returns a sendInit function the caller uses once prepare data is available.
1191
+ */
1192
+ waitForDialogReadyDeferred(dialog, iframe, cleanup) {
1193
+ const dialogOrigin = this.getDialogOrigin();
1194
+ return new Promise((resolve) => {
1195
+ let settled = false;
1196
+ const teardown = () => {
1197
+ if (settled) return;
1198
+ settled = true;
1199
+ clearTimeout(readyTimeout);
1200
+ window.removeEventListener("message", handleMessage);
1201
+ dialog.removeEventListener("close", handleClose);
1202
+ };
1203
+ const handleMessage = (event) => {
1204
+ if (event.origin !== dialogOrigin) return;
1205
+ if (event.data?.type === "PASSKEY_READY") {
1206
+ teardown();
1207
+ resolve({
1208
+ ready: true,
1209
+ sendInit: (initMessage) => {
1210
+ iframe.contentWindow?.postMessage(
1211
+ { type: "PASSKEY_INIT", ...initMessage },
1212
+ dialogOrigin
1213
+ );
1214
+ }
1215
+ });
1216
+ } else if (event.data?.type === "PASSKEY_CLOSE") {
1217
+ teardown();
1218
+ cleanup();
1219
+ resolve({ ready: false });
1220
+ }
1221
+ };
1222
+ const handleClose = () => {
1223
+ teardown();
1224
+ resolve({ ready: false });
1225
+ };
1226
+ const readyTimeout = setTimeout(() => {
1227
+ teardown();
1228
+ cleanup();
1229
+ resolve({ ready: false });
1230
+ }, 1e4);
1231
+ window.addEventListener("message", handleMessage);
1232
+ dialog.addEventListener("close", handleClose);
1233
+ });
1234
+ }
1235
+ /**
1236
+ * Prepare an intent by calling the orchestrator for a quote.
1237
+ * Returns the prepare response and the origin tier from the response header.
1238
+ */
1239
+ async prepareIntent(requestBody) {
1240
+ try {
1241
+ const response = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
1242
+ method: "POST",
1243
+ headers: { "Content-Type": "application/json" },
1244
+ body: JSON.stringify(requestBody)
1245
+ });
1246
+ if (!response.ok) {
1247
+ const errorData = await response.json().catch(() => ({}));
1248
+ const errorMessage = errorData.error || "Failed to prepare intent";
1249
+ if (errorMessage.includes("User not found")) {
1250
+ localStorage.removeItem("1auth-user");
1251
+ }
1252
+ return {
1253
+ success: false,
1254
+ error: {
1255
+ code: errorMessage.includes("User not found") ? "USER_NOT_FOUND" : "PREPARE_FAILED",
1256
+ message: errorMessage
1257
+ }
1258
+ };
1259
+ }
1260
+ const tier = response.headers.get("X-Origin-Tier");
1261
+ return { success: true, data: await response.json(), tier };
1262
+ } catch (error) {
1263
+ return {
1264
+ success: false,
1265
+ error: {
1266
+ code: "NETWORK_ERROR",
1267
+ message: error instanceof Error ? error.message : "Network error"
1268
+ }
1269
+ };
1270
+ }
1271
+ }
1272
+ /**
1273
+ * Prepare a batch intent by calling the orchestrator for quotes on all intents.
1274
+ */
1275
+ async prepareBatchIntent(requestBody) {
1276
+ try {
1277
+ const response = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
1278
+ method: "POST",
1279
+ headers: { "Content-Type": "application/json" },
1280
+ body: JSON.stringify(requestBody)
1281
+ });
1282
+ if (!response.ok) {
1283
+ const errorData = await response.json().catch(() => ({}));
1284
+ const errorMessage = errorData.error || "Failed to prepare batch intent";
1285
+ if (errorMessage.includes("User not found")) {
1286
+ localStorage.removeItem("1auth-user");
1287
+ }
1288
+ return {
1289
+ success: false,
1290
+ error: errorMessage,
1291
+ failedIntents: errorData.failedIntents
1292
+ };
1293
+ }
1294
+ const tier = response.headers.get("X-Origin-Tier");
1295
+ return { success: true, data: await response.json(), tier };
1296
+ } catch {
1297
+ return { success: false, error: "Network error" };
1298
+ }
1299
+ }
1300
+ /**
1301
+ * Send a prepare error message to the dialog iframe.
1302
+ */
1303
+ sendPrepareError(iframe, error) {
1304
+ const dialogOrigin = this.getDialogOrigin();
1305
+ iframe.contentWindow?.postMessage(
1306
+ { type: "PASSKEY_PREPARE_ERROR", error },
1307
+ dialogOrigin
1308
+ );
1309
+ }
997
1310
  /**
998
1311
  * Poll for intent status
999
1312
  *
@@ -1305,6 +1618,7 @@ var OneAuthClient = class {
1305
1618
  message: options.message,
1306
1619
  challenge: options.challenge || options.message,
1307
1620
  username: options.username,
1621
+ accountAddress: options.accountAddress,
1308
1622
  description: options.description,
1309
1623
  metadata: options.metadata
1310
1624
  });
@@ -1396,6 +1710,7 @@ var OneAuthClient = class {
1396
1710
  },
1397
1711
  challenge: signedHash,
1398
1712
  username: options.username,
1713
+ accountAddress: options.accountAddress,
1399
1714
  description: options.description
1400
1715
  });
1401
1716
  if (!ready) {
@@ -1469,7 +1784,7 @@ var OneAuthClient = class {
1469
1784
  iframe.style.borderRadius = "12px";
1470
1785
  iframe.style.boxShadow = "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)";
1471
1786
  iframe.id = `passkey-embed-${requestId}`;
1472
- iframe.allow = "publickey-credentials-get *; publickey-credentials-create *";
1787
+ iframe.allow = "publickey-credentials-get *; publickey-credentials-create *; identity-credentials-get";
1473
1788
  iframe.onload = () => {
1474
1789
  options.onReady?.();
1475
1790
  };
@@ -1617,6 +1932,7 @@ var OneAuthClient = class {
1617
1932
  const teardown = () => {
1618
1933
  if (settled) return;
1619
1934
  settled = true;
1935
+ clearTimeout(readyTimeout);
1620
1936
  window.removeEventListener("message", handleMessage);
1621
1937
  dialog.removeEventListener("close", handleClose);
1622
1938
  };
@@ -1639,6 +1955,11 @@ var OneAuthClient = class {
1639
1955
  teardown();
1640
1956
  resolve(false);
1641
1957
  };
1958
+ const readyTimeout = setTimeout(() => {
1959
+ teardown();
1960
+ cleanup();
1961
+ resolve(false);
1962
+ }, 1e4);
1642
1963
  window.addEventListener("message", handleMessage);
1643
1964
  dialog.addEventListener("close", handleClose);
1644
1965
  });
@@ -1679,7 +2000,9 @@ var OneAuthClient = class {
1679
2000
  border-radius: 14px;
1680
2001
  border: none;
1681
2002
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);
1682
- transition: width 0.2s ease-out, height 0.15s ease-out;
2003
+ transition: height 0.15s ease-out;
2004
+ max-height: calc(100vh - 100px);
2005
+ max-height: calc(100dvh - 100px);
1683
2006
  }
1684
2007
 
1685
2008
  @media (min-width: 769px) {
@@ -1741,14 +2064,10 @@ var OneAuthClient = class {
1741
2064
  const iframe = document.createElement("iframe");
1742
2065
  iframe.setAttribute(
1743
2066
  "allow",
1744
- "publickey-credentials-get *; publickey-credentials-create *; clipboard-write"
2067
+ "publickey-credentials-get *; publickey-credentials-create *; clipboard-write; identity-credentials-get"
1745
2068
  );
1746
2069
  iframe.setAttribute("aria-label", "Passkey Authentication");
1747
2070
  iframe.setAttribute("tabindex", "0");
1748
- iframe.setAttribute(
1749
- "sandbox",
1750
- "allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox"
1751
- );
1752
2071
  iframe.setAttribute("src", url);
1753
2072
  iframe.setAttribute("title", "Passkey");
1754
2073
  iframe.style.border = "none";
@@ -1758,10 +2077,8 @@ var OneAuthClient = class {
1758
2077
  const handleMessage = (event) => {
1759
2078
  if (event.origin !== hostUrl.origin) return;
1760
2079
  if (event.data?.type === "PASSKEY_RESIZE") {
1761
- iframe.style.height = `${event.data.height}px`;
1762
- if (event.data.width) {
1763
- iframe.style.width = `${event.data.width}px`;
1764
- }
2080
+ const maxHeight = window.innerHeight - 100;
2081
+ iframe.style.height = `${Math.min(event.data.height, maxHeight)}px`;
1765
2082
  } else if (event.data?.type === "PASSKEY_DISCONNECT") {
1766
2083
  localStorage.removeItem("1auth-user");
1767
2084
  }
@@ -1779,7 +2096,10 @@ var OneAuthClient = class {
1779
2096
  }
1780
2097
  });
1781
2098
  dialog.showModal();
2099
+ let cleanedUp = false;
1782
2100
  const cleanup = () => {
2101
+ if (cleanedUp) return;
2102
+ cleanedUp = true;
1783
2103
  window.removeEventListener("message", handleMessage);
1784
2104
  document.removeEventListener("keydown", handleEscape);
1785
2105
  dialog.close();
@@ -1812,6 +2132,7 @@ var OneAuthClient = class {
1812
2132
  resolve({
1813
2133
  success: true,
1814
2134
  username: data.data?.username,
2135
+ address: data.data?.address,
1815
2136
  user: data.data?.user
1816
2137
  });
1817
2138
  } else {
@@ -1826,7 +2147,8 @@ var OneAuthClient = class {
1826
2147
  if (data.success) {
1827
2148
  resolve({
1828
2149
  success: true,
1829
- username: data.data?.username
2150
+ username: data.data?.username,
2151
+ address: data.data?.address
1830
2152
  });
1831
2153
  } else {
1832
2154
  resolve({
@@ -1886,6 +2208,7 @@ var OneAuthClient = class {
1886
2208
  resolve({
1887
2209
  success: true,
1888
2210
  username: data.data?.username,
2211
+ address: data.data?.address,
1889
2212
  user: data.data?.user
1890
2213
  });
1891
2214
  } else {
@@ -1901,7 +2224,8 @@ var OneAuthClient = class {
1901
2224
  if (data.success) {
1902
2225
  resolve({
1903
2226
  success: true,
1904
- username: data.data?.username
2227
+ username: data.data?.username,
2228
+ address: data.data?.address
1905
2229
  });
1906
2230
  } else {
1907
2231
  resolve({
@@ -1977,6 +2301,7 @@ var OneAuthClient = class {
1977
2301
  resolve({
1978
2302
  success: true,
1979
2303
  username: data.data?.username,
2304
+ address: data.data?.address,
1980
2305
  autoConnected: data.data?.autoConnected
1981
2306
  });
1982
2307
  } else {
@@ -2002,6 +2327,45 @@ var OneAuthClient = class {
2002
2327
  window.addEventListener("message", handleMessage);
2003
2328
  });
2004
2329
  }
2330
+ waitForConsentResponse(_dialog, _iframe, cleanup) {
2331
+ const dialogOrigin = this.getDialogOrigin();
2332
+ return new Promise((resolve) => {
2333
+ const handleMessage = (event) => {
2334
+ if (event.origin !== dialogOrigin) return;
2335
+ const data = event.data;
2336
+ if (data?.type === "PASSKEY_CONSENT_RESULT") {
2337
+ window.removeEventListener("message", handleMessage);
2338
+ cleanup();
2339
+ if (data.success) {
2340
+ resolve({
2341
+ success: true,
2342
+ data: data.data,
2343
+ grantedAt: data.data?.grantedAt
2344
+ });
2345
+ } else {
2346
+ resolve({
2347
+ success: false,
2348
+ error: data.error ?? {
2349
+ code: "USER_REJECTED",
2350
+ message: "User denied the consent request"
2351
+ }
2352
+ });
2353
+ }
2354
+ } else if (data?.type === "PASSKEY_CLOSE") {
2355
+ window.removeEventListener("message", handleMessage);
2356
+ cleanup();
2357
+ resolve({
2358
+ success: false,
2359
+ error: {
2360
+ code: "USER_CANCELLED",
2361
+ message: "User closed the dialog"
2362
+ }
2363
+ });
2364
+ }
2365
+ };
2366
+ window.addEventListener("message", handleMessage);
2367
+ });
2368
+ }
2005
2369
  waitForModalSigningResponse(requestId, _dialog, _iframe, cleanup) {
2006
2370
  const dialogOrigin = this.getDialogOrigin();
2007
2371
  return new Promise((resolve) => {