@tempoxyz/id 0.0.0-stage → 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 (712) hide show
  1. package/README.md +80 -2
  2. package/dist/App.d.ts +20 -0
  3. package/dist/App.d.ts.map +1 -0
  4. package/dist/App.js +26 -0
  5. package/dist/App.js.map +1 -0
  6. package/dist/app/client/.assetsignore +2 -0
  7. package/dist/app/client/assets/BRLA-CZvVDNYr.png +0 -0
  8. package/dist/app/client/assets/CHFAU-DCedc4aH.png +0 -0
  9. package/dist/app/client/assets/DLUSD-BnRXm3EI.png +0 -0
  10. package/dist/app/client/assets/PRIME-CiFp2-3e.svg +14 -0
  11. package/dist/app/client/assets/Pilat-Bold-C1t1eIrF.woff2 +0 -0
  12. package/dist/app/client/assets/Pilat-Book-CbS8ofT8.woff2 +0 -0
  13. package/dist/app/client/assets/Pilat-Demi-CADSbPAt.woff2 +0 -0
  14. package/dist/app/client/assets/Pilat-Regular-Bx8vzE7q.woff2 +0 -0
  15. package/dist/app/client/assets/USD1-COVcO_PB.svg +30 -0
  16. package/dist/app/client/assets/YLDS-CRh8Zw0g.svg +3 -0
  17. package/dist/app/client/assets/ccip-Cq1mMiVl.js +1 -0
  18. package/dist/app/client/assets/frxUSD-n9pTpinR.svg +1 -0
  19. package/dist/app/client/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  20. package/dist/app/client/assets/index-B7YJVP00.css +1 -0
  21. package/dist/app/client/assets/index-UXTxXCLk.js +55 -0
  22. package/dist/app/client/assets/pathUSD-BvaO6ooS.svg +25 -0
  23. package/dist/app/client/assets/reset-co-CHrff.css +1 -0
  24. package/dist/app/client/assets/wYLDS-CaCKDrO-.svg +10 -0
  25. package/dist/app/db/auth.d.ts +3207 -0
  26. package/dist/app/db/auth.d.ts.map +1 -0
  27. package/dist/app/db/auth.js +330 -0
  28. package/dist/app/db/auth.js.map +1 -0
  29. package/dist/app/db/schema.d.ts +932 -0
  30. package/dist/app/db/schema.d.ts.map +1 -0
  31. package/dist/app/db/schema.js +171 -0
  32. package/dist/app/db/schema.js.map +1 -0
  33. package/dist/app/server/.vite/manifest.json +489 -0
  34. package/dist/app/server/assets/BRLA-CZvVDNYr.png +0 -0
  35. package/dist/app/server/assets/CHFAU-DCedc4aH.png +0 -0
  36. package/dist/app/server/assets/DLUSD-BnRXm3EI.png +0 -0
  37. package/dist/app/server/assets/PRIME-CiFp2-3e.svg +14 -0
  38. package/dist/app/server/assets/Pilat-Bold-C1t1eIrF.woff2 +0 -0
  39. package/dist/app/server/assets/Pilat-Book-CbS8ofT8.woff2 +0 -0
  40. package/dist/app/server/assets/Pilat-Demi-CADSbPAt.woff2 +0 -0
  41. package/dist/app/server/assets/Pilat-Regular-Bx8vzE7q.woff2 +0 -0
  42. package/dist/app/server/assets/USD1-COVcO_PB.svg +30 -0
  43. package/dist/app/server/assets/YLDS-CRh8Zw0g.svg +3 -0
  44. package/dist/app/server/assets/_tanstack-start-manifest_v-11GtFGrq.js +1 -0
  45. package/dist/app/server/assets/arktype-aI7TBD0R-D1R3Byo5.js +2 -0
  46. package/dist/app/server/assets/arktype-aI7TBD0R-D1R3Byo5.js.map +1 -0
  47. package/dist/app/server/assets/authorize-C95F-9E1-CbESJKGU.js +31 -0
  48. package/dist/app/server/assets/authorize-C95F-9E1-CbESJKGU.js.map +1 -0
  49. package/dist/app/server/assets/bun-sqlite-dialect-DoOVo4ja-CtyBkVMb.js +2 -0
  50. package/dist/app/server/assets/bun-sqlite-dialect-DoOVo4ja-CtyBkVMb.js.map +1 -0
  51. package/dist/app/server/assets/call-UQamaCKN.js +32 -0
  52. package/dist/app/server/assets/call-UQamaCKN.js.map +1 -0
  53. package/dist/app/server/assets/ccip-CbNKFO4n.js +2 -0
  54. package/dist/app/server/assets/ccip-CbNKFO4n.js.map +1 -0
  55. package/dist/app/server/assets/compiled-query-Ds2NVcFT.js +2 -0
  56. package/dist/app/server/assets/compiled-query-Ds2NVcFT.js.map +1 -0
  57. package/dist/app/server/assets/convert-BIC8n32x.js +2 -0
  58. package/dist/app/server/assets/convert-BIC8n32x.js.map +1 -0
  59. package/dist/app/server/assets/cookie-CS900nSP.css +1 -0
  60. package/dist/app/server/assets/cookie-Cce9CrIP.js +10 -0
  61. package/dist/app/server/assets/cookie-Cce9CrIP.js.map +1 -0
  62. package/dist/app/server/assets/core-B1HCXUmx.js +56 -0
  63. package/dist/app/server/assets/core-B1HCXUmx.js.map +1 -0
  64. package/dist/app/server/assets/d1-sqlite-dialect-m0f01-4z-Cv-csE1u.js +2 -0
  65. package/dist/app/server/assets/d1-sqlite-dialect-m0f01-4z-Cv-csE1u.js.map +1 -0
  66. package/dist/app/server/assets/default-u_dwuiYb-BJJ9PwCi.js +2 -0
  67. package/dist/app/server/assets/default-u_dwuiYb-BJJ9PwCi.js.map +1 -0
  68. package/dist/app/server/assets/dist-BxSwugSY.js +1 -0
  69. package/dist/app/server/assets/dist-CZGFhmQd.js +2 -0
  70. package/dist/app/server/assets/dist-CZGFhmQd.js.map +1 -0
  71. package/dist/app/server/assets/dist-Dg3PQqiW.js +15 -0
  72. package/dist/app/server/assets/dist-Dg3PQqiW.js.map +1 -0
  73. package/dist/app/server/assets/effect-QlVUlMFu-2tSPDT2b.js +2 -0
  74. package/dist/app/server/assets/effect-QlVUlMFu-2tSPDT2b.js.map +1 -0
  75. package/dist/app/server/assets/empty-plugin-adapters-BaGcmrjC.js +2 -0
  76. package/dist/app/server/assets/empty-plugin-adapters-BaGcmrjC.js.map +1 -0
  77. package/dist/app/server/assets/factory-ipHlJXZY.js +5 -0
  78. package/dist/app/server/assets/factory-ipHlJXZY.js.map +1 -0
  79. package/dist/app/server/assets/frxUSD-n9pTpinR.svg +1 -0
  80. package/dist/app/server/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  81. package/dist/app/server/assets/get-id-field-B0x109VU.js +6 -0
  82. package/dist/app/server/assets/get-id-field-B0x109VU.js.map +1 -0
  83. package/dist/app/server/assets/index-CLddUTqr-xpSuLpBt.js +2 -0
  84. package/dist/app/server/assets/index-CLddUTqr-xpSuLpBt.js.map +1 -0
  85. package/dist/app/server/assets/index-DZEfthgZ-8Uq5s5Pt.js +2 -0
  86. package/dist/app/server/assets/index-DZEfthgZ-8Uq5s5Pt.js.map +1 -0
  87. package/dist/app/server/assets/kysely-adapter-MkHAI9Jk.js +1 -0
  88. package/dist/app/server/assets/node-sqlite-dialect-BowWLXvK.js +2 -0
  89. package/dist/app/server/assets/node-sqlite-dialect-BowWLXvK.js.map +1 -0
  90. package/dist/app/server/assets/pathUSD-BvaO6ooS.svg +25 -0
  91. package/dist/app/server/assets/reset-co-CHrff.css +1 -0
  92. package/dist/app/server/assets/rolldown-runtime-B0Z9INg1.js +1 -0
  93. package/dist/app/server/assets/router-CcbyBCI4.css +1 -0
  94. package/dist/app/server/assets/router-xjZtEeaI.js +7 -0
  95. package/dist/app/server/assets/router-xjZtEeaI.js.map +1 -0
  96. package/dist/app/server/assets/schema-check-Cf7QOZZD.js +9 -0
  97. package/dist/app/server/assets/schema-check-Cf7QOZZD.js.map +1 -0
  98. package/dist/app/server/assets/schemas-C2lCYmR5.js +2 -0
  99. package/dist/app/server/assets/schemas-C2lCYmR5.js.map +1 -0
  100. package/dist/app/server/assets/secp256k1-buM5g4AL.js +2 -0
  101. package/dist/app/server/assets/secp256k1-buM5g4AL.js.map +1 -0
  102. package/dist/app/server/assets/sqlite-adapter-C_LAV4e3.js +2 -0
  103. package/dist/app/server/assets/sqlite-adapter-C_LAV4e3.js.map +1 -0
  104. package/dist/app/server/assets/sqlite-introspector-DSCAP82F-Crx7Ny14.js +6 -0
  105. package/dist/app/server/assets/sqlite-introspector-DSCAP82F-Crx7Ny14.js.map +1 -0
  106. package/dist/app/server/assets/standard-json-Cg1N07hM.js +2 -0
  107. package/dist/app/server/assets/standard-json-Cg1N07hM.js.map +1 -0
  108. package/dist/app/server/assets/standard-json-DY77efq0.js +2 -0
  109. package/dist/app/server/assets/standard-json-DY77efq0.js.map +1 -0
  110. package/dist/app/server/assets/standard-json-ig2t0wRt.js +2 -0
  111. package/dist/app/server/assets/standard-json-ig2t0wRt.js.map +1 -0
  112. package/dist/app/server/assets/standard-json-t-LNckA6.js +2 -0
  113. package/dist/app/server/assets/standard-json-t-LNckA6.js.map +1 -0
  114. package/dist/app/server/assets/standard-openapi-CONmVRl8.js +2 -0
  115. package/dist/app/server/assets/standard-openapi-CONmVRl8.js.map +1 -0
  116. package/dist/app/server/assets/start-C63VetBp.js +2 -0
  117. package/dist/app/server/assets/start-C63VetBp.js.map +1 -0
  118. package/dist/app/server/assets/sury-CWZTCd75-xt1mWc9f.js +2 -0
  119. package/dist/app/server/assets/sury-CWZTCd75-xt1mWc9f.js.map +1 -0
  120. package/dist/app/server/assets/typebox-Dei93FPO-D_lCVdAz.js +2 -0
  121. package/dist/app/server/assets/typebox-Dei93FPO-D_lCVdAz.js.map +1 -0
  122. package/dist/app/server/assets/utils-C4ZXZIZW.js +2 -0
  123. package/dist/app/server/assets/utils-C4ZXZIZW.js.map +1 -0
  124. package/dist/app/server/assets/valibot--1zFm7rT-NSoY2xTp.js +2 -0
  125. package/dist/app/server/assets/valibot--1zFm7rT-NSoY2xTp.js.map +1 -0
  126. package/dist/app/server/assets/valibot-D_HTw1Gn-CJwWeOUE.js +2 -0
  127. package/dist/app/server/assets/valibot-D_HTw1Gn-CJwWeOUE.js.map +1 -0
  128. package/dist/app/server/assets/wYLDS-CaCKDrO-.svg +10 -0
  129. package/dist/app/server/assets/zod-Bwrt9trS-CV38yPnQ.js +2 -0
  130. package/dist/app/server/assets/zod-Bwrt9trS-CV38yPnQ.js.map +1 -0
  131. package/dist/app/server/assets/zod-DSgpEGAE-BvlnBE2L.js +2 -0
  132. package/dist/app/server/assets/zod-DSgpEGAE-BvlnBE2L.js.map +1 -0
  133. package/dist/app/server/index.js +574 -0
  134. package/dist/app/server/index.js.map +1 -0
  135. package/dist/app/server/wrangler.json +1 -0
  136. package/dist/cli/Admin.d.ts +38 -0
  137. package/dist/cli/Admin.d.ts.map +1 -0
  138. package/dist/cli/Admin.js +62 -0
  139. package/dist/cli/Admin.js.map +1 -0
  140. package/dist/cli/Apps.d.ts +9 -0
  141. package/dist/cli/Apps.d.ts.map +1 -0
  142. package/dist/cli/Apps.js +36 -0
  143. package/dist/cli/Apps.js.map +1 -0
  144. package/dist/cli/Cli.d.ts +14 -0
  145. package/dist/cli/Cli.d.ts.map +1 -0
  146. package/dist/cli/Cli.js +27 -0
  147. package/dist/cli/Cli.js.map +1 -0
  148. package/dist/cli/Project.d.ts +20 -0
  149. package/dist/cli/Project.d.ts.map +1 -0
  150. package/dist/cli/Project.js +88 -0
  151. package/dist/cli/Project.js.map +1 -0
  152. package/dist/cli/Realms.d.ts +24 -0
  153. package/dist/cli/Realms.d.ts.map +1 -0
  154. package/dist/cli/Realms.js +49 -0
  155. package/dist/cli/Realms.js.map +1 -0
  156. package/dist/cli/Session.d.ts +85 -0
  157. package/dist/cli/Session.d.ts.map +1 -0
  158. package/dist/cli/Session.js +178 -0
  159. package/dist/cli/Session.js.map +1 -0
  160. package/dist/cli/User.d.ts +29 -0
  161. package/dist/cli/User.d.ts.map +1 -0
  162. package/dist/cli/User.js +39 -0
  163. package/dist/cli/User.js.map +1 -0
  164. package/dist/cli/V1.d.ts +35 -0
  165. package/dist/cli/V1.d.ts.map +1 -0
  166. package/dist/cli/V1.js +41 -0
  167. package/dist/cli/V1.js.map +1 -0
  168. package/dist/cli/bin.d.ts +3 -0
  169. package/dist/cli/bin.d.ts.map +1 -0
  170. package/dist/cli/bin.js +46 -0
  171. package/dist/cli/bin.js.map +1 -0
  172. package/dist/cli/commands/admin/apps/create.d.ts +23 -0
  173. package/dist/cli/commands/admin/apps/create.d.ts.map +1 -0
  174. package/dist/cli/commands/admin/apps/create.js +89 -0
  175. package/dist/cli/commands/admin/apps/create.js.map +1 -0
  176. package/dist/cli/commands/admin/apps/delete.d.ts +14 -0
  177. package/dist/cli/commands/admin/apps/delete.d.ts.map +1 -0
  178. package/dist/cli/commands/admin/apps/delete.js +41 -0
  179. package/dist/cli/commands/admin/apps/delete.js.map +1 -0
  180. package/dist/cli/commands/admin/apps/index.d.ts +5 -0
  181. package/dist/cli/commands/admin/apps/index.d.ts.map +1 -0
  182. package/dist/cli/commands/admin/apps/index.js +14 -0
  183. package/dist/cli/commands/admin/apps/index.js.map +1 -0
  184. package/dist/cli/commands/admin/apps/list.d.ts +9 -0
  185. package/dist/cli/commands/admin/apps/list.d.ts.map +1 -0
  186. package/dist/cli/commands/admin/apps/list.js +26 -0
  187. package/dist/cli/commands/admin/apps/list.js.map +1 -0
  188. package/dist/cli/commands/admin/apps/rotate-secret.d.ts +16 -0
  189. package/dist/cli/commands/admin/apps/rotate-secret.d.ts.map +1 -0
  190. package/dist/cli/commands/admin/apps/rotate-secret.js +47 -0
  191. package/dist/cli/commands/admin/apps/rotate-secret.js.map +1 -0
  192. package/dist/cli/commands/admin/apps/update.d.ts +20 -0
  193. package/dist/cli/commands/admin/apps/update.d.ts.map +1 -0
  194. package/dist/cli/commands/admin/apps/update.js +76 -0
  195. package/dist/cli/commands/admin/apps/update.js.map +1 -0
  196. package/dist/cli/commands/admin/index.d.ts +5 -0
  197. package/dist/cli/commands/admin/index.d.ts.map +1 -0
  198. package/dist/cli/commands/admin/index.js +13 -0
  199. package/dist/cli/commands/admin/index.js.map +1 -0
  200. package/dist/cli/commands/admin/migrate.d.ts +9 -0
  201. package/dist/cli/commands/admin/migrate.d.ts.map +1 -0
  202. package/dist/cli/commands/admin/migrate.js +17 -0
  203. package/dist/cli/commands/admin/migrate.js.map +1 -0
  204. package/dist/cli/commands/admin/realms/add.d.ts +12 -0
  205. package/dist/cli/commands/admin/realms/add.d.ts.map +1 -0
  206. package/dist/cli/commands/admin/realms/add.js +42 -0
  207. package/dist/cli/commands/admin/realms/add.js.map +1 -0
  208. package/dist/cli/commands/admin/realms/index.d.ts +5 -0
  209. package/dist/cli/commands/admin/realms/index.d.ts.map +1 -0
  210. package/dist/cli/commands/admin/realms/index.js +13 -0
  211. package/dist/cli/commands/admin/realms/index.js.map +1 -0
  212. package/dist/cli/commands/admin/realms/list.d.ts +13 -0
  213. package/dist/cli/commands/admin/realms/list.d.ts.map +1 -0
  214. package/dist/cli/commands/admin/realms/list.js +36 -0
  215. package/dist/cli/commands/admin/realms/list.js.map +1 -0
  216. package/dist/cli/commands/admin/realms/passkeys/index.d.ts +5 -0
  217. package/dist/cli/commands/admin/realms/passkeys/index.d.ts.map +1 -0
  218. package/dist/cli/commands/admin/realms/passkeys/index.js +13 -0
  219. package/dist/cli/commands/admin/realms/passkeys/index.js.map +1 -0
  220. package/dist/cli/commands/admin/realms/passkeys/set.d.ts +16 -0
  221. package/dist/cli/commands/admin/realms/passkeys/set.d.ts.map +1 -0
  222. package/dist/cli/commands/admin/realms/passkeys/set.js +46 -0
  223. package/dist/cli/commands/admin/realms/passkeys/set.js.map +1 -0
  224. package/dist/cli/commands/admin/realms/privy/index.d.ts +5 -0
  225. package/dist/cli/commands/admin/realms/privy/index.d.ts.map +1 -0
  226. package/dist/cli/commands/admin/realms/privy/index.js +13 -0
  227. package/dist/cli/commands/admin/realms/privy/index.js.map +1 -0
  228. package/dist/cli/commands/admin/realms/privy/set.d.ts +18 -0
  229. package/dist/cli/commands/admin/realms/privy/set.d.ts.map +1 -0
  230. package/dist/cli/commands/admin/realms/privy/set.js +66 -0
  231. package/dist/cli/commands/admin/realms/privy/set.js.map +1 -0
  232. package/dist/cli/commands/admin/realms/remove.d.ts +11 -0
  233. package/dist/cli/commands/admin/realms/remove.d.ts.map +1 -0
  234. package/dist/cli/commands/admin/realms/remove.js +35 -0
  235. package/dist/cli/commands/admin/realms/remove.js.map +1 -0
  236. package/dist/cli/commands/admin/realms/secp256k1/disable.d.ts +15 -0
  237. package/dist/cli/commands/admin/realms/secp256k1/disable.d.ts.map +1 -0
  238. package/dist/cli/commands/admin/realms/secp256k1/disable.js +42 -0
  239. package/dist/cli/commands/admin/realms/secp256k1/disable.js.map +1 -0
  240. package/dist/cli/commands/admin/realms/secp256k1/enable.d.ts +15 -0
  241. package/dist/cli/commands/admin/realms/secp256k1/enable.d.ts.map +1 -0
  242. package/dist/cli/commands/admin/realms/secp256k1/enable.js +42 -0
  243. package/dist/cli/commands/admin/realms/secp256k1/enable.js.map +1 -0
  244. package/dist/cli/commands/admin/realms/secp256k1/index.d.ts +9 -0
  245. package/dist/cli/commands/admin/realms/secp256k1/index.d.ts.map +1 -0
  246. package/dist/cli/commands/admin/realms/secp256k1/index.js +17 -0
  247. package/dist/cli/commands/admin/realms/secp256k1/index.js.map +1 -0
  248. package/dist/cli/commands/admin/realms/set.d.ts +16 -0
  249. package/dist/cli/commands/admin/realms/set.d.ts.map +1 -0
  250. package/dist/cli/commands/admin/realms/set.js +46 -0
  251. package/dist/cli/commands/admin/realms/set.js.map +1 -0
  252. package/dist/cli/commands/admin/rotate-key.d.ts +15 -0
  253. package/dist/cli/commands/admin/rotate-key.d.ts.map +1 -0
  254. package/dist/cli/commands/admin/rotate-key.js +46 -0
  255. package/dist/cli/commands/admin/rotate-key.js.map +1 -0
  256. package/dist/cli/commands/init.d.ts +11 -0
  257. package/dist/cli/commands/init.d.ts.map +1 -0
  258. package/dist/cli/commands/init.js +34 -0
  259. package/dist/cli/commands/init.js.map +1 -0
  260. package/dist/cli/commands/login.d.ts +12 -0
  261. package/dist/cli/commands/login.d.ts.map +1 -0
  262. package/dist/cli/commands/login.js +29 -0
  263. package/dist/cli/commands/login.js.map +1 -0
  264. package/dist/cli/commands/logout.d.ts +8 -0
  265. package/dist/cli/commands/logout.d.ts.map +1 -0
  266. package/dist/cli/commands/logout.js +19 -0
  267. package/dist/cli/commands/logout.js.map +1 -0
  268. package/dist/cli/commands/whoami.d.ts +12 -0
  269. package/dist/cli/commands/whoami.d.ts.map +1 -0
  270. package/dist/cli/commands/whoami.js +24 -0
  271. package/dist/cli/commands/whoami.js.map +1 -0
  272. package/dist/client/Actions.d.ts +101 -0
  273. package/dist/client/Actions.d.ts.map +1 -0
  274. package/dist/client/Actions.js +90 -0
  275. package/dist/client/Actions.js.map +1 -0
  276. package/dist/client/Auth.d.ts +161 -0
  277. package/dist/client/Auth.d.ts.map +1 -0
  278. package/dist/client/Auth.js +113 -0
  279. package/dist/client/Auth.js.map +1 -0
  280. package/dist/client/Authorization.d.ts +29 -0
  281. package/dist/client/Authorization.d.ts.map +1 -0
  282. package/dist/client/Authorization.js +12 -0
  283. package/dist/client/Authorization.js.map +1 -0
  284. package/dist/client/Decorator.d.ts +170 -0
  285. package/dist/client/Decorator.d.ts.map +1 -0
  286. package/dist/client/Decorator.js +227 -0
  287. package/dist/client/Decorator.js.map +1 -0
  288. package/dist/client/Key.d.ts +85 -0
  289. package/dist/client/Key.d.ts.map +1 -0
  290. package/dist/client/Key.js +83 -0
  291. package/dist/client/Key.js.map +1 -0
  292. package/dist/client/Pending.d.ts +39 -0
  293. package/dist/client/Pending.d.ts.map +1 -0
  294. package/dist/client/Pending.js +12 -0
  295. package/dist/client/Pending.js.map +1 -0
  296. package/dist/client/Permissions.d.ts +34 -0
  297. package/dist/client/Permissions.d.ts.map +1 -0
  298. package/dist/client/Permissions.js +16 -0
  299. package/dist/client/Permissions.js.map +1 -0
  300. package/dist/client/Provider.d.ts +16 -0
  301. package/dist/client/Provider.d.ts.map +1 -0
  302. package/dist/client/Provider.js +12 -0
  303. package/dist/client/Provider.js.map +1 -0
  304. package/dist/client/Step.d.ts +21 -0
  305. package/dist/client/Step.d.ts.map +1 -0
  306. package/dist/client/Step.js +16 -0
  307. package/dist/client/Step.js.map +1 -0
  308. package/dist/client/Storage.d.ts +27 -0
  309. package/dist/client/Storage.d.ts.map +1 -0
  310. package/dist/client/Storage.js +16 -0
  311. package/dist/client/Storage.js.map +1 -0
  312. package/dist/client/TempoAccount.d.ts +40 -0
  313. package/dist/client/TempoAccount.d.ts.map +1 -0
  314. package/dist/client/TempoAccount.js +16 -0
  315. package/dist/client/TempoAccount.js.map +1 -0
  316. package/dist/client/Tokens.d.ts +25 -0
  317. package/dist/client/Tokens.d.ts.map +1 -0
  318. package/dist/client/Tokens.js +12 -0
  319. package/dist/client/Tokens.js.map +1 -0
  320. package/dist/client/User.d.ts +21 -0
  321. package/dist/client/User.d.ts.map +1 -0
  322. package/dist/client/User.js +12 -0
  323. package/dist/client/User.js.map +1 -0
  324. package/dist/client/actions/account.d.ts +25 -0
  325. package/dist/client/actions/account.d.ts.map +1 -0
  326. package/dist/client/actions/account.js +34 -0
  327. package/dist/client/actions/account.js.map +1 -0
  328. package/dist/client/actions/device.d.ts +53 -0
  329. package/dist/client/actions/device.d.ts.map +1 -0
  330. package/dist/client/actions/device.js +79 -0
  331. package/dist/client/actions/device.js.map +1 -0
  332. package/dist/client/actions/email.d.ts +84 -0
  333. package/dist/client/actions/email.d.ts.map +1 -0
  334. package/dist/client/actions/email.js +63 -0
  335. package/dist/client/actions/email.js.map +1 -0
  336. package/dist/client/actions/oauth.d.ts +115 -0
  337. package/dist/client/actions/oauth.d.ts.map +1 -0
  338. package/dist/client/actions/oauth.js +105 -0
  339. package/dist/client/actions/oauth.js.map +1 -0
  340. package/dist/client/actions/passkey.d.ts +88 -0
  341. package/dist/client/actions/passkey.d.ts.map +1 -0
  342. package/dist/client/actions/passkey.js +60 -0
  343. package/dist/client/actions/passkey.js.map +1 -0
  344. package/dist/client/actions/provider.d.ts +49 -0
  345. package/dist/client/actions/provider.d.ts.map +1 -0
  346. package/dist/client/actions/provider.js +34 -0
  347. package/dist/client/actions/provider.js.map +1 -0
  348. package/dist/client/actions/realm.d.ts +24 -0
  349. package/dist/client/actions/realm.d.ts.map +1 -0
  350. package/dist/client/actions/realm.js +15 -0
  351. package/dist/client/actions/realm.js.map +1 -0
  352. package/dist/client/actions/redirect.d.ts +153 -0
  353. package/dist/client/actions/redirect.d.ts.map +1 -0
  354. package/dist/client/actions/redirect.js +236 -0
  355. package/dist/client/actions/redirect.js.map +1 -0
  356. package/dist/client/internal/accessKey.d.ts +21 -0
  357. package/dist/client/internal/accessKey.d.ts.map +1 -0
  358. package/dist/client/internal/accessKey.js +38 -0
  359. package/dist/client/internal/accessKey.js.map +1 -0
  360. package/dist/client/internal/account.d.ts +14 -0
  361. package/dist/client/internal/account.d.ts.map +1 -0
  362. package/dist/client/internal/account.js +3 -0
  363. package/dist/client/internal/account.js.map +1 -0
  364. package/dist/client/internal/api.d.ts +106 -0
  365. package/dist/client/internal/api.d.ts.map +1 -0
  366. package/dist/client/internal/api.js +129 -0
  367. package/dist/client/internal/api.js.map +1 -0
  368. package/dist/client/internal/headless.d.ts +33 -0
  369. package/dist/client/internal/headless.d.ts.map +1 -0
  370. package/dist/client/internal/headless.js +46 -0
  371. package/dist/client/internal/headless.js.map +1 -0
  372. package/dist/client/internal/indexedDb.d.ts +9 -0
  373. package/dist/client/internal/indexedDb.d.ts.map +1 -0
  374. package/dist/client/internal/indexedDb.js +32 -0
  375. package/dist/client/internal/indexedDb.js.map +1 -0
  376. package/dist/client/internal/request.d.ts +58 -0
  377. package/dist/client/internal/request.d.ts.map +1 -0
  378. package/dist/client/internal/request.js +45 -0
  379. package/dist/client/internal/request.js.map +1 -0
  380. package/dist/client/internal/store.d.ts +12 -0
  381. package/dist/client/internal/store.d.ts.map +1 -0
  382. package/dist/client/internal/store.js +15 -0
  383. package/dist/client/internal/store.js.map +1 -0
  384. package/dist/client.d.ts +20 -0
  385. package/dist/client.d.ts.map +1 -0
  386. package/dist/client.js +20 -0
  387. package/dist/client.js.map +1 -0
  388. package/dist/index.d.ts +7 -0
  389. package/dist/index.d.ts.map +1 -0
  390. package/dist/index.js +7 -0
  391. package/dist/index.js.map +1 -0
  392. package/dist/internal/Apps.d.ts +3051 -0
  393. package/dist/internal/Apps.d.ts.map +1 -0
  394. package/dist/internal/Apps.js +472 -0
  395. package/dist/internal/Apps.js.map +1 -0
  396. package/dist/internal/Auth.d.ts +4058 -0
  397. package/dist/internal/Auth.d.ts.map +1 -0
  398. package/dist/internal/Auth.js +137 -0
  399. package/dist/internal/Auth.js.map +1 -0
  400. package/dist/internal/Authorize.d.ts +532 -0
  401. package/dist/internal/Authorize.d.ts.map +1 -0
  402. package/dist/internal/Authorize.js +326 -0
  403. package/dist/internal/Authorize.js.map +1 -0
  404. package/dist/internal/Device.d.ts +371 -0
  405. package/dist/internal/Device.d.ts.map +1 -0
  406. package/dist/internal/Device.js +271 -0
  407. package/dist/internal/Device.js.map +1 -0
  408. package/dist/internal/Env.d.ts +57 -0
  409. package/dist/internal/Env.d.ts.map +1 -0
  410. package/dist/internal/Env.js +2 -0
  411. package/dist/internal/Env.js.map +1 -0
  412. package/dist/internal/ErrorResponse.d.ts +609 -0
  413. package/dist/internal/ErrorResponse.d.ts.map +1 -0
  414. package/dist/internal/ErrorResponse.js +679 -0
  415. package/dist/internal/ErrorResponse.js.map +1 -0
  416. package/dist/internal/Id.d.ts +10 -0
  417. package/dist/internal/Id.d.ts.map +1 -0
  418. package/dist/internal/Id.js +34 -0
  419. package/dist/internal/Id.js.map +1 -0
  420. package/dist/internal/KeyAuthorization.d.ts +1456 -0
  421. package/dist/internal/KeyAuthorization.d.ts.map +1 -0
  422. package/dist/internal/KeyAuthorization.js +1030 -0
  423. package/dist/internal/KeyAuthorization.js.map +1 -0
  424. package/dist/internal/Limits.d.ts +65 -0
  425. package/dist/internal/Limits.d.ts.map +1 -0
  426. package/dist/internal/Limits.js +35 -0
  427. package/dist/internal/Limits.js.map +1 -0
  428. package/dist/internal/Log.d.ts +49 -0
  429. package/dist/internal/Log.d.ts.map +1 -0
  430. package/dist/internal/Log.js +101 -0
  431. package/dist/internal/Log.js.map +1 -0
  432. package/dist/internal/LogLine.d.ts +33 -0
  433. package/dist/internal/LogLine.d.ts.map +1 -0
  434. package/dist/internal/LogLine.js +56 -0
  435. package/dist/internal/LogLine.js.map +1 -0
  436. package/dist/internal/Migrations.d.ts +19 -0
  437. package/dist/internal/Migrations.d.ts.map +1 -0
  438. package/dist/internal/Migrations.js +57 -0
  439. package/dist/internal/Migrations.js.map +1 -0
  440. package/dist/internal/Owner.d.ts +146 -0
  441. package/dist/internal/Owner.d.ts.map +1 -0
  442. package/dist/internal/Owner.js +65 -0
  443. package/dist/internal/Owner.js.map +1 -0
  444. package/dist/internal/Passkey.d.ts +528 -0
  445. package/dist/internal/Passkey.d.ts.map +1 -0
  446. package/dist/internal/Passkey.js +658 -0
  447. package/dist/internal/Passkey.js.map +1 -0
  448. package/dist/internal/PendingKey.d.ts +49 -0
  449. package/dist/internal/PendingKey.d.ts.map +1 -0
  450. package/dist/internal/PendingKey.js +58 -0
  451. package/dist/internal/PendingKey.js.map +1 -0
  452. package/dist/internal/Privy.d.ts +105 -0
  453. package/dist/internal/Privy.d.ts.map +1 -0
  454. package/dist/internal/Privy.js +224 -0
  455. package/dist/internal/Privy.js.map +1 -0
  456. package/dist/internal/RateLimit.d.ts +190 -0
  457. package/dist/internal/RateLimit.d.ts.map +1 -0
  458. package/dist/internal/RateLimit.js +148 -0
  459. package/dist/internal/RateLimit.js.map +1 -0
  460. package/dist/internal/Realm.d.ts +281 -0
  461. package/dist/internal/Realm.d.ts.map +1 -0
  462. package/dist/internal/Realm.js +477 -0
  463. package/dist/internal/Realm.js.map +1 -0
  464. package/dist/internal/RequestId.d.ts +16 -0
  465. package/dist/internal/RequestId.d.ts.map +1 -0
  466. package/dist/internal/RequestId.js +78 -0
  467. package/dist/internal/RequestId.js.map +1 -0
  468. package/dist/internal/SigningKey.d.ts +115 -0
  469. package/dist/internal/SigningKey.d.ts.map +1 -0
  470. package/dist/internal/SigningKey.js +197 -0
  471. package/dist/internal/SigningKey.js.map +1 -0
  472. package/dist/internal/TestInbox.d.ts +42 -0
  473. package/dist/internal/TestInbox.d.ts.map +1 -0
  474. package/dist/internal/TestInbox.js +20 -0
  475. package/dist/internal/TestInbox.js.map +1 -0
  476. package/dist/internal/Timing.d.ts +51 -0
  477. package/dist/internal/Timing.d.ts.map +1 -0
  478. package/dist/internal/Timing.js +103 -0
  479. package/dist/internal/Timing.js.map +1 -0
  480. package/dist/internal/Validation.d.ts +52 -0
  481. package/dist/internal/Validation.d.ts.map +1 -0
  482. package/dist/internal/Validation.js +22 -0
  483. package/dist/internal/Validation.js.map +1 -0
  484. package/dist/internal/Wallet.d.ts +37 -0
  485. package/dist/internal/Wallet.d.ts.map +1 -0
  486. package/dist/internal/Wallet.js +43 -0
  487. package/dist/internal/Wallet.js.map +1 -0
  488. package/dist/worker.d.ts +12 -0
  489. package/dist/worker.d.ts.map +1 -0
  490. package/dist/worker.js +12 -0
  491. package/dist/worker.js.map +1 -0
  492. package/package.json +79 -3
  493. package/src/Api.ts +151 -0
  494. package/src/App.ts +56 -0
  495. package/src/api/account.ts +311 -0
  496. package/src/api/authenticate.ts +1290 -0
  497. package/src/api/authorize.ts +1030 -0
  498. package/src/api/device.ts +874 -0
  499. package/src/api/health.ts +72 -0
  500. package/src/api/lib/AccountStep.ts +335 -0
  501. package/src/api/lib/Auth.ts +134 -0
  502. package/src/api/lib/Consent.ts +339 -0
  503. package/src/api/lib/Cors.ts +107 -0
  504. package/src/api/lib/Database.ts +10 -0
  505. package/src/api/lib/DeviceRequest.ts +265 -0
  506. package/src/api/lib/FirstAccount.ts +90 -0
  507. package/src/api/lib/KeySigner.ts +393 -0
  508. package/src/api/lib/PasskeyLink.ts +162 -0
  509. package/src/api/lib/Session.ts +34 -0
  510. package/src/api/lib/SignInConsent.ts +316 -0
  511. package/src/api/oauth.ts +1282 -0
  512. package/src/api/provider.ts +441 -0
  513. package/src/app/components/Button.tsx +128 -0
  514. package/src/app/components/CodeInput.tsx +186 -0
  515. package/src/app/components/ErrorMessage.tsx +66 -0
  516. package/src/app/components/Grant.tsx +323 -0
  517. package/src/app/components/Input.tsx +48 -0
  518. package/src/app/components/Tooltip.tsx +45 -0
  519. package/src/app/components/icons/BRLA.png +0 -0
  520. package/src/app/components/icons/BRZ.svg +1 -0
  521. package/src/app/components/icons/CHFAU.png +0 -0
  522. package/src/app/components/icons/DLUSD.png +0 -0
  523. package/src/app/components/icons/EURAU.svg +1 -0
  524. package/src/app/components/icons/EURC.e.svg +1 -0
  525. package/src/app/components/icons/GBPA.svg +4 -0
  526. package/src/app/components/icons/GUSD.svg +1 -0
  527. package/src/app/components/icons/MACH.svg +4 -0
  528. package/src/app/components/icons/OUSD.svg +5 -0
  529. package/src/app/components/icons/PRIME.svg +14 -0
  530. package/src/app/components/icons/SBC.svg +1 -0
  531. package/src/app/components/icons/SEKAU.svg +5 -0
  532. package/src/app/components/icons/USD1.svg +30 -0
  533. package/src/app/components/icons/USDB.svg +11 -0
  534. package/src/app/components/icons/USDC.e.svg +1 -0
  535. package/src/app/components/icons/USDT0.svg +1 -0
  536. package/src/app/components/icons/USDY.svg +16 -0
  537. package/src/app/components/icons/USDe.svg +1 -0
  538. package/src/app/components/icons/YLDS.svg +3 -0
  539. package/src/app/components/icons/alphaUSD.svg +1 -0
  540. package/src/app/components/icons/betaUSD.svg +1 -0
  541. package/src/app/components/icons/cUSD.svg +1 -0
  542. package/src/app/components/icons/cbBTC.svg +1 -0
  543. package/src/app/components/icons/frxUSD.svg +1 -0
  544. package/src/app/components/icons/goUSD.svg +5 -0
  545. package/src/app/components/icons/iUSD.svg +1 -0
  546. package/src/app/components/icons/pathUSD.svg +25 -0
  547. package/src/app/components/icons/rUSD.svg +1 -0
  548. package/src/app/components/icons/reUSD.svg +1 -0
  549. package/src/app/components/icons/sUSDe.svg +1 -0
  550. package/src/app/components/icons/siUSD.svg +1 -0
  551. package/src/app/components/icons/stcUSD.svg +1 -0
  552. package/src/app/components/icons/syrupUSDC.svg +5 -0
  553. package/src/app/components/icons/thetaUSD.svg +1 -0
  554. package/src/app/components/icons/wYLDS.svg +10 -0
  555. package/src/app/components/icons/wsrUSD.svg +1 -0
  556. package/src/app/components/logos/OUSD.dark.svg +17 -0
  557. package/src/app/components/logos/OUSD.light.svg +17 -0
  558. package/src/app/crons/cleanup.ts +42 -0
  559. package/src/app/db/auth.ts +416 -0
  560. package/src/app/db/migrations/0000_better_auth.sql +53 -0
  561. package/src/app/db/migrations/0001_oauth_provider.sql +162 -0
  562. package/src/app/db/migrations/0002_device_code.sql +17 -0
  563. package/src/app/db/migrations/0003_realms.sql +27 -0
  564. package/src/app/db/migrations/0004_default_realm.sql +2 -0
  565. package/src/app/db/migrations/0005_passkeys.sql +30 -0
  566. package/src/app/db/migrations/0006_first_accounts.sql +9 -0
  567. package/src/app/db/migrations/0007_pending_keys.sql +13 -0
  568. package/src/app/db/migrations/0008_secp256k1_owners.sql +18 -0
  569. package/src/app/db/migrations/0009_device_requests.sql +8 -0
  570. package/src/app/db/migrations/0010_device_keys.sql +4 -0
  571. package/src/app/db/migrations/meta/0000_snapshot.json +362 -0
  572. package/src/app/db/migrations/meta/0001_snapshot.json +1487 -0
  573. package/src/app/db/migrations/meta/0002_snapshot.json +1603 -0
  574. package/src/app/db/migrations/meta/0003_snapshot.json +1760 -0
  575. package/src/app/db/migrations/meta/0004_snapshot.json +1760 -0
  576. package/src/app/db/migrations/meta/0005_snapshot.json +1911 -0
  577. package/src/app/db/migrations/meta/0006_snapshot.json +1967 -0
  578. package/src/app/db/migrations/meta/0007_snapshot.json +2047 -0
  579. package/src/app/db/migrations/meta/0008_snapshot.json +2157 -0
  580. package/src/app/db/migrations/meta/0009_snapshot.json +2207 -0
  581. package/src/app/db/migrations/meta/0010_snapshot.json +2231 -0
  582. package/src/app/db/migrations/meta/_journal.json +83 -0
  583. package/src/app/db/schema.ts +218 -0
  584. package/src/app/env.ts +49 -0
  585. package/src/app/lib/Client.ts +82 -0
  586. package/src/app/lib/Loader.ts +24 -0
  587. package/src/app/package.json +8 -0
  588. package/src/app/routeTree.gen.ts +198 -0
  589. package/src/app/router.tsx +48 -0
  590. package/src/app/routes/[_].flows.tsx +3001 -0
  591. package/src/app/routes/__root.tsx +91 -0
  592. package/src/app/routes/authorize.tsx +197 -0
  593. package/src/app/routes/choose-account.tsx +111 -0
  594. package/src/app/routes/deposit.tsx +105 -0
  595. package/src/app/routes/device.tsx +227 -0
  596. package/src/app/routes/index.tsx +115 -0
  597. package/src/app/routes/login.tsx +506 -0
  598. package/src/app/screens/Account.tsx +347 -0
  599. package/src/app/screens/Approval.tsx +276 -0
  600. package/src/app/screens/ChooseAccount.tsx +240 -0
  601. package/src/app/screens/Deposit.tsx +762 -0
  602. package/src/app/screens/DeviceApproval.tsx +480 -0
  603. package/src/app/screens/SignIn.tsx +362 -0
  604. package/src/app/screens/chains/base.svg +3 -0
  605. package/src/app/screens/chains/ethereum.svg +11 -0
  606. package/src/app/screens/chains/solana.svg +4 -0
  607. package/src/app/screens/chains/tempo.svg +4 -0
  608. package/src/app/server.ts +204 -0
  609. package/src/app/vite.config.ts +95 -0
  610. package/src/app/void.config.ts +29 -0
  611. package/src/app/zyzz.config.ts +274 -0
  612. package/src/cli/Admin.ts +73 -0
  613. package/src/cli/Apps.ts +29 -0
  614. package/src/cli/Cli.ts +42 -0
  615. package/src/cli/Project.ts +117 -0
  616. package/src/cli/Realms.ts +50 -0
  617. package/src/cli/Session.ts +239 -0
  618. package/src/cli/User.ts +45 -0
  619. package/src/cli/V1.ts +57 -0
  620. package/src/cli/bin.ts +44 -0
  621. package/src/cli/commands/admin/apps/create.ts +101 -0
  622. package/src/cli/commands/admin/apps/delete.ts +38 -0
  623. package/src/cli/commands/admin/apps/index.ts +15 -0
  624. package/src/cli/commands/admin/apps/list.ts +25 -0
  625. package/src/cli/commands/admin/apps/rotate-secret.ts +47 -0
  626. package/src/cli/commands/admin/apps/update.ts +86 -0
  627. package/src/cli/commands/admin/index.ts +14 -0
  628. package/src/cli/commands/admin/migrate.ts +16 -0
  629. package/src/cli/commands/admin/realms/add.ts +40 -0
  630. package/src/cli/commands/admin/realms/index.ts +14 -0
  631. package/src/cli/commands/admin/realms/list.ts +38 -0
  632. package/src/cli/commands/admin/realms/passkeys/index.ts +15 -0
  633. package/src/cli/commands/admin/realms/passkeys/set.ts +44 -0
  634. package/src/cli/commands/admin/realms/privy/index.ts +14 -0
  635. package/src/cli/commands/admin/realms/privy/set.ts +69 -0
  636. package/src/cli/commands/admin/realms/remove.ts +33 -0
  637. package/src/cli/commands/admin/realms/secp256k1/disable.ts +40 -0
  638. package/src/cli/commands/admin/realms/secp256k1/enable.ts +40 -0
  639. package/src/cli/commands/admin/realms/secp256k1/index.ts +19 -0
  640. package/src/cli/commands/admin/realms/set.ts +46 -0
  641. package/src/cli/commands/admin/rotate-key.ts +47 -0
  642. package/src/cli/commands/init.ts +42 -0
  643. package/src/cli/commands/login.ts +32 -0
  644. package/src/cli/commands/logout.ts +19 -0
  645. package/src/cli/commands/whoami.ts +23 -0
  646. package/src/client/Actions.ts +168 -0
  647. package/src/client/Auth.ts +285 -0
  648. package/src/client/Authorization.ts +34 -0
  649. package/src/client/Decorator.ts +410 -0
  650. package/src/client/Key.ts +140 -0
  651. package/src/client/Pending.ts +46 -0
  652. package/src/client/Permissions.ts +39 -0
  653. package/src/client/Provider.ts +18 -0
  654. package/src/client/Step.ts +24 -0
  655. package/src/client/Storage.ts +29 -0
  656. package/src/client/TempoAccount.ts +42 -0
  657. package/src/client/Tokens.ts +27 -0
  658. package/src/client/User.ts +23 -0
  659. package/src/client/actions/account.ts +57 -0
  660. package/src/client/actions/device.ts +151 -0
  661. package/src/client/actions/email.ts +141 -0
  662. package/src/client/actions/oauth.ts +182 -0
  663. package/src/client/actions/passkey.ts +141 -0
  664. package/src/client/actions/provider.ts +70 -0
  665. package/src/client/actions/realm.ts +36 -0
  666. package/src/client/actions/redirect.ts +408 -0
  667. package/src/client/internal/accessKey.ts +54 -0
  668. package/src/client/internal/account.ts +15 -0
  669. package/src/client/internal/api.ts +224 -0
  670. package/src/client/internal/headless.ts +77 -0
  671. package/src/client/internal/indexedDb.ts +44 -0
  672. package/src/client/internal/request.ts +71 -0
  673. package/src/client/internal/store.ts +20 -0
  674. package/src/client.ts +19 -0
  675. package/src/index.ts +6 -0
  676. package/src/internal/Apps.ts +718 -0
  677. package/src/internal/Auth.ts +203 -0
  678. package/src/internal/Authorize.ts +424 -0
  679. package/src/internal/Balance.ts +168 -0
  680. package/src/internal/BodyLimit.ts +36 -0
  681. package/src/internal/Chain.ts +16 -0
  682. package/src/internal/Device.ts +469 -0
  683. package/src/internal/Env.ts +58 -0
  684. package/src/internal/ErrorResponse.ts +854 -0
  685. package/src/internal/Id.ts +41 -0
  686. package/src/internal/KeyAuthorization.ts +1339 -0
  687. package/src/internal/Limits.ts +39 -0
  688. package/src/internal/Log.ts +178 -0
  689. package/src/internal/LogLine.ts +87 -0
  690. package/src/internal/Logout.ts +336 -0
  691. package/src/internal/Migrations.ts +86 -0
  692. package/src/internal/OpenApi.ts +300 -0
  693. package/src/internal/Owner.ts +211 -0
  694. package/src/internal/Passkey.ts +1052 -0
  695. package/src/internal/PendingKey.ts +104 -0
  696. package/src/internal/Privy.ts +402 -0
  697. package/src/internal/Provider.ts +125 -0
  698. package/src/internal/RateLimit.ts +259 -0
  699. package/src/internal/Realm.ts +768 -0
  700. package/src/internal/RequestId.ts +118 -0
  701. package/src/internal/SignInGrant.ts +168 -0
  702. package/src/internal/SigningKey.ts +286 -0
  703. package/src/internal/TempoAccount.ts +342 -0
  704. package/src/internal/TestInbox.ts +51 -0
  705. package/src/internal/Timing.ts +158 -0
  706. package/src/internal/Validation.ts +52 -0
  707. package/src/internal/Wallet.ts +62 -0
  708. package/src/internal/owners/Passkey.ts +72 -0
  709. package/src/internal/owners/Privy.ts +81 -0
  710. package/src/internal/owners/Secp256k1.ts +143 -0
  711. package/src/types/app.d.ts +19 -0
  712. package/src/worker.ts +11 -0
@@ -0,0 +1,1052 @@
1
+ /**
2
+ * Passkeys: the WebAuthn credentials users sign in with, each a row of the `keys` table linked to a
3
+ * user. A linked wallet.tempo passkey also owns Tempo Accounts, as the kind in `owners/Passkey.ts`
4
+ * lists, and signs their key authorizations over a signature challenge. A realm's `realm_passkeys` row names the relying party whose passkeys belong to it. Every
5
+ * challenge comes from Tempo ID, lives in Better Auth's `verification` table, and is spent once. The
6
+ * plugin's server-only endpoint creates a native Better Auth session once a route verifies an
7
+ * assertion.
8
+ * @module
9
+ */
10
+ import type { BetterAuthPlugin } from 'better-auth'
11
+ import { createAuthEndpoint } from 'better-auth/api'
12
+ import { setSessionCookie } from 'better-auth/cookies'
13
+ import { symmetricDecrypt, symmetricEncrypt } from 'better-auth/crypto'
14
+ import { and, eq, gt, sql } from 'drizzle-orm'
15
+ import { Base64, Hash, Hex } from 'ox'
16
+ import { Authentication } from 'webauthx/server'
17
+ import { z } from 'zod'
18
+ import * as Schema from '../app/db/schema.js'
19
+ import type * as Apps from './Apps.js'
20
+ import * as Authorize from './Authorize.js'
21
+ import * as ErrorResponse from './ErrorResponse.js'
22
+ import * as KeyAuthorization from './KeyAuthorization.js'
23
+ import * as Owner from './Owner.js'
24
+ import type * as Realm from './Realm.js'
25
+
26
+ /**
27
+ * The email step's link to a wallet.tempo passkey expired, or the browser holds no link cookie for it.
28
+ * The refusal comes before the code is checked, so the code stays valid for a sign-in without the
29
+ * passkey.
30
+ */
31
+ export class LinkExpiredError extends ErrorResponse.define(
32
+ 'passkey_link_expired',
33
+ ) {
34
+ override readonly name = 'Passkey.LinkExpiredError'
35
+
36
+ constructor() {
37
+ super({
38
+ error_description:
39
+ 'Expired passkey sign-in: the link from the passkey step is no longer valid. Enter the code again to sign in with email only, or sign in with the passkey again.',
40
+ field: 'link',
41
+ })
42
+ }
43
+ }
44
+
45
+ /**
46
+ * A passkey sign-in names an app, or reaches a realm, that holds no passkeys for Tempo ID, or no
47
+ * passkeys whose relying party covers the app's page.
48
+ */
49
+ export class NotPermittedError extends ErrorResponse.define(
50
+ 'passkey_not_permitted',
51
+ ) {
52
+ override readonly name = 'Passkey.NotPermittedError'
53
+
54
+ constructor(options: NotPermittedError.Options = {}) {
55
+ const { clientId, origin } = options
56
+ super(
57
+ (() => {
58
+ if (clientId && origin)
59
+ return {
60
+ error_description: `Passkey not permitted: no realm of the app ${clientId} holds passkeys for ${origin}. Sign in with an emailed code.`,
61
+ meta: { client_id: clientId, origin },
62
+ }
63
+ if (clientId)
64
+ return {
65
+ error_description: `Passkey not permitted: the app ${clientId} holds no passkeys. Sign in with an emailed code.`,
66
+ meta: { client_id: clientId },
67
+ }
68
+ return {
69
+ error_description:
70
+ 'Passkey not permitted: no realm holds passkeys for this sign-in. Sign in with an emailed code.',
71
+ }
72
+ })(),
73
+ )
74
+ }
75
+ }
76
+
77
+ export declare namespace NotPermittedError {
78
+ type Options = {
79
+ /** The client ID of the app the sign-in names, if any. */
80
+ clientId?: string | undefined
81
+ /** The origin of the app's page that runs the ceremony, or absent for Tempo ID's own page. */
82
+ origin?: string | undefined
83
+ }
84
+ }
85
+
86
+ /**
87
+ * A passkey ceremony failed: its challenge is unknown, spent, or expired, or the response does not
88
+ * verify. One refusal covers every case, so it reveals neither which check failed nor whether a
89
+ * credential exists.
90
+ */
91
+ export class WebAuthnRejectedError extends ErrorResponse.define(
92
+ 'webauthn_rejected',
93
+ ) {
94
+ override readonly name = 'Passkey.WebAuthnRejectedError'
95
+
96
+ constructor() {
97
+ super({
98
+ error_description:
99
+ 'Passkey rejected. Request new options, then try again with the same passkey or another one.',
100
+ })
101
+ }
102
+ }
103
+
104
+ /** A passkey assertion, as `webauthx/client`'s `Authentication.sign` serializes it. */
105
+ export type Assertion = z.output<typeof schema.assertion>
106
+
107
+ /** A challenge that Tempo ID issued, as `take` returns it once spent. */
108
+ export type Challenge = {
109
+ /** The challenge, as hex. */
110
+ challenge: Hex.Hex
111
+ /** The client ID of the app the sign-in named, or `null` for none. */
112
+ clientId: string | null
113
+ /**
114
+ * The SHA-256 digest of the email step's link that the challenge confirms, as hex, or `undefined`
115
+ * for a sign-in challenge.
116
+ */
117
+ link: string | undefined
118
+ /**
119
+ * The origin of the app's page that the ceremony runs at, or `undefined` for Tempo ID's own page.
120
+ */
121
+ origin: string | undefined
122
+ /**
123
+ * The realm whose relying party the challenge was issued for, or `undefined` when the stored
124
+ * challenge names none, which skips the wallet.tempo lookup.
125
+ */
126
+ realmId: string | undefined
127
+ /** The relying party the challenge was issued for. */
128
+ rpId: string
129
+ /**
130
+ * The witness that a one-tap challenge's key authorization digest binds, or `undefined` for a
131
+ * random challenge.
132
+ */
133
+ witness: Hex.Hex | undefined
134
+ }
135
+
136
+ /** A row of the `keys` table. */
137
+ export type Key = typeof Schema.keys.$inferSelect
138
+
139
+ /** A verified wallet.tempo passkey that waits for an emailed code to name its user. */
140
+ export type Link = z.output<typeof schema.link>
141
+
142
+ /**
143
+ * A challenge for a passkey's signature of a key authorization, as `takeSignature` returns it once
144
+ * spent. The challenge is the key authorization's digest.
145
+ */
146
+ export type SignatureChallenge = {
147
+ /** The address of the Tempo Account that the key acts for, lowercase. */
148
+ account: string
149
+ /** The client ID of the app that requests the key. */
150
+ clientId: string
151
+ /** The passkey that must sign. */
152
+ credential: Owner.Credential
153
+ /** The digest of the key authorization, as hex. */
154
+ digest: Hex.Hex
155
+ }
156
+
157
+ /** Seconds a challenge lasts. */
158
+ export const lifetime = 5 * 60
159
+
160
+ /** Seconds a deferred link lasts. */
161
+ export const linkLifetime = 5 * 60
162
+
163
+ /** The prefix of a challenge's `verification` identifier, which its hex value follows. */
164
+ const prefix = 'passkey-challenge:'
165
+
166
+ /**
167
+ * The prefix of a signature challenge's `verification` identifier, which {@link signatureIdentifier}
168
+ * completes. It differs from {@link prefix}, so a sign-in never spends a signature challenge.
169
+ */
170
+ const signaturePrefix = 'passkey-signature:'
171
+
172
+ /**
173
+ * The longest a spent signature's record lasts, in seconds: a year, the longest a headless key
174
+ * lasts. A redirect's key can name a much later expiry, so the record does not follow it, and each
175
+ * user's approvals add rows that the cleanup cron removes within the year.
176
+ */
177
+ const spentLifetime = KeyAuthorization.lifetime
178
+
179
+ /**
180
+ * The prefix of a spent signature's `verification` identifier, which the SHA-256 digest of the
181
+ * assertion's authenticator data and signature follows.
182
+ */
183
+ const spentPrefix = 'passkey-signature-spent:'
184
+
185
+ /** Zod schemas for passkey ceremonies. */
186
+ export namespace schema {
187
+ const base64url = z.string().regex(/^[A-Za-z0-9_-]+$/)
188
+ const hex = z.templateLiteral(['0x', z.string().regex(/^[0-9a-fA-F]*$/)])
189
+
190
+ /**
191
+ * A credential ID in canonical unpadded base64url, of at most 1,023 bytes, as WebAuthn bounds it:
192
+ * 1,364 characters.
193
+ */
194
+ export const credentialId = base64url.min(1).max(1364)
195
+
196
+ /** A passkey assertion, as `webauthx/client`'s `Authentication.sign` serializes it. */
197
+ export const assertion = z
198
+ .object({
199
+ id: credentialId.meta({
200
+ description: 'The credential ID of the passkey that signed, base64url.',
201
+ examples: ['KEbWNCc7NgaYnUyrNeFGX9_3Y-8'],
202
+ }),
203
+ metadata: z
204
+ .object({
205
+ authenticatorData: hex.meta({
206
+ description:
207
+ 'The authenticator data the passkey signed, as hex: the relying party ID hash, flags, and signature counter.',
208
+ examples: [
209
+ '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000',
210
+ ],
211
+ }),
212
+ challengeIndex: z
213
+ .number()
214
+ .int()
215
+ .nonnegative()
216
+ .optional()
217
+ .meta({
218
+ description:
219
+ 'The character offset of `"challenge"` in `clientDataJSON`. Absent, verification searches for it.',
220
+ examples: [23],
221
+ }),
222
+ clientDataJSON: z
223
+ .string()
224
+ .max(4096)
225
+ .meta({
226
+ description:
227
+ 'The client data the browser collected, as a JSON string, which holds the type, challenge, and origin.',
228
+ examples: [
229
+ '{"type":"webauthn.get","challenge":"9jEFijuhEWrM4SOW-tChJbUEHEP44VcjcJ-Bqo1fTM8","origin":"https://id.tempo.xyz","crossOrigin":false}',
230
+ ],
231
+ }),
232
+ typeIndex: z
233
+ .number()
234
+ .int()
235
+ .nonnegative()
236
+ .optional()
237
+ .meta({
238
+ description:
239
+ 'The character offset of `"type"` in `clientDataJSON`. Absent, verification searches for it.',
240
+ examples: [1],
241
+ }),
242
+ userVerificationRequired: z
243
+ .boolean()
244
+ .optional()
245
+ .meta({
246
+ description:
247
+ 'Whether the client required user verification. Verification ignores it and always requires user verification.',
248
+ examples: [true],
249
+ }),
250
+ })
251
+ .meta({
252
+ description:
253
+ 'The signed data that verification reads with `signature`, decoded from the browser response.',
254
+ }),
255
+ raw: z
256
+ .object({
257
+ authenticatorAttachment: z
258
+ .string()
259
+ .nullable()
260
+ .default(null)
261
+ .meta({
262
+ description:
263
+ 'How the authenticator attaches to the client, such as `platform` or `cross-platform`. Defaults to `null` when the browser reports none.',
264
+ examples: ['platform'],
265
+ }),
266
+ id: base64url.meta({
267
+ description:
268
+ 'The credential ID, base64url, as the browser returns it.',
269
+ examples: ['KEbWNCc7NgaYnUyrNeFGX9_3Y-8'],
270
+ }),
271
+ rawId: base64url.meta({
272
+ description: 'The bytes of the credential ID, base64url.',
273
+ examples: ['KEbWNCc7NgaYnUyrNeFGX9_3Y-8'],
274
+ }),
275
+ // Verification reads the top-level `metadata` and `signature`, so other fields are dropped.
276
+ response: z
277
+ .object({
278
+ clientDataJSON: base64url.meta({
279
+ description: 'The client data JSON, base64url.',
280
+ examples: [
281
+ 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiOWpFRmlqdWhFV3JNNFNPVy10Q2hKYlVFSEVQNDRWY2pjSi1CcW8xZlRNOCIsIm9yaWdpbiI6Imh0dHBzOi8vaWQudGVtcG8ueHl6IiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ',
282
+ ],
283
+ }),
284
+ })
285
+ .meta({
286
+ description:
287
+ "The authenticator's response. Verification keeps only `clientDataJSON` and drops other fields.",
288
+ }),
289
+ type: z.string().meta({
290
+ description: 'The credential type.',
291
+ examples: ['public-key'],
292
+ }),
293
+ })
294
+ .meta({
295
+ description:
296
+ 'The `PublicKeyCredential` that `navigator.credentials.get()` returns, serialized as JSON.',
297
+ }),
298
+ signature: hex.meta({
299
+ description:
300
+ 'The P-256 signature over the authenticator data and the client data hash, as 64-byte hex: `r` then `s`.',
301
+ examples: [
302
+ '0x6c3f1e0a9b7d24f58e1c3a6b9d0f2e4a7c5b8d1e3f6a9c2b4d7e0f1a3c5b8d9e1f2a7c4b6d8e0f3a5c7b9d1e4f6a8c0b2d5e7f9a1c3b6d8e0f2a4c7b9d1e3f5a',
303
+ ],
304
+ }),
305
+ })
306
+ .meta({
307
+ description: 'The assertion, serialized as `webauthx/client` returns it.',
308
+ id: 'PasskeyAssertion',
309
+ })
310
+
311
+ /** The client data fields that `take` and `verify` read. */
312
+ export const clientData = z.object({
313
+ challenge: base64url,
314
+ crossOrigin: z.boolean().optional(),
315
+ })
316
+
317
+ /** A deferred link, as `seal` encrypts it. */
318
+ export const link = z.object({
319
+ address: z.string(),
320
+ createdAt: z.number().nullable(),
321
+ /** When the link expires, in seconds. */
322
+ expiresAt: z.number(),
323
+ /** The host that sealed it. Another host that shares the secret does not accept it. */
324
+ host: z.string(),
325
+ id: z.string(),
326
+ /** The value that the email step carries, which the emailed-code sign-in sends back. */
327
+ nonce: z.string(),
328
+ publicKey: z.templateLiteral(['0x', z.string()]),
329
+ rpId: z.string(),
330
+ })
331
+
332
+ /** A signature challenge's `verification` value. */
333
+ export const signature = z.object({
334
+ account: z.string(),
335
+ clientId: z.string(),
336
+ credential: z.object({
337
+ id: z.string(),
338
+ publicKey: hex,
339
+ rpId: z.string(),
340
+ }),
341
+ })
342
+
343
+ /** The passkey step, which an approval of a key for a passkey-owned account returns. */
344
+ export const step = z
345
+ .object({
346
+ step: z
347
+ .object({
348
+ options: z
349
+ .object({
350
+ publicKey: z
351
+ .object({
352
+ allowCredentials: z
353
+ .array(
354
+ z
355
+ .object({
356
+ id: credentialId.meta({
357
+ description:
358
+ "The credential ID of the account's passkey, base64url.",
359
+ examples: ['KEbWNCc7NgaYnUyrNeFGX9_3Y-8'],
360
+ }),
361
+ type: z.literal('public-key').meta({
362
+ description: 'The credential type.',
363
+ }),
364
+ })
365
+ .meta({
366
+ description: 'The passkey that must sign.',
367
+ }),
368
+ )
369
+ .meta({
370
+ description:
371
+ "The account's passkey, the only one the browser offers.",
372
+ }),
373
+ challenge: z.templateLiteral(['0x', z.string()]).meta({
374
+ description:
375
+ "The key authorization's digest, as hex. It is single-use, and expires after 5 minutes.",
376
+ examples: [
377
+ '0xf6310589abb21116acc6e123967ed0a125b5041c43f8e157237093e07eaa354c',
378
+ ],
379
+ }),
380
+ rpId: z.string().meta({
381
+ description:
382
+ "The relying party ID of the account's passkey.",
383
+ examples: ['tempo.xyz'],
384
+ }),
385
+ userVerification: z.literal('required').meta({
386
+ description:
387
+ 'Every passkey signature requires user verification.',
388
+ }),
389
+ })
390
+ .meta({
391
+ description:
392
+ 'The `PublicKeyCredentialRequestOptions`, serialized with the challenge as hex.',
393
+ }),
394
+ })
395
+ .meta({
396
+ description: 'The options for `navigator.credentials.get()`.',
397
+ }),
398
+ type: z.literal('passkey').meta({
399
+ description:
400
+ 'Sign the options with the passkey, then repeat the approval with `passkey: { assertion }`.',
401
+ }),
402
+ })
403
+ .meta({
404
+ description:
405
+ "The next step: the passkey's signature over the key authorization's digest.",
406
+ }),
407
+ })
408
+ .meta({
409
+ description:
410
+ 'An approval that continues when the passkey signs the key authorization.',
411
+ id: 'PasskeyStep',
412
+ })
413
+
414
+ /** A challenge's `verification` value. */
415
+ export const value = z.object({
416
+ clientId: z.string().nullable(),
417
+ link: z.string().optional(),
418
+ // A challenge without an origin runs at Tempo ID's own page.
419
+ origin: z.string().optional(),
420
+ // A stored challenge can name no realm. It verifies against stored passkeys only.
421
+ realmId: z.string().optional(),
422
+ rpId: z.string(),
423
+ // A one-tap challenge is the digest of a key authorization with this witness.
424
+ witness: z.templateLiteral(['0x', z.string()]).optional(),
425
+ })
426
+ }
427
+
428
+ /**
429
+ * Issues a challenge for a sign-in at `rpId`, stores it until it expires, and returns it as hex. The
430
+ * challenge is random, or the digest of a key authorization for a one-tap sign-in, which stores its
431
+ * witness beside it.
432
+ */
433
+ export async function challenge(
434
+ db: Realm.Database,
435
+ options: challenge.Options,
436
+ ) {
437
+ const value = (
438
+ options.oneTap?.digest ??
439
+ Hex.fromBytes(crypto.getRandomValues(new Uint8Array(32)))
440
+ ).toLowerCase() as Hex.Hex
441
+ await db.insert(Schema.verification).values({
442
+ expiresAt: new Date(Date.now() + lifetime * 1000),
443
+ id: crypto.randomUUID(),
444
+ identifier: prefix + value,
445
+ value: JSON.stringify(
446
+ schema.value.parse({
447
+ clientId: options.clientId,
448
+ link: options.link,
449
+ origin: options.origin,
450
+ realmId: options.realmId,
451
+ rpId: options.rpId,
452
+ witness: options.oneTap?.witness,
453
+ }),
454
+ ),
455
+ })
456
+ return value
457
+ }
458
+
459
+ export declare namespace challenge {
460
+ type Options = {
461
+ /** The client ID of the app the sign-in names, or `null` for none. */
462
+ clientId: string | null
463
+ /**
464
+ * The SHA-256 digest of the email step's link that the challenge confirms, as hex. Absent for a
465
+ * sign-in challenge.
466
+ */
467
+ link?: string | undefined
468
+ /**
469
+ * The digest of the key authorization that a one-tap sign-in signs, and the witness that the
470
+ * digest binds. The digest takes the place of a random challenge. Absent for a plain sign-in.
471
+ */
472
+ oneTap?:
473
+ | {
474
+ /** The key authorization's digest, as hex. */
475
+ digest: Hex.Hex
476
+ /** The witness that the digest binds. */
477
+ witness: Hex.Hex
478
+ }
479
+ | undefined
480
+ /**
481
+ * The origin of the app's page that the ceremony runs at. Absent for Tempo ID's own page. Only
482
+ * an assertion that reports it, from a request that page sends, spends the challenge.
483
+ */
484
+ origin?: string | undefined
485
+ /** The realm whose relying party the sign-in uses. */
486
+ realmId: string
487
+ /** The relying party ID the passkey must belong to. */
488
+ rpId: string
489
+ }
490
+ }
491
+
492
+ /**
493
+ * Returns why Tempo ID refuses a passkey ceremony on an app's page, or `undefined` when the page may
494
+ * run one. Tempo ID verifies the assertion for the page's origin, so only a first-party web app's
495
+ * page, at the origin of one of its redirect URIs, runs a ceremony:
496
+ *
497
+ * - A page sends the app's `client_id`, and the app must exist, be enabled, and skip consent.
498
+ * - A native app's request proves no app, so it signs in through an authentication sheet.
499
+ * - The request's `Origin` must be the origin of one of the app's redirect URIs.
500
+ */
501
+ export function checkPage(
502
+ options: checkPage.Options,
503
+ ): ErrorResponse.Instance<typeof checkPage.errors> | undefined {
504
+ const { app, clientId, origin } = options
505
+ if (!clientId)
506
+ return new Authorize.InvalidOriginError({
507
+ error_description: `Invalid \`Origin\`: ${origin ?? 'none'}. A passkey sign-in from an app's page sends the app's \`client_id\`.`,
508
+ field: 'Origin',
509
+ ...(origin ? { meta: { origin } } : {}),
510
+ })
511
+ if (!app || !app.skipConsent)
512
+ return new Authorize.RejectedError({
513
+ description: `Passkey sign-in refused: ${clientId} is not a first-party app. Send the user to \`/oauth2/authorize\`, which offers passkey sign-in.`,
514
+ })
515
+ if (app.type === 'native')
516
+ return new Authorize.RejectedError({
517
+ description:
518
+ "Passkey sign-in refused: a native app's request does not prove which app sends it. Open `/oauth2/authorize` in an authentication sheet.",
519
+ })
520
+
521
+ const origins = app.redirectUris.flatMap((uri) => {
522
+ const url = URL.parse(uri)
523
+ return url?.protocol === 'https:' || url?.protocol === 'http:'
524
+ ? [url.origin]
525
+ : []
526
+ })
527
+ if (!origin || !origins.includes(origin))
528
+ return new Authorize.InvalidOriginError({
529
+ error_description: `Invalid \`Origin\`: ${origin ?? 'none'}. Passkey sign-in for ${clientId} must come from the origin of one of its redirect URIs.`,
530
+ field: 'Origin',
531
+ ...(origin ? { meta: { origin } } : {}),
532
+ })
533
+ return undefined
534
+ }
535
+
536
+ export namespace checkPage {
537
+ export type Options = {
538
+ /** The app that `clientId` names, or `undefined` when no enabled app has it. */
539
+ app: Pick<Apps.App, 'redirectUris' | 'skipConsent' | 'type'> | undefined
540
+ /** The client ID that the request sends, if any. */
541
+ clientId: string | undefined
542
+ /** The request's `Origin`, if any. */
543
+ origin: string | undefined
544
+ }
545
+
546
+ /** The refusals `checkPage` returns. Its return type admits no other. */
547
+ export const errors = [
548
+ Authorize.InvalidOriginError,
549
+ Authorize.RejectedError,
550
+ ] as const
551
+ }
552
+
553
+ /**
554
+ * Returns the passkey with the credential `id` that belongs to `rpId`, or `undefined` when Tempo ID
555
+ * stores none.
556
+ */
557
+ export async function get(db: Realm.Database, options: get.Options) {
558
+ const [key] = await db
559
+ .select()
560
+ .from(Schema.keys)
561
+ .where(
562
+ and(eq(Schema.keys.id, options.id), eq(Schema.keys.rpId, options.rpId)),
563
+ )
564
+ return key
565
+ }
566
+
567
+ export declare namespace get {
568
+ type Options = {
569
+ /** The credential ID, base64url. */
570
+ id: string
571
+ /** The relying party ID the passkey must belong to. */
572
+ rpId: string
573
+ }
574
+ }
575
+
576
+ /**
577
+ * Stores a verified wallet.tempo passkey for `userId`, and returns the user the passkey's row names.
578
+ * A concurrent first sign-in of the same passkey can store the row first, so the row decides.
579
+ * Returns `undefined` when another passkey holds the address.
580
+ */
581
+ export async function link(db: Realm.Database, options: link.Options) {
582
+ const { credential, id, rpId, userId } = options
583
+ // `TempoAccount.create` takes the same lock to claim the realm of a first account, so a link
584
+ // either commits before the claim, and the claim gives way to it, or after it. A link after the
585
+ // claim adds the passkey's account beside the created one, as a link after its creation does.
586
+ return await db.transaction(async (tx) => {
587
+ await Owner.lock(tx, { userId })
588
+ // The `default` realm without a row signs in with a relying party that `Realm.withDefaults`
589
+ // derives. The first linked passkey stores it, so the unique `rp_id` keeps another realm from
590
+ // taking it, and a later change to `URL` keeps the passkeys that exist. A row already stored
591
+ // for `default`, or for this relying party, leaves the insert without effect.
592
+ await tx
593
+ .insert(Schema.realmPasskeys)
594
+ .values({ realmId: 'default', rpId })
595
+ .onConflictDoNothing()
596
+ const [inserted] = await tx
597
+ .insert(Schema.keys)
598
+ .values({
599
+ address: credential.address.toLowerCase(),
600
+ ...(credential.createdAt === null
601
+ ? {}
602
+ : { createdAt: new Date(credential.createdAt) }),
603
+ id,
604
+ publicKey: credential.publicKey,
605
+ rpId,
606
+ source: 'wallet',
607
+ type: 'passkey',
608
+ userId,
609
+ })
610
+ .onConflictDoNothing()
611
+ .returning({ userId: Schema.keys.userId })
612
+ if (inserted) return inserted.userId
613
+
614
+ const existing = await get(tx, { id, rpId })
615
+ return existing?.userId
616
+ })
617
+ }
618
+
619
+ export declare namespace link {
620
+ type Options = {
621
+ /** The passkey that wallet.tempo returned, which the assertion verified. */
622
+ credential: {
623
+ /** The address that the public key derives. */
624
+ address: string
625
+ /** When wallet.tempo created the passkey, in milliseconds, or `null` when it has no time. */
626
+ createdAt: number | null
627
+ /** The uncompressed P256 public key, hex. */
628
+ publicKey: Hex.Hex
629
+ }
630
+ /** The credential ID, base64url. */
631
+ id: string
632
+ /** The relying party of the passkey: the `default` realm's, which wallet.tempo shares. */
633
+ rpId: string
634
+ /** The user the passkey links to. */
635
+ userId: string
636
+ }
637
+ }
638
+
639
+ /**
640
+ * Derives the link cookie's own key from `secret` with HKDF-SHA256, as hex. The cookie thus shares
641
+ * no key with anything else that `SECRET` encrypts, such as the signing keys.
642
+ */
643
+ async function linkKey(secret: string) {
644
+ const encoder = new TextEncoder()
645
+ const material = await crypto.subtle.importKey(
646
+ 'raw',
647
+ encoder.encode(secret),
648
+ 'HKDF',
649
+ false,
650
+ ['deriveBits'],
651
+ )
652
+ const bits = await crypto.subtle.deriveBits(
653
+ {
654
+ hash: 'SHA-256',
655
+ info: encoder.encode('tempo-id:passkey-link:v1'),
656
+ name: 'HKDF',
657
+ salt: new Uint8Array(),
658
+ },
659
+ material,
660
+ 256,
661
+ )
662
+ return Hex.fromBytes(new Uint8Array(bits))
663
+ }
664
+
665
+ /**
666
+ * Instantiates a passkey sign-in plugin for Better Auth. Its server-only `signInPasskey` endpoint
667
+ * creates a session for a user whose assertion a route verified, and sets the session cookie. Like
668
+ * every sign-in, it continues an app's authorization that `oauth_query` names.
669
+ */
670
+ export function plugin() {
671
+ return {
672
+ endpoints: {
673
+ signInPasskey: createAuthEndpoint.serverOnly(
674
+ {
675
+ body: z.object({
676
+ oauth_query: z.string().optional(),
677
+ userId: z.string(),
678
+ }),
679
+ method: 'POST',
680
+ },
681
+ async (ctx) => {
682
+ const user = await ctx.context.internalAdapter.findUserById(
683
+ ctx.body.userId,
684
+ )
685
+ if (!user) throw new Error('The passkey user is missing.')
686
+ const session = await ctx.context.internalAdapter.createSession(
687
+ user.id,
688
+ )
689
+ await setSessionCookie(ctx, { session, user })
690
+ return ctx.json({ user: { email: user.email, id: user.id } })
691
+ },
692
+ ),
693
+ },
694
+ id: 'tempo-id-passkey',
695
+ } satisfies BetterAuthPlugin
696
+ }
697
+
698
+ /**
699
+ * Returns the first of `realms` that holds passkeys which can sign in at `origin`, and at `page`
700
+ * when given, with its relying party ID, or `undefined` when none can. A WebAuthn ceremony runs
701
+ * only at an origin whose host is the relying party ID or one of its subdomains.
702
+ */
703
+ export function relyingParty(
704
+ realms: readonly Realm.Realm[],
705
+ options: relyingParty.Options,
706
+ ) {
707
+ const hosts = [options.origin, options.page].flatMap((origin) =>
708
+ origin ? [new URL(origin).hostname] : [],
709
+ )
710
+ for (const realm of realms) {
711
+ const rpId = realm.passkeys?.rpId
712
+ if (
713
+ rpId !== undefined &&
714
+ hosts.every((host) => host === rpId || host.endsWith(`.${rpId}`))
715
+ )
716
+ return { realmId: realm.id, rpId }
717
+ }
718
+ return undefined
719
+ }
720
+
721
+ export declare namespace relyingParty {
722
+ type Options = {
723
+ /** Tempo ID's own origin. */
724
+ origin: string
725
+ /**
726
+ * The origin of the app's page that runs the ceremony, if any. The relying party must cover it
727
+ * and Tempo ID's origin both, so the page shares a site with Tempo ID, and the browser sends the
728
+ * session cookie on the page's requests after the sign-in.
729
+ */
730
+ page?: string | undefined
731
+ }
732
+ }
733
+
734
+ /**
735
+ * Stores the digest of a key authorization as the challenge of a signature by `credential`, until it
736
+ * expires. Only the user with `userId` spends it, for the same account, app, and passkey.
737
+ */
738
+ export async function requestSignature(
739
+ db: Realm.Database,
740
+ options: requestSignature.Options,
741
+ ) {
742
+ const { account, clientId, credential, digest, userId } = options
743
+ await db.insert(Schema.verification).values({
744
+ expiresAt: new Date(Date.now() + lifetime * 1000),
745
+ id: crypto.randomUUID(),
746
+ identifier: signatureIdentifier({
747
+ account,
748
+ clientId,
749
+ credentialId: credential.id,
750
+ digest,
751
+ userId,
752
+ }),
753
+ value: JSON.stringify(
754
+ schema.signature.parse({
755
+ account: account.toLowerCase(),
756
+ clientId,
757
+ credential,
758
+ }),
759
+ ),
760
+ })
761
+ }
762
+
763
+ export declare namespace requestSignature {
764
+ type Options = {
765
+ /** The address of the Tempo Account that the key acts for. */
766
+ account: string
767
+ /** The client ID of the app that requests the key. */
768
+ clientId: string
769
+ /** The passkey that must sign: the account's owner. */
770
+ credential: Owner.Credential
771
+ /** The digest of the key authorization, as hex. */
772
+ digest: Hex.Hex
773
+ /** The signed-in user's ID. */
774
+ userId: string
775
+ }
776
+ }
777
+
778
+ /**
779
+ * Encrypts a deferred link with a key derived from `secret`, for the link cookie. The cookie cannot
780
+ * be read, changed, or forged without the secret.
781
+ */
782
+ export async function seal(value: Link, options: seal.Options) {
783
+ return await symmetricEncrypt({
784
+ data: JSON.stringify(schema.link.parse(value)),
785
+ key: await linkKey(options.secret),
786
+ })
787
+ }
788
+
789
+ export declare namespace seal {
790
+ type Options = {
791
+ /** The `SECRET` variable. */
792
+ secret: string
793
+ }
794
+ }
795
+
796
+ /**
797
+ * Spends the challenge that `clientDataJSON` names, and returns it, or `undefined` when it is
798
+ * unknown, spent, or expired. One statement deletes and returns the row, so two concurrent
799
+ * assertions over one challenge cannot both spend it.
800
+ */
801
+ export async function take(
802
+ db: Realm.Database,
803
+ options: take.Options,
804
+ ): Promise<Challenge | undefined> {
805
+ const challenge = (() => {
806
+ try {
807
+ const data = schema.clientData.parse(JSON.parse(options.clientDataJSON))
808
+ return Base64.toHex(data.challenge)
809
+ } catch {
810
+ return undefined
811
+ }
812
+ })()
813
+ if (!challenge) return undefined
814
+
815
+ const [row] = await db
816
+ .delete(Schema.verification)
817
+ .where(
818
+ and(
819
+ eq(Schema.verification.identifier, prefix + challenge.toLowerCase()),
820
+ gt(Schema.verification.expiresAt, new Date()),
821
+ ),
822
+ )
823
+ .returning({ value: Schema.verification.value })
824
+ if (!row) return undefined
825
+
826
+ const value = schema.value.parse(JSON.parse(row.value))
827
+ return {
828
+ challenge,
829
+ clientId: value.clientId,
830
+ link: value.link,
831
+ origin: value.origin,
832
+ realmId: value.realmId,
833
+ rpId: value.rpId,
834
+ witness: value.witness,
835
+ }
836
+ }
837
+
838
+ export declare namespace take {
839
+ type Options = {
840
+ /** The assertion's client data, as the JSON string the browser collected. */
841
+ clientDataJSON: string
842
+ }
843
+ }
844
+
845
+ /**
846
+ * Spends the signature challenge that `clientDataJSON` names, for the user, app, account, and
847
+ * passkey of `options`, and returns it, or `undefined` when it is unknown, spent, or expired. One
848
+ * statement deletes and returns the rows, so two concurrent assertions over one challenge cannot
849
+ * both spend it, and an approval for another app, account, or passkey spends none of its rows.
850
+ *
851
+ * Returns the challenge only when `verify` accepts it with the assertion. Every step for the same
852
+ * key issues the same digest, so the transaction then records the assertion as spent until
853
+ * `expiry`, for a year at most, and returns `undefined` for an assertion that it recorded before. A captured assertion
854
+ * thus signs no second approval, and an assertion that does not verify leaves no record.
855
+ */
856
+ export async function takeSignature(
857
+ db: Realm.Database,
858
+ options: takeSignature.Options,
859
+ ): Promise<SignatureChallenge | undefined> {
860
+ const digest = (() => {
861
+ try {
862
+ const data = schema.clientData.parse(JSON.parse(options.clientDataJSON))
863
+ return Base64.toHex(data.challenge)
864
+ } catch {
865
+ return undefined
866
+ }
867
+ })()
868
+ if (!digest) return undefined
869
+
870
+ const spent =
871
+ spentPrefix +
872
+ Hash.sha256(Hex.concat(options.authenticatorData, options.signature))
873
+
874
+ return await db.transaction(async (tx) => {
875
+ // The lock serializes requests with one assertion, so only the first records it.
876
+ await tx.execute(
877
+ sql`select pg_advisory_xact_lock(hashtextextended(${spent}, 0))`,
878
+ )
879
+ const [used] = await tx
880
+ .select({ id: Schema.verification.id })
881
+ .from(Schema.verification)
882
+ .where(
883
+ and(
884
+ eq(Schema.verification.identifier, spent),
885
+ gt(Schema.verification.expiresAt, new Date()),
886
+ ),
887
+ )
888
+
889
+ // Each approval of one request issues the same digest, so every row that it names is spent.
890
+ // A replayed assertion spends the challenge too, so the step must run again.
891
+ const [row] = await tx
892
+ .delete(Schema.verification)
893
+ .where(
894
+ and(
895
+ eq(
896
+ Schema.verification.identifier,
897
+ signatureIdentifier({ ...options, digest }),
898
+ ),
899
+ gt(Schema.verification.expiresAt, new Date()),
900
+ ),
901
+ )
902
+ .returning({ value: Schema.verification.value })
903
+ if (!row || used) return undefined
904
+ const value = { ...schema.signature.parse(JSON.parse(row.value)), digest }
905
+ if (!options.verify(value)) return undefined
906
+
907
+ // A signed request with the same key can reach a new step until the key expires.
908
+ await tx.insert(Schema.verification).values({
909
+ expiresAt: new Date(
910
+ Math.min(
911
+ Math.max(options.expiry * 1000, Date.now() + lifetime * 1000),
912
+ Date.now() + spentLifetime * 1000,
913
+ ),
914
+ ),
915
+ id: crypto.randomUUID(),
916
+ identifier: spent,
917
+ value: '{}',
918
+ })
919
+ return value
920
+ })
921
+ }
922
+
923
+ export declare namespace takeSignature {
924
+ type Options = {
925
+ /** The address of the Tempo Account that the key acts for. */
926
+ account: string
927
+ /** The assertion's authenticator data, as hex. */
928
+ authenticatorData: Hex.Hex
929
+ /** The client ID of the app that requests the key. */
930
+ clientId: string
931
+ /** The assertion's client data, as the JSON string the browser collected. */
932
+ clientDataJSON: string
933
+ /** The credential ID of the passkey that signed, base64url. */
934
+ credentialId: string
935
+ /**
936
+ * When the key expires, in Unix seconds. The spent assertion stays recorded until then, for a
937
+ * year at most.
938
+ */
939
+ expiry: number
940
+ /** The assertion's signature, as hex. */
941
+ signature: Hex.Hex
942
+ /** The signed-in user's ID. */
943
+ userId: string
944
+ /**
945
+ * Returns whether the assertion verifies over the spent challenge, for the account, app, and
946
+ * passkey that it names. It runs in the transaction, so it performs no I/O.
947
+ */
948
+ verify: (challenge: SignatureChallenge) => boolean
949
+ }
950
+ }
951
+
952
+ /**
953
+ * Returns the `verification` identifier of a signature challenge, which names its user, app,
954
+ * account, passkey, and digest. JSON keeps each value apart, whatever characters it holds.
955
+ */
956
+ function signatureIdentifier(options: signatureIdentifier.Options) {
957
+ return (
958
+ signaturePrefix +
959
+ JSON.stringify([
960
+ options.userId,
961
+ options.clientId,
962
+ options.account.toLowerCase(),
963
+ options.credentialId,
964
+ options.digest.toLowerCase(),
965
+ ])
966
+ )
967
+ }
968
+
969
+ declare namespace signatureIdentifier {
970
+ type Options = {
971
+ /** The address of the Tempo Account that the key acts for. */
972
+ account: string
973
+ /** The client ID of the app that requests the key. */
974
+ clientId: string
975
+ /** The credential ID of the passkey that signs, base64url. */
976
+ credentialId: string
977
+ /** The digest of the key authorization, as hex. */
978
+ digest: Hex.Hex
979
+ /** The signed-in user's ID. */
980
+ userId: string
981
+ }
982
+ }
983
+
984
+ /**
985
+ * Decrypts the link cookie's value, and returns the deferred link, or `undefined` when the value
986
+ * does not decrypt with `secret`, has another shape, came from another host, or expired.
987
+ */
988
+ export async function unseal(sealed: string, options: unseal.Options) {
989
+ const value = await symmetricDecrypt({
990
+ data: sealed,
991
+ key: await linkKey(options.secret),
992
+ })
993
+ .then((data) => schema.link.safeParse(JSON.parse(data)))
994
+ .catch(() => undefined)
995
+ if (!value?.success) return undefined
996
+ if (value.data.host !== options.host) return undefined
997
+ if (value.data.expiresAt <= Date.now() / 1000) return undefined
998
+ return value.data
999
+ }
1000
+
1001
+ export declare namespace unseal {
1002
+ type Options = {
1003
+ /** The host that serves the request, which must be the one that sealed the link. */
1004
+ host: string
1005
+ /** The `SECRET` variable. */
1006
+ secret: string
1007
+ }
1008
+ }
1009
+
1010
+ /**
1011
+ * Returns whether `assertion` signs `challenge` with `publicKey`, at `origin`, for `rpId`, with the
1012
+ * user present and verified, outside a cross-origin iframe. The client cannot waive user
1013
+ * verification.
1014
+ */
1015
+ export function verify(assertion: Assertion, options: verify.Options) {
1016
+ try {
1017
+ // WebAuthn verification leaves `crossOrigin` to the relying party. A frame on another site
1018
+ // could otherwise run the ceremony for Tempo ID's origin.
1019
+ const data = schema.clientData.parse(
1020
+ JSON.parse(assertion.metadata.clientDataJSON),
1021
+ )
1022
+ if (data.crossOrigin) return false
1023
+
1024
+ return Authentication.verify(
1025
+ {
1026
+ ...assertion,
1027
+ metadata: { ...assertion.metadata, userVerificationRequired: true },
1028
+ },
1029
+ {
1030
+ challenge: options.challenge,
1031
+ origin: options.origin,
1032
+ publicKey: options.publicKey,
1033
+ rpId: options.rpId,
1034
+ },
1035
+ )
1036
+ } catch {
1037
+ return false
1038
+ }
1039
+ }
1040
+
1041
+ export declare namespace verify {
1042
+ type Options = {
1043
+ /** The challenge that Tempo ID issued, as hex. */
1044
+ challenge: Hex.Hex
1045
+ /** The origin the ceremony must report: Tempo ID's own, or the app's page that the challenge names. */
1046
+ origin: string
1047
+ /** The passkey's uncompressed P256 public key, hex. */
1048
+ publicKey: Hex.Hex
1049
+ /** The relying party ID the challenge was issued for. */
1050
+ rpId: string
1051
+ }
1052
+ }