@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.js CHANGED
@@ -71,12 +71,15 @@ var import_viem = require("viem");
71
71
  var viemChains = __toESM(require("viem/chains"));
72
72
  var import_sdk = require("@rhinestone/sdk");
73
73
  var env = typeof process !== "undefined" ? process.env : {};
74
- var ALL_VIEM_CHAINS = Object.values(viemChains).filter(
75
- (value) => typeof value === "object" && value !== null && "id" in value && "name" in value
76
- );
77
- var VIEM_CHAIN_BY_ID = new Map(
78
- ALL_VIEM_CHAINS.map((chain) => [chain.id, chain])
79
- );
74
+ var VIEM_CHAIN_BY_ID = /* @__PURE__ */ new Map();
75
+ for (const value of Object.values(viemChains)) {
76
+ if (typeof value !== "object" || value === null || !("id" in value) || !("name" in value)) continue;
77
+ const chain = value;
78
+ const existing = VIEM_CHAIN_BY_ID.get(chain.id);
79
+ if (!existing || existing.testnet && !chain.testnet) {
80
+ VIEM_CHAIN_BY_ID.set(chain.id, chain);
81
+ }
82
+ }
80
83
  var SUPPORTED_CHAIN_IDS = new Set(
81
84
  (0, import_sdk.getAllSupportedChainsAndTokens)().map((entry) => entry.chainId)
82
85
  );
@@ -197,7 +200,7 @@ var POPUP_WIDTH = 450;
197
200
  var POPUP_HEIGHT = 600;
198
201
  var DEFAULT_EMBED_WIDTH = "400px";
199
202
  var DEFAULT_EMBED_HEIGHT = "500px";
200
- var MODAL_WIDTH = 360;
203
+ var MODAL_WIDTH = 340;
201
204
  var DEFAULT_PROVIDER_URL = "https://passkey.1auth.box";
