@thru/passkey 0.2.13 → 0.2.14

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.
Files changed (71) hide show
  1. package/README.md +73 -90
  2. package/dist/auth.cjs +672 -0
  3. package/dist/auth.cjs.map +1 -0
  4. package/dist/auth.d.cts +60 -0
  5. package/dist/auth.d.ts +60 -0
  6. package/dist/auth.js +422 -0
  7. package/dist/auth.js.map +1 -0
  8. package/dist/chunk-2JHC7OOH.js +250 -0
  9. package/dist/chunk-2JHC7OOH.js.map +1 -0
  10. package/dist/chunk-75G2FPYW.js +54 -0
  11. package/dist/chunk-75G2FPYW.js.map +1 -0
  12. package/dist/chunk-B5SN7AS7.js +586 -0
  13. package/dist/chunk-B5SN7AS7.js.map +1 -0
  14. package/dist/chunk-LNDWK3FA.js +163 -0
  15. package/dist/chunk-LNDWK3FA.js.map +1 -0
  16. package/dist/index.cjs +27 -94
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +4 -187
  19. package/dist/index.d.ts +4 -187
  20. package/dist/index.js +47 -810
  21. package/dist/index.js.map +1 -1
  22. package/dist/mobile.cjs +301 -0
  23. package/dist/mobile.cjs.map +1 -0
  24. package/dist/mobile.d.cts +49 -0
  25. package/dist/mobile.d.ts +49 -0
  26. package/dist/mobile.js +41 -0
  27. package/dist/mobile.js.map +1 -0
  28. package/dist/popup.cjs +247 -0
  29. package/dist/popup.cjs.map +1 -0
  30. package/dist/popup.d.cts +22 -0
  31. package/dist/popup.d.ts +22 -0
  32. package/dist/popup.js +31 -0
  33. package/dist/popup.js.map +1 -0
  34. package/dist/server.cjs +351 -0
  35. package/dist/server.cjs.map +1 -0
  36. package/dist/server.d.cts +119 -0
  37. package/dist/server.d.ts +119 -0
  38. package/dist/server.js +340 -0
  39. package/dist/server.js.map +1 -0
  40. package/dist/types-_HRzmn-j.d.cts +125 -0
  41. package/dist/types-_HRzmn-j.d.ts +125 -0
  42. package/dist/web.cjs +758 -0
  43. package/dist/web.cjs.map +1 -0
  44. package/dist/web.d.cts +32 -0
  45. package/dist/web.d.ts +32 -0
  46. package/dist/web.js +60 -0
  47. package/dist/web.js.map +1 -0
  48. package/package.json +47 -2
  49. package/src/auth/execute-tx.ts +87 -0
  50. package/src/auth/index.ts +18 -0
  51. package/src/auth/types.ts +56 -0
  52. package/src/auth/use-passkey-auth.ts +428 -0
  53. package/src/index.ts +37 -39
  54. package/src/mobile/errors.ts +31 -0
  55. package/src/mobile/index.ts +33 -0
  56. package/src/mobile/passkey.ts +154 -0
  57. package/src/mobile/storage.ts +115 -0
  58. package/src/mobile/types.ts +24 -0
  59. package/src/popup-entry.ts +33 -0
  60. package/src/popup-service.ts +0 -103
  61. package/src/server/challenge.ts +26 -0
  62. package/src/server/create-wallet.ts +149 -0
  63. package/src/server/handlers.ts +93 -0
  64. package/src/server/index.ts +13 -0
  65. package/src/server/submit.ts +47 -0
  66. package/src/server/types.ts +70 -0
  67. package/src/server/utils.ts +69 -0
  68. package/src/types.ts +1 -0
  69. package/src/web.ts +51 -0
  70. package/tsconfig.json +6 -1
  71. package/tsup.config.ts +9 -1
package/dist/index.js CHANGED
@@ -1,809 +1,48 @@
1
- // src/register.ts
2
- import { arrayBufferToBase64Url, bytesToHex } from "@thru/passkey-manager";
3
-
4
- // src/capabilities.ts
5
- var DEBUG = typeof process !== "undefined" && process.env?.NEXT_PUBLIC_PASSKEY_DEBUG === "1";
6
- var cachedClientCapabilities;
7
- var clientCapabilitiesPromise = null;
8
- function isWebAuthnSupported() {
9
- const supported = typeof window !== "undefined" && typeof window.PublicKeyCredential !== "undefined" && typeof navigator.credentials !== "undefined";
10
- if (DEBUG) {
11
- console.log("[Passkey] WebAuthn support check:", {
12
- window: typeof window !== "undefined",
13
- PublicKeyCredential: typeof window !== "undefined" && typeof window.PublicKeyCredential !== "undefined",
14
- credentials: typeof window !== "undefined" && typeof navigator !== "undefined" && typeof navigator.credentials !== "undefined",
15
- supported
16
- });
17
- }
18
- return supported;
19
- }
20
- async function fetchPasskeyClientCapabilities() {
21
- if (typeof window === "undefined" || typeof window.PublicKeyCredential === "undefined") {
22
- return null;
23
- }
24
- const getClientCapabilities = window.PublicKeyCredential.getClientCapabilities;
25
- if (typeof getClientCapabilities !== "function") {
26
- return null;
27
- }
28
- try {
29
- const capabilities = await getClientCapabilities.call(window.PublicKeyCredential);
30
- if (DEBUG) {
31
- console.log("[Passkey] WebAuthn client capabilities:", capabilities);
32
- }
33
- return capabilities ?? null;
34
- } catch (error) {
35
- if (DEBUG) {
36
- console.warn("[Passkey] Failed to read client capabilities:", error);
37
- }
38
- return null;
39
- }
40
- }
41
- function preloadPasskeyClientCapabilities() {
42
- if (cachedClientCapabilities !== void 0 || clientCapabilitiesPromise) {
43
- return;
44
- }
45
- clientCapabilitiesPromise = fetchPasskeyClientCapabilities().then((capabilities) => {
46
- cachedClientCapabilities = capabilities;
47
- return capabilities;
48
- });
49
- }
50
- async function getPasskeyClientCapabilities() {
51
- if (cachedClientCapabilities !== void 0) {
52
- return cachedClientCapabilities;
53
- }
54
- if (!clientCapabilitiesPromise) {
55
- preloadPasskeyClientCapabilities();
56
- }
57
- if (!clientCapabilitiesPromise) {
58
- cachedClientCapabilities = null;
59
- return null;
60
- }
61
- const capabilities = await clientCapabilitiesPromise;
62
- cachedClientCapabilities = capabilities;
63
- return capabilities;
64
- }
65
- function getCachedPasskeyClientCapabilities() {
66
- return cachedClientCapabilities;
67
- }
68
- function isInIframe() {
69
- if (typeof window === "undefined") {
70
- return false;
71
- }
72
- try {
73
- return window.self !== window.top;
74
- } catch {
75
- return true;
76
- }
77
- }
78
- async function shouldUsePasskeyPopup(action) {
79
- if (!isInIframe()) {
80
- return false;
81
- }
82
- const mode = await getPasskeyPromptMode(action);
83
- return mode === "popup";
84
- }
85
- function getPermissionsPolicyAllowsFeature(feature) {
86
- if (typeof document === "undefined") {
87
- return null;
88
- }
89
- const policy = document.permissionsPolicy;
90
- const featurePolicy = document.featurePolicy;
91
- const allowsFeature = policy?.allowsFeature || featurePolicy?.allowsFeature;
92
- if (typeof allowsFeature !== "function") {
93
- return null;
94
- }
95
- try {
96
- return allowsFeature(feature);
97
- } catch {
98
- return null;
99
- }
100
- }
101
- function getCachedPromptMode(action) {
102
- if (!isInIframe()) {
103
- return "inline";
104
- }
105
- if (cachedClientCapabilities === void 0 && !clientCapabilitiesPromise) {
106
- preloadPasskeyClientCapabilities();
107
- }
108
- const feature = action === "create" ? "publickey-credentials-create" : "publickey-credentials-get";
109
- const policyAllows = getPermissionsPolicyAllowsFeature(feature);
110
- const capabilities = getCachedPasskeyClientCapabilities();
111
- const supportsInline = capabilities?.passkeyPlatformAuthenticator === true || capabilities?.userVerifyingPlatformAuthenticator === true;
112
- if (policyAllows === false) {
113
- return "popup";
114
- }
115
- if (capabilities === void 0) {
116
- return "unknown";
117
- }
118
- if (!supportsInline) {
119
- return "popup";
120
- }
121
- return "inline";
122
- }
123
- async function getPasskeyPromptMode(action) {
124
- if (!isInIframe()) {
125
- return "inline";
126
- }
127
- const feature = action === "create" ? "publickey-credentials-create" : "publickey-credentials-get";
128
- const policyAllows = getPermissionsPolicyAllowsFeature(feature);
129
- const capabilities = await getPasskeyClientCapabilities();
130
- const supportsInline = capabilities?.passkeyPlatformAuthenticator === true || capabilities?.userVerifyingPlatformAuthenticator === true;
131
- if (DEBUG) {
132
- console.log("[Passkey] Prompt mode check:", {
133
- action,
134
- policyAllows,
135
- supportsInline,
136
- capabilities
137
- });
138
- }
139
- if (!supportsInline) {
140
- return "popup";
141
- }
142
- if (policyAllows === false) {
143
- return "popup";
144
- }
145
- return "inline";
146
- }
147
- function maybePreopenPopup(action, openPopupFn) {
148
- const cachedMode = getCachedPromptMode(action);
149
- if (cachedMode !== "popup") {
150
- return null;
151
- }
152
- try {
153
- return openPopupFn();
154
- } catch {
155
- return null;
156
- }
157
- }
158
- function shouldFallbackToPopup(error) {
159
- if (!isInIframe()) {
160
- return false;
161
- }
162
- const name = error && typeof error === "object" && "name" in error ? String(error.name) : "";
163
- const message = error && typeof error === "object" && "message" in error ? String(error.message) : "";
164
- const normalized = `${name} ${message}`.toLowerCase();
165
- if (normalized.includes("cancel") || normalized.includes("canceled") || normalized.includes("cancelled") || normalized.includes("user canceled") || normalized.includes("user cancelled") || normalized.includes("aborted")) {
166
- return false;
167
- }
168
- if (normalized.includes("securityerror")) {
169
- return true;
170
- }
171
- if (normalized.includes("notallowederror")) {
172
- if (normalized.includes("permission") || normalized.includes("policy") || normalized.includes("iframe") || normalized.includes("frame")) {
173
- return true;
174
- }
175
- }
176
- return false;
177
- }
178
-
179
- // src/popup.ts
180
- var PASSKEY_POPUP_PATH = "/passkey/popup";
181
- var PASSKEY_POPUP_READY_EVENT = "thru:passkey-popup-ready";
182
- var PASSKEY_POPUP_REQUEST_EVENT = "thru:passkey-popup-request";
183
- var PASSKEY_POPUP_RESPONSE_EVENT = "thru:passkey-popup-response";
184
- var PASSKEY_POPUP_CHANNEL = "thru:passkey-popup-channel";
185
- var PASSKEY_POPUP_TIMEOUT_MS = 6e4;
186
- function closePopup(popup) {
187
- if (popup && !popup.closed) {
188
- popup.close();
189
- }
190
- }
191
- function openPasskeyPopupWindow() {
192
- const popupUrl = new URL(PASSKEY_POPUP_PATH, window.location.origin).toString();
193
- const popup = window.open(
194
- popupUrl,
195
- "thru_passkey_popup",
196
- "popup=yes,width=440,height=640"
197
- );
198
- if (!popup) {
199
- throw new Error("Passkey popup was blocked");
200
- }
201
- return popup;
202
- }
203
- function createPopupRequestId() {
204
- const rand = Math.random().toString(36).slice(2, 10);
205
- return `passkey_${Date.now()}_${rand}`;
206
- }
207
- async function requestPasskeyPopup(action, payload, preopenedPopup) {
208
- if (typeof window === "undefined") {
209
- throw new Error("Passkey popup is only available in the browser");
210
- }
211
- const requestId = createPopupRequestId();
212
- const targetOrigin = window.location.origin;
213
- let popup = preopenedPopup ?? null;
214
- const channel = typeof BroadcastChannel !== "undefined" ? new BroadcastChannel(PASSKEY_POPUP_CHANNEL) : null;
215
- return new Promise((resolve, reject) => {
216
- let timeout = null;
217
- let closePoll = null;
218
- let requestSent = false;
219
- const cleanup = () => {
220
- if (timeout) {
221
- clearTimeout(timeout);
222
- timeout = null;
223
- }
224
- if (closePoll) {
225
- clearInterval(closePoll);
226
- closePoll = null;
227
- }
228
- window.removeEventListener("message", handleMessage);
229
- if (channel) {
230
- channel.removeEventListener("message", handleChannelMessage);
231
- channel.close();
232
- }
233
- };
234
- const sendRequest = (viaChannel) => {
235
- if (requestSent) {
236
- return;
237
- }
238
- requestSent = true;
239
- const request = {
240
- type: PASSKEY_POPUP_REQUEST_EVENT,
241
- requestId,
242
- action,
243
- payload
244
- };
245
- if (viaChannel) {
246
- channel?.postMessage(request);
247
- return;
248
- }
249
- popup?.postMessage(request, targetOrigin);
250
- };
251
- const handleResponse = (data) => {
252
- if (data.requestId !== requestId) {
253
- return;
254
- }
255
- cleanup();
256
- if (popup && !popup.closed) {
257
- popup.close();
258
- }
259
- if (data.success) {
260
- resolve(data.result);
261
- } else {
262
- const err = new Error(data.error?.message || "Passkey popup failed");
263
- if (data.error?.name) {
264
- err.name = data.error.name;
265
- }
266
- reject(err);
267
- }
268
- };
269
- const handleMessage = (event) => {
270
- if (event.origin !== targetOrigin) {
271
- return;
272
- }
273
- const data = event.data;
274
- if (!data || typeof data !== "object") {
275
- return;
276
- }
277
- if (data.type === PASSKEY_POPUP_READY_EVENT) {
278
- if (popup && event.source !== popup) {
279
- return;
280
- }
281
- sendRequest(false);
282
- return;
283
- }
284
- if (data.type === PASSKEY_POPUP_RESPONSE_EVENT && "requestId" in data) {
285
- handleResponse(data);
286
- }
287
- };
288
- window.addEventListener("message", handleMessage);
289
- const handleChannelMessage = (event) => {
290
- const data = event.data;
291
- if (!data || typeof data !== "object") {
292
- return;
293
- }
294
- if (data.type === PASSKEY_POPUP_READY_EVENT) {
295
- sendRequest(true);
296
- return;
297
- }
298
- if (data.type === PASSKEY_POPUP_RESPONSE_EVENT && "requestId" in data) {
299
- handleResponse(data);
300
- }
301
- };
302
- if (channel) {
303
- channel.addEventListener("message", handleChannelMessage);
304
- }
305
- if (!popup) {
306
- try {
307
- popup = openPasskeyPopupWindow();
308
- } catch (error) {
309
- cleanup();
310
- reject(error);
311
- return;
312
- }
313
- }
314
- timeout = setTimeout(() => {
315
- cleanup();
316
- try {
317
- popup?.close();
318
- } catch {
319
- }
320
- reject(new Error("Passkey popup timed out"));
321
- }, PASSKEY_POPUP_TIMEOUT_MS);
322
- closePoll = setInterval(() => {
323
- if (popup && popup.closed) {
324
- cleanup();
325
- reject(new Error("Passkey popup was closed"));
326
- }
327
- }, 250);
328
- });
329
- }
330
-
331
- // src/register.ts
332
- async function registerPasskey(alias, userId, rpId) {
333
- if (!isWebAuthnSupported()) {
334
- throw new Error("WebAuthn is not supported in this browser");
335
- }
336
- return runWithPromptMode(
337
- "create",
338
- () => registerPasskeyInline(alias, userId, rpId),
339
- (preopenedPopup) => registerPasskeyViaPopup(alias, userId, rpId, preopenedPopup)
340
- );
341
- }
342
- async function runWithPromptMode(action, inlineFn, popupFn) {
343
- const preopenedPopup = maybePreopenPopup(action, openPasskeyPopupWindow);
344
- const promptMode = await getPasskeyPromptMode(action);
345
- if (promptMode === "popup") {
346
- return popupFn(preopenedPopup);
347
- }
348
- closePopup(preopenedPopup);
349
- try {
350
- return await inlineFn();
351
- } catch (error) {
352
- if (shouldFallbackToPopup(error)) {
353
- return popupFn();
354
- }
355
- throw error;
356
- }
357
- }
358
- async function registerPasskeyInline(alias, userId, rpId) {
359
- const rpName = "Thru Wallet";
360
- const userIdBytes = new TextEncoder().encode(userId);
361
- const userIdBuffer = userIdBytes.slice(0, 64);
362
- const challenge = crypto.getRandomValues(new Uint8Array(32));
363
- const createOptions = {
364
- challenge,
365
- rp: {
366
- id: rpId,
367
- name: rpName
368
- },
369
- user: {
370
- id: userIdBuffer,
371
- name: alias,
372
- displayName: alias
373
- },
374
- pubKeyCredParams: [
375
- { type: "public-key", alg: -7 }
376
- ],
377
- authenticatorSelection: {
378
- authenticatorAttachment: "platform",
379
- userVerification: "required",
380
- residentKey: "required",
381
- requireResidentKey: true
382
- },
383
- attestation: "none",
384
- timeout: 6e4
385
- };
386
- const credential = await navigator.credentials.create({
387
- publicKey: createOptions
388
- });
389
- if (!credential) {
390
- throw new Error("Passkey registration was cancelled");
391
- }
392
- const response = credential.response;
393
- const { x, y } = extractP256PublicKey(response);
394
- return {
395
- credentialId: arrayBufferToBase64Url(credential.rawId),
396
- publicKeyX: bytesToHex(x),
397
- publicKeyY: bytesToHex(y),
398
- rpId
399
- };
400
- }
401
- async function registerPasskeyViaPopup(alias, userId, rpId, preopenedPopup) {
402
- const result = await requestPasskeyPopup(
403
- "create",
404
- { alias, userId, rpId },
405
- preopenedPopup
406
- );
407
- return result;
408
- }
409
- function extractP256PublicKey(response) {
410
- if (typeof response.getPublicKey === "function") {
411
- const spkiKey = response.getPublicKey();
412
- if (spkiKey) {
413
- return extractFromSpki(new Uint8Array(spkiKey));
414
- }
415
- }
416
- if (typeof response.getAuthenticatorData === "function") {
417
- const authData = new Uint8Array(response.getAuthenticatorData());
418
- return extractFromAuthenticatorData(authData);
419
- }
420
- throw new Error("Unable to extract public key: browser does not support required WebAuthn methods");
421
- }
422
- function extractFromSpki(spki) {
423
- const pointStart = spki.length - 65;
424
- if (spki[pointStart] !== 4) {
425
- throw new Error("Invalid SPKI format: expected uncompressed point");
426
- }
427
- const x = spki.slice(pointStart + 1, pointStart + 33);
428
- const y = spki.slice(pointStart + 33, pointStart + 65);
429
- if (x.length !== 32 || y.length !== 32) {
430
- throw new Error("Invalid SPKI format: incorrect coordinate length");
431
- }
432
- return { x, y };
433
- }
434
- function extractFromAuthenticatorData(authData) {
435
- const rpIdHashLength = 32;
436
- const flagsLength = 1;
437
- const counterLength = 4;
438
- const offset = rpIdHashLength + flagsLength + counterLength;
439
- const aaguidLength = 16;
440
- const credIdLenOffset = offset + aaguidLength;
441
- const credIdLength = authData[credIdLenOffset] << 8 | authData[credIdLenOffset + 1];
442
- const coseKeyOffset = credIdLenOffset + 2 + credIdLength;
443
- const coseKey = authData.slice(coseKeyOffset);
444
- return extractFromCoseKey(coseKey);
445
- }
446
- function extractFromCoseKey(coseKey) {
447
- const mapStart = coseKey[0];
448
- if (mapStart !== 165 && mapStart !== 164) {
449
- throw new Error("Invalid COSE key format");
450
- }
451
- let offset = 1;
452
- let x = null;
453
- let y = null;
454
- while (offset < coseKey.length) {
455
- const key = coseKey[offset++];
456
- const valueType = coseKey[offset++];
457
- if (key === 33) {
458
- const length = valueType & 31;
459
- x = coseKey.slice(offset, offset + length);
460
- offset += length;
461
- continue;
462
- }
463
- if (key === 34) {
464
- const length = valueType & 31;
465
- y = coseKey.slice(offset, offset + length);
466
- offset += length;
467
- continue;
468
- }
469
- if (valueType >= 64 && valueType <= 95) {
470
- const length = valueType & 31;
471
- offset += length;
472
- continue;
473
- }
474
- if (valueType === 1 || valueType === 2 || valueType === 3) {
475
- continue;
476
- }
477
- if (valueType >= 24 && valueType <= 27) {
478
- const size = 1 << valueType - 24;
479
- offset += size;
480
- continue;
481
- }
482
- }
483
- if (!x || !y) {
484
- throw new Error("Failed to extract P-256 public key from COSE data");
485
- }
486
- if (x.length !== 32 || y.length !== 32) {
487
- throw new Error("Invalid COSE key: incorrect coordinate length");
488
- }
489
- return { x, y };
490
- }
491
-
492
- // src/sign.ts
493
1
  import {
494
- arrayBufferToBase64Url as arrayBufferToBase64Url2,
2
+ P256_HALF_N,
3
+ P256_N,
4
+ arrayBufferToBase64Url,
495
5
  base64UrlToArrayBuffer,
496
- bytesToBase64Url,
497
6
  base64UrlToBytes,
498
- parseDerSignature,
499
- normalizeLowS
500
- } from "@thru/passkey-manager";
501
- async function signWithPasskey(credentialId, challenge, rpId) {
502
- if (!isWebAuthnSupported()) {
503
- throw new Error("WebAuthn is not supported in this browser");
504
- }
505
- return runWithPromptMode2(
506
- "get",
507
- () => signWithPasskeyInline(credentialId, challenge, rpId),
508
- (preopenedPopup) => signWithPasskeyViaPopup(credentialId, challenge, rpId, preopenedPopup)
509
- );
510
- }
511
- async function signWithStoredPasskey(challenge, rpId, preferredPasskey, allPasskeys, context) {
512
- if (!isWebAuthnSupported()) {
513
- throw new Error("WebAuthn is not supported in this browser");
514
- }
515
- const preopenedPopup = maybePreopenPopup("get", openPasskeyPopupWindow);
516
- const promptMode = await getPasskeyPromptMode("get");
517
- const storedPasskey = preferredPasskey;
518
- const canUsePopup = isInIframe();
519
- if (promptMode === "popup" || canUsePopup && !storedPasskey) {
520
- return requestStoredPasskeyPopup(challenge, preopenedPopup, context);
521
- }
522
- closePopup(preopenedPopup);
523
- try {
524
- if (storedPasskey) {
525
- const result = await signWithPasskeyInline(
526
- storedPasskey.credentialId,
527
- challenge,
528
- storedPasskey.rpId
529
- );
530
- return {
531
- ...result,
532
- passkey: storedPasskey
533
- };
534
- }
535
- const discoverable = await signWithDiscoverablePasskey(challenge, rpId);
536
- const matchingPasskey = allPasskeys.find((p) => p.credentialId === discoverable.credentialId) ?? null;
537
- const now = (/* @__PURE__ */ new Date()).toISOString();
538
- const passkey = matchingPasskey ?? {
539
- credentialId: discoverable.credentialId,
540
- publicKeyX: "",
541
- publicKeyY: "",
542
- rpId: discoverable.rpId,
543
- createdAt: now,
544
- lastUsedAt: now
545
- };
546
- return {
547
- signature: discoverable.signature,
548
- authenticatorData: discoverable.authenticatorData,
549
- clientDataJSON: discoverable.clientDataJSON,
550
- signatureR: discoverable.signatureR,
551
- signatureS: discoverable.signatureS,
552
- passkey
553
- };
554
- } catch (error) {
555
- if (canUsePopup && shouldFallbackToPopup(error)) {
556
- return requestStoredPasskeyPopup(challenge, void 0, context);
557
- }
558
- throw error;
559
- }
560
- }
561
- async function signWithDiscoverablePasskey(challenge, rpId) {
562
- if (!isWebAuthnSupported()) {
563
- throw new Error("WebAuthn is not supported in this browser");
564
- }
565
- const resolvedRpId = rpId;
566
- const result = await signWithPasskeyAssertion(challenge, resolvedRpId);
567
- return {
568
- signature: result.signature,
569
- authenticatorData: result.authenticatorData,
570
- clientDataJSON: result.clientDataJSON,
571
- signatureR: result.signatureR,
572
- signatureS: result.signatureS,
573
- credentialId: result.credentialId,
574
- rpId: resolvedRpId
575
- };
576
- }
577
- async function runWithPromptMode2(action, inlineFn, popupFn) {
578
- const preopenedPopup = maybePreopenPopup(action, openPasskeyPopupWindow);
579
- const promptMode = await getPasskeyPromptMode(action);
580
- if (promptMode === "popup") {
581
- return popupFn(preopenedPopup);
582
- }
583
- closePopup(preopenedPopup);
584
- try {
585
- return await inlineFn();
586
- } catch (error) {
587
- if (shouldFallbackToPopup(error)) {
588
- return popupFn();
589
- }
590
- throw error;
591
- }
592
- }
593
- async function signWithPasskeyInline(credentialId, challenge, rpId) {
594
- const result = await signWithPasskeyAssertion(challenge, rpId, credentialId);
595
- return {
596
- signature: result.signature,
597
- authenticatorData: result.authenticatorData,
598
- clientDataJSON: result.clientDataJSON,
599
- signatureR: result.signatureR,
600
- signatureS: result.signatureS
601
- };
602
- }
603
- async function signWithPasskeyAssertion(challenge, rpId, credentialId) {
604
- const challengeBytes = new Uint8Array(challenge);
605
- const getOptions = {
606
- challenge: challengeBytes,
607
- rpId,
608
- userVerification: "required",
609
- timeout: 6e4
610
- };
611
- if (credentialId) {
612
- const credentialIdBuffer = base64UrlToArrayBuffer(credentialId);
613
- getOptions.allowCredentials = [
614
- {
615
- type: "public-key",
616
- id: credentialIdBuffer,
617
- transports: ["internal", "hybrid", "usb", "ble", "nfc"]
618
- }
619
- ];
620
- }
621
- const assertion = await navigator.credentials.get({
622
- publicKey: getOptions
623
- });
624
- if (!assertion) {
625
- throw new Error("Passkey authentication was cancelled");
626
- }
627
- const response = assertion.response;
628
- const signature = new Uint8Array(response.signature);
629
- let { r, s } = parseDerSignature(signature);
630
- s = normalizeLowS(s);
631
- return {
632
- signature: new Uint8Array([...r, ...s]),
633
- authenticatorData: new Uint8Array(response.authenticatorData),
634
- clientDataJSON: new Uint8Array(response.clientDataJSON),
635
- signatureR: r,
636
- signatureS: s,
637
- credentialId: arrayBufferToBase64Url2(assertion.rawId)
638
- };
639
- }
640
- async function signWithPasskeyViaPopup(credentialId, challenge, rpId, preopenedPopup) {
641
- const result = await requestPasskeyPopup(
642
- "get",
643
- {
644
- credentialId,
645
- challengeBase64Url: bytesToBase64Url(challenge),
646
- rpId
647
- },
648
- preopenedPopup
649
- );
650
- return decodePopupSigningResult(result);
651
- }
652
- async function requestStoredPasskeyPopup(challenge, preopenedPopup, context) {
653
- const result = await requestPasskeyPopup(
654
- "getStored",
655
- {
656
- challengeBase64Url: bytesToBase64Url(challenge),
657
- context
658
- },
659
- preopenedPopup
660
- );
661
- return decodePopupStoredSigningResult(result);
662
- }
663
- function decodePopupSigningResult(result) {
664
- return {
665
- signature: base64UrlToBytes(result.signatureBase64Url),
666
- authenticatorData: base64UrlToBytes(result.authenticatorDataBase64Url),
667
- clientDataJSON: base64UrlToBytes(result.clientDataJSONBase64Url),
668
- signatureR: base64UrlToBytes(result.signatureRBase64Url),
669
- signatureS: base64UrlToBytes(result.signatureSBase64Url)
670
- };
671
- }
672
- function decodePopupStoredSigningResult(result) {
673
- return {
674
- ...decodePopupSigningResult(result),
675
- passkey: result.passkey,
676
- accounts: result.accounts
677
- };
678
- }
679
-
680
- // src/index.ts
681
- import {
682
- parseDerSignature as parseDerSignature2,
683
- normalizeLowS as normalizeLowS2,
684
- normalizeSignatureComponent,
685
- P256_N,
686
- P256_HALF_N,
687
- bytesToBigIntBE,
688
- bigIntToBytesBE
689
- } from "@thru/passkey-manager";
690
- import {
691
- arrayBufferToBase64Url as arrayBufferToBase64Url3,
692
- base64UrlToArrayBuffer as base64UrlToArrayBuffer2,
693
- bytesToBase64Url as bytesToBase64Url3,
694
- base64UrlToBytes as base64UrlToBytes3,
695
- bytesToHex as bytesToHex2,
696
- hexToBytes,
7
+ bigIntToBytesBE,
697
8
  bytesEqual,
9
+ bytesToBase64,
10
+ bytesToBase64Url,
11
+ bytesToBigIntBE,
12
+ bytesToHex,
698
13
  compareBytes,
14
+ getCachedPasskeyClientCapabilities,
15
+ getPasskeyClientCapabilities,
16
+ hexToBytes,
17
+ isInIframe,
18
+ isWebAuthnSupported,
19
+ normalizeLowS,
20
+ normalizeSignatureComponent,
21
+ parseDerSignature,
22
+ preloadPasskeyClientCapabilities,
23
+ registerPasskey,
24
+ shouldUsePasskeyPopup,
25
+ signWithDiscoverablePasskey,
26
+ signWithPasskey,
27
+ signWithStoredPasskey,
699
28
  uniqueAccounts
700
- } from "@thru/passkey-manager";
701
-
702
- // src/popup-service.ts
703
- import { bytesToBase64Url as bytesToBase64Url2, base64UrlToBytes as base64UrlToBytes2 } from "@thru/passkey-manager";
704
- function toPopupSigningResult(result) {
705
- return {
706
- signatureBase64Url: bytesToBase64Url2(result.signature),
707
- authenticatorDataBase64Url: bytesToBase64Url2(result.authenticatorData),
708
- clientDataJSONBase64Url: bytesToBase64Url2(result.clientDataJSON),
709
- signatureRBase64Url: bytesToBase64Url2(result.signatureR),
710
- signatureSBase64Url: bytesToBase64Url2(result.signatureS)
711
- };
712
- }
713
- function buildSuccessResponse(requestId, action, result) {
714
- return {
715
- type: PASSKEY_POPUP_RESPONSE_EVENT,
716
- requestId,
717
- action,
718
- success: true,
719
- result
720
- };
721
- }
722
- function decodeChallenge(base64Url) {
723
- return base64UrlToBytes2(base64Url);
724
- }
725
- function getPopupDisplayInfo(context) {
726
- const name = context?.appName || context?.origin || "A dApp";
727
- const url = context?.appUrl || context?.origin;
728
- const logoText = name.charAt(0).toUpperCase() || "A";
729
- return {
730
- name,
731
- url,
732
- imageUrl: context?.imageUrl,
733
- logoText
734
- };
735
- }
736
- function getResponseError(action, error) {
737
- const { name, message } = normalizeError(error);
738
- const actionLabel = `Popup ${action}`;
739
- const messageText = message || "Passkey popup failed";
740
- const detailedMessage = messageText.includes("Popup") ? messageText : `${actionLabel}: ${messageText}`;
741
- return {
742
- name,
743
- message: detailedMessage
744
- };
745
- }
746
- async function signWithPreferredPasskey(preferredPasskey, challenge, log) {
747
- const resolvedRpId = preferredPasskey?.rpId ?? window.location.hostname;
748
- if (preferredPasskey?.credentialId && preferredPasskey.rpId) {
749
- try {
750
- const storedResult = await signWithPasskey(
751
- preferredPasskey.credentialId,
752
- challenge,
753
- preferredPasskey.rpId
754
- );
755
- return {
756
- result: storedResult,
757
- credentialId: preferredPasskey.credentialId,
758
- rpId: preferredPasskey.rpId
759
- };
760
- } catch (error) {
761
- if (!shouldFallbackToDiscoverable(error)) {
762
- throw error;
763
- }
764
- if (log) {
765
- log("stored passkey failed; falling back to discoverable prompt");
766
- }
767
- }
768
- }
769
- const discovered = await signWithDiscoverablePasskey(challenge, resolvedRpId);
770
- return {
771
- result: discovered,
772
- credentialId: discovered.credentialId,
773
- rpId: resolvedRpId
774
- };
775
- }
776
- function buildStoredPasskeyResult(signed, preferredPasskey, profiles, accounts) {
777
- const now = (/* @__PURE__ */ new Date()).toISOString();
778
- const matchingPasskey = profiles.find((profile) => profile.passkey?.credentialId === signed.credentialId)?.passkey ?? null;
779
- const passkey = matchingPasskey ?? {
780
- credentialId: signed.credentialId,
781
- publicKeyX: "",
782
- publicKeyY: "",
783
- rpId: signed.rpId,
784
- label: preferredPasskey?.label,
785
- createdAt: now,
786
- lastUsedAt: now
787
- };
788
- return {
789
- ...toPopupSigningResult(signed.result),
790
- passkey: matchingPasskey ? { ...passkey, lastUsedAt: now } : passkey,
791
- accounts
792
- };
793
- }
794
- function normalizeError(error) {
795
- const name = error && typeof error === "object" && "name" in error ? String(error.name) : "";
796
- const message = error && typeof error === "object" && "message" in error ? String(error.message) : "";
797
- return {
798
- name,
799
- message,
800
- normalized: `${name} ${message}`.toLowerCase()
801
- };
802
- }
803
- function shouldFallbackToDiscoverable(error) {
804
- const normalized = normalizeError(error).normalized;
805
- return normalized.includes("notfounderror") || normalized.includes("notallowederror") || normalized.includes("securityerror");
806
- }
29
+ } from "./chunk-B5SN7AS7.js";
30
+ import {
31
+ buildSuccessResponse,
32
+ decodeChallenge,
33
+ getResponseError,
34
+ toPopupSigningResult
35
+ } from "./chunk-75G2FPYW.js";
36
+ import {
37
+ PASSKEY_POPUP_CHANNEL,
38
+ PASSKEY_POPUP_PATH,
39
+ PASSKEY_POPUP_READY_EVENT,
40
+ PASSKEY_POPUP_REQUEST_EVENT,
41
+ PASSKEY_POPUP_RESPONSE_EVENT,
42
+ closePopup,
43
+ openPasskeyPopupWindow,
44
+ requestPasskeyPopup
45
+ } from "./chunk-LNDWK3FA.js";
807
46
  export {
808
47
  P256_HALF_N,
809
48
  P256_N,
@@ -812,37 +51,35 @@ export {
812
51
  PASSKEY_POPUP_READY_EVENT,
813
52
  PASSKEY_POPUP_REQUEST_EVENT,
814
53
  PASSKEY_POPUP_RESPONSE_EVENT,
815
- arrayBufferToBase64Url3 as arrayBufferToBase64Url,
816
- base64UrlToArrayBuffer2 as base64UrlToArrayBuffer,
817
- base64UrlToBytes3 as base64UrlToBytes,
54
+ arrayBufferToBase64Url,
55
+ base64UrlToArrayBuffer,
56
+ base64UrlToBytes,
818
57
  bigIntToBytesBE,
819
- buildStoredPasskeyResult,
820
58
  buildSuccessResponse,
821
59
  bytesEqual,
822
- bytesToBase64Url3 as bytesToBase64Url,
60
+ bytesToBase64,
61
+ bytesToBase64Url,
823
62
  bytesToBigIntBE,
824
- bytesToHex2 as bytesToHex,
63
+ bytesToHex,
825
64
  closePopup,
826
65
  compareBytes,
827
66
  decodeChallenge,
828
67
  getCachedPasskeyClientCapabilities,
829
68
  getPasskeyClientCapabilities,
830
- getPopupDisplayInfo,
831
69
  getResponseError,
832
70
  hexToBytes,
833
71
  isInIframe,
834
72
  isWebAuthnSupported,
835
- normalizeLowS2 as normalizeLowS,
73
+ normalizeLowS,
836
74
  normalizeSignatureComponent,
837
75
  openPasskeyPopupWindow,
838
- parseDerSignature2 as parseDerSignature,
76
+ parseDerSignature,
839
77
  preloadPasskeyClientCapabilities,
840
78
  registerPasskey,
841
79
  requestPasskeyPopup,
842
80
  shouldUsePasskeyPopup,
843
81
  signWithDiscoverablePasskey,
844
82
  signWithPasskey,
845
- signWithPreferredPasskey,
846
83
  signWithStoredPasskey,
847
84
  toPopupSigningResult,
848
85
  uniqueAccounts