@rhinestone/1auth 0.1.2 → 0.5.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
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  buildTransactionReview,
3
3
  createOneAuthProvider,
4
- createPasskeyProvider,
5
4
  encodeWebAuthnSignature,
6
5
  getAllSupportedChainsAndTokens,
7
6
  getChainById,
@@ -19,7 +18,7 @@ import {
19
18
  isTestnet,
20
19
  isTokenAddressSupported,
21
20
  resolveTokenAddress
22
- } from "./chunk-TACK3LJN.mjs";
21
+ } from "./chunk-N4BLW5UR.mjs";
23
22
 
24
23
  // src/client.ts
25
24
  import { parseUnits, hashTypedData } from "viem";
@@ -27,7 +26,7 @@ var POPUP_WIDTH = 450;
27
26
  var POPUP_HEIGHT = 600;
28
27
  var DEFAULT_EMBED_WIDTH = "400px";
29
28
  var DEFAULT_EMBED_HEIGHT = "500px";
30
- var MODAL_WIDTH = 360;
29
+ var MODAL_WIDTH = 340;
31
30
  var DEFAULT_PROVIDER_URL = "https://passkey.1auth.box";
32
31
  var OneAuthClient = class {
33
32
  constructor(config) {
@@ -35,6 +34,12 @@ var OneAuthClient = class {
35
34
  const dialogUrl = config.dialogUrl || providerUrl;
36
35
  this.config = { ...config, providerUrl, dialogUrl };
37
36
  this.theme = this.config.theme || {};
37
+ if (typeof document !== "undefined") {
38
+ this.injectPreconnect(providerUrl);
39
+ if (dialogUrl !== providerUrl) {
40
+ this.injectPreconnect(dialogUrl);
41
+ }
42
+ }
38
43
  }
39
44
  /**
40
45
  * Update the theme configuration at runtime
@@ -115,7 +120,28 @@ var OneAuthClient = class {
115
120
  return void 0;
116
121
  }
117
122
  /**
118
- * Open the auth dialog (sign in + sign up).
123
+ * Open the authentication modal (sign in + sign up).
124
+ *
125
+ * Handles both new user registration and returning user login in a single
126
+ * call — the modal shows the appropriate flow based on whether the user
127
+ * has a passkey registered.
128
+ *
129
+ * @param options.username - Pre-fill the username field
130
+ * @param options.theme - Override the theme for this modal invocation
131
+ * @param options.oauthEnabled - Set to false to hide OAuth sign-in buttons
132
+ * @returns {@link AuthResult} with user details and typed address
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const result = await client.authWithModal();
137
+ *
138
+ * if (result.success) {
139
+ * console.log('Signed in as:', result.user?.username);
140
+ * console.log('Address:', result.user?.address); // typed `0x${string}`
141
+ * } else if (result.error?.code === 'USER_CANCELLED') {
142
+ * // User closed the modal
143
+ * }
144
+ * ```
119
145
  */
120
146
  async authWithModal(options) {
121
147
  const dialogUrl = this.getDialogUrl();
@@ -155,7 +181,7 @@ var OneAuthClient = class {
155
181
  * const result = await client.connectWithModal();
156
182
  *
157
183
  * if (result.success) {
158
- * console.log('Connected as:', result.username);
184
+ * console.log('Connected as:', result.user?.username);
159
185
  * } else if (result.action === 'switch') {
160
186
  * // User needs to sign in first
161
187
  * const authResult = await client.authWithModal();
@@ -189,6 +215,133 @@ var OneAuthClient = class {
189
215
  }
190
216
  return this.waitForConnectResponse(dialog, iframe, cleanup);
191
217
  }
218
+ /**
219
+ * Check if a user has already granted consent for the requested fields.
220
+ * This is a read-only check — no dialog is shown.
221
+ *
222
+ * @example
223
+ * ```typescript
224
+ * const result = await client.checkConsent({
225
+ * username: "alice",
226
+ * fields: ["email"],
227
+ * });
228
+ * if (result.hasConsent) {
229
+ * console.log(result.data?.email);
230
+ * }
231
+ * ```
232
+ */
233
+ async checkConsent(options) {
234
+ const clientId = options.clientId ?? this.config.clientId;
235
+ if (!clientId) {
236
+ return {
237
+ hasConsent: false
238
+ };
239
+ }
240
+ const username = options.username ?? options.accountAddress;
241
+ if (!username) {
242
+ return {
243
+ hasConsent: false
244
+ };
245
+ }
246
+ try {
247
+ const res = await fetch(`${this.config.providerUrl || "https://passkey.1auth.box"}/api/consent`, {
248
+ method: "POST",
249
+ headers: {
250
+ "Content-Type": "application/json",
251
+ ...clientId ? { "x-client-id": clientId } : {}
252
+ },
253
+ body: JSON.stringify({
254
+ username,
255
+ requestedFields: options.fields,
256
+ clientId
257
+ })
258
+ });
259
+ if (!res.ok) {
260
+ return { hasConsent: false };
261
+ }
262
+ const data = await res.json();
263
+ return {
264
+ hasConsent: data.hasConsent ?? false,
265
+ data: data.data,
266
+ grantedAt: data.grantedAt
267
+ };
268
+ } catch {
269
+ return { hasConsent: false };
270
+ }
271
+ }
272
+ /**
273
+ * Request consent from the user to share their data.
274
+ *
275
+ * First checks if consent was already granted (returns cached data immediately).
276
+ * If not, opens the consent dialog where the user can review and approve sharing.
277
+ *
278
+ * @example
279
+ * ```typescript
280
+ * const result = await client.requestConsent({
281
+ * username: "alice",
282
+ * fields: ["email", "deviceNames"],
283
+ * });
284
+ * if (result.success) {
285
+ * console.log(result.data?.email);
286
+ * console.log(result.cached); // true if no dialog was shown
287
+ * }
288
+ * ```
289
+ */
290
+ async requestConsent(options) {
291
+ const clientId = options.clientId ?? this.config.clientId;
292
+ if (!clientId) {
293
+ return {
294
+ success: false,
295
+ error: { code: "INVALID_REQUEST", message: "clientId is required (set in config or options)" }
296
+ };
297
+ }
298
+ const username = options.username ?? options.accountAddress;
299
+ if (!username) {
300
+ return {
301
+ success: false,
302
+ error: { code: "INVALID_REQUEST", message: "username or accountAddress is required" }
303
+ };
304
+ }
305
+ if (!options.fields || options.fields.length === 0) {
306
+ return {
307
+ success: false,
308
+ error: { code: "INVALID_REQUEST", message: "At least one field is required" }
309
+ };
310
+ }
311
+ const existing = await this.checkConsent({ ...options, clientId });
312
+ if (existing.hasConsent && existing.data) {
313
+ return {
314
+ success: true,
315
+ data: existing.data,
316
+ grantedAt: existing.grantedAt,
317
+ cached: true
318
+ };
319
+ }
320
+ const dialogUrl = this.getDialogUrl();
321
+ const params = new URLSearchParams({
322
+ mode: "iframe",
323
+ username,
324
+ clientId,
325
+ fields: options.fields.join(",")
326
+ });
327
+ const themeParams = this.getThemeParams(options.theme);
328
+ if (themeParams) {
329
+ const themeParsed = new URLSearchParams(themeParams);
330
+ themeParsed.forEach((value, key) => params.set(key, value));
331
+ }
332
+ const url = `${dialogUrl}/dialog/consent?${params.toString()}`;
333
+ const { dialog, iframe, cleanup } = this.createModalDialog(url);
334
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
335
+ mode: "iframe"
336
+ });
337
+ if (!ready) {
338
+ return {
339
+ success: false,
340
+ error: { code: "USER_CANCELLED", message: "User closed the dialog" }
341
+ };
342
+ }
343
+ return this.waitForConsentResponse(dialog, iframe, cleanup);
344
+ }
192
345
  /**
193
346
  * Authenticate a user with an optional challenge to sign.
194
347
  *
@@ -206,17 +359,16 @@ var OneAuthClient = class {
206
359
  *
207
360
  * @example
208
361
  * ```typescript
209
- * // Authenticate with a login challenge
210
362
  * const result = await client.authenticate({
211
363
  * challenge: `Login to MyApp\nTimestamp: ${Date.now()}\nNonce: ${crypto.randomUUID()}`
212
364
  * });
213
365
  *
214
- * if (result.success && result.signature) {
366
+ * if (result.success && result.challenge) {
215
367
  * // Verify signature server-side
216
368
  * const isValid = await verifyOnServer(
217
- * result.username,
218
- * result.signature,
219
- * result.signedHash
369
+ * result.user?.username,
370
+ * result.challenge.signature,
371
+ * result.challenge.signedHash
220
372
  * );
221
373
  * }
222
374
  * ```
@@ -316,7 +468,8 @@ var OneAuthClient = class {
316
468
  }
317
469
  };
318
470
  }
319
- if (!username && !signedIntent?.accountAddress) {
471
+ const accountAddress = signedIntent?.accountAddress || options.accountAddress;
472
+ if (!username && !accountAddress) {
320
473
  return {
321
474
  success: false,
322
475
  intentId: "",
@@ -345,6 +498,7 @@ var OneAuthClient = class {
345
498
  let prepareResponse;
346
499
  const requestBody = signedIntent || {
347
500
  username: options.username,
501
+ accountAddress: options.accountAddress,
348
502
  targetChain: options.targetChain,
349
503
  calls: options.calls,
350
504
  tokenRequests: serializedTokenRequests,
@@ -352,48 +506,35 @@ var OneAuthClient = class {
352
506
  sourceChainId: options.sourceChainId,
353
507
  ...this.config.clientId && { clientId: this.config.clientId }
354
508
  };
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) {
509
+ const dialogUrl = this.getDialogUrl();
510
+ const themeParams = this.getThemeParams();
511
+ const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
512
+ const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
513
+ const [prepareResult, dialogResult] = await Promise.all([
514
+ this.prepareIntent(requestBody),
515
+ this.waitForDialogReadyDeferred(dialog, iframe, cleanup)
516
+ ]);
517
+ if (!dialogResult.ready) {
381
518
  return {
382
519
  success: false,
383
520
  intentId: "",
384
521
  status: "failed",
385
- error: {
386
- code: "NETWORK_ERROR",
387
- message: error instanceof Error ? error.message : "Network error"
388
- }
522
+ error: { code: "USER_CANCELLED", message: "User closed the dialog" }
389
523
  };
390
524
  }
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);
525
+ if (!prepareResult.success) {
526
+ this.sendPrepareError(iframe, prepareResult.error.message);
527
+ await this.waitForDialogClose(dialog, cleanup);
528
+ return {
529
+ success: false,
530
+ intentId: "",
531
+ status: "failed",
532
+ error: prepareResult.error
533
+ };
534
+ }
535
+ prepareResponse = prepareResult.data;
395
536
  const dialogOrigin = this.getDialogOrigin();
396
- const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
537
+ const initPayload = {
397
538
  mode: "iframe",
398
539
  calls,
399
540
  chainId: targetChain,
@@ -405,16 +546,21 @@ var OneAuthClient = class {
405
546
  tokenRequests: serializedTokenRequests,
406
547
  expiresAt: prepareResponse.expiresAt,
407
548
  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
- }
549
+ intentOp: prepareResponse.intentOp,
550
+ digestResult: prepareResponse.digestResult,
551
+ tier: prepareResult.tier
552
+ };
553
+ dialogResult.sendInit(initPayload);
554
+ const handleReReady = (event) => {
555
+ if (event.origin !== dialogOrigin) return;
556
+ if (event.data?.type === "PASSKEY_READY") {
557
+ iframe.contentWindow?.postMessage(
558
+ { type: "PASSKEY_INIT", ...initPayload },
559
+ dialogOrigin
560
+ );
561
+ }
562
+ };
563
+ window.addEventListener("message", handleReReady);
418
564
  const signingResult = await this.waitForSigningWithRefresh(
419
565
  dialog,
420
566
  iframe,
@@ -441,7 +587,8 @@ var OneAuthClient = class {
441
587
  expiresAt: refreshedData.expiresAt,
442
588
  challenge: refreshedData.challenge,
443
589
  originMessages: refreshedData.originMessages,
444
- transaction: refreshedData.transaction
590
+ transaction: refreshedData.transaction,
591
+ digestResult: refreshedData.digestResult
445
592
  };
446
593
  } catch (error) {
447
594
  console.error("[SDK] Quote refresh error:", error);
@@ -449,6 +596,7 @@ var OneAuthClient = class {
449
596
  }
450
597
  }
451
598
  );
599
+ window.removeEventListener("message", handleReReady);
452
600
  if (!signingResult.success) {
453
601
  return {
454
602
  success: false,
@@ -480,6 +628,7 @@ var OneAuthClient = class {
480
628
  targetChain: prepareResponse.targetChain,
481
629
  calls: prepareResponse.calls,
482
630
  expiresAt: prepareResponse.expiresAt,
631
+ digestResult: prepareResponse.digestResult,
483
632
  // Signature from dialog
484
633
  signature: signingResult.signature,
485
634
  passkey: signingResult.passkey
@@ -521,10 +670,21 @@ var OneAuthClient = class {
521
670
  let finalTxHash = executeResponse.transactionHash;
522
671
  if (finalStatus === "pending") {
523
672
  this.sendTransactionStatus(iframe, "pending");
673
+ let userClosedEarly = false;
674
+ const dialogOrigin2 = this.getDialogOrigin();
675
+ const earlyCloseHandler = (event) => {
676
+ if (event.origin !== dialogOrigin2) return;
677
+ if (event.data?.type === "PASSKEY_CLOSE") {
678
+ userClosedEarly = true;
679
+ cleanup();
680
+ }
681
+ };
682
+ window.addEventListener("message", earlyCloseHandler);
524
683
  const maxAttempts = 120;
525
684
  const pollIntervalMs = 1500;
526
685
  let lastStatus = "pending";
527
686
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
687
+ if (userClosedEarly) break;
528
688
  try {
529
689
  const statusResponse = await fetch(
530
690
  `${this.config.providerUrl}/api/intent/status/${executeResponse.intentId}`,
@@ -559,6 +719,21 @@ var OneAuthClient = class {
559
719
  }
560
720
  await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
561
721
  }
722
+ window.removeEventListener("message", earlyCloseHandler);
723
+ if (userClosedEarly) {
724
+ cleanup();
725
+ return {
726
+ success: false,
727
+ intentId: executeResponse.intentId,
728
+ status: finalStatus,
729
+ transactionHash: finalTxHash,
730
+ operationId: executeResponse.operationId,
731
+ error: {
732
+ code: "USER_CANCELLED",
733
+ message: "User closed the dialog"
734
+ }
735
+ };
736
+ }
562
737
  }
563
738
  const closeOn = options.closeOn || "preconfirmed";
564
739
  const successStatuses = {
@@ -569,8 +744,9 @@ var OneAuthClient = class {
569
744
  };
570
745
  const isSuccessStatus = successStatuses[closeOn]?.includes(finalStatus) ?? false;
571
746
  const displayStatus = isSuccessStatus ? "confirmed" : finalStatus;
747
+ const closePromise = this.waitForDialogClose(dialog, cleanup);
572
748
  this.sendTransactionStatus(iframe, displayStatus, finalTxHash);
573
- await this.waitForDialogClose(dialog, cleanup);
749
+ await closePromise;
574
750
  if (options.waitForHash && !finalTxHash) {
575
751
  const hash = await this.waitForTransactionHash(executeResponse.intentId, {
576
752
  timeoutMs: options.hashTimeoutMs,
@@ -631,7 +807,7 @@ var OneAuthClient = class {
631
807
  * ```
632
808
  */
633
809
  async sendBatchIntent(options) {
634
- if (!options.username) {
810
+ if (!options.username && !options.accountAddress) {
635
811
  return {
636
812
  success: false,
637
813
  results: [],
@@ -655,35 +831,24 @@ var OneAuthClient = class {
655
831
  amount: r.amount.toString()
656
832
  })),
657
833
  sourceAssets: intent.sourceAssets,
658
- sourceChainId: intent.sourceChainId
834
+ sourceChainId: intent.sourceChainId,
835
+ moduleInstall: intent.moduleInstall
659
836
  }));
660
837
  const requestBody = {
661
- username: options.username,
838
+ ...options.username && { username: options.username },
839
+ ...options.accountAddress && { accountAddress: options.accountAddress },
662
840
  intents: serializedIntents,
663
841
  ...this.config.clientId && { clientId: this.config.clientId }
664
842
  };
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 {
843
+ const dialogUrl = this.getDialogUrl();
844
+ const themeParams = this.getThemeParams();
845
+ const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
846
+ const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
847
+ const [prepareResult, dialogResult] = await Promise.all([
848
+ this.prepareBatchIntent(requestBody),
849
+ this.waitForDialogReadyDeferred(dialog, iframe, cleanup)
850
+ ]);
851
+ if (!dialogResult.ready) {
687
852
  return {
688
853
  success: false,
689
854
  results: [],
@@ -691,29 +856,50 @@ var OneAuthClient = class {
691
856
  failureCount: 0
692
857
  };
693
858
  }
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);
859
+ if (!prepareResult.success) {
860
+ const failedIntents = prepareResult.failedIntents;
861
+ const failureResults = failedIntents?.map((f) => ({
862
+ index: f.index,
863
+ success: false,
864
+ intentId: "",
865
+ status: "failed",
866
+ error: { message: f.error, code: "PREPARE_FAILED" }
867
+ })) ?? [];
868
+ this.sendPrepareError(iframe, prepareResult.error);
869
+ await this.waitForDialogClose(dialog, cleanup);
870
+ return {
871
+ success: false,
872
+ results: failureResults,
873
+ successCount: 0,
874
+ failureCount: failureResults.length,
875
+ error: prepareResult.error
876
+ };
877
+ }
878
+ let prepareResponse = prepareResult.data;
698
879
  const dialogOrigin = this.getDialogOrigin();
699
- const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
880
+ const batchInitPayload = {
700
881
  mode: "iframe",
701
882
  batchMode: true,
702
883
  batchIntents: prepareResponse.intents,
884
+ batchFailedIntents: prepareResponse.failedIntents,
703
885
  challenge: prepareResponse.challenge,
704
886
  username: options.username,
705
887
  accountAddress: prepareResponse.accountAddress,
706
888
  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
- }
889
+ expiresAt: prepareResponse.expiresAt,
890
+ tier: prepareResult.tier
891
+ };
892
+ dialogResult.sendInit(batchInitPayload);
893
+ const handleBatchReReady = (event) => {
894
+ if (event.origin !== dialogOrigin) return;
895
+ if (event.data?.type === "PASSKEY_READY") {
896
+ iframe.contentWindow?.postMessage(
897
+ { type: "PASSKEY_INIT", ...batchInitPayload },
898
+ dialogOrigin
899
+ );
900
+ }
901
+ };
902
+ window.addEventListener("message", handleBatchReReady);
717
903
  const batchResult = await new Promise((resolve) => {
718
904
  const handleMessage = async (event) => {
719
905
  if (event.origin !== dialogOrigin) return;
@@ -760,13 +946,21 @@ var OneAuthClient = class {
760
946
  status: r.status === "FAILED" ? "failed" : "pending",
761
947
  error: r.error ? { code: "EXECUTE_FAILED", message: r.error } : void 0
762
948
  }));
763
- const successCount = results.filter((r) => r.success).length;
949
+ const prepareFailures = (prepareResponse.failedIntents ?? []).map((f) => ({
950
+ index: f.index,
951
+ success: false,
952
+ intentId: "",
953
+ status: "failed",
954
+ error: { code: "PREPARE_FAILED", message: f.error }
955
+ }));
956
+ const allResults = [...results, ...prepareFailures].sort((a, b) => a.index - b.index);
957
+ const successCount = allResults.filter((r) => r.success).length;
764
958
  await this.waitForDialogClose(dialog, cleanup);
765
959
  resolve({
766
- success: successCount === results.length,
767
- results,
960
+ success: successCount === allResults.length,
961
+ results: allResults,
768
962
  successCount,
769
- failureCount: results.length - successCount
963
+ failureCount: allResults.length - successCount
770
964
  });
771
965
  } else {
772
966
  cleanup();
@@ -791,6 +985,7 @@ var OneAuthClient = class {
791
985
  };
792
986
  window.addEventListener("message", handleMessage);
793
987
  });
988
+ window.removeEventListener("message", handleBatchReReady);
794
989
  return batchResult;
795
990
  }
796
991
  /**
@@ -994,6 +1189,143 @@ var OneAuthClient = class {
994
1189
  dialog.addEventListener("close", handleClose);
995
1190
  });
996
1191
  }
1192
+ /**
1193
+ * Inject a preconnect link tag to pre-warm DNS + TLS for a given URL.
1194
+ */
1195
+ injectPreconnect(url) {
1196
+ try {
1197
+ const origin = new URL(url).origin;
1198
+ if (document.querySelector(`link[rel="preconnect"][href="${origin}"]`)) return;
1199
+ const link = document.createElement("link");
1200
+ link.rel = "preconnect";
1201
+ link.href = origin;
1202
+ link.crossOrigin = "anonymous";
1203
+ document.head.appendChild(link);
1204
+ } catch {
1205
+ }
1206
+ }
1207
+ /**
1208
+ * Wait for the dialog iframe to signal ready without sending init data.
1209
+ * Returns a sendInit function the caller uses once prepare data is available.
1210
+ */
1211
+ waitForDialogReadyDeferred(dialog, iframe, cleanup) {
1212
+ const dialogOrigin = this.getDialogOrigin();
1213
+ return new Promise((resolve) => {
1214
+ let settled = false;
1215
+ const teardown = () => {
1216
+ if (settled) return;
1217
+ settled = true;
1218
+ clearTimeout(readyTimeout);
1219
+ window.removeEventListener("message", handleMessage);
1220
+ dialog.removeEventListener("close", handleClose);
1221
+ };
1222
+ const handleMessage = (event) => {
1223
+ if (event.origin !== dialogOrigin) return;
1224
+ if (event.data?.type === "PASSKEY_READY") {
1225
+ teardown();
1226
+ resolve({
1227
+ ready: true,
1228
+ sendInit: (initMessage) => {
1229
+ iframe.contentWindow?.postMessage(
1230
+ { type: "PASSKEY_INIT", ...initMessage },
1231
+ dialogOrigin
1232
+ );
1233
+ }
1234
+ });
1235
+ } else if (event.data?.type === "PASSKEY_CLOSE") {
1236
+ teardown();
1237
+ cleanup();
1238
+ resolve({ ready: false });
1239
+ }
1240
+ };
1241
+ const handleClose = () => {
1242
+ teardown();
1243
+ resolve({ ready: false });
1244
+ };
1245
+ const readyTimeout = setTimeout(() => {
1246
+ teardown();
1247
+ cleanup();
1248
+ resolve({ ready: false });
1249
+ }, 1e4);
1250
+ window.addEventListener("message", handleMessage);
1251
+ dialog.addEventListener("close", handleClose);
1252
+ });
1253
+ }
1254
+ /**
1255
+ * Prepare an intent by calling the orchestrator for a quote.
1256
+ * Returns the prepare response and the origin tier from the response header.
1257
+ */
1258
+ async prepareIntent(requestBody) {
1259
+ try {
1260
+ const response = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
1261
+ method: "POST",
1262
+ headers: { "Content-Type": "application/json" },
1263
+ body: JSON.stringify(requestBody)
1264
+ });
1265
+ if (!response.ok) {
1266
+ const errorData = await response.json().catch(() => ({}));
1267
+ const errorMessage = errorData.error || "Failed to prepare intent";
1268
+ if (errorMessage.includes("User not found")) {
1269
+ localStorage.removeItem("1auth-user");
1270
+ }
1271
+ return {
1272
+ success: false,
1273
+ error: {
1274
+ code: errorMessage.includes("User not found") ? "USER_NOT_FOUND" : "PREPARE_FAILED",
1275
+ message: errorMessage
1276
+ }
1277
+ };
1278
+ }
1279
+ const tier = response.headers.get("X-Origin-Tier");
1280
+ return { success: true, data: await response.json(), tier };
1281
+ } catch (error) {
1282
+ return {
1283
+ success: false,
1284
+ error: {
1285
+ code: "NETWORK_ERROR",
1286
+ message: error instanceof Error ? error.message : "Network error"
1287
+ }
1288
+ };
1289
+ }
1290
+ }
1291
+ /**
1292
+ * Prepare a batch intent by calling the orchestrator for quotes on all intents.
1293
+ */
1294
+ async prepareBatchIntent(requestBody) {
1295
+ try {
1296
+ const response = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
1297
+ method: "POST",
1298
+ headers: { "Content-Type": "application/json" },
1299
+ body: JSON.stringify(requestBody)
1300
+ });
1301
+ if (!response.ok) {
1302
+ const errorData = await response.json().catch(() => ({}));
1303
+ const errorMessage = errorData.error || "Failed to prepare batch intent";
1304
+ if (errorMessage.includes("User not found")) {
1305
+ localStorage.removeItem("1auth-user");
1306
+ }
1307
+ return {
1308
+ success: false,
1309
+ error: errorMessage,
1310
+ failedIntents: errorData.failedIntents
1311
+ };
1312
+ }
1313
+ const tier = response.headers.get("X-Origin-Tier");
1314
+ return { success: true, data: await response.json(), tier };
1315
+ } catch {
1316
+ return { success: false, error: "Network error" };
1317
+ }
1318
+ }
1319
+ /**
1320
+ * Send a prepare error message to the dialog iframe.
1321
+ */
1322
+ sendPrepareError(iframe, error) {
1323
+ const dialogOrigin = this.getDialogOrigin();
1324
+ iframe.contentWindow?.postMessage(
1325
+ { type: "PASSKEY_PREPARE_ERROR", error },
1326
+ dialogOrigin
1327
+ );
1328
+ }
997
1329
  /**
998
1330
  * Poll for intent status
999
1331
  *
@@ -1305,6 +1637,7 @@ var OneAuthClient = class {
1305
1637
  message: options.message,
1306
1638
  challenge: options.challenge || options.message,
1307
1639
  username: options.username,
1640
+ accountAddress: options.accountAddress,
1308
1641
  description: options.description,
1309
1642
  metadata: options.metadata
1310
1643
  });
@@ -1396,6 +1729,7 @@ var OneAuthClient = class {
1396
1729
  },
1397
1730
  challenge: signedHash,
1398
1731
  username: options.username,
1732
+ accountAddress: options.accountAddress,
1399
1733
  description: options.description
1400
1734
  });
1401
1735
  if (!ready) {
@@ -1469,7 +1803,7 @@ var OneAuthClient = class {
1469
1803
  iframe.style.borderRadius = "12px";
1470
1804
  iframe.style.boxShadow = "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)";
1471
1805
  iframe.id = `passkey-embed-${requestId}`;
1472
- iframe.allow = "publickey-credentials-get *; publickey-credentials-create *";
1806
+ iframe.allow = "publickey-credentials-get *; publickey-credentials-create *; identity-credentials-get";
1473
1807
  iframe.onload = () => {
1474
1808
  options.onReady?.();
1475
1809
  };
@@ -1617,6 +1951,7 @@ var OneAuthClient = class {
1617
1951
  const teardown = () => {
1618
1952
  if (settled) return;
1619
1953
  settled = true;
1954
+ clearTimeout(readyTimeout);
1620
1955
  window.removeEventListener("message", handleMessage);
1621
1956
  dialog.removeEventListener("close", handleClose);
1622
1957
  };
@@ -1639,6 +1974,11 @@ var OneAuthClient = class {
1639
1974
  teardown();
1640
1975
  resolve(false);
1641
1976
  };
1977
+ const readyTimeout = setTimeout(() => {
1978
+ teardown();
1979
+ cleanup();
1980
+ resolve(false);
1981
+ }, 1e4);
1642
1982
  window.addEventListener("message", handleMessage);
1643
1983
  dialog.addEventListener("close", handleClose);
1644
1984
  });
@@ -1679,7 +2019,9 @@ var OneAuthClient = class {
1679
2019
  border-radius: 14px;
1680
2020
  border: none;
1681
2021
  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;
2022
+ transition: height 0.15s ease-out;
2023
+ max-height: calc(100vh - 100px);
2024
+ max-height: calc(100dvh - 100px);
1683
2025
  }
1684
2026
 
1685
2027
  @media (min-width: 769px) {
@@ -1741,14 +2083,10 @@ var OneAuthClient = class {
1741
2083
  const iframe = document.createElement("iframe");
1742
2084
  iframe.setAttribute(
1743
2085
  "allow",
1744
- "publickey-credentials-get *; publickey-credentials-create *; clipboard-write"
2086
+ "publickey-credentials-get *; publickey-credentials-create *; clipboard-write; identity-credentials-get"
1745
2087
  );
1746
2088
  iframe.setAttribute("aria-label", "Passkey Authentication");
1747
2089
  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
2090
  iframe.setAttribute("src", url);
1753
2091
  iframe.setAttribute("title", "Passkey");
1754
2092
  iframe.style.border = "none";
@@ -1758,10 +2096,8 @@ var OneAuthClient = class {
1758
2096
  const handleMessage = (event) => {
1759
2097
  if (event.origin !== hostUrl.origin) return;
1760
2098
  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
- }
2099
+ const maxHeight = window.innerHeight - 100;
2100
+ iframe.style.height = `${Math.min(event.data.height, maxHeight)}px`;
1765
2101
  } else if (event.data?.type === "PASSKEY_DISCONNECT") {
1766
2102
  localStorage.removeItem("1auth-user");
1767
2103
  }
@@ -1779,7 +2115,10 @@ var OneAuthClient = class {
1779
2115
  }
1780
2116
  });
1781
2117
  dialog.showModal();
2118
+ let cleanedUp = false;
1782
2119
  const cleanup = () => {
2120
+ if (cleanedUp) return;
2121
+ cleanedUp = true;
1783
2122
  window.removeEventListener("message", handleMessage);
1784
2123
  document.removeEventListener("keydown", handleEscape);
1785
2124
  dialog.close();
@@ -1811,22 +2150,11 @@ var OneAuthClient = class {
1811
2150
  if (data.success) {
1812
2151
  resolve({
1813
2152
  success: true,
1814
- username: data.data?.username,
1815
- user: data.data?.user
1816
- });
1817
- } else {
1818
- resolve({
1819
- success: false,
1820
- error: data.error
1821
- });
1822
- }
1823
- } else if (data?.type === "PASSKEY_REGISTER_RESULT") {
1824
- window.removeEventListener("message", handleMessage);
1825
- cleanup();
1826
- if (data.success) {
1827
- resolve({
1828
- success: true,
1829
- username: data.data?.username
2153
+ user: {
2154
+ id: data.data?.user?.id,
2155
+ username: data.data?.username,
2156
+ address: data.data?.address
2157
+ }
1830
2158
  });
1831
2159
  } else {
1832
2160
  resolve({
@@ -1885,8 +2213,11 @@ var OneAuthClient = class {
1885
2213
  if (data.success) {
1886
2214
  resolve({
1887
2215
  success: true,
1888
- username: data.data?.username,
1889
- user: data.data?.user
2216
+ user: {
2217
+ id: data.data?.user?.id,
2218
+ username: data.data?.username,
2219
+ address: data.data?.address
2220
+ }
1890
2221
  });
1891
2222
  } else {
1892
2223
  resolve({
@@ -1894,14 +2225,43 @@ var OneAuthClient = class {
1894
2225
  error: data.error
1895
2226
  });
1896
2227
  }
1897
- } else if (data?.type === "PASSKEY_REGISTER_RESULT") {
2228
+ } else if (data?.type === "PASSKEY_CLOSE") {
1898
2229
  clearInterval(pollTimer);
1899
2230
  window.removeEventListener("message", handleMessage);
1900
2231
  popup?.close();
2232
+ resolve({
2233
+ success: false,
2234
+ error: {
2235
+ code: "USER_CANCELLED",
2236
+ message: "Authentication was cancelled"
2237
+ }
2238
+ });
2239
+ }
2240
+ };
2241
+ window.addEventListener("message", handleMessage);
2242
+ });
2243
+ }
2244
+ waitForAuthenticateResponse(_dialog, _iframe, cleanup) {
2245
+ const dialogOrigin = this.getDialogOrigin();
2246
+ return new Promise((resolve) => {
2247
+ const handleMessage = (event) => {
2248
+ if (event.origin !== dialogOrigin) return;
2249
+ const data = event.data;
2250
+ if (data?.type === "PASSKEY_AUTHENTICATE_RESULT") {
2251
+ window.removeEventListener("message", handleMessage);
2252
+ cleanup();
1901
2253
  if (data.success) {
1902
2254
  resolve({
1903
2255
  success: true,
1904
- username: data.data?.username
2256
+ user: {
2257
+ id: data.data?.user?.id,
2258
+ username: data.data?.username,
2259
+ address: data.data?.accountAddress
2260
+ },
2261
+ challenge: data.data?.signature ? {
2262
+ signature: data.data.signature,
2263
+ signedHash: data.data.signedHash
2264
+ } : void 0
1905
2265
  });
1906
2266
  } else {
1907
2267
  resolve({
@@ -1910,9 +2270,8 @@ var OneAuthClient = class {
1910
2270
  });
1911
2271
  }
1912
2272
  } else if (data?.type === "PASSKEY_CLOSE") {
1913
- clearInterval(pollTimer);
1914
2273
  window.removeEventListener("message", handleMessage);
1915
- popup?.close();
2274
+ cleanup();
1916
2275
  resolve({
1917
2276
  success: false,
1918
2277
  error: {
@@ -1925,27 +2284,28 @@ var OneAuthClient = class {
1925
2284
  window.addEventListener("message", handleMessage);
1926
2285
  });
1927
2286
  }
1928
- waitForAuthenticateResponse(_dialog, _iframe, cleanup) {
2287
+ waitForConnectResponse(_dialog, _iframe, cleanup) {
1929
2288
  const dialogOrigin = this.getDialogOrigin();
1930
2289
  return new Promise((resolve) => {
1931
2290
  const handleMessage = (event) => {
1932
2291
  if (event.origin !== dialogOrigin) return;
1933
2292
  const data = event.data;
1934
- if (data?.type === "PASSKEY_AUTHENTICATE_RESULT") {
2293
+ if (data?.type === "PASSKEY_CONNECT_RESULT") {
1935
2294
  window.removeEventListener("message", handleMessage);
1936
2295
  cleanup();
1937
2296
  if (data.success) {
1938
2297
  resolve({
1939
2298
  success: true,
1940
- username: data.data?.username,
1941
- user: data.data?.user,
1942
- accountAddress: data.data?.accountAddress,
1943
- signature: data.data?.signature,
1944
- signedHash: data.data?.signedHash
2299
+ user: {
2300
+ username: data.data?.username,
2301
+ address: data.data?.address
2302
+ },
2303
+ autoConnected: data.data?.autoConnected
1945
2304
  });
1946
2305
  } else {
1947
2306
  resolve({
1948
2307
  success: false,
2308
+ action: data.data?.action,
1949
2309
  error: data.error
1950
2310
  });
1951
2311
  }
@@ -1954,9 +2314,10 @@ var OneAuthClient = class {
1954
2314
  cleanup();
1955
2315
  resolve({
1956
2316
  success: false,
2317
+ action: "cancel",
1957
2318
  error: {
1958
2319
  code: "USER_CANCELLED",
1959
- message: "Authentication was cancelled"
2320
+ message: "Connection was cancelled"
1960
2321
  }
1961
2322
  });
1962
2323
  }
@@ -1964,26 +2325,28 @@ var OneAuthClient = class {
1964
2325
  window.addEventListener("message", handleMessage);
1965
2326
  });
1966
2327
  }
1967
- waitForConnectResponse(_dialog, _iframe, cleanup) {
2328
+ waitForConsentResponse(_dialog, _iframe, cleanup) {
1968
2329
  const dialogOrigin = this.getDialogOrigin();
1969
2330
  return new Promise((resolve) => {
1970
2331
  const handleMessage = (event) => {
1971
2332
  if (event.origin !== dialogOrigin) return;
1972
2333
  const data = event.data;
1973
- if (data?.type === "PASSKEY_CONNECT_RESULT") {
2334
+ if (data?.type === "PASSKEY_CONSENT_RESULT") {
1974
2335
  window.removeEventListener("message", handleMessage);
1975
2336
  cleanup();
1976
2337
  if (data.success) {
1977
2338
  resolve({
1978
2339
  success: true,
1979
- username: data.data?.username,
1980
- autoConnected: data.data?.autoConnected
2340
+ data: data.data,
2341
+ grantedAt: data.data?.grantedAt
1981
2342
  });
1982
2343
  } else {
1983
2344
  resolve({
1984
2345
  success: false,
1985
- action: data.data?.action,
1986
- error: data.error
2346
+ error: data.error ?? {
2347
+ code: "USER_REJECTED",
2348
+ message: "User denied the consent request"
2349
+ }
1987
2350
  });
1988
2351
  }
1989
2352
  } else if (data?.type === "PASSKEY_CLOSE") {
@@ -1991,10 +2354,9 @@ var OneAuthClient = class {
1991
2354
  cleanup();
1992
2355
  resolve({
1993
2356
  success: false,
1994
- action: "cancel",
1995
2357
  error: {
1996
2358
  code: "USER_CANCELLED",
1997
- message: "Connection was cancelled"
2359
+ message: "User closed the dialog"
1998
2360
  }
1999
2361
  });
2000
2362
  }
@@ -2800,7 +3162,6 @@ function BatchQueueWidget({ onSignAll }) {
2800
3162
  // src/verify.ts
2801
3163
  import { keccak256, toBytes } from "viem";
2802
3164
  var ETHEREUM_MESSAGE_PREFIX = "Ethereum Signed Message:\n";
2803
- var PASSKEY_MESSAGE_PREFIX = ETHEREUM_MESSAGE_PREFIX;
2804
3165
  function hashMessage2(message) {
2805
3166
  const prefixed = ETHEREUM_MESSAGE_PREFIX + message.length.toString() + message;
2806
3167
  return keccak256(toBytes(prefixed));
@@ -2815,11 +3176,8 @@ export {
2815
3176
  BatchQueueWidget,
2816
3177
  ETHEREUM_MESSAGE_PREFIX,
2817
3178
  OneAuthClient,
2818
- PASSKEY_MESSAGE_PREFIX,
2819
- OneAuthClient as PasskeyProviderClient,
2820
3179
  createOneAuthProvider,
2821
3180
  createPasskeyAccount,
2822
- createPasskeyProvider,
2823
3181
  createPasskeyWalletClient,
2824
3182
  encodeWebAuthnSignature,
2825
3183
  getAllSupportedChainsAndTokens,