202
205
  var OneAuthClient = class {
203
206
  constructor(config) {
@@ -205,6 +208,12 @@ var OneAuthClient = class {
205
208
  const dialogUrl = config.dialogUrl || providerUrl;
206
209
  this.config = { ...config, providerUrl, dialogUrl };
207
210
  this.theme = this.config.theme || {};
211
+ if (typeof document !== "undefined") {
212
+ this.injectPreconnect(providerUrl);
213
+ if (dialogUrl !== providerUrl) {
214
+ this.injectPreconnect(dialogUrl);
215
+ }
216
+ }
208
217
  }
209
218
  /**
210
219
  * Update the theme configuration at runtime
@@ -359,6 +368,133 @@ var OneAuthClient = class {
359
368
  }
360
369
  return this.waitForConnectResponse(dialog, iframe, cleanup);
361
370
  }
371
+ /**
372
+ * Check if a user has already granted consent for the requested fields.
373
+ * This is a read-only check — no dialog is shown.
374
+ *
375
+ * @example
376
+ * ```typescript
377
+ * const result = await client.checkConsent({
378
+ * username: "alice",
379
+ * fields: ["email"],
380
+ * });
381
+ * if (result.hasConsent) {
382
+ * console.log(result.data?.email);
383
+ * }
384
+ * ```
385
+ */
386
+ async checkConsent(options) {
387
+ const clientId = options.clientId ?? this.config.clientId;
388
+ if (!clientId) {
389
+ return {
390
+ hasConsent: false
391
+ };
392
+ }
393
+ const username = options.username ?? options.accountAddress;
394
+ if (!username) {
395
+ return {
396
+ hasConsent: false
397
+ };
398
+ }
399
+ try {
400
+ const res = await fetch(`${this.config.providerUrl || "https://passkey.1auth.box"}/api/consent`, {
401
+ method: "POST",
402
+ headers: {
403
+ "Content-Type": "application/json",
404
+ ...clientId ? { "x-client-id": clientId } : {}
405
+ },
406
+ body: JSON.stringify({
407
+ username,
408
+ requestedFields: options.fields,
409
+ clientId
410
+ })
411
+ });
412
+ if (!res.ok) {
413
+ return { hasConsent: false };
414
+ }
415
+ const data = await res.json();
416
+ return {
417
+ hasConsent: data.hasConsent ?? false,
418
+ data: data.data,
419
+ grantedAt: data.grantedAt
420
+ };
421
+ } catch {
422
+ return { hasConsent: false };
423
+ }
424
+ }
425
+ /**
426
+ * Request consent from the user to share their data.
427
+ *
428
+ * First checks if consent was already granted (returns cached data immediately).
429
+ * If not, opens the consent dialog where the user can review and approve sharing.
430
+ *
431
+ * @example
432
+ * ```typescript
433
+ * const result = await client.requestConsent({
434
+ * username: "alice",
435
+ * fields: ["email", "deviceNames"],
436
+ * });
437
+ * if (result.success) {
438
+ * console.log(result.data?.email);
439
+ * console.log(result.cached); // true if no dialog was shown
440
+ * }
441
+ * ```
442
+ */
443
+ async requestConsent(options) {
444
+ const clientId = options.clientId ?? this.config.clientId;
445
+ if (!clientId) {
446
+ return {
447
+ success: false,
448
+ error: { code: "INVALID_REQUEST", message: "clientId is required (set in config or options)" }
449
+ };
450
+ }
451
+ const username = options.username ?? options.accountAddress;
452
+ if (!username) {
453
+ return {
454
+ success: false,
455
+ error: { code: "INVALID_REQUEST", message: "username or accountAddress is required" }
456
+ };
457
+ }
458
+ if (!options.fields || options.fields.length === 0) {
459
+ return {
460
+ success: false,
461
+ error: { code: "INVALID_REQUEST", message: "At least one field is required" }
462
+ };
463
+ }
464
+ const existing = await this.checkConsent({ ...options, clientId });
465
+ if (existing.hasConsent && existing.data) {
466
+ return {
467
+ success: true,
468
+ data: existing.data,
469
+ grantedAt: existing.grantedAt,
470
+ cached: true
471
+ };
472
+ }
473
+ const dialogUrl = this.getDialogUrl();
474
+ const params = new URLSearchParams({
475
+ mode: "iframe",
476
+ username,
477
+ clientId,
478
+ fields: options.fields.join(",")
479
+ });
480
+ const themeParams = this.getThemeParams(options.theme);
481
+ if (themeParams) {
482
+ const themeParsed = new URLSearchParams(themeParams);
483
+ themeParsed.forEach((value, key) => params.set(key, value));
484
+ }
485
+ const url = `${dialogUrl}/dialog/consent?${params.toString()}`;
486
+ const { dialog, iframe, cleanup } = this.createModalDialog(url);
487
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
488
+ mode: "iframe"
489
+ });
490
+ if (!ready) {
491
+ return {
492
+ success: false,
493
+ error: { code: "USER_CANCELLED", message: "User closed the dialog" }
494
+ };
495
+ }
496
+ return this.waitForConsentResponse(dialog, iframe, cleanup);
497
+ }
362
498
  /**
363
499
  * Authenticate a user with an optional challenge to sign.
364
500
  *
@@ -486,7 +622,8 @@ var OneAuthClient = class {
486
622
  }
487
623
  };
488
624
  }
489
- if (!username && !signedIntent?.accountAddress) {
625
+ const accountAddress = signedIntent?.accountAddress || options.accountAddress;
626
+ if (!username && !accountAddress) {
490
627
  return {
491
628
  success: false,
492
629
  intentId: "",
@@ -515,6 +652,7 @@ var OneAuthClient = class {
515
652
  let prepareResponse;
516
653
  const requestBody = signedIntent || {
517
654
  username: options.username,
655
+ accountAddress: options.accountAddress,
518
656
  targetChain: options.targetChain,
519
657
  calls: options.calls,
520
658
  tokenRequests: serializedTokenRequests,
@@ -522,48 +660,35 @@ var OneAuthClient = class {
522
660
  sourceChainId: options.sourceChainId,
523
661
  ...this.config.clientId && { clientId: this.config.clientId }
524
662
  };
525
- try {
526
- const response = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
527
- method: "POST",
528
- headers: {
529
- "Content-Type": "application/json"
530
- },
531
- body: JSON.stringify(requestBody)
532
- });
533
- if (!response.ok) {
534
- const errorData = await response.json().catch(() => ({}));
535
- const errorMessage = errorData.error || "Failed to prepare intent";
536
- if (errorMessage.includes("User not found")) {
537
- localStorage.removeItem("1auth-user");
538
- }
539
- return {
540
- success: false,
541
- intentId: "",
542
- status: "failed",
543
- error: {
544
- code: errorMessage.includes("User not found") ? "USER_NOT_FOUND" : "PREPARE_FAILED",
545
- message: errorMessage
546
- }
547
- };
548
- }
549
- prepareResponse = await response.json();
550
- } catch (error) {
663
+ const dialogUrl = this.getDialogUrl();
664
+ const themeParams = this.getThemeParams();
665
+ const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
666
+ const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
667
+ const [prepareResult, dialogResult] = await Promise.all([
668
+ this.prepareIntent(requestBody),
669
+ this.waitForDialogReadyDeferred(dialog, iframe, cleanup)
670
+ ]);
671
+ if (!dialogResult.ready) {
551
672
  return {
552
673
  success: false,
553
674
  intentId: "",
554
675
  status: "failed",
555
- error: {
556
- code: "NETWORK_ERROR",
557
- message: error instanceof Error ? error.message : "Network error"
558
- }
676
+ error: { code: "USER_CANCELLED", message: "User closed the dialog" }
559
677
  };
560
678
  }
561
- const dialogUrl = this.getDialogUrl();
562
- const themeParams = this.getThemeParams();
563
- const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
564
- const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
679
+ if (!prepareResult.success) {
680
+ this.sendPrepareError(iframe, prepareResult.error.message);
681
+ await this.waitForDialogClose(dialog, cleanup);
682
+ return {
683
+ success: false,
684
+ intentId: "",
685
+ status: "failed",
686
+ error: prepareResult.error
687
+ };
688
+ }
689
+ prepareResponse = prepareResult.data;
565
690
  const dialogOrigin = this.getDialogOrigin();
566
- const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
691
+ const initPayload = {
567
692
  mode: "iframe",
568
693
  calls,
569
694
  chainId: targetChain,
@@ -575,16 +700,21 @@ var OneAuthClient = class {
575
700
  tokenRequests: serializedTokenRequests,
576
701
  expiresAt: prepareResponse.expiresAt,
577
702
  userId: prepareResponse.userId,
578
- intentOp: prepareResponse.intentOp
579
- });
580
- if (!ready) {
581
- return {
582
- success: false,
583
- intentId: "",
584
- status: "failed",
585
- error: { code: "USER_CANCELLED", message: "User closed the dialog" }
586
- };
587
- }
703
+ intentOp: prepareResponse.intentOp,
704
+ digestResult: prepareResponse.digestResult,
705
+ tier: prepareResult.tier
706
+ };
707
+ dialogResult.sendInit(initPayload);
708
+ const handleReReady = (event) => {
709
+ if (event.origin !== dialogOrigin) return;
710
+ if (event.data?.type === "PASSKEY_READY") {
711
+ iframe.contentWindow?.postMessage(
712
+ { type: "PASSKEY_INIT", ...initPayload },
713
+ dialogOrigin
714
+ );
715
+ }
716
+ };
717
+ window.addEventListener("message", handleReReady);
588
718
  const signingResult = await this.waitForSigningWithRefresh(
589
719
  dialog,
590
720
  iframe,
@@ -611,7 +741,8 @@ var OneAuthClient = class {
611
741
  expiresAt: refreshedData.expiresAt,
612
742
  challenge: refreshedData.challenge,
613
743
  originMessages: refreshedData.originMessages,
614
- transaction: refreshedData.transaction
744
+ transaction: refreshedData.transaction,
745
+ digestResult: refreshedData.digestResult
615
746
  };
616
747
  } catch (error) {
617
748
  console.error("[SDK] Quote refresh error:", error);
@@ -619,6 +750,7 @@ var OneAuthClient = class {
619
750
  }
620
751
  }
621
752
  );
753
+ window.removeEventListener("message", handleReReady);
622
754
  if (!signingResult.success) {
623
755
  return {
624
756
  success: false,
@@ -650,6 +782,7 @@ var OneAuthClient = class {
650
782
  targetChain: prepareResponse.targetChain,
651
783
  calls: prepareResponse.calls,
652
784
  expiresAt: prepareResponse.expiresAt,
785
+ digestResult: prepareResponse.digestResult,
653
786
  // Signature from dialog
654
787
  signature: signingResult.signature,
655
788
  passkey: signingResult.passkey
@@ -691,10 +824,21 @@ var OneAuthClient = class {
691
824
  let finalTxHash = executeResponse.transactionHash;
692
825
  if (finalStatus === "pending") {
693
826
  this.sendTransactionStatus(iframe, "pending");
827
+ let userClosedEarly = false;
828
+ const dialogOrigin2 = this.getDialogOrigin();
829
+ const earlyCloseHandler = (event) => {
830
+ if (event.origin !== dialogOrigin2) return;
831
+ if (event.data?.type === "PASSKEY_CLOSE") {
832
+ userClosedEarly = true;
833
+ cleanup();
834
+ }
835
+ };
836
+ window.addEventListener("message", earlyCloseHandler);
694
837
  const maxAttempts = 120;
695
838
  const pollIntervalMs = 1500;
696
839
  let lastStatus = "pending";
697
840
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
841
+ if (userClosedEarly) break;
698
842
  try {
699
843
  const statusResponse = await fetch(
700
844
  `${this.config.providerUrl}/api/intent/status/${executeResponse.intentId}`,
@@ -729,6 +873,21 @@ var OneAuthClient = class {
729
873
  }
730
874
  await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
731
875
  }
876
+ window.removeEventListener("message", earlyCloseHandler);
877
+ if (userClosedEarly) {
878
+ cleanup();
879
+ return {
880
+ success: false,
881
+ intentId: executeResponse.intentId,
882
+ status: finalStatus,
883
+ transactionHash: finalTxHash,
884
+ operationId: executeResponse.operationId,
885
+ error: {
886
+ code: "USER_CANCELLED",
887
+ message: "User closed the dialog"
888
+ }
889
+ };
890
+ }
732
891
  }
733
892
  const closeOn = options.closeOn || "preconfirmed";
734
893
  const successStatuses = {
@@ -739,8 +898,9 @@ var OneAuthClient = class {
739
898
  };
740
899
  const isSuccessStatus = successStatuses[closeOn]?.includes(finalStatus) ?? false;
741
900
  const displayStatus = isSuccessStatus ? "confirmed" : finalStatus;
901
+ const closePromise = this.waitForDialogClose(dialog, cleanup);
742
902
  this.sendTransactionStatus(iframe, displayStatus, finalTxHash);
743
- await this.waitForDialogClose(dialog, cleanup);
903
+ await closePromise;
744
904
  if (options.waitForHash && !finalTxHash) {
745
905
  const hash = await this.waitForTransactionHash(executeResponse.intentId, {
746
906
  timeoutMs: options.hashTimeoutMs,
@@ -801,7 +961,7 @@ var OneAuthClient = class {
801
961
  * ```
802
962
  */
803
963
  async sendBatchIntent(options) {
804
- if (!options.username) {
964
+ if (!options.username && !options.accountAddress) {
805
965
  return {
806
966
  success: false,
807
967
  results: [],
@@ -825,35 +985,24 @@ var OneAuthClient = class {
825
985
  amount: r.amount.toString()
826
986
  })),
827
987
  sourceAssets: intent.sourceAssets,
828
- sourceChainId: intent.sourceChainId
988
+ sourceChainId: intent.sourceChainId,
989
+ moduleInstall: intent.moduleInstall
829
990
  }));
830
991
  const requestBody = {
831
- username: options.username,
992
+ ...options.username && { username: options.username },
993
+ ...options.accountAddress && { accountAddress: options.accountAddress },
832
994
  intents: serializedIntents,
833
995
  ...this.config.clientId && { clientId: this.config.clientId }
834
996
  };
835
- let prepareResponse;
836
- try {
837
- const response = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
838
- method: "POST",
839
- headers: { "Content-Type": "application/json" },
840
- body: JSON.stringify(requestBody)
841
- });
842
- if (!response.ok) {
843
- const errorData = await response.json().catch(() => ({}));
844
- const errorMessage = errorData.error || "Failed to prepare batch intent";
845
- if (errorMessage.includes("User not found")) {
846
- localStorage.removeItem("1auth-user");
847
- }
848
- return {
849
- success: false,
850
- results: [],
851
- successCount: 0,
852
- failureCount: 0
853
- };
854
- }
855
- prepareResponse = await response.json();
856
- } catch {
997
+ const dialogUrl = this.getDialogUrl();
998
+ const themeParams = this.getThemeParams();
999
+ const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
1000
+ const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
1001
+ const [prepareResult, dialogResult] = await Promise.all([
1002
+ this.prepareBatchIntent(requestBody),
1003
+ this.waitForDialogReadyDeferred(dialog, iframe, cleanup)
1004
+ ]);
1005
+ if (!dialogResult.ready) {
857
1006
  return {
858
1007
  success: false,
859
1008
  results: [],
@@ -861,29 +1010,50 @@ var OneAuthClient = class {
861
1010
  failureCount: 0
862
1011
  };
863
1012
  }
864
- const dialogUrl = this.getDialogUrl();
865
- const themeParams = this.getThemeParams();
866
- const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
867
- const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
1013
+ if (!prepareResult.success) {
1014
+ const failedIntents = prepareResult.failedIntents;
1015
+ const failureResults = failedIntents?.map((f) => ({
1016
+ index: f.index,
1017
+ success: false,
1018
+ intentId: "",
1019
+ status: "failed",
1020
+ error: { message: f.error, code: "PREPARE_FAILED" }
1021
+ })) ?? [];
1022
+ this.sendPrepareError(iframe, prepareResult.error);
1023
+ await this.waitForDialogClose(dialog, cleanup);
1024
+ return {
1025
+ success: false,
1026
+ results: failureResults,
1027
+ successCount: 0,
1028
+ failureCount: failureResults.length,
1029
+ error: prepareResult.error
1030
+ };
1031
+ }
1032
+ let prepareResponse = prepareResult.data;
868
1033
  const dialogOrigin = this.getDialogOrigin();
869
- const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
1034
+ const batchInitPayload = {
870
1035
  mode: "iframe",
871
1036
  batchMode: true,
872
1037
  batchIntents: prepareResponse.intents,
1038
+ batchFailedIntents: prepareResponse.failedIntents,
873
1039
  challenge: prepareResponse.challenge,
874
1040
  username: options.username,
875
1041
  accountAddress: prepareResponse.accountAddress,
876
1042
  userId: prepareResponse.userId,
877
- expiresAt: prepareResponse.expiresAt
878
- });
879
- if (!ready) {
880
- return {
881
- success: false,
882
- results: [],
883
- successCount: 0,
884
- failureCount: 0
885
- };
886
- }
1043
+ expiresAt: prepareResponse.expiresAt,
1044
+ tier: prepareResult.tier
1045
+ };
1046
+ dialogResult.sendInit(batchInitPayload);
1047
+ const handleBatchReReady = (event) => {
1048
+ if (event.origin !== dialogOrigin) return;
1049
+ if (event.data?.type === "PASSKEY_READY") {
1050
+ iframe.contentWindow?.postMessage(
1051
+ { type: "PASSKEY_INIT", ...batchInitPayload },
1052
+ dialogOrigin
1053
+ );
1054
+ }
1055
+ };
1056
+ window.addEventListener("message", handleBatchReReady);
887
1057
  const batchResult = await new Promise((resolve) => {
888
1058
  const handleMessage = async (event) => {
889
1059
  if (event.origin !== dialogOrigin) return;
@@ -930,13 +1100,21 @@ var OneAuthClient = class {
930
1100
  status: r.status === "FAILED" ? "failed" : "pending",
931
1101
  error: r.error ? { code: "EXECUTE_FAILED", message: r.error } : void 0
932
1102
  }));
933
- const successCount = results.filter((r) => r.success).length;
1103
+ const prepareFailures = (prepareResponse.failedIntents ?? []).map((f) => ({
1104
+ index: f.index,
1105
+ success: false,
1106
+ intentId: "",
1107
+ status: "failed",
1108
+ error: { code: "PREPARE_FAILED", message: f.error }
1109
+ }));
1110
+ const allResults = [...results, ...prepareFailures].sort((a, b) => a.index - b.index);
1111
+ const successCount = allResults.filter((r) => r.success).length;
934
1112
  await this.waitForDialogClose(dialog, cleanup);
935
1113
  resolve({
936
- success: successCount === results.length,
937
- results,
1114
+ success: successCount === allResults.length,
1115
+ results: allResults,
938
1116
  successCount,
939
- failureCount: results.length - successCount
1117
+ failureCount: allResults.length - successCount
940
1118
  });
941
1119
  } else {
942
1120
  cleanup();
@@ -961,6 +1139,7 @@ var OneAuthClient = class {
961
1139
  };
962
1140
  window.addEventListener("message", handleMessage);
963
1141
  });
1142
+ window.removeEventListener("message", handleBatchReReady);
964
1143
  return batchResult;
965
1144
  }
966
1145
  /**
@@ -1164,6 +1343,143 @@ var OneAuthClient = class {
1164
1343
  dialog.addEventListener("close", handleClose);
1165
1344
  });
1166
1345
  }
1346
+ /**
1347
+ * Inject a preconnect link tag to pre-warm DNS + TLS for a given URL.
1348
+ */
1349
+ injectPreconnect(url) {
1350
+ try {
1351
+ const origin = new URL(url).origin;
1352
+ if (document.querySelector(`link[rel="preconnect"][href="${origin}"]`)) return;
1353
+ const link = document.createElement("link");
1354
+ link.rel = "preconnect";
1355
+ link.href = origin;
1356
+ link.crossOrigin = "anonymous";
1357
+ document.head.appendChild(link);
1358
+ } catch {
1359
+ }
1360
+ }
1361
+ /**
1362
+ * Wait for the dialog iframe to signal ready without sending init data.
1363
+ * Returns a sendInit function the caller uses once prepare data is available.
1364
+ */
1365
+ waitForDialogReadyDeferred(dialog, iframe, cleanup) {
1366
+ const dialogOrigin = this.getDialogOrigin();
1367
+ return new Promise((resolve) => {
1368
+ let settled = false;
1369
+ const teardown = () => {
1370
+ if (settled) return;
1371
+ settled = true;
1372
+ clearTimeout(readyTimeout);
1373
+ window.removeEventListener("message", handleMessage);
1374
+ dialog.removeEventListener("close", handleClose);
1375
+ };
1376
+ const handleMessage = (event) => {
1377
+ if (event.origin !== dialogOrigin) return;
1378
+ if (event.data?.type === "PASSKEY_READY") {
1379
+ teardown();
1380
+ resolve({
1381
+ ready: true,
1382
+ sendInit: (initMessage) => {
1383
+ iframe.contentWindow?.postMessage(
1384
+ { type: "PASSKEY_INIT", ...initMessage },
1385
+ dialogOrigin
1386
+ );
1387
+ }
1388
+ });
1389
+ } else if (event.data?.type === "PASSKEY_CLOSE") {
1390
+ teardown();
1391
+ cleanup();
1392
+ resolve({ ready: false });
1393
+ }
1394
+ };
1395
+ const handleClose = () => {
1396
+ teardown();
1397
+ resolve({ ready: false });
1398
+ };
1399
+ const readyTimeout = setTimeout(() => {
1400
+ teardown();
1401
+ cleanup();
1402
+ resolve({ ready: false });
1403
+ }, 1e4);
1404
+ window.addEventListener("message", handleMessage);
1405
+ dialog.addEventListener("close", handleClose);
1406
+ });
1407
+ }
1408
+ /**
1409
+ * Prepare an intent by calling the orchestrator for a quote.
1410
+ * Returns the prepare response and the origin tier from the response header.
1411
+ */
1412
+ async prepareIntent(requestBody) {
1413
+ try {
1414
+ const response = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
1415
+ method: "POST",
1416
+ headers: { "Content-Type": "application/json" },
1417
+ body: JSON.stringify(requestBody)
1418
+ });
1419
+ if (!response.ok) {
1420
+ const errorData = await response.json().catch(() => ({}));
1421
+ const errorMessage = errorData.error || "Failed to prepare intent";
1422
+ if (errorMessage.includes("User not found")) {
1423
+ localStorage.removeItem("1auth-user");
1424
+ }
1425
+ return {
1426
+ success: false,
1427
+ error: {
1428
+ code: errorMessage.includes("User not found") ? "USER_NOT_FOUND" : "PREPARE_FAILED",
1429
+ message: errorMessage
1430
+ }
1431
+ };
1432
+ }
1433
+ const tier = response.headers.get("X-Origin-Tier");
1434
+ return { success: true, data: await response.json(), tier };
1435
+ } catch (error) {
1436
+ return {
1437
+ success: false,
1438
+ error: {
1439
+ code: "NETWORK_ERROR",
1440
+ message: error instanceof Error ? error.message : "Network error"
1441
+ }
1442
+ };
1443
+ }
1444
+ }
1445
+ /**
1446
+ * Prepare a batch intent by calling the orchestrator for quotes on all intents.
1447
+ */
1448
+ async prepareBatchIntent(requestBody) {
1449
+ try {
1450
+ const response = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
1451
+ method: "POST",
1452
+ headers: { "Content-Type": "application/json" },
1453
+ body: JSON.stringify(requestBody)
1454
+ });
1455
+ if (!response.ok) {
1456
+ const errorData = await response.json().catch(() => ({}));
1457
+ const errorMessage = errorData.error || "Failed to prepare batch intent";
1458
+ if (errorMessage.includes("User not found")) {
1459
+ localStorage.removeItem("1auth-user");
1460
+ }
1461
+ return {
1462
+ success: false,
1463
+ error: errorMessage,
1464
+ failedIntents: errorData.failedIntents
1465
+ };
1466
+ }
1467
+ const tier = response.headers.get("X-Origin-Tier");
1468
+ return { success: true, data: await response.json(), tier };
1469
+ } catch {
1470
+ return { success: false, error: "Network error" };
1471
+ }
1472
+ }
1473
+ /**
1474
+ * Send a prepare error message to the dialog iframe.
1475
+ */
1476
+ sendPrepareError(iframe, error) {
1477
+ const dialogOrigin = this.getDialogOrigin();
1478
+ iframe.contentWindow?.postMessage(
1479
+ { type: "PASSKEY_PREPARE_ERROR", error },
1480
+ dialogOrigin
1481
+ );
1482
+ }
1167
1483
  /**
1168
1484
  * Poll for intent status
1169
1485
  *
@@ -1475,6 +1791,7 @@ var OneAuthClient = class {
1475
1791
  message: options.message,
1476
1792
  challenge: options.challenge || options.message,
1477
1793
  username: options.username,
1794
+ accountAddress: options.accountAddress,
1478
1795
  description: options.description,
1479
1796
  metadata: options.metadata
1480
1797
  });
@@ -1566,6 +1883,7 @@ var OneAuthClient = class {
1566
1883
  },
1567
1884
  challenge: signedHash,
1568
1885
  username: options.username,
1886
+ accountAddress: options.accountAddress,
1569
1887
  description: options.description
1570
1888
  });
1571
1889
  if (!ready) {
@@ -1639,7 +1957,7 @@ var OneAuthClient = class {
1639
1957
  iframe.style.borderRadius = "12px";
1640
1958
  iframe.style.boxShadow = "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)";
1641
1959
  iframe.id = `passkey-embed-${requestId}`;
1642
- iframe.allow = "publickey-credentials-get *; publickey-credentials-create *";
1960
+ iframe.allow = "publickey-credentials-get *; publickey-credentials-create *; identity-credentials-get";
1643
1961
  iframe.onload = () => {
1644
1962
  options.onReady?.();
1645
1963
  };
@@ -1787,6 +2105,7 @@ var OneAuthClient = class {
1787
2105
  const teardown = () => {
1788
2106
  if (settled) return;
1789
2107
  settled = true;
2108
+ clearTimeout(readyTimeout);
1790
2109
  window.removeEventListener("message", handleMessage);
1791
2110
  dialog.removeEventListener("close", handleClose);
1792
2111
  };
@@ -1809,6 +2128,11 @@ var OneAuthClient = class {
1809
2128
  teardown();
1810
2129
  resolve(false);
1811
2130
  };
2131
+ const readyTimeout = setTimeout(() => {
2132
+ teardown();
2133
+ cleanup();
2134
+ resolve(false);
2135
+ }, 1e4);
1812
2136
  window.addEventListener("message", handleMessage);
1813
2137
  dialog.addEventListener("close", handleClose);
1814
2138
  });
@@ -1849,7 +2173,9 @@ var OneAuthClient = class {
1849
2173
  border-radius: 14px;
1850
2174
  border: none;
1851
2175
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);
1852
- transition: width 0.2s ease-out, height 0.15s ease-out;
2176
+ transition: height 0.15s ease-out;
2177
+ max-height: calc(100vh - 100px);
2178
+ max-height: calc(100dvh - 100px);
1853
2179
  }
1854
2180
 
1855
2181
  @media (min-width: 769px) {
@@ -1911,14 +2237,10 @@ var OneAuthClient = class {
1911
2237
  const iframe = document.createElement("iframe");
1912
2238
  iframe.setAttribute(
1913
2239
  "allow",
1914
- "publickey-credentials-get *; publickey-credentials-create *; clipboard-write"
2240
+ "publickey-credentials-get *; publickey-credentials-create *; clipboard-write; identity-credentials-get"
1915
2241
  );
1916
2242
  iframe.setAttribute("aria-label", "Passkey Authentication");
1917
2243
  iframe.setAttribute("tabindex", "0");
1918
- iframe.setAttribute(
1919
- "sandbox",
1920
- "allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox"
1921
- );
1922
2244
  iframe.setAttribute("src", url);
1923
2245
  iframe.setAttribute("title", "Passkey");
1924
2246
  iframe.style.border = "none";
@@ -1928,10 +2250,8 @@ var OneAuthClient = class {
1928
2250
  const handleMessage = (event) => {
1929
2251
  if (event.origin !== hostUrl.origin) return;
1930
2252
  if (event.data?.type === "PASSKEY_RESIZE") {
1931
- iframe.style.height = `${event.data.height}px`;
1932
- if (event.data.width) {
1933
- iframe.style.width = `${event.data.width}px`;
1934
- }
2253
+ const maxHeight = window.innerHeight - 100;
2254
+ iframe.style.height = `${Math.min(event.data.height, maxHeight)}px`;
1935
2255
  } else if (event.data?.type === "PASSKEY_DISCONNECT") {
1936
2256
  localStorage.removeItem("1auth-user");
1937
2257
  }
@@ -1949,7 +2269,10 @@ var OneAuthClient = class {
1949
2269
  }
1950
2270
  });
1951
2271
  dialog.showModal();
2272
+ let cleanedUp = false;
1952
2273
  const cleanup = () => {
2274
+ if (cleanedUp) return;
2275
+ cleanedUp = true;
1953
2276
  window.removeEventListener("message", handleMessage);
1954
2277
  document.removeEventListener("keydown", handleEscape);
1955
2278
  dialog.close();
@@ -1982,6 +2305,7 @@ var OneAuthClient = class {
1982
2305
  resolve({
1983
2306
  success: true,
1984
2307
  username: data.data?.username,
2308
+ address: data.data?.address,
1985
2309
  user: data.data?.user
1986
2310
  });
1987
2311
  } else {
@@ -1996,7 +2320,8 @@ var OneAuthClient = class {
1996
2320
  if (data.success) {
1997
2321
  resolve({
1998
2322
  success: true,
1999
- username: data.data?.username
2323
+ username: data.data?.username,
2324
+ address: data.data?.address
2000
2325
  });
2001
2326
  } else {
2002
2327
  resolve({
@@ -2056,6 +2381,7 @@ var OneAuthClient = class {
2056
2381
  resolve({
2057
2382
  success: true,
2058
2383
  username: data.data?.username,
2384
+ address: data.data?.address,
2059
2385
  user: data.data?.user
2060
2386
  });
2061
2387
  } else {
@@ -2071,7 +2397,8 @@ var OneAuthClient = class {
2071
2397
  if (data.success) {
2072
2398
  resolve({
2073
2399
  success: true,
2074
- username: data.data?.username
2400
+ username: data.data?.username,
2401
+ address: data.data?.address
2075
2402
  });
2076
2403
  } else {
2077
2404
  resolve({
@@ -2147,6 +2474,7 @@ var OneAuthClient = class {
2147
2474
  resolve({
2148
2475
  success: true,
2149
2476
  username: data.data?.username,
2477
+ address: data.data?.address,
2150
2478
  autoConnected: data.data?.autoConnected
2151
2479
  });
2152
2480
  } else {
@@ -2172,6 +2500,45 @@ var OneAuthClient = class {
2172
2500
  window.addEventListener("message", handleMessage);
2173
2501
  });
2174
2502
  }
2503
+ waitForConsentResponse(_dialog, _iframe, cleanup) {
2504
+ const dialogOrigin = this.getDialogOrigin();
2505
+ return new Promise((resolve) => {
2506
+ const handleMessage = (event) => {
2507
+ if (event.origin !== dialogOrigin) return;
2508
+ const data = event.data;
2509
+ if (data?.type === "PASSKEY_CONSENT_RESULT") {
2510
+ window.removeEventListener("message", handleMessage);
2511
+ cleanup();
2512
+ if (data.success) {
2513
+ resolve({
2514
+ success: true,
2515
+ data: data.data,
2516
+ grantedAt: data.data?.grantedAt
2517
+ });
2518
+ } else {
2519
+ resolve({
2520
+ success: false,
2521
+ error: data.error ?? {
2522
+ code: "USER_REJECTED",
2523
+ message: "User denied the consent request"
2524
+ }
2525
+ });
2526
+ }
2527
+ } else if (data?.type === "PASSKEY_CLOSE") {
2528
+ window.removeEventListener("message", handleMessage);
2529
+ cleanup();
2530
+ resolve({
2531
+ success: false,
2532
+ error: {
2533
+ code: "USER_CANCELLED",
2534
+ message: "User closed the dialog"
2535
+ }
2536
+ });
2537
+ }
2538
+ };
2539
+ window.addEventListener("message", handleMessage);
2540
+ });
2541
+ }
2175
2542
  waitForModalSigningResponse(requestId, _dialog, _iframe, cleanup) {
2176
2543
  const dialogOrigin = this.getDialogOrigin();
2177
2544
  return new Promise((resolve) => {
@@ -2380,7 +2747,7 @@ function createOneAuthProvider(options) {
2380
2747
  const raw = localStorage.getItem(storageKey);
2381
2748
  if (!raw) return null;
2382
2749
  const parsed = JSON.parse(raw);
2383
- if (!parsed?.username || !parsed?.address) return null;
2750
+ if (!parsed?.address) return null;
2384
2751
  return parsed;
2385
2752
  } catch {
2386
2753
  return null;
@@ -2416,18 +2783,26 @@ function createOneAuthProvider(options) {
2416
2783
  }
2417
2784
  const connectResult = await client.connectWithModal();
2418
2785
  let username;
2419
- if (connectResult.success && connectResult.username) {
2786
+ let address;
2787
+ if (connectResult.success) {
2420
2788
  username = connectResult.username;
2789
+ address = connectResult.address;
2421
2790
  } else if (connectResult.action === "switch") {
2422
2791
  const authResult = await client.authWithModal();
2423
- if (!authResult.success || !authResult.username) {
2792
+ if (!authResult.success) {
2424
2793
  throw new Error(authResult.error?.message || "Authentication failed");
2425
2794
  }
2426
2795
  username = authResult.username;
2796
+ address = authResult.address;
2427
2797
  } else {
2428
2798
  throw new Error(connectResult.error?.message || "Connection cancelled");
2429
2799
  }
2430
- const address = await resolveAccountAddress(username);
2800
+ if (!address && username) {
2801
+ address = await resolveAccountAddress(username);
2802
+ }
2803
+ if (!address) {
2804
+ throw new Error("No account address available");
2805
+ }
2431
2806
  setStoredUser({ username, address });
2432
2807
  emit("accountsChanged", [address]);
2433
2808
  emit("connect", { chainId: (0, import_viem4.numberToHex)(chainId) });
@@ -2442,11 +2817,11 @@ function createOneAuthProvider(options) {
2442
2817
  const stored = getStoredUser();
2443
2818
  if (stored) return stored;
2444
2819
  const [address] = await connect();
2445
- const username = getStoredUser()?.username;
2446
- if (!username || !address) {
2820
+ if (!address) {
2447
2821
  throw new Error("Failed to resolve user session");
2448
2822
  }
2449
- return { username, address };
2823
+ const user = getStoredUser();
2824
+ return user || { address };
2450
2825
  };
2451
2826
  const parseChainId = (value) => {
2452
2827
  if (typeof value === "number") return value;
@@ -2504,9 +2879,13 @@ function createOneAuthProvider(options) {
2504
2879
  }
2505
2880
  };
2506
2881
  const signMessage = async (message) => {
2507
- const { username } = await ensureUser();
2882
+ const user = await ensureUser();
2883
+ if (!user.username && !user.address) {
2884
+ throw new Error("Username or address required for signing.");
2885
+ }
2508
2886
  const result = await client.signMessage({
2509
- username,
2887
+ username: user.username,
2888
+ accountAddress: user.address,
2510
2889
  message
2511
2890
  });
2512
2891
  if (!result.success || !result.signature) {
@@ -2515,10 +2894,14 @@ function createOneAuthProvider(options) {
2515
2894
  return encodeWebAuthnSignature(result.signature);
2516
2895
  };
2517
2896
  const signTypedData = async (typedData) => {
2518
- const { username } = await ensureUser();
2897
+ const user = await ensureUser();
2898
+ if (!user.username && !user.address) {
2899
+ throw new Error("Username or address required for signing.");
2900
+ }
2519
2901
  const data = typeof typedData === "string" ? JSON.parse(typedData) : typedData;
2520
2902
  const result = await client.signTypedData({
2521
- username,
2903
+ username: user.username,
2904
+ accountAddress: user.address,
2522
2905
  domain: data.domain,
2523
2906
  types: data.types,
2524
2907
  primaryType: data.primaryType,
@@ -2533,11 +2916,15 @@ function createOneAuthProvider(options) {
2533
2916
  if (!options.signIntent) {
2534
2917
  return {
2535
2918
  username: payload.username,
2919
+ accountAddress: payload.accountAddress,
2536
2920
  targetChain: payload.targetChain,
2537
2921
  calls: payload.calls,
2538
2922
  tokenRequests: payload.tokenRequests
2539
2923
  };
2540
2924
  }
2925
+ if (!payload.username) {
2926
+ throw new Error("Username required for signed intents. Set a username first.");
2927
+ }
2541
2928
  const signedIntent = await options.signIntent({
2542
2929
  username: payload.username,
2543
2930
  accountAddress: payload.accountAddress,
@@ -2663,10 +3050,13 @@ function createOneAuthProvider(options) {
2663
3050
  return capabilities;
2664
3051
  }
2665
3052
  case "wallet_getAssets": {
2666
- const { username } = await ensureUser();
3053
+ const user = await ensureUser();
3054
+ if (!user.username) {
3055
+ throw new Error("Username required to fetch assets. Set a username first.");
3056
+ }
2667
3057
  const clientId = client.getClientId();
2668
3058
  const response = await fetch(
2669
- `${client.getProviderUrl()}/api/users/${encodeURIComponent(username)}/portfolio`,
3059
+ `${client.getProviderUrl()}/api/users/${encodeURIComponent(user.username)}/portfolio`,
2670
3060
  {
2671
3061
  headers: clientId ? { "x-client-id": clientId } : {}
2672
3062
  }