@freqhole/haruspex 0.0.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.
Files changed (250) hide show
  1. package/dist/flows/add-peer/machine.d.ts +36 -0
  2. package/dist/flows/add-peer/machine.d.ts.map +1 -0
  3. package/dist/flows/add-peer/machine.js +500 -0
  4. package/dist/flows/add-peer/machine.test.d.ts +2 -0
  5. package/dist/flows/add-peer/machine.test.d.ts.map +1 -0
  6. package/dist/flows/add-peer/machine.test.js +421 -0
  7. package/dist/flows/add-peer/runner.d.ts +16 -0
  8. package/dist/flows/add-peer/runner.d.ts.map +1 -0
  9. package/dist/flows/add-peer/runner.js +349 -0
  10. package/dist/flows/add-peer/runner.test.d.ts +2 -0
  11. package/dist/flows/add-peer/runner.test.d.ts.map +1 -0
  12. package/dist/flows/add-peer/runner.test.js +205 -0
  13. package/dist/flows/add-peer/types.d.ts +271 -0
  14. package/dist/flows/add-peer/types.d.ts.map +1 -0
  15. package/dist/flows/add-peer/types.js +10 -0
  16. package/dist/flows/index.d.ts +6 -0
  17. package/dist/flows/index.d.ts.map +1 -0
  18. package/dist/flows/index.js +6 -0
  19. package/dist/identity/idbStore.d.ts +51 -0
  20. package/dist/identity/idbStore.d.ts.map +1 -0
  21. package/dist/identity/idbStore.js +183 -0
  22. package/dist/identity/idbStore.test.d.ts +2 -0
  23. package/dist/identity/idbStore.test.d.ts.map +1 -0
  24. package/dist/identity/idbStore.test.js +103 -0
  25. package/dist/identity/index.d.ts +8 -0
  26. package/dist/identity/index.d.ts.map +1 -0
  27. package/dist/identity/index.js +3 -0
  28. package/dist/identity/resolve.d.ts +31 -0
  29. package/dist/identity/resolve.d.ts.map +1 -0
  30. package/dist/identity/resolve.js +37 -0
  31. package/dist/identity/resolve.test.d.ts +2 -0
  32. package/dist/identity/resolve.test.d.ts.map +1 -0
  33. package/dist/identity/resolve.test.js +105 -0
  34. package/dist/identity/types.d.ts +21 -0
  35. package/dist/identity/types.d.ts.map +1 -0
  36. package/dist/identity/types.js +1 -0
  37. package/dist/identity/webLocks.d.ts +22 -0
  38. package/dist/identity/webLocks.d.ts.map +1 -0
  39. package/dist/identity/webLocks.js +67 -0
  40. package/dist/identity/webLocks.test.d.ts +2 -0
  41. package/dist/identity/webLocks.test.d.ts.map +1 -0
  42. package/dist/identity/webLocks.test.js +125 -0
  43. package/dist/index.d.ts +2 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +1 -0
  46. package/dist/index.test.d.ts +2 -0
  47. package/dist/index.test.d.ts.map +1 -0
  48. package/dist/index.test.js +7 -0
  49. package/dist/knock/index.d.ts +10 -0
  50. package/dist/knock/index.d.ts.map +1 -0
  51. package/dist/knock/index.js +5 -0
  52. package/dist/knock/pending-checker.d.ts +19 -0
  53. package/dist/knock/pending-checker.d.ts.map +1 -0
  54. package/dist/knock/pending-checker.js +27 -0
  55. package/dist/knock/pending-checker.test.d.ts +2 -0
  56. package/dist/knock/pending-checker.test.d.ts.map +1 -0
  57. package/dist/knock/pending-checker.test.js +93 -0
  58. package/dist/knock/requester.d.ts +26 -0
  59. package/dist/knock/requester.d.ts.map +1 -0
  60. package/dist/knock/requester.js +72 -0
  61. package/dist/knock/requester.test.d.ts +2 -0
  62. package/dist/knock/requester.test.d.ts.map +1 -0
  63. package/dist/knock/requester.test.js +108 -0
  64. package/dist/knock/responder.d.ts +13 -0
  65. package/dist/knock/responder.d.ts.map +1 -0
  66. package/dist/knock/responder.js +40 -0
  67. package/dist/knock/responder.test.d.ts +2 -0
  68. package/dist/knock/responder.test.d.ts.map +1 -0
  69. package/dist/knock/responder.test.js +89 -0
  70. package/dist/knock/store.d.ts +11 -0
  71. package/dist/knock/store.d.ts.map +1 -0
  72. package/dist/knock/store.js +211 -0
  73. package/dist/knock/store.test.d.ts +2 -0
  74. package/dist/knock/store.test.d.ts.map +1 -0
  75. package/dist/knock/store.test.js +195 -0
  76. package/dist/knock/types.d.ts +144 -0
  77. package/dist/knock/types.d.ts.map +1 -0
  78. package/dist/knock/types.js +26 -0
  79. package/dist/permissions/access.d.ts +54 -0
  80. package/dist/permissions/access.d.ts.map +1 -0
  81. package/dist/permissions/access.js +56 -0
  82. package/dist/permissions/access.test.d.ts +2 -0
  83. package/dist/permissions/access.test.d.ts.map +1 -0
  84. package/dist/permissions/access.test.js +72 -0
  85. package/dist/permissions/index.d.ts +5 -0
  86. package/dist/permissions/index.d.ts.map +1 -0
  87. package/dist/permissions/index.js +2 -0
  88. package/dist/permissions/role-table.d.ts +11 -0
  89. package/dist/permissions/role-table.d.ts.map +1 -0
  90. package/dist/permissions/role-table.js +16 -0
  91. package/dist/protocol/client.d.ts +67 -0
  92. package/dist/protocol/client.d.ts.map +1 -0
  93. package/dist/protocol/client.js +195 -0
  94. package/dist/protocol/client.test.d.ts +2 -0
  95. package/dist/protocol/client.test.d.ts.map +1 -0
  96. package/dist/protocol/client.test.js +282 -0
  97. package/dist/protocol/codec.d.ts +45 -0
  98. package/dist/protocol/codec.d.ts.map +1 -0
  99. package/dist/protocol/codec.js +111 -0
  100. package/dist/protocol/codec.test.d.ts +2 -0
  101. package/dist/protocol/codec.test.d.ts.map +1 -0
  102. package/dist/protocol/codec.test.js +93 -0
  103. package/dist/protocol/extensions.d.ts +33 -0
  104. package/dist/protocol/extensions.d.ts.map +1 -0
  105. package/dist/protocol/extensions.js +36 -0
  106. package/dist/protocol/extensions.test.d.ts +2 -0
  107. package/dist/protocol/extensions.test.d.ts.map +1 -0
  108. package/dist/protocol/extensions.test.js +65 -0
  109. package/dist/protocol/fixtures.test.d.ts +2 -0
  110. package/dist/protocol/fixtures.test.d.ts.map +1 -0
  111. package/dist/protocol/fixtures.test.js +51 -0
  112. package/dist/protocol/index.d.ts +9 -0
  113. package/dist/protocol/index.d.ts.map +1 -0
  114. package/dist/protocol/index.js +4 -0
  115. package/dist/protocol/messages.d.ts +480 -0
  116. package/dist/protocol/messages.d.ts.map +1 -0
  117. package/dist/protocol/messages.js +255 -0
  118. package/dist/protocol/messages.test.d.ts +2 -0
  119. package/dist/protocol/messages.test.d.ts.map +1 -0
  120. package/dist/protocol/messages.test.js +75 -0
  121. package/dist/share/codec.d.ts +58 -0
  122. package/dist/share/codec.d.ts.map +1 -0
  123. package/dist/share/codec.js +222 -0
  124. package/dist/share/codec.test.d.ts +2 -0
  125. package/dist/share/codec.test.d.ts.map +1 -0
  126. package/dist/share/codec.test.js +150 -0
  127. package/dist/share/detect.d.ts +24 -0
  128. package/dist/share/detect.d.ts.map +1 -0
  129. package/dist/share/detect.js +25 -0
  130. package/dist/share/detect.test.d.ts +2 -0
  131. package/dist/share/detect.test.d.ts.map +1 -0
  132. package/dist/share/detect.test.js +31 -0
  133. package/dist/share/index.d.ts +7 -0
  134. package/dist/share/index.d.ts.map +1 -0
  135. package/dist/share/index.js +3 -0
  136. package/dist/share/peer-addr.d.ts +32 -0
  137. package/dist/share/peer-addr.d.ts.map +1 -0
  138. package/dist/share/peer-addr.js +72 -0
  139. package/dist/share/peer-addr.test.d.ts +2 -0
  140. package/dist/share/peer-addr.test.d.ts.map +1 -0
  141. package/dist/share/peer-addr.test.js +58 -0
  142. package/dist/solid/auth-form.d.ts +43 -0
  143. package/dist/solid/auth-form.d.ts.map +1 -0
  144. package/dist/solid/auth-form.js +55 -0
  145. package/dist/solid/auth-form.test.d.ts +2 -0
  146. package/dist/solid/auth-form.test.d.ts.map +1 -0
  147. package/dist/solid/auth-form.test.js +131 -0
  148. package/dist/solid/auth-status.d.ts +21 -0
  149. package/dist/solid/auth-status.d.ts.map +1 -0
  150. package/dist/solid/auth-status.js +58 -0
  151. package/dist/solid/auth-status.test.d.ts +2 -0
  152. package/dist/solid/auth-status.test.d.ts.map +1 -0
  153. package/dist/solid/auth-status.test.js +60 -0
  154. package/dist/solid/index.d.ts +5 -0
  155. package/dist/solid/index.d.ts.map +1 -0
  156. package/dist/solid/index.js +10 -0
  157. package/dist/solid/knock-inbox.d.ts +61 -0
  158. package/dist/solid/knock-inbox.d.ts.map +1 -0
  159. package/dist/solid/knock-inbox.js +123 -0
  160. package/dist/solid/knock-inbox.test.d.ts +2 -0
  161. package/dist/solid/knock-inbox.test.d.ts.map +1 -0
  162. package/dist/solid/knock-inbox.test.js +153 -0
  163. package/dist/solid/reauth-flow.d.ts +26 -0
  164. package/dist/solid/reauth-flow.d.ts.map +1 -0
  165. package/dist/solid/reauth-flow.js +34 -0
  166. package/dist/solid/reauth-flow.test.d.ts +2 -0
  167. package/dist/solid/reauth-flow.test.d.ts.map +1 -0
  168. package/dist/solid/reauth-flow.test.js +91 -0
  169. package/dist/state/endpoint-control.d.ts +22 -0
  170. package/dist/state/endpoint-control.d.ts.map +1 -0
  171. package/dist/state/endpoint-control.js +34 -0
  172. package/dist/state/endpoint-control.test.d.ts +2 -0
  173. package/dist/state/endpoint-control.test.d.ts.map +1 -0
  174. package/dist/state/endpoint-control.test.js +63 -0
  175. package/dist/state/friend-directory.d.ts +37 -0
  176. package/dist/state/friend-directory.d.ts.map +1 -0
  177. package/dist/state/friend-directory.js +44 -0
  178. package/dist/state/friend-directory.test.d.ts +2 -0
  179. package/dist/state/friend-directory.test.d.ts.map +1 -0
  180. package/dist/state/friend-directory.test.js +63 -0
  181. package/dist/state/index.d.ts +6 -0
  182. package/dist/state/index.d.ts.map +1 -0
  183. package/dist/state/index.js +3 -0
  184. package/dist/state/peer-names.d.ts +7 -0
  185. package/dist/state/peer-names.d.ts.map +1 -0
  186. package/dist/state/peer-names.js +21 -0
  187. package/dist/state/peer-names.test.d.ts +2 -0
  188. package/dist/state/peer-names.test.d.ts.map +1 -0
  189. package/dist/state/peer-names.test.js +29 -0
  190. package/dist/testing/idb-harness.d.ts +9 -0
  191. package/dist/testing/idb-harness.d.ts.map +1 -0
  192. package/dist/testing/idb-harness.js +19 -0
  193. package/dist/testing/idb-harness.test.d.ts +2 -0
  194. package/dist/testing/idb-harness.test.d.ts.map +1 -0
  195. package/dist/testing/idb-harness.test.js +18 -0
  196. package/dist/testing/identity-fixtures.d.ts +10 -0
  197. package/dist/testing/identity-fixtures.d.ts.map +1 -0
  198. package/dist/testing/identity-fixtures.js +21 -0
  199. package/dist/testing/identity-fixtures.test.d.ts +2 -0
  200. package/dist/testing/identity-fixtures.test.d.ts.map +1 -0
  201. package/dist/testing/identity-fixtures.test.js +28 -0
  202. package/dist/testing/index.d.ts +7 -0
  203. package/dist/testing/index.d.ts.map +1 -0
  204. package/dist/testing/index.js +11 -0
  205. package/dist/testing/knock-fixtures.d.ts +13 -0
  206. package/dist/testing/knock-fixtures.d.ts.map +1 -0
  207. package/dist/testing/knock-fixtures.js +45 -0
  208. package/dist/testing/knock-fixtures.test.d.ts +2 -0
  209. package/dist/testing/knock-fixtures.test.d.ts.map +1 -0
  210. package/dist/testing/knock-fixtures.test.js +65 -0
  211. package/dist/testing/protocol-fixtures.d.ts +7 -0
  212. package/dist/testing/protocol-fixtures.d.ts.map +1 -0
  213. package/dist/testing/protocol-fixtures.js +28 -0
  214. package/dist/testing/protocol-fixtures.test.d.ts +2 -0
  215. package/dist/testing/protocol-fixtures.test.d.ts.map +1 -0
  216. package/dist/testing/protocol-fixtures.test.js +19 -0
  217. package/dist/testing/state-fixtures.d.ts +33 -0
  218. package/dist/testing/state-fixtures.d.ts.map +1 -0
  219. package/dist/testing/state-fixtures.js +67 -0
  220. package/dist/testing/state-fixtures.test.d.ts +2 -0
  221. package/dist/testing/state-fixtures.test.d.ts.map +1 -0
  222. package/dist/testing/state-fixtures.test.js +68 -0
  223. package/dist/testing/webauthn-mocks.d.ts +27 -0
  224. package/dist/testing/webauthn-mocks.d.ts.map +1 -0
  225. package/dist/testing/webauthn-mocks.js +78 -0
  226. package/dist/testing/webauthn-mocks.test.d.ts +2 -0
  227. package/dist/testing/webauthn-mocks.test.d.ts.map +1 -0
  228. package/dist/testing/webauthn-mocks.test.js +40 -0
  229. package/dist/webauthn/ceremony.d.ts +76 -0
  230. package/dist/webauthn/ceremony.d.ts.map +1 -0
  231. package/dist/webauthn/ceremony.js +136 -0
  232. package/dist/webauthn/ceremony.test.d.ts +2 -0
  233. package/dist/webauthn/ceremony.test.d.ts.map +1 -0
  234. package/dist/webauthn/ceremony.test.js +245 -0
  235. package/dist/webauthn/codec.d.ts +39 -0
  236. package/dist/webauthn/codec.d.ts.map +1 -0
  237. package/dist/webauthn/codec.js +118 -0
  238. package/dist/webauthn/codec.test.d.ts +2 -0
  239. package/dist/webauthn/codec.test.d.ts.map +1 -0
  240. package/dist/webauthn/codec.test.js +126 -0
  241. package/dist/webauthn/index.d.ts +7 -0
  242. package/dist/webauthn/index.d.ts.map +1 -0
  243. package/dist/webauthn/index.js +3 -0
  244. package/dist/webauthn/transport.d.ts +58 -0
  245. package/dist/webauthn/transport.d.ts.map +1 -0
  246. package/dist/webauthn/transport.js +6 -0
  247. package/dist/webauthn/types.d.ts +78 -0
  248. package/dist/webauthn/types.d.ts.map +1 -0
  249. package/dist/webauthn/types.js +29 -0
  250. package/package.json +83 -0
@@ -0,0 +1,76 @@
1
+ import type { AuthOutcome, WebauthnTransport, WhoamiOutcome } from "./transport.js";
2
+ export type CeremonyResult = ({
3
+ success: true;
4
+ } & AuthOutcome) | CeremonyFailure;
5
+ /**
6
+ * a failed ceremony. `error` is always a human-readable message, matching
7
+ * the shape callers already relied on. `name` and `cause` are populated
8
+ * only when the failure came from a thrown value (e.g. the browser's own
9
+ * `DOMException` from `navigator.credentials.create`/`.get`) rather than a
10
+ * transport-reported failure (`start.error`/`finish.error`, which carry no
11
+ * such name) - so callers can distinguish "user cancelled"
12
+ * (`NotAllowedError`) from "passkeys not supported" (`NotSupportedError`)
13
+ * from a plain transport error, instead of pattern-matching the message
14
+ * string.
15
+ */
16
+ export interface CeremonyFailure {
17
+ success: false;
18
+ error: string;
19
+ /** the thrown value's `name`, if it has one (e.g. a `DOMException`'s
20
+ * `NotAllowedError`/`NotSupportedError`, or `Error`'s own `name`). */
21
+ name?: string;
22
+ /** the original thrown value, for callers that need more than `name`. */
23
+ cause?: unknown;
24
+ }
25
+ /** creates a new passkey credential; defaults to `navigator.credentials.create`. */
26
+ export type CreateCredentialFn = (options: CredentialCreationOptions) => Promise<PublicKeyCredential | null>;
27
+ /** asserts an existing passkey credential; defaults to `navigator.credentials.get`. */
28
+ export type GetCredentialFn = (options: CredentialRequestOptions) => Promise<PublicKeyCredential | null>;
29
+ export interface PasskeyCeremonyDeps {
30
+ createCredential?: CreateCredentialFn;
31
+ getCredential?: GetCredentialFn;
32
+ /** the origin reported to the server; defaults to `window.location.origin`. */
33
+ origin?: string;
34
+ }
35
+ export interface RegisterPasskeyArgs {
36
+ username: string;
37
+ /** required unless the caller already has an authenticated device link
38
+ * to the target identity. */
39
+ inviteCode?: string;
40
+ }
41
+ /**
42
+ * register a new passkey: start the ceremony, prompt the browser to create
43
+ * a credential, then finish it.
44
+ *
45
+ * the p2p/http transport difference (nonce threading vs a session cookie)
46
+ * lives entirely in the injected `transport` - this function's logic is the
47
+ * same either way.
48
+ */
49
+ export declare function registerWithPasskey(transport: WebauthnTransport, args: RegisterPasskeyArgs, deps?: PasskeyCeremonyDeps): Promise<CeremonyResult>;
50
+ export interface LoginPasskeyArgs {
51
+ /** omit for the discoverable-credential flow. */
52
+ username?: string;
53
+ }
54
+ /**
55
+ * authenticate with an existing passkey: start the ceremony, prompt the
56
+ * browser for an assertion, then finish it.
57
+ */
58
+ export declare function loginWithPasskey(transport: WebauthnTransport, args?: LoginPasskeyArgs, deps?: PasskeyCeremonyDeps): Promise<CeremonyResult>;
59
+ export interface AuthenticateArgs {
60
+ username: string;
61
+ inviteCode?: string;
62
+ mode: "login" | "register";
63
+ }
64
+ /** unified login-or-register dispatch, driven by `args.mode`. */
65
+ export declare function authenticate(transport: WebauthnTransport, args: AuthenticateArgs, deps?: PasskeyCeremonyDeps): Promise<CeremonyResult>;
66
+ /**
67
+ * check whether the caller is already authenticated to a remote.
68
+ *
69
+ * unlike the register/login ceremonies above, this does not swallow
70
+ * transport errors - callers need to distinguish "not authenticated"
71
+ * (`{ authenticated: false }`) from "the transport itself isn't ready yet"
72
+ * (a thrown error), so they can retry the latter instead of treating it as
73
+ * a login prompt.
74
+ */
75
+ export declare function whoami(transport: WebauthnTransport): Promise<WhoamiOutcome>;
76
+ //# sourceMappingURL=ceremony.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ceremony.d.ts","sourceRoot":"","sources":["../../src/webauthn/ceremony.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,aAAa,EACd,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,cAAc,GACtB,CAAC;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG,WAAW,CAAC,GACjC,eAAe,CAAC;AAEpB;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd;2EACuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAmBD,oFAAoF;AACpF,MAAM,MAAM,kBAAkB,GAAG,CAC/B,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;AAEzC,uFAAuF;AACvF,MAAM,MAAM,eAAe,GAAG,CAC5B,OAAO,EAAE,wBAAwB,KAC9B,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;AAEzC,MAAM,WAAW,mBAAmB;IAClC,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;IACtC,aAAa,CAAC,EAAE,eAAe,CAAC;IAChC,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA8BD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB;kCAC8B;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,iBAAiB,EAC5B,IAAI,EAAE,mBAAmB,EACzB,IAAI,GAAE,mBAAwB,GAC7B,OAAO,CAAC,cAAc,CAAC,CA8BzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,iBAAiB,EAC5B,IAAI,GAAE,gBAAqB,EAC3B,IAAI,GAAE,mBAAwB,GAC7B,OAAO,CAAC,cAAc,CAAC,CA6BzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,OAAO,GAAG,UAAU,CAAC;CAC5B;AAED,iEAAiE;AACjE,wBAAsB,YAAY,CAChC,SAAS,EAAE,iBAAiB,EAC5B,IAAI,EAAE,gBAAgB,EACtB,IAAI,GAAE,mBAAwB,GAC7B,OAAO,CAAC,cAAc,CAAC,CAezB;AAED;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC,CAE3E"}
@@ -0,0 +1,136 @@
1
+ // transport-injected ceremony runners: the pure register/login logic that
2
+ // used to be duplicated per transport (http session cookies vs p2p nonces)
3
+ // now runs once against the WebauthnTransport interface.
4
+ import { prepareAuthenticationOptions, prepareRegistrationOptions, serializeAuthenticationCredential, serializeRegistrationCredential, } from "./codec.js";
5
+ /** builds a `CeremonyFailure` from a value thrown by a browser api call
6
+ * (`navigator.credentials.create`/`.get`), preserving its `name`/`message`
7
+ * when present instead of collapsing every failure into one string. */
8
+ function ceremonyFailureFromThrown(err, fallbackMessage) {
9
+ if (err && typeof err === "object") {
10
+ const record = err;
11
+ const name = typeof record.name === "string" ? record.name : undefined;
12
+ const message = typeof record.message === "string" ? record.message : fallbackMessage;
13
+ return { success: false, error: message, name, cause: err };
14
+ }
15
+ return { success: false, error: fallbackMessage, cause: err };
16
+ }
17
+ function resolveOrigin(deps) {
18
+ if (deps.origin)
19
+ return deps.origin;
20
+ if (typeof window !== "undefined" && window.location) {
21
+ return window.location.origin;
22
+ }
23
+ throw new Error("no origin available outside a browser context - pass deps.origin");
24
+ }
25
+ function resolveCreateCredential(deps) {
26
+ if (deps.createCredential)
27
+ return deps.createCredential;
28
+ if (typeof navigator === "undefined" || !navigator.credentials) {
29
+ throw new Error("webauthn is not available in this environment");
30
+ }
31
+ return (options) => navigator.credentials.create(options);
32
+ }
33
+ function resolveGetCredential(deps) {
34
+ if (deps.getCredential)
35
+ return deps.getCredential;
36
+ if (typeof navigator === "undefined" || !navigator.credentials) {
37
+ throw new Error("webauthn is not available in this environment");
38
+ }
39
+ return (options) => navigator.credentials.get(options);
40
+ }
41
+ /**
42
+ * register a new passkey: start the ceremony, prompt the browser to create
43
+ * a credential, then finish it.
44
+ *
45
+ * the p2p/http transport difference (nonce threading vs a session cookie)
46
+ * lives entirely in the injected `transport` - this function's logic is the
47
+ * same either way.
48
+ */
49
+ export async function registerWithPasskey(transport, args, deps = {}) {
50
+ try {
51
+ const origin = resolveOrigin(deps);
52
+ const createCredential = resolveCreateCredential(deps);
53
+ const start = await transport.registerStart({
54
+ username: args.username,
55
+ origin,
56
+ inviteCode: args.inviteCode,
57
+ });
58
+ if (!start.success)
59
+ return { success: false, error: start.error };
60
+ const options = prepareRegistrationOptions(start.data.challenge);
61
+ const credential = await createCredential(options);
62
+ if (!credential) {
63
+ return { success: false, error: "browser did not return a credential" };
64
+ }
65
+ const serialized = serializeRegistrationCredential(credential);
66
+ const finish = await transport.registerFinish({
67
+ nonce: start.data.nonce,
68
+ origin,
69
+ credential: serialized,
70
+ });
71
+ if (!finish.success)
72
+ return { success: false, error: finish.error };
73
+ return { success: true, ...finish.data };
74
+ }
75
+ catch (err) {
76
+ return ceremonyFailureFromThrown(err, "registration failed");
77
+ }
78
+ }
79
+ /**
80
+ * authenticate with an existing passkey: start the ceremony, prompt the
81
+ * browser for an assertion, then finish it.
82
+ */
83
+ export async function loginWithPasskey(transport, args = {}, deps = {}) {
84
+ try {
85
+ const origin = resolveOrigin(deps);
86
+ const getCredential = resolveGetCredential(deps);
87
+ const start = await transport.loginStart({
88
+ username: args.username,
89
+ origin,
90
+ });
91
+ if (!start.success)
92
+ return { success: false, error: start.error };
93
+ const options = prepareAuthenticationOptions(start.data.challenge);
94
+ const credential = await getCredential(options);
95
+ if (!credential) {
96
+ return { success: false, error: "browser did not return a credential" };
97
+ }
98
+ const serialized = serializeAuthenticationCredential(credential);
99
+ const finish = await transport.loginFinish({
100
+ nonce: start.data.nonce,
101
+ origin,
102
+ credential: serialized,
103
+ });
104
+ if (!finish.success)
105
+ return { success: false, error: finish.error };
106
+ return { success: true, ...finish.data };
107
+ }
108
+ catch (err) {
109
+ return ceremonyFailureFromThrown(err, "login failed");
110
+ }
111
+ }
112
+ /** unified login-or-register dispatch, driven by `args.mode`. */
113
+ export async function authenticate(transport, args, deps = {}) {
114
+ if (args.mode === "register") {
115
+ if (!args.inviteCode) {
116
+ return {
117
+ success: false,
118
+ error: "invite code required for registration",
119
+ };
120
+ }
121
+ return registerWithPasskey(transport, { username: args.username, inviteCode: args.inviteCode }, deps);
122
+ }
123
+ return loginWithPasskey(transport, { username: args.username }, deps);
124
+ }
125
+ /**
126
+ * check whether the caller is already authenticated to a remote.
127
+ *
128
+ * unlike the register/login ceremonies above, this does not swallow
129
+ * transport errors - callers need to distinguish "not authenticated"
130
+ * (`{ authenticated: false }`) from "the transport itself isn't ready yet"
131
+ * (a thrown error), so they can retry the latter instead of treating it as
132
+ * a login prompt.
133
+ */
134
+ export function whoami(transport) {
135
+ return transport.whoami();
136
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ceremony.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ceremony.test.d.ts","sourceRoot":"","sources":["../../src/webauthn/ceremony.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,245 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { authenticate, loginWithPasskey, registerWithPasskey, whoami, } from "./ceremony.js";
3
+ const REGISTRATION_CHALLENGE = {
4
+ publicKey: {
5
+ rp: { name: "freqhole" },
6
+ user: { id: "AA", name: "alice", displayName: "Alice" },
7
+ challenge: "AQ",
8
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
9
+ },
10
+ };
11
+ const AUTHENTICATION_CHALLENGE = {
12
+ publicKey: { challenge: "AQ" },
13
+ };
14
+ const FAKE_CREDENTIAL = {
15
+ id: "cred-id",
16
+ rawId: new Uint8Array([1]).buffer,
17
+ response: {},
18
+ };
19
+ /** a scriptable stand-in for a real http/p2p webauthn transport. */
20
+ function fakeTransport(overrides = {}) {
21
+ return {
22
+ registerStart: vi.fn(async (_req) => ({
23
+ success: true,
24
+ data: { nonce: "nonce-1", challenge: REGISTRATION_CHALLENGE },
25
+ })),
26
+ registerFinish: vi.fn(async (_req) => ({
27
+ success: true,
28
+ data: { userId: "user-1", username: "alice" },
29
+ })),
30
+ loginStart: vi.fn(async (_req) => ({
31
+ success: true,
32
+ data: { nonce: "nonce-2", challenge: AUTHENTICATION_CHALLENGE },
33
+ })),
34
+ loginFinish: vi.fn(async (_req) => ({
35
+ success: true,
36
+ data: { userId: "user-1", username: "alice" },
37
+ })),
38
+ whoami: vi.fn(async () => ({ authenticated: false })),
39
+ ...overrides,
40
+ };
41
+ }
42
+ const deps = {
43
+ origin: "https://example.test",
44
+ createCredential: vi.fn(async () => FAKE_CREDENTIAL),
45
+ getCredential: vi.fn(async () => FAKE_CREDENTIAL),
46
+ };
47
+ describe("registerWithPasskey", () => {
48
+ it("runs start -> browser create -> finish and returns the outcome", async () => {
49
+ const transport = fakeTransport();
50
+ const createCredential = vi.fn(async () => FAKE_CREDENTIAL);
51
+ const result = await registerWithPasskey(transport, { username: "alice", inviteCode: "invite-1" }, { origin: deps.origin, createCredential });
52
+ expect(result).toEqual({
53
+ success: true,
54
+ userId: "user-1",
55
+ username: "alice",
56
+ });
57
+ expect(transport.registerStart).toHaveBeenCalledWith({
58
+ username: "alice",
59
+ origin: deps.origin,
60
+ inviteCode: "invite-1",
61
+ });
62
+ expect(createCredential).toHaveBeenCalledOnce();
63
+ expect(transport.registerFinish).toHaveBeenCalledWith({
64
+ nonce: "nonce-1",
65
+ origin: deps.origin,
66
+ credential: expect.objectContaining({ id: "cred-id" }),
67
+ });
68
+ });
69
+ it("propagates a registerStart failure without calling the browser", async () => {
70
+ const createCredential = vi.fn(async () => FAKE_CREDENTIAL);
71
+ const transport = fakeTransport({
72
+ registerStart: vi.fn(async () => ({
73
+ success: false,
74
+ error: "username taken",
75
+ })),
76
+ });
77
+ const result = await registerWithPasskey(transport, { username: "alice" }, { origin: deps.origin, createCredential });
78
+ expect(result).toEqual({ success: false, error: "username taken" });
79
+ expect(createCredential).not.toHaveBeenCalled();
80
+ });
81
+ it("fails when the browser returns no credential", async () => {
82
+ const transport = fakeTransport();
83
+ const result = await registerWithPasskey(transport, { username: "alice" }, { origin: deps.origin, createCredential: vi.fn(async () => null) });
84
+ expect(result).toEqual({
85
+ success: false,
86
+ error: "browser did not return a credential",
87
+ });
88
+ expect(transport.registerFinish).not.toHaveBeenCalled();
89
+ });
90
+ it("propagates a registerFinish failure", async () => {
91
+ const transport = fakeTransport({
92
+ registerFinish: vi.fn(async () => ({
93
+ success: false,
94
+ error: "invalid credential",
95
+ })),
96
+ });
97
+ const result = await registerWithPasskey(transport, { username: "alice" }, { origin: deps.origin, createCredential: vi.fn(async () => FAKE_CREDENTIAL) });
98
+ expect(result).toEqual({ success: false, error: "invalid credential" });
99
+ });
100
+ it("turns a thrown DOMException into a failure result carrying its name and itself as cause", async () => {
101
+ const transport = fakeTransport();
102
+ const thrown = new DOMException("user cancelled", "NotAllowedError");
103
+ const createCredential = vi.fn(async () => {
104
+ throw thrown;
105
+ });
106
+ const result = await registerWithPasskey(transport, { username: "alice" }, { origin: deps.origin, createCredential });
107
+ expect(result).toEqual({
108
+ success: false,
109
+ error: "user cancelled",
110
+ name: "NotAllowedError",
111
+ cause: thrown,
112
+ });
113
+ });
114
+ it("turns a thrown plain Error into a failure result carrying its name and itself as cause", async () => {
115
+ const transport = fakeTransport();
116
+ const thrown = new Error("boom");
117
+ const createCredential = vi.fn(async () => {
118
+ throw thrown;
119
+ });
120
+ const result = await registerWithPasskey(transport, { username: "alice" }, { origin: deps.origin, createCredential });
121
+ expect(result).toEqual({
122
+ success: false,
123
+ error: "boom",
124
+ name: "Error",
125
+ cause: thrown,
126
+ });
127
+ });
128
+ it("falls back to a generic message and no name for a non-error thrown value", async () => {
129
+ const transport = fakeTransport();
130
+ const createCredential = vi.fn(async () => {
131
+ // eslint-disable-next-line @typescript-eslint/only-throw-error
132
+ throw "nope";
133
+ });
134
+ const result = await registerWithPasskey(transport, { username: "alice" }, { origin: deps.origin, createCredential });
135
+ expect(result).toEqual({
136
+ success: false,
137
+ error: "registration failed",
138
+ cause: "nope",
139
+ });
140
+ });
141
+ });
142
+ describe("loginWithPasskey", () => {
143
+ it("runs start -> browser get -> finish and returns the outcome", async () => {
144
+ const transport = fakeTransport();
145
+ const getCredential = vi.fn(async () => FAKE_CREDENTIAL);
146
+ const result = await loginWithPasskey(transport, { username: "alice" }, { origin: deps.origin, getCredential });
147
+ expect(result).toEqual({
148
+ success: true,
149
+ userId: "user-1",
150
+ username: "alice",
151
+ });
152
+ expect(transport.loginStart).toHaveBeenCalledWith({
153
+ username: "alice",
154
+ origin: deps.origin,
155
+ });
156
+ expect(transport.loginFinish).toHaveBeenCalledWith({
157
+ nonce: "nonce-2",
158
+ origin: deps.origin,
159
+ credential: expect.objectContaining({ id: "cred-id" }),
160
+ });
161
+ });
162
+ it("supports the discoverable flow (no username)", async () => {
163
+ const transport = fakeTransport();
164
+ await loginWithPasskey(transport, {}, { origin: deps.origin, getCredential: vi.fn(async () => FAKE_CREDENTIAL) });
165
+ expect(transport.loginStart).toHaveBeenCalledWith({
166
+ username: undefined,
167
+ origin: deps.origin,
168
+ });
169
+ });
170
+ it("fails when the browser returns no assertion", async () => {
171
+ const transport = fakeTransport();
172
+ const result = await loginWithPasskey(transport, { username: "alice" }, { origin: deps.origin, getCredential: vi.fn(async () => null) });
173
+ expect(result).toEqual({
174
+ success: false,
175
+ error: "browser did not return a credential",
176
+ });
177
+ });
178
+ it("turns a thrown DOMException into a failure result carrying its name and itself as cause", async () => {
179
+ const transport = fakeTransport();
180
+ const thrown = new DOMException("no authenticator available", "NotSupportedError");
181
+ const getCredential = vi.fn(async () => {
182
+ throw thrown;
183
+ });
184
+ const result = await loginWithPasskey(transport, { username: "alice" }, { origin: deps.origin, getCredential });
185
+ expect(result).toEqual({
186
+ success: false,
187
+ error: "no authenticator available",
188
+ name: "NotSupportedError",
189
+ cause: thrown,
190
+ });
191
+ });
192
+ });
193
+ describe("authenticate", () => {
194
+ it("dispatches to registerWithPasskey in register mode", async () => {
195
+ const transport = fakeTransport();
196
+ const result = await authenticate(transport, { username: "alice", inviteCode: "invite-1", mode: "register" }, { origin: deps.origin, createCredential: vi.fn(async () => FAKE_CREDENTIAL) });
197
+ expect(result.success).toBe(true);
198
+ expect(transport.registerStart).toHaveBeenCalled();
199
+ });
200
+ it("requires an invite code for register mode", async () => {
201
+ const transport = fakeTransport();
202
+ const result = await authenticate(transport, {
203
+ username: "alice",
204
+ mode: "register",
205
+ });
206
+ expect(result).toEqual({
207
+ success: false,
208
+ error: "invite code required for registration",
209
+ });
210
+ expect(transport.registerStart).not.toHaveBeenCalled();
211
+ });
212
+ it("dispatches to loginWithPasskey in login mode", async () => {
213
+ const transport = fakeTransport();
214
+ const result = await authenticate(transport, { username: "alice", mode: "login" }, { origin: deps.origin, getCredential: vi.fn(async () => FAKE_CREDENTIAL) });
215
+ expect(result.success).toBe(true);
216
+ expect(transport.loginStart).toHaveBeenCalled();
217
+ });
218
+ });
219
+ describe("whoami", () => {
220
+ it("passes the transport's outcome straight through", async () => {
221
+ const transport = fakeTransport({
222
+ whoami: vi.fn(async () => ({
223
+ authenticated: true,
224
+ userId: "user-1",
225
+ username: "alice",
226
+ role: "admin",
227
+ })),
228
+ });
229
+ const result = await whoami(transport);
230
+ expect(result).toEqual({
231
+ authenticated: true,
232
+ userId: "user-1",
233
+ username: "alice",
234
+ role: "admin",
235
+ });
236
+ });
237
+ it("does not swallow a transport error - it propagates for the caller to retry", async () => {
238
+ const transport = fakeTransport({
239
+ whoami: vi.fn(async () => {
240
+ throw new Error("p2p transport not warm yet");
241
+ }),
242
+ });
243
+ await expect(whoami(transport)).rejects.toThrow("p2p transport not warm yet");
244
+ });
245
+ });
@@ -0,0 +1,39 @@
1
+ import { type AuthenticationChallenge, type AuthenticationPublicKeyCredential, type RegisterPublicKeyCredential, type RegistrationChallenge } from "./types.js";
2
+ /**
3
+ * convert a base64url string to a Uint8Array.
4
+ * handles both standard base64 and base64url encoding.
5
+ */
6
+ export declare function base64ToUint8Array(base64: string): Uint8Array<ArrayBuffer>;
7
+ /**
8
+ * convert a Uint8Array to a base64url string (no padding, `-` instead of
9
+ * `+`, `_` instead of `/`).
10
+ */
11
+ export declare function uint8ArrayToBase64(uint8Array: Uint8Array): string;
12
+ /** convert an ArrayBuffer to a base64url string. */
13
+ export declare function arrayBufferToBase64(buffer: ArrayBuffer): string;
14
+ /**
15
+ * convert the server's registration challenge into browser-compatible
16
+ * options for `navigator.credentials.create()`, converting base64url
17
+ * fields to the raw bytes the browser api expects.
18
+ */
19
+ export declare function prepareRegistrationOptions(challenge: RegistrationChallenge): CredentialCreationOptions;
20
+ /**
21
+ * convert the server's authentication challenge into browser-compatible
22
+ * options for `navigator.credentials.get()`.
23
+ */
24
+ export declare function prepareAuthenticationOptions(challenge: AuthenticationChallenge): CredentialRequestOptions;
25
+ /**
26
+ * convert a browser registration credential into the server-compatible
27
+ * wire format for a `register_finish` call, validating the result against
28
+ * the schema.
29
+ */
30
+ export declare function serializeRegistrationCredential(credential: PublicKeyCredential): RegisterPublicKeyCredential;
31
+ /**
32
+ * convert a browser authentication assertion into the server-compatible
33
+ * wire format for a `login_finish` call, validating the result against the
34
+ * schema.
35
+ */
36
+ export declare function serializeAuthenticationCredential(credential: PublicKeyCredential): AuthenticationPublicKeyCredential;
37
+ /** check whether the webauthn create() api is available in this context. */
38
+ export declare function isWebAuthnAvailable(): boolean;
39
+ //# sourceMappingURL=codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/webauthn/codec.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,uBAAuB,EAC5B,KAAK,iCAAiC,EACtC,KAAK,2BAA2B,EAChC,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAQ1E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CASjE;AAED,oDAAoD;AACpD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAE/D;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,qBAAqB,GAC/B,yBAAyB,CAgB3B;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,uBAAuB,GACjC,wBAAwB,CAY1B;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,mBAAmB,GAC9B,2BAA2B,CAW7B;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC/C,UAAU,EAAE,mBAAmB,GAC9B,iCAAiC,CAenC;AAED,4EAA4E;AAC5E,wBAAgB,mBAAmB,IAAI,OAAO,CAM7C"}
@@ -0,0 +1,118 @@
1
+ // conversion between the server's wire format (base64url strings) and the
2
+ // browser webauthn api's format (Uint8Array/ArrayBuffer), plus the
3
+ // serialization of a completed ceremony back into the wire format.
4
+ import { AuthenticationPublicKeyCredentialSchema, RegisterPublicKeyCredentialSchema, } from "./types.js";
5
+ /**
6
+ * convert a base64url string to a Uint8Array.
7
+ * handles both standard base64 and base64url encoding.
8
+ */
9
+ export function base64ToUint8Array(base64) {
10
+ const standardBase64 = base64.replace(/-/g, "+").replace(/_/g, "/");
11
+ const binaryString = atob(standardBase64);
12
+ const bytes = new Uint8Array(binaryString.length);
13
+ for (let i = 0; i < binaryString.length; i++) {
14
+ bytes[i] = binaryString.charCodeAt(i);
15
+ }
16
+ return bytes;
17
+ }
18
+ /**
19
+ * convert a Uint8Array to a base64url string (no padding, `-` instead of
20
+ * `+`, `_` instead of `/`).
21
+ */
22
+ export function uint8ArrayToBase64(uint8Array) {
23
+ let binaryString = "";
24
+ for (let i = 0; i < uint8Array.length; i++) {
25
+ binaryString += String.fromCharCode(uint8Array[i]);
26
+ }
27
+ return btoa(binaryString)
28
+ .replace(/\+/g, "-")
29
+ .replace(/\//g, "_")
30
+ .replace(/=/g, "");
31
+ }
32
+ /** convert an ArrayBuffer to a base64url string. */
33
+ export function arrayBufferToBase64(buffer) {
34
+ return uint8ArrayToBase64(new Uint8Array(buffer));
35
+ }
36
+ /**
37
+ * convert the server's registration challenge into browser-compatible
38
+ * options for `navigator.credentials.create()`, converting base64url
39
+ * fields to the raw bytes the browser api expects.
40
+ */
41
+ export function prepareRegistrationOptions(challenge) {
42
+ const { publicKey } = challenge;
43
+ return {
44
+ publicKey: {
45
+ ...publicKey,
46
+ challenge: base64ToUint8Array(publicKey.challenge),
47
+ user: {
48
+ ...publicKey.user,
49
+ id: base64ToUint8Array(publicKey.user.id),
50
+ },
51
+ excludeCredentials: publicKey.excludeCredentials?.map((cred) => ({
52
+ ...cred,
53
+ id: base64ToUint8Array(cred.id),
54
+ })),
55
+ },
56
+ };
57
+ }
58
+ /**
59
+ * convert the server's authentication challenge into browser-compatible
60
+ * options for `navigator.credentials.get()`.
61
+ */
62
+ export function prepareAuthenticationOptions(challenge) {
63
+ const { publicKey } = challenge;
64
+ return {
65
+ publicKey: {
66
+ ...publicKey,
67
+ challenge: base64ToUint8Array(publicKey.challenge),
68
+ allowCredentials: publicKey.allowCredentials?.map((cred) => ({
69
+ ...cred,
70
+ id: base64ToUint8Array(cred.id),
71
+ })),
72
+ },
73
+ };
74
+ }
75
+ /**
76
+ * convert a browser registration credential into the server-compatible
77
+ * wire format for a `register_finish` call, validating the result against
78
+ * the schema.
79
+ */
80
+ export function serializeRegistrationCredential(credential) {
81
+ const response = credential.response;
82
+ return RegisterPublicKeyCredentialSchema.parse({
83
+ id: credential.id,
84
+ rawId: arrayBufferToBase64(credential.rawId),
85
+ type: "public-key",
86
+ response: {
87
+ attestationObject: arrayBufferToBase64(response.attestationObject),
88
+ clientDataJSON: arrayBufferToBase64(credential.response.clientDataJSON),
89
+ },
90
+ });
91
+ }
92
+ /**
93
+ * convert a browser authentication assertion into the server-compatible
94
+ * wire format for a `login_finish` call, validating the result against the
95
+ * schema.
96
+ */
97
+ export function serializeAuthenticationCredential(credential) {
98
+ const response = credential.response;
99
+ return AuthenticationPublicKeyCredentialSchema.parse({
100
+ id: credential.id,
101
+ rawId: arrayBufferToBase64(credential.rawId),
102
+ type: "public-key",
103
+ response: {
104
+ authenticatorData: arrayBufferToBase64(response.authenticatorData),
105
+ clientDataJSON: arrayBufferToBase64(credential.response.clientDataJSON),
106
+ signature: arrayBufferToBase64(response.signature),
107
+ userHandle: response.userHandle
108
+ ? arrayBufferToBase64(response.userHandle)
109
+ : undefined,
110
+ },
111
+ });
112
+ }
113
+ /** check whether the webauthn create() api is available in this context. */
114
+ export function isWebAuthnAvailable() {
115
+ return (typeof navigator !== "undefined" &&
116
+ typeof navigator.credentials !== "undefined" &&
117
+ typeof navigator.credentials.create === "function");
118
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=codec.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.test.d.ts","sourceRoot":"","sources":["../../src/webauthn/codec.test.ts"],"names":[],"mappings":""}