@tempoxyz/id 0.0.0-pr-187.sha-eed04d3

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 +81 -0
  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-DjQNeRZp.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 +82 -0
  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,874 @@
1
+ /**
2
+ * The device grant (RFC 8628) for CLIs under `/v1/device`. A CLI requests a code, a signed-in user
3
+ * approves it on the device page from any browser, and the CLI polls until it receives tokens for
4
+ * its device app, `tempo-cli` unless it names another. Better Auth issues the codes and the tokens,
5
+ * in process. A CLI can also request a key authorization, which the device page shows, approval
6
+ * signs, and the first redemption returns beside the tokens. Approval and denial accept requests
7
+ * only from Tempo ID's own page, so no app's page decides a code for the user.
8
+ * @module
9
+ */
10
+ import { isAPIError } from 'better-auth/api'
11
+ import type { Context } from 'hono'
12
+ import { Hono } from 'hono'
13
+ import { env } from 'void/env'
14
+ import * as z from 'zod'
15
+ import * as Apps from '../internal/Apps.js'
16
+ import * as core_Auth from '../internal/Auth.js'
17
+ import * as Authorize from '../internal/Authorize.js'
18
+ import * as BodyLimit from '../internal/BodyLimit.js'
19
+ import * as Device from '../internal/Device.js'
20
+ import type { Env } from '../internal/Env.js'
21
+ import * as ErrorResponse from '../internal/ErrorResponse.js'
22
+ import * as KeyAuthorization from '../internal/KeyAuthorization.js'
23
+ import * as Log from '../internal/Log.js'
24
+ import * as OpenApi from '../internal/OpenApi.js'
25
+ import * as Passkey from '../internal/Passkey.js'
26
+ import * as RateLimit from '../internal/RateLimit.js'
27
+ import * as TempoAccount from '../internal/TempoAccount.js'
28
+ import * as Timing from '../internal/Timing.js'
29
+ import * as Cors from './lib/Cors.js'
30
+ import { db } from './lib/Database.js'
31
+ import * as DeviceRequest from './lib/DeviceRequest.js'
32
+ import * as KeySigner from './lib/KeySigner.js'
33
+ import * as Session from './lib/Session.js'
34
+
35
+ namespace schema {
36
+ export namespace authorize {
37
+ export const json = z
38
+ .strictObject({
39
+ // The device page shows the challenge as the message that the key signs, so control and
40
+ // format characters, such as bidi overrides, cannot reorder it. Newlines and tabs stay.
41
+ challenge: Authorize.schema.challenge.refine(
42
+ (value) =>
43
+ value === undefined || /^(?:[\t\n]|[^\p{Cc}\p{Cf}])*$/u.test(value),
44
+ {
45
+ message:
46
+ 'Remove control and format characters, such as bidi overrides',
47
+ },
48
+ ),
49
+ client_id: z
50
+ .string()
51
+ .min(1)
52
+ .max(255)
53
+ .optional()
54
+ .meta({
55
+ description:
56
+ 'The client ID of the device app that the CLI signs in to, whose realms the sign-in and the device page use. Defaults to `tempo-cli`.',
57
+ examples: ['tempo-cli'],
58
+ }),
59
+ device: z
60
+ .string()
61
+ .trim()
62
+ .min(1)
63
+ .max(64)
64
+ // The device page shows the name as the device reports it, so control and format characters,
65
+ // such as bidi overrides, cannot reorder the text around it.
66
+ .regex(/^[^\p{Cc}\p{Cf}]*$/u, {
67
+ message:
68
+ 'Remove control and format characters, such as bidi overrides',
69
+ })
70
+ .meta({
71
+ description:
72
+ "The name that the device page shows, such as the machine's hostname. It holds no control or format characters.",
73
+ examples: ['ada-macbook'],
74
+ }),
75
+ key_authorization: KeyAuthorization.schema.request,
76
+ })
77
+ .refine((request) => !request.challenge || request.key_authorization, {
78
+ message:
79
+ 'A challenge requires `key_authorization`, whose witness binds it',
80
+ path: ['challenge'],
81
+ })
82
+
83
+ export const response = z.object({
84
+ device_code: z.string().meta({
85
+ description:
86
+ 'The secret code that the CLI polls `/v1/device/token` with (RFC 8628 §3.2). The CLI keeps it private.',
87
+ examples: ['GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS'],
88
+ }),
89
+ expires_in: z.number().meta({
90
+ description: 'Seconds until the device code and user code expire.',
91
+ examples: [600],
92
+ }),
93
+ interval: z.number().meta({
94
+ description: 'Seconds between polls.',
95
+ examples: [5],
96
+ }),
97
+ user_code: z.string().meta({
98
+ description:
99
+ 'The code that the user enters on the device page: eight consonants in two groups of four.',
100
+ examples: ['WDJB-MJHT'],
101
+ }),
102
+ verification_uri: z.url().meta({
103
+ description: 'The page where the user enters the user code.',
104
+ examples: ['https://id.tempo.xyz/device'],
105
+ }),
106
+ verification_uri_complete: z.url().meta({
107
+ description:
108
+ 'The device page with the user code filled in, for a link or QR code.',
109
+ examples: ['https://id.tempo.xyz/device?code=WDJB-MJHT'],
110
+ }),
111
+ })
112
+ }
113
+
114
+ export namespace token {
115
+ export const json = z.object({
116
+ client_id: authorize.json.shape.client_id.meta({
117
+ description:
118
+ 'The client ID that `/v1/device/authorize` named. Defaults to `tempo-cli`.',
119
+ examples: ['tempo-cli'],
120
+ }),
121
+ device_code: z
122
+ .string()
123
+ .min(1)
124
+ .max(128)
125
+ .meta({
126
+ description: 'The device code that `/v1/device/authorize` returned.',
127
+ examples: ['GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS'],
128
+ }),
129
+ })
130
+
131
+ export const response = Authorize.schema.tokens
132
+ .extend({
133
+ account: KeyAuthorization.schema.account.optional(),
134
+ key_authorization: KeyAuthorization.schema.rpc.optional().meta({
135
+ description:
136
+ "The signed key authorization, in the node's RPC format, when the CLI requested a key. Only the first redemption returns it.",
137
+ }),
138
+ })
139
+ .meta({
140
+ description:
141
+ 'The tokens, with the signed key authorization when the CLI requested a key.',
142
+ id: 'DeviceTokens',
143
+ })
144
+
145
+ /** Better Auth's token response for the device grant. */
146
+ export const issued = z.object({
147
+ access_token: z.string(),
148
+ expires_in: z.number(),
149
+ id_token: z.string().optional(),
150
+ refresh_token: z.string().optional(),
151
+ token_type: z.string(),
152
+ })
153
+ }
154
+
155
+ export namespace requests {
156
+ export const param = z.object({
157
+ code: z
158
+ .string()
159
+ .meta({ description: 'The user code.', examples: ['WDJB-MJHT'] }),
160
+ })
161
+
162
+ export const response = z.object({
163
+ request: z
164
+ .object({
165
+ app: z
166
+ .object({
167
+ name: z.string().meta({
168
+ description: "The app's display name.",
169
+ examples: ['Tempo CLI'],
170
+ }),
171
+ })
172
+ .optional()
173
+ .meta({
174
+ description:
175
+ 'The app that requested the code. Absent when the app has no display name.',
176
+ }),
177
+ challenge: z
178
+ .string()
179
+ .optional()
180
+ .meta({
181
+ description:
182
+ "The message whose EIP-191 hash is the key's witness, which approval signs. Absent when the CLI sent none.",
183
+ examples: ['Sign in to app.example.com\nNonce: 7f3a9c2e5b1d4f60'],
184
+ }),
185
+ created_at: z.number().meta({
186
+ description: 'When the CLI requested the code, in Unix seconds.',
187
+ examples: [1792075800],
188
+ }),
189
+ device: z
190
+ .string()
191
+ .optional()
192
+ .meta({
193
+ description:
194
+ 'The name that the CLI reported, such as its hostname. Absent for a code from `POST /device/code`.',
195
+ examples: ['ada-macbook'],
196
+ }),
197
+ ip: z
198
+ .string()
199
+ .optional()
200
+ .meta({
201
+ description: "The CLI's IP address. Absent when it is unknown.",
202
+ examples: ['203.0.113.7'],
203
+ }),
204
+ key_authorization: KeyAuthorization.schema.description
205
+ .extend({
206
+ account: KeyAuthorization.schema.description.shape.account.meta({
207
+ description:
208
+ 'The Tempo Account that approval signs for: the one the request names, else the one the user selected for the app, else the oldest. Absent when the request names none and the accounts cannot be read, or the user holds none.',
209
+ examples: ['0x9a073914db0d149375ac2b7fbb72c68c0780fb5f'],
210
+ }),
211
+ refusal: KeySigner.schema.refusal.optional(),
212
+ })
213
+ .optional()
214
+ .meta({
215
+ description:
216
+ 'What the key that the CLI requested may do. Absent when the CLI requested no key.',
217
+ }),
218
+ location: z
219
+ .string()
220
+ .optional()
221
+ .meta({
222
+ description:
223
+ "The CLI's city and country, from its IP address. Absent when they are unknown.",
224
+ examples: ['Berlin, DE'],
225
+ }),
226
+ status: z.enum(['approved', 'denied', 'pending']).meta({
227
+ description:
228
+ 'Whether the user approved or denied the code. `pending` until the user decides.',
229
+ examples: ['pending'],
230
+ }),
231
+ user_code: z
232
+ .string()
233
+ .meta({ description: 'The user code.', examples: ['WDJB-MJHT'] }),
234
+ })
235
+ .meta({ description: 'The request that the CLI made with the code.' }),
236
+ })
237
+ }
238
+
239
+ export namespace approve {
240
+ export const json = z.object({
241
+ account: TempoAccount.schema.tempoAccount.shape.address.optional().meta({
242
+ description:
243
+ "The Tempo Account that the device page showed for the request's key authorization, one of the user's accounts in the app's realms. Ignored when the key names its account, and absent for a request without a key.",
244
+ examples: ['0x8C3b5d0e2A4f6B1c9D7e3F5a0B2c4D6e8F1a3B5c'],
245
+ }),
246
+ code: z
247
+ .string()
248
+ .max(32)
249
+ .meta({ description: 'The user code.', examples: ['WDJB-MJHT'] }),
250
+ passkey: z
251
+ .object({ assertion: Passkey.schema.assertion })
252
+ .optional()
253
+ .meta({
254
+ description:
255
+ "The passkey's assertion over the options of the passkey step, which a passkey-owned account's approval returns. The repeated approval signs the key with it.",
256
+ }),
257
+ })
258
+
259
+ export const step = Passkey.schema.step
260
+
261
+ export const response = z.union([
262
+ z
263
+ .object({
264
+ status: z.literal('approved').meta({
265
+ description:
266
+ 'The user approved the code. The CLI receives its tokens, and any signed key, on its next poll.',
267
+ }),
268
+ })
269
+ .meta({ description: 'A stored approval.' }),
270
+ step,
271
+ ])
272
+ }
273
+
274
+ export namespace deny {
275
+ export const json = z.object({ code: approve.json.shape.code })
276
+
277
+ export const response = z.object({
278
+ status: z.literal('denied').meta({
279
+ description:
280
+ 'The user denied the code. The next poll returns `access_denied`.',
281
+ }),
282
+ })
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Records the signed-in user's decision on the live user code `code`, from Tempo ID's own page.
288
+ * Better Auth first binds the code to the user, so no other user can sign its key or decide it
289
+ * after. An approval of a request with a key then signs the key and holds it for the code's
290
+ * redemption, so a refused key approves nothing, and a failed approval releases it. For a passkey-owned account, it returns the
291
+ * options of the passkey step, and the repeated approval with the assertion signs the key. Returns
292
+ * `undefined` once the decision is stored, the passkey step, the rate limit's response, or a
293
+ * refusal.
294
+ */
295
+ async function decide(
296
+ c: Context<Env>,
297
+ options: decide.Options,
298
+ ): Promise<
299
+ | ErrorResponse.Instance<typeof decide.errors>
300
+ | NonNullable<Awaited<ReturnType<typeof RateLimit.check>>>
301
+ | { options: Awaited<ReturnType<typeof KeySigner.challenge>> }
302
+ | undefined
303
+ > {
304
+ if (Cors.appOrigin(c)) return new ErrorResponse.CrossSiteRequestError()
305
+
306
+ const session = await Timing.measure(c, Session.get(c), 'Session.get')
307
+ if (!session) return new core_Auth.SessionRequiredError()
308
+
309
+ // Counted per user, as the approval page's decisions are, since each decision reads the database.
310
+ const limited = await Timing.measure(
311
+ c,
312
+ RateLimit.check(c, {
313
+ limits: [{ binding: 'AUTHORIZE_LIMIT', key: session.user.id }],
314
+ }),
315
+ 'RateLimit.check',
316
+ )
317
+ if (limited) return limited
318
+
319
+ const userCode = Device.normalize(options.code)
320
+ const found = userCode
321
+ ? await Timing.measure(c, Device.get(db, { userCode }), 'Device.get')
322
+ : undefined
323
+ // A code that another user holds is unknown to this one, as `DeviceRequest.read` returns it.
324
+ if (!userCode || !found || (found.userId && found.userId !== session.user.id))
325
+ return new Device.UnknownCodeError()
326
+ // A decided code refuses before Better Auth binds it, and before any passkey step.
327
+ if (found.status !== 'pending') return new Device.AlreadyDecidedError()
328
+
329
+ // Better Auth binds the code to this user first, in one conditional update, so of concurrent
330
+ // decisions by several users only the bound user's signs a key or decides.
331
+ const headers = c.req.raw.headers
332
+ const bound = await (async () => {
333
+ if (found.userId) return found
334
+ try {
335
+ await Timing.measure(
336
+ c,
337
+ c.var.auth.api.deviceVerify({
338
+ headers,
339
+ query: { user_code: userCode },
340
+ }),
341
+ 'auth.deviceVerify',
342
+ )
343
+ } catch (error) {
344
+ if (!isAPIError(error)) throw error
345
+ }
346
+ return await Timing.measure(c, Device.get(db, { userCode }), 'Device.get')
347
+ })()
348
+ // A code that another user bound in the meantime stays unknown to this one.
349
+ if (!bound || bound.userId !== session.user.id)
350
+ return new Device.UnknownCodeError()
351
+ if (bound.status !== 'pending') return new Device.AlreadyDecidedError()
352
+
353
+ // An approval of a request with a key signs the key and holds it first, so a refused key
354
+ // approves nothing.
355
+ const held = await (async () => {
356
+ if (options.decision !== 'approve' || !bound.key) return undefined
357
+ const signed = await Timing.measure(
358
+ c,
359
+ DeviceRequest.sign(c, {
360
+ account: options.account,
361
+ assertion: options.assertion,
362
+ request: bound,
363
+ session,
364
+ }),
365
+ 'DeviceRequest.sign',
366
+ )
367
+ if (signed instanceof ErrorResponse.ResponseError) return signed
368
+ // A passkey signs only in this browser, so the page signs the digest and repeats the approval.
369
+ if (signed.options) return { options: signed.options }
370
+ const key = {
371
+ account: signed.account,
372
+ keyAuthorization: signed.keyAuthorization,
373
+ }
374
+ const stored = await Timing.measure(
375
+ c,
376
+ Device.hold(db, { id: bound.id, key, userId: session.user.id }),
377
+ 'Device.hold',
378
+ )
379
+ // Another decision, the key of a concurrent approval, or the code's expiry came first.
380
+ if (!stored) return await refusal()
381
+ return { key }
382
+ })()
383
+ if (held instanceof ErrorResponse.ResponseError) return held
384
+ if (held && 'options' in held) return { options: held.options }
385
+
386
+ try {
387
+ await Timing.measure(
388
+ c,
389
+ options.decision === 'approve'
390
+ ? c.var.auth.api.deviceApprove({ body: { userCode }, headers })
391
+ : c.var.auth.api.deviceDeny({ body: { userCode }, headers }),
392
+ options.decision === 'approve' ? 'auth.deviceApprove' : 'auth.deviceDeny',
393
+ )
394
+ return undefined
395
+ } catch (error) {
396
+ // A failed approval releases its key while the code is pending, so the user can approve again.
397
+ if (held)
398
+ await Timing.measure(
399
+ c,
400
+ Device.release(db, { id: bound.id, key: held.key }),
401
+ 'Device.release',
402
+ )
403
+ if (!isAPIError(error)) throw error
404
+ // Another decision, by this user, or the code's expiry, came between the read and Better
405
+ // Auth's own checks.
406
+ return await refusal()
407
+ }
408
+
409
+ /** Returns the refusal of a code that changed after the read: unknown when it expired. */
410
+ async function refusal() {
411
+ const current = await Timing.measure(
412
+ c,
413
+ Device.get(db, { userCode: userCode! }),
414
+ 'Device.get',
415
+ )
416
+ if (!current) return new Device.UnknownCodeError()
417
+ return new Device.AlreadyDecidedError()
418
+ }
419
+ }
420
+
421
+ declare namespace decide {
422
+ type Options = {
423
+ /** The account that the device page showed for the request's key, if any. */
424
+ account?: `0x${string}` | undefined
425
+ /** The passkey's assertion over the options of the passkey step, if any. */
426
+ assertion?: Passkey.Assertion | undefined
427
+ /** The user code, as the user typed it. */
428
+ code: string
429
+ /** Whether the user approves or denies the code. */
430
+ decision: 'approve' | 'deny'
431
+ }
432
+ }
433
+
434
+ namespace decide {
435
+ /** The refusals `decide` returns. Its return type admits no other. */
436
+ export const errors = [
437
+ ...DeviceRequest.sign.errors,
438
+ core_Auth.SessionRequiredError,
439
+ Device.AlreadyDecidedError,
440
+ Device.UnknownCodeError,
441
+ ErrorResponse.CrossSiteRequestError,
442
+ ] as const
443
+ }
444
+
445
+ /**
446
+ * Redeems the device code `deviceCode` for tokens at Better Auth's token endpoint, in process, as
447
+ * the app with `clientId`. Returns the tokens, or the RFC 8628 refusal of a code that is pending,
448
+ * denied, polled too fast, expired, unknown, already redeemed, or for another app.
449
+ */
450
+ async function poll(
451
+ c: Context<Env>,
452
+ options: poll.Options,
453
+ ): Promise<
454
+ | ErrorResponse.Instance<typeof poll.errors>
455
+ | z.output<typeof schema.token.issued>
456
+ > {
457
+ const response = await Timing.measure(
458
+ c,
459
+ c.var.auth.handler(
460
+ new Request(new URL('/oauth2/token', env.URL), {
461
+ body: new URLSearchParams({
462
+ client_id: options.clientId,
463
+ device_code: options.deviceCode,
464
+ grant_type: Apps.deviceGrant,
465
+ resource: env.URL,
466
+ }),
467
+ headers: { 'content-type': 'application/x-www-form-urlencoded' },
468
+ method: 'POST',
469
+ }),
470
+ ),
471
+ 'auth.handler',
472
+ )
473
+ const body: unknown = await Timing.measure(
474
+ c,
475
+ response.json(),
476
+ 'response.json',
477
+ )
478
+ if (response.ok) return schema.token.issued.parse(body)
479
+
480
+ const error = z.object({ error: z.string() }).safeParse(body).data?.error
481
+ if (error === 'authorization_pending')
482
+ return new Device.ApprovalPendingError()
483
+ if (error === 'slow_down') return new Device.PollingTooFastError()
484
+ if (error === 'access_denied') return new Device.RequestDeniedError()
485
+ // Better Auth refuses an unknown or redeemed code with `invalid_grant`, and an expired one with
486
+ // `expired_token`.
487
+ if (error === 'expired_token' || error === 'invalid_grant')
488
+ return new Device.DeviceCodeExpiredError()
489
+ throw new Error(
490
+ `The token endpoint refused a device code with ${error ?? response.status}.`,
491
+ )
492
+ }
493
+
494
+ declare namespace poll {
495
+ type Options = {
496
+ /** The client ID of the device app that requested the code. */
497
+ clientId: string
498
+ /** The device code that the CLI holds. */
499
+ deviceCode: string
500
+ }
501
+ }
502
+
503
+ namespace poll {
504
+ /** The refusals `poll` returns, each an RFC 8628 error. */
505
+ export const errors = [
506
+ Device.ApprovalPendingError,
507
+ Device.DeviceCodeExpiredError,
508
+ Device.PollingTooFastError,
509
+ Device.RequestDeniedError,
510
+ ] as const
511
+ }
512
+
513
+ /** The device grant API for CLIs, and the decisions of the device page. */
514
+ export const device = new Hono<Env>()
515
+ .post(
516
+ '/v1/device/authorize',
517
+ OpenApi.describeRoute({
518
+ description:
519
+ "Starts the device grant (RFC 8628) for a CLI, as the device app that `client_id` names, or `tempo-cli`. The user approves the user code on the device page, and the CLI polls `/v1/device/token`. A request with `key_authorization` asks for a key, which the device page shows and approval signs. The key follows the rules of the redirect's approval page, without the headless ceiling, and a `challenge` requires the app's `tempo-id:sign` scope.",
520
+ responses: OpenApi.responses({
521
+ errors: [
522
+ ...BodyLimit.errors,
523
+ ...KeyAuthorization.check.errors,
524
+ ...KeyAuthorization.checkChallenge.errors,
525
+ ...OpenApi.validate.errors,
526
+ ...RateLimit.check.errors,
527
+ Authorize.RejectedError,
528
+ ErrorResponse.CrossSiteRequestError,
529
+ ],
530
+ success: {
531
+ description: 'The device code.',
532
+ schema: schema.authorize.response,
533
+ },
534
+ }),
535
+ tags: ['Device grant'],
536
+ }),
537
+ OpenApi.validate('json', schema.authorize.json),
538
+ async (c) => {
539
+ // Any CLI can name any device app, so the IP address is the only useful key.
540
+ const limited = await Timing.measure(
541
+ c,
542
+ RateLimit.check(c, {
543
+ limits: [{ binding: 'DEVICE_LIMIT', key: RateLimit.ip(c) }],
544
+ }),
545
+ 'RateLimit.check',
546
+ )
547
+ if (limited) return limited
548
+
549
+ const {
550
+ challenge,
551
+ client_id = Device.clientId,
552
+ device,
553
+ key_authorization,
554
+ } = c.req.valid('json')
555
+ const app = await Timing.measure(
556
+ c,
557
+ Apps.get(db, { clientId: client_id, enabled: true }).catch(
558
+ (error: unknown) => {
559
+ if (error instanceof Apps.NotFoundError) return undefined
560
+ throw error
561
+ },
562
+ ),
563
+ 'Apps.get',
564
+ )
565
+ // A device app with a secret, which an import can hold, cannot redeem at the poll, which sends
566
+ // none, so it is refused as no device app.
567
+ if (!app?.device || app.secret)
568
+ return ErrorResponse.json(
569
+ c,
570
+ new Authorize.RejectedError({
571
+ description: `Device grant refused: \`client_id\` ${client_id} names no enabled public device app. Send the client ID of a device app, or leave out \`client_id\`.`,
572
+ }),
573
+ )
574
+
575
+ // The device page shows the key, so the request is refused here for a key that approval
576
+ // would refuse, before the CLI shows a code.
577
+ if (key_authorization) {
578
+ const key = KeyAuthorization.bind(key_authorization, { challenge })
579
+ const refused =
580
+ KeyAuthorization.check(key) ??
581
+ KeyAuthorization.checkChallenge({ app, clientId: client_id, key })
582
+ if (refused) return ErrorResponse.json(c, refused)
583
+ }
584
+
585
+ const code = await Timing.measure(
586
+ c,
587
+ c.var.auth.api.deviceCode({
588
+ body: {
589
+ client_id,
590
+ // Every redemption must match the code's resource, which `/oauth2/token` defaults to.
591
+ resource: env.URL,
592
+ scope: Device.scope,
593
+ },
594
+ }),
595
+ 'auth.deviceCode',
596
+ )
597
+ await Timing.measure(
598
+ c,
599
+ DeviceRequest.create(c, {
600
+ challenge,
601
+ device,
602
+ deviceCode: code.device_code,
603
+ expiresIn: code.expires_in,
604
+ key: key_authorization,
605
+ }),
606
+ 'DeviceRequest.create',
607
+ )
608
+
609
+ const userCode = Device.format(code.user_code)
610
+ const verification = new URL(Device.page, env.URL)
611
+ c.header('cache-control', 'no-store')
612
+ return c.json(
613
+ {
614
+ device_code: code.device_code,
615
+ expires_in: code.expires_in,
616
+ interval: code.interval,
617
+ user_code: userCode,
618
+ verification_uri: verification.href,
619
+ verification_uri_complete: `${verification.href}?${new URLSearchParams({ code: userCode })}`,
620
+ },
621
+ 200,
622
+ )
623
+ },
624
+ )
625
+ .post(
626
+ '/v1/device/token',
627
+ OpenApi.describeRoute({
628
+ description:
629
+ "Polls for the device grant's tokens (RFC 8628 §3.4). The tokens of an approved code, and the key authorization that approval signed, are returned once.",
630
+ responses: OpenApi.responses({
631
+ errors: [
632
+ ...BodyLimit.errors,
633
+ ...OpenApi.validate.errors,
634
+ ...RateLimit.check.errors,
635
+ ...poll.errors,
636
+ ErrorResponse.CrossSiteRequestError,
637
+ ],
638
+ success: {
639
+ description: 'The tokens.',
640
+ schema: schema.token.response,
641
+ },
642
+ }),
643
+ tags: ['Device grant'],
644
+ }),
645
+ OpenApi.validate('json', schema.token.json),
646
+ async (c) => {
647
+ // The poll redeems at `/oauth2/token` in process, so it counts as that route counts its requests.
648
+ const limited = await Timing.measure(
649
+ c,
650
+ RateLimit.check(c, {
651
+ limits: [{ binding: 'TOKEN_LIMIT', key: RateLimit.ip(c) }],
652
+ }),
653
+ 'RateLimit.check',
654
+ )
655
+ if (limited) return limited
656
+
657
+ const deviceCode = c.req.valid('json').device_code
658
+ // The redemption deletes the code's row, so its ID, which names the held key, is read first.
659
+ const id = await Timing.measure(
660
+ c,
661
+ Device.id(db, { deviceCode }),
662
+ 'Device.id',
663
+ )
664
+ const result = await Timing.measure(
665
+ c,
666
+ poll(c, {
667
+ clientId: c.req.valid('json').client_id ?? Device.clientId,
668
+ deviceCode,
669
+ }),
670
+ 'device.poll',
671
+ )
672
+ c.header('cache-control', 'no-store')
673
+ if (result instanceof ErrorResponse.ResponseError)
674
+ return ErrorResponse.json(c, result)
675
+
676
+ // Only the one poll that redeems the code reaches here, so only it takes the key. The code is
677
+ // spent, so a failed take still returns the tokens, without the key.
678
+ const key = id
679
+ ? await Timing.measure(c, Device.take(db, { id }), 'Device.take').catch(
680
+ (error: unknown) => {
681
+ Log.error(c, {
682
+ error,
683
+ message: 'The device token poll failed to take the held key.',
684
+ })
685
+ return undefined
686
+ },
687
+ )
688
+ : undefined
689
+ const { access_token, expires_in, id_token, refresh_token, token_type } =
690
+ result
691
+ return c.json(
692
+ {
693
+ access_token,
694
+ ...(key
695
+ ? { account: key.account, key_authorization: key.keyAuthorization }
696
+ : {}),
697
+ expires_in,
698
+ ...(id_token ? { id_token } : {}),
699
+ ...(refresh_token ? { refresh_token } : {}),
700
+ token_type,
701
+ },
702
+ 200,
703
+ )
704
+ },
705
+ )
706
+ .get(
707
+ '/v1/device/requests/:code',
708
+ OpenApi.describeRoute({
709
+ description:
710
+ "Reads a device code's request for the device page, by its user code. The first signed-in user to read a pending code binds it, so no other user decides it. A pending request's key carries the account that approval signs for, and why approval cannot sign it, if it cannot. That lookup counts against the user's `ACCOUNT_LIMIT`, as `GET /v1/account` does. Creates no account.",
711
+ responses: OpenApi.responses({
712
+ errors: [
713
+ ...OpenApi.validate.errors,
714
+ ...DeviceRequest.read.errors,
715
+ ...RateLimit.check.errors,
716
+ core_Auth.SessionRequiredError,
717
+ ErrorResponse.CrossSiteRequestError,
718
+ ],
719
+ success: {
720
+ description: 'What the CLI requested.',
721
+ schema: schema.requests.response,
722
+ },
723
+ }),
724
+ security: [{ sessionCookie: [] }],
725
+ tags: ['Device grant'],
726
+ }),
727
+ OpenApi.validate('param', schema.requests.param),
728
+ async (c) => {
729
+ // The session cookie is `Lax`, so a top-level navigation from another site carries it. The read
730
+ // binds a pending code and spends the user's limits, so no other site's page starts one.
731
+ if (c.req.header('sec-fetch-site') === 'cross-site')
732
+ return ErrorResponse.json(c, new ErrorResponse.CrossSiteRequestError())
733
+
734
+ const session = await Timing.measure(c, Session.get(c), 'Session.get')
735
+ if (!session)
736
+ return ErrorResponse.json(c, new core_Auth.SessionRequiredError())
737
+
738
+ // A user code holds about 34 bits, so each user's lookups count, which keeps a guess of a live
739
+ // code out of reach (RFC 8628 §5.1). They count apart from the user's authorizations, so a
740
+ // page that sends the user's lookups spends none of that quota.
741
+ const limited = await Timing.measure(
742
+ c,
743
+ RateLimit.check(c, {
744
+ limits: [
745
+ { binding: 'AUTHORIZE_LIMIT', key: `${session.user.id}:device` },
746
+ ],
747
+ }),
748
+ 'RateLimit.check',
749
+ )
750
+ if (limited) return limited
751
+
752
+ const found = await Timing.measure(
753
+ c,
754
+ DeviceRequest.read(c, {
755
+ code: c.req.valid('param').code,
756
+ userId: session.user.id,
757
+ }),
758
+ 'DeviceRequest.read',
759
+ )
760
+ c.header('cache-control', 'private, no-store')
761
+ if (found instanceof ErrorResponse.ResponseError)
762
+ return ErrorResponse.json(c, found)
763
+
764
+ // The key's account resolves in the realms of the app that requested the code.
765
+ const { clientId, request } = found
766
+ const { key_authorization, ...rest } = request
767
+ if (!key_authorization || request.status !== 'pending')
768
+ return c.json({ request }, 200)
769
+ const preview = await Timing.measure(
770
+ c,
771
+ KeySigner.preview(c, {
772
+ account: key_authorization.account,
773
+ clientId,
774
+ sessionId: session.session.id,
775
+ userId: session.user.id,
776
+ }),
777
+ 'KeySigner.preview',
778
+ )
779
+ return c.json(
780
+ {
781
+ request: {
782
+ ...rest,
783
+ key_authorization: {
784
+ ...key_authorization,
785
+ ...(preview.account ? { account: preview.account } : {}),
786
+ ...(preview.refusal ? { refusal: preview.refusal } : {}),
787
+ },
788
+ },
789
+ },
790
+ 200,
791
+ )
792
+ },
793
+ )
794
+ .post(
795
+ '/v1/device/approve',
796
+ OpenApi.describeRoute({
797
+ description:
798
+ "Approves a device code for the signed-in user. Only Tempo ID's device page may call it. The CLI receives its tokens on its next poll. An approval of a request with a key authorization signs the key as the account's owner, and the poll returns it beside the tokens. For a passkey-owned account, the approval first returns the passkey step, and the repeated approval with `passkey` signs the key. A user who holds no account in the app's realms, and whose key names none, first receives a Privy-owned account, as `POST /v1/account` creates it.",
799
+ responses: OpenApi.responses({
800
+ errors: [
801
+ ...BodyLimit.errors,
802
+ ...OpenApi.validate.errors,
803
+ ...RateLimit.check.errors,
804
+ ...decide.errors,
805
+ ],
806
+ success: {
807
+ description: 'The code is approved, or the passkey step.',
808
+ schema: schema.approve.response,
809
+ },
810
+ }),
811
+ security: [{ sessionCookie: [] }],
812
+ tags: ['Device grant'],
813
+ }),
814
+ OpenApi.validate('json', schema.approve.json),
815
+ async (c) => {
816
+ const { account, code, passkey } = c.req.valid('json')
817
+ const result = await Timing.measure(
818
+ c,
819
+ decide(c, {
820
+ account,
821
+ assertion: passkey?.assertion,
822
+ code,
823
+ decision: 'approve',
824
+ }),
825
+ 'device.decide',
826
+ )
827
+ if (result instanceof Response) return result
828
+ if (result instanceof ErrorResponse.ResponseError)
829
+ return ErrorResponse.json(c, result)
830
+ if (result) {
831
+ c.header('cache-control', 'private, no-store')
832
+ return c.json(
833
+ schema.approve.step.parse({
834
+ step: { options: result.options, type: 'passkey' },
835
+ }),
836
+ 200,
837
+ )
838
+ }
839
+ return c.json({ status: 'approved' as const }, 200)
840
+ },
841
+ )
842
+ .post(
843
+ '/v1/device/deny',
844
+ OpenApi.describeRoute({
845
+ description:
846
+ "Denies a device code for the signed-in user. Only Tempo ID's device page may call it. The CLI's next poll returns `access_denied`.",
847
+ responses: OpenApi.responses({
848
+ errors: [
849
+ ...BodyLimit.errors,
850
+ ...OpenApi.validate.errors,
851
+ ...RateLimit.check.errors,
852
+ ...decide.errors,
853
+ ],
854
+ success: {
855
+ description: 'The code is denied.',
856
+ schema: schema.deny.response,
857
+ },
858
+ }),
859
+ security: [{ sessionCookie: [] }],
860
+ tags: ['Device grant'],
861
+ }),
862
+ OpenApi.validate('json', schema.deny.json),
863
+ async (c) => {
864
+ const result = await Timing.measure(
865
+ c,
866
+ decide(c, { code: c.req.valid('json').code, decision: 'deny' }),
867
+ 'device.decide',
868
+ )
869
+ if (result instanceof Response) return result
870
+ if (result instanceof ErrorResponse.ResponseError)
871
+ return ErrorResponse.json(c, result)
872
+ return c.json({ status: 'denied' as const }, 200)
873
+ },
874
+ )