@tempoxyz/id 0.0.0-stage → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +121 -2
- package/dist/App.d.ts +121 -0
- package/dist/App.d.ts.map +1 -0
- package/dist/App.js +132 -0
- package/dist/App.js.map +1 -0
- package/dist/app/client/.assetsignore +2 -0
- package/dist/app/client/_headers +4 -0
- package/dist/app/client/assets/Account-BYLpNFf3.css +1 -0
- package/dist/app/client/assets/Account-BllJah07.js +1 -0
- package/dist/app/client/assets/Amount-E_7snI0-.js +1 -0
- package/dist/app/client/assets/Amount-ZdtluGf-.css +1 -0
- package/dist/app/client/assets/Approval-CgV5JHlb.css +1 -0
- package/dist/app/client/assets/Approval-yKflypU2.js +1 -0
- package/dist/app/client/assets/ArrowUp-CRvqfMNU.js +1 -0
- package/dist/app/client/assets/Authentication-BAXhOh_4.js +2 -0
- package/dist/app/client/assets/BRLA-CZvVDNYr.png +0 -0
- package/dist/app/client/assets/BRZ-Cn3jEi9t.svg +1 -0
- package/dist/app/client/assets/CHFAU-DCedc4aH.png +0 -0
- package/dist/app/client/assets/Callout-B736759d.js +1 -0
- package/dist/app/client/assets/Callout-DBfEmP7q.css +1 -0
- package/dist/app/client/assets/Check-DVgD8tDG.js +1 -0
- package/dist/app/client/assets/ChevronRight-CsXtIZCB.js +1 -0
- package/dist/app/client/assets/ChooseAccount-BjxfQjPK.js +1 -0
- package/dist/app/client/assets/ChooseAccount-DL3wgJhE.css +1 -0
- package/dist/app/client/assets/Client-JrABNovb.js +1 -0
- package/dist/app/client/assets/Close-CRvWlhqc.js +1 -0
- package/dist/app/client/assets/Cloud-DgJsrqpQ.js +1 -0
- package/dist/app/client/assets/CodeInput-79Ctx8ZA.js +1 -0
- package/dist/app/client/assets/CodeInput-BqRteSwD.css +1 -0
- package/dist/app/client/assets/DLUSD-BnRXm3EI.png +0 -0
- package/dist/app/client/assets/Deposit-CclrAoXL.css +1 -0
- package/dist/app/client/assets/Deposit-c7uYn2Eb.js +1 -0
- package/dist/app/client/assets/DevTools-BYkmtuV5.css +1 -0
- package/dist/app/client/assets/DevTools-BgnzDGo2.js +1 -0
- package/dist/app/client/assets/DeviceApproval-BBUVv58e.js +1 -0
- package/dist/app/client/assets/DeviceApproval-BffFLD8F.css +1 -0
- package/dist/app/client/assets/EURAU-sjieTTB9.svg +1 -0
- package/dist/app/client/assets/EURC.e-CxYiiwkz.svg +1 -0
- package/dist/app/client/assets/Fingerprint-BNCA6qOJ.js +1 -0
- package/dist/app/client/assets/GBPA-DI_25xSG.svg +4 -0
- package/dist/app/client/assets/GUSD-BDEP2Qb-.svg +1 -0
- package/dist/app/client/assets/GeistMono-Variable-BRXCGrxA.woff2 +0 -0
- package/dist/app/client/assets/Globe-f0eWzcEI.js +1 -0
- package/dist/app/client/assets/Grant-5a-n1gJH.css +1 -0
- package/dist/app/client/assets/Grant-Dp7mpcEi.js +1 -0
- package/dist/app/client/assets/Hex-C9KeV7_9.css +1 -0
- package/dist/app/client/assets/Hex-CSp2U46W.js +10 -0
- package/dist/app/client/assets/IconChip-DpZHa1JW.js +1 -0
- package/dist/app/client/assets/IconChip-yqaQ2y_d.css +1 -0
- package/dist/app/client/assets/InputGroup-BlZvt9In.css +1 -0
- package/dist/app/client/assets/InputGroup-CsOeBnpB.js +1 -0
- package/dist/app/client/assets/Inter-cyrillic-e3OmmzmJ.woff2 +0 -0
- package/dist/app/client/assets/Inter-cyrillic-ext-DOWZeO4Q.woff2 +0 -0
- package/dist/app/client/assets/Inter-greek-BD3ujQlX.woff2 +0 -0
- package/dist/app/client/assets/Inter-greek-ext-CXEwbus4.woff2 +0 -0
- package/dist/app/client/assets/Inter-latin-BDFm4vTb.woff2 +0 -0
- package/dist/app/client/assets/Inter-latin-ext-E7a9JEPe.woff2 +0 -0
- package/dist/app/client/assets/Inter-symbols-DEch7Rxr.woff2 +0 -0
- package/dist/app/client/assets/Inter-vietnamese-Bg9723vR.woff2 +0 -0
- package/dist/app/client/assets/Loader-Cm9YJNNJ.js +1 -0
- package/dist/app/client/assets/MACH-sK0r79kQ.svg +4 -0
- package/dist/app/client/assets/OUSD-BFXgVo-Z.svg +5 -0
- package/dist/app/client/assets/OUSD.dark-BbbjMvA1.svg +17 -0
- package/dist/app/client/assets/OUSD.light-B_zpSz_c.svg +17 -0
- package/dist/app/client/assets/PRIME-CiFp2-3e.svg +14 -0
- package/dist/app/client/assets/PopoverPopup-DprRXRQe.js +1 -0
- package/dist/app/client/assets/Rows-DPGTUdA8.css +1 -0
- package/dist/app/client/assets/Rows-Qqo3evtk.js +1 -0
- package/dist/app/client/assets/SBC-BTYkbkiD.svg +1 -0
- package/dist/app/client/assets/SEKAU-CB5Fc6o8.svg +5 -0
- package/dist/app/client/assets/Separator-CZUjVQOL.js +1 -0
- package/dist/app/client/assets/SignIn-HdaDzFmq.js +1 -0
- package/dist/app/client/assets/SignIn-mlBTwUq4.css +1 -0
- package/dist/app/client/assets/Stack-CkkvkOzj.js +1 -0
- package/dist/app/client/assets/Stack-D7Yt22Ed.css +1 -0
- package/dist/app/client/assets/Tag-BMh6ajrW.css +1 -0
- package/dist/app/client/assets/Tag-CLOVANTX.js +1 -0
- package/dist/app/client/assets/ToolbarRootContext-Zty_Dh3J.js +1 -0
- package/dist/app/client/assets/Tooltip-CLATV6YU.js +1 -0
- package/dist/app/client/assets/Tooltip-d3y9ESUJ.css +1 -0
- package/dist/app/client/assets/USD1-COVcO_PB.svg +30 -0
- package/dist/app/client/assets/USDB-Ckwb6UQw.svg +11 -0
- package/dist/app/client/assets/USDC.e-3nMsKAMC.svg +1 -0
- package/dist/app/client/assets/USDT0-ia60Dw0e.svg +1 -0
- package/dist/app/client/assets/USDY-CB4iet2q.svg +16 -0
- package/dist/app/client/assets/USDe-BXHQCd92.svg +1 -0
- package/dist/app/client/assets/Upgrade-D0_aO5AP.js +1 -0
- package/dist/app/client/assets/WalletUpgrade-BPsPNQsd.js +1 -0
- package/dist/app/client/assets/WalletUpgrade-C04kW0Jl.css +1 -0
- package/dist/app/client/assets/YLDS-CRh8Zw0g.svg +3 -0
- package/dist/app/client/assets/___-I98bns4o.css +1 -0
- package/dist/app/client/assets/___.flows-XOlLwMIy.js +5 -0
- package/dist/app/client/assets/alphaUSD-CpCFoVuh.svg +1 -0
- package/dist/app/client/assets/authorize-C-MlCyaM.js +1 -0
- package/dist/app/client/assets/betaUSD-9A8AjgYg.svg +1 -0
- package/dist/app/client/assets/cUSD-WsQfa-iC.svg +1 -0
- package/dist/app/client/assets/cbBTC-DvUDqrDi.svg +1 -0
- package/dist/app/client/assets/choose-account-nXR-SKa2.js +1 -0
- package/dist/app/client/assets/deposit-G8tiLPN4.js +1 -0
- package/dist/app/client/assets/device-BojBF7hZ.js +1 -0
- package/dist/app/client/assets/error-KiOpX7GD.js +1 -0
- package/dist/app/client/assets/frxUSD-n9pTpinR.svg +1 -0
- package/dist/app/client/assets/getPseudoElementBounds-BsLy4zf_.js +1 -0
- package/dist/app/client/assets/goUSD-D9kUvHsF.svg +5 -0
- package/dist/app/client/assets/iUSD-CWCJAq9X.svg +1 -0
- package/dist/app/client/assets/index-BZH0M7Nf.js +13 -0
- package/dist/app/client/assets/index-CIYxHmYA.css +1 -0
- package/dist/app/client/assets/login-DUjpk60p.js +1 -0
- package/dist/app/client/assets/pathUSD-BvaO6ooS.svg +25 -0
- package/dist/app/client/assets/preload-helper-Bs9KvdYl.js +1 -0
- package/dist/app/client/assets/rUSD-B-xuFS7T.svg +1 -0
- package/dist/app/client/assets/reUSD-CkIUs0Lx.svg +1 -0
- package/dist/app/client/assets/removable-DMiA3t_o.js +1 -0
- package/dist/app/client/assets/reset-co-CHrff.css +1 -0
- package/dist/app/client/assets/rolldown-runtime-WkwZ5CU-.js +1 -0
- package/dist/app/client/assets/routes-CK8cthzA.js +1 -0
- package/dist/app/client/assets/sUSDe-j9GOnH7I.svg +1 -0
- package/dist/app/client/assets/siUSD-C10QUNYS.svg +1 -0
- package/dist/app/client/assets/stcUSD-DIntDJeh.svg +1 -0
- package/dist/app/client/assets/syrupUSDC-DhYwZqdM.svg +5 -0
- package/dist/app/client/assets/thetaUSD-Dgmksf6G.svg +1 -0
- package/dist/app/client/assets/useNavigate-CB6S2u4J.js +1 -0
- package/dist/app/client/assets/useQuery-BpHfPhTT.js +1 -0
- package/dist/app/client/assets/useRegisterFieldControl-DsslqY-V.js +1 -0
- package/dist/app/client/assets/wYLDS-CaCKDrO-.svg +10 -0
- package/dist/app/client/assets/wsrUSD-B2Ey0bDQ.svg +1 -0
- package/dist/app/client/assets/zyzz-DYC-X0_B.css +1 -0
- package/dist/app/client/assets/zyzz.config-DX_D_7j3.js +2 -0
- package/dist/app/db/auth.d.ts +3412 -0
- package/dist/app/db/auth.d.ts.map +1 -0
- package/dist/app/db/auth.js +349 -0
- package/dist/app/db/auth.js.map +1 -0
- package/dist/app/db/schema.d.ts +1091 -0
- package/dist/app/db/schema.d.ts.map +1 -0
- package/dist/app/db/schema.js +183 -0
- package/dist/app/db/schema.js.map +1 -0
- package/dist/app/db/table.d.ts +8 -0
- package/dist/app/db/table.d.ts.map +1 -0
- package/dist/app/db/table.js +28 -0
- package/dist/app/db/table.js.map +1 -0
- package/dist/app/server/.vite/manifest.json +2093 -0
- package/dist/app/server/assets/Account-BYLpNFf3.css +1 -0
- package/dist/app/server/assets/Account-G_SUeRU3.js +2 -0
- package/dist/app/server/assets/Account-G_SUeRU3.js.map +1 -0
- package/dist/app/server/assets/Amount-Cgwdr3Xt.js +2 -0
- package/dist/app/server/assets/Amount-Cgwdr3Xt.js.map +1 -0
- package/dist/app/server/assets/Amount-ZdtluGf-.css +1 -0
- package/dist/app/server/assets/Approval-CgV5JHlb.css +1 -0
- package/dist/app/server/assets/Approval-DvkB9tWA.js +2 -0
- package/dist/app/server/assets/Approval-DvkB9tWA.js.map +1 -0
- package/dist/app/server/assets/ArrowUp-DpK_zNgZ.js +2 -0
- package/dist/app/server/assets/ArrowUp-DpK_zNgZ.js.map +1 -0
- package/dist/app/server/assets/Authentication-BOkhlNQu.js +2 -0
- package/dist/app/server/assets/Authentication-BOkhlNQu.js.map +1 -0
- package/dist/app/server/assets/Authentication-C3oNZAVX.js +2 -0
- package/dist/app/server/assets/Authentication-C3oNZAVX.js.map +1 -0
- package/dist/app/server/assets/BRLA-CZvVDNYr.png +0 -0
- package/dist/app/server/assets/BRZ-Cn3jEi9t.svg +1 -0
- package/dist/app/server/assets/Bytes-C7CRVS3J.js +4 -0
- package/dist/app/server/assets/Bytes-C7CRVS3J.js.map +1 -0
- package/dist/app/server/assets/CHFAU-DCedc4aH.png +0 -0
- package/dist/app/server/assets/Callout-DBfEmP7q.css +1 -0
- package/dist/app/server/assets/Callout-tQoAitWq.js +2 -0
- package/dist/app/server/assets/Callout-tQoAitWq.js.map +1 -0
- package/dist/app/server/assets/ChainList-C4A_7e-9.js +6 -0
- package/dist/app/server/assets/ChainList-C4A_7e-9.js.map +1 -0
- package/dist/app/server/assets/ChainName-BMnJyJrd.js +2 -0
- package/dist/app/server/assets/ChainName-BMnJyJrd.js.map +1 -0
- package/dist/app/server/assets/Check-DU_oR3Ev.js +2 -0
- package/dist/app/server/assets/Check-DU_oR3Ev.js.map +1 -0
- package/dist/app/server/assets/ChevronRight-DTBnw-W6.js +2 -0
- package/dist/app/server/assets/ChevronRight-DTBnw-W6.js.map +1 -0
- package/dist/app/server/assets/ChooseAccount-BizJvMrY.js +2 -0
- package/dist/app/server/assets/ChooseAccount-BizJvMrY.js.map +1 -0
- package/dist/app/server/assets/ChooseAccount-DL3wgJhE.css +1 -0
- package/dist/app/server/assets/Client-Dx3KrpJZ.js +2 -0
- package/dist/app/server/assets/Client-Dx3KrpJZ.js.map +1 -0
- package/dist/app/server/assets/ClientOnly-CKLiu67O.js +2 -0
- package/dist/app/server/assets/ClientOnly-CKLiu67O.js.map +1 -0
- package/dist/app/server/assets/Close-DUeE37ev.js +2 -0
- package/dist/app/server/assets/Close-DUeE37ev.js.map +1 -0
- package/dist/app/server/assets/Cloud-CLB3rTTy.js +2 -0
- package/dist/app/server/assets/Cloud-CLB3rTTy.js.map +1 -0
- package/dist/app/server/assets/CodeInput-BqRteSwD.css +1 -0
- package/dist/app/server/assets/CodeInput-FsPzVuLU.js +2 -0
- package/dist/app/server/assets/CodeInput-FsPzVuLU.js.map +1 -0
- package/dist/app/server/assets/Credential-DFjcrdyI.js +2 -0
- package/dist/app/server/assets/Credential-DFjcrdyI.js.map +1 -0
- package/dist/app/server/assets/DLUSD-BnRXm3EI.png +0 -0
- package/dist/app/server/assets/Deposit-CclrAoXL.css +1 -0
- package/dist/app/server/assets/Deposit-ChYGRfW2.js +2 -0
- package/dist/app/server/assets/Deposit-ChYGRfW2.js.map +1 -0
- package/dist/app/server/assets/DevTools-BYkmtuV5.css +1 -0
- package/dist/app/server/assets/DevTools-ZJVXqrIK.js +2 -0
- package/dist/app/server/assets/DevTools-ZJVXqrIK.js.map +1 -0
- package/dist/app/server/assets/DeviceApproval-BffFLD8F.css +1 -0
- package/dist/app/server/assets/DeviceApproval-Bqe3Mi78.js +2 -0
- package/dist/app/server/assets/DeviceApproval-Bqe3Mi78.js.map +1 -0
- package/dist/app/server/assets/EURAU-sjieTTB9.svg +1 -0
- package/dist/app/server/assets/EURC.e-CxYiiwkz.svg +1 -0
- package/dist/app/server/assets/ExecutionError-CDszGSF1.js +9 -0
- package/dist/app/server/assets/ExecutionError-CDszGSF1.js.map +1 -0
- package/dist/app/server/assets/Fingerprint-DhOPOdRr.js +2 -0
- package/dist/app/server/assets/Fingerprint-DhOPOdRr.js.map +1 -0
- package/dist/app/server/assets/GBPA-DI_25xSG.svg +4 -0
- package/dist/app/server/assets/GUSD-BDEP2Qb-.svg +1 -0
- package/dist/app/server/assets/GeistMono-Variable-BRXCGrxA.woff2 +0 -0
- package/dist/app/server/assets/Globe-2CxqxU-7.js +2 -0
- package/dist/app/server/assets/Globe-2CxqxU-7.js.map +1 -0
- package/dist/app/server/assets/Grant-5a-n1gJH.css +1 -0
- package/dist/app/server/assets/Grant-D_tun0mF.js +2 -0
- package/dist/app/server/assets/Grant-D_tun0mF.js.map +1 -0
- package/dist/app/server/assets/Hex-C9KeV7_9.css +1 -0
- package/dist/app/server/assets/Hex-Z6m_0Y0m.js +11 -0
- package/dist/app/server/assets/Hex-Z6m_0Y0m.js.map +1 -0
- package/dist/app/server/assets/IconChip-C9ZrmGB5.js +2 -0
- package/dist/app/server/assets/IconChip-C9ZrmGB5.js.map +1 -0
- package/dist/app/server/assets/IconChip-yqaQ2y_d.css +1 -0
- package/dist/app/server/assets/InputGroup-BlZvt9In.css +1 -0
- package/dist/app/server/assets/InputGroup-hpjuLuR7.js +2 -0
- package/dist/app/server/assets/InputGroup-hpjuLuR7.js.map +1 -0
- package/dist/app/server/assets/Inter-cyrillic-e3OmmzmJ.woff2 +0 -0
- package/dist/app/server/assets/Inter-cyrillic-ext-DOWZeO4Q.woff2 +0 -0
- package/dist/app/server/assets/Inter-greek-BD3ujQlX.woff2 +0 -0
- package/dist/app/server/assets/Inter-greek-ext-CXEwbus4.woff2 +0 -0
- package/dist/app/server/assets/Inter-latin-BDFm4vTb.woff2 +0 -0
- package/dist/app/server/assets/Inter-latin-ext-E7a9JEPe.woff2 +0 -0
- package/dist/app/server/assets/Inter-symbols-DEch7Rxr.woff2 +0 -0
- package/dist/app/server/assets/Inter-vietnamese-Bg9723vR.woff2 +0 -0
- package/dist/app/server/assets/Loader-DnKC9P5p.js +2 -0
- package/dist/app/server/assets/Loader-DnKC9P5p.js.map +1 -0
- package/dist/app/server/assets/MACH-sK0r79kQ.svg +4 -0
- package/dist/app/server/assets/Match-CyVdYcSf.js +2 -0
- package/dist/app/server/assets/Match-CyVdYcSf.js.map +1 -0
- package/dist/app/server/assets/OUSD-BFXgVo-Z.svg +5 -0
- package/dist/app/server/assets/OUSD.dark-BbbjMvA1.svg +17 -0
- package/dist/app/server/assets/OUSD.light-B_zpSz_c.svg +17 -0
- package/dist/app/server/assets/PRIME-CiFp2-3e.svg +14 -0
- package/dist/app/server/assets/PopoverPopup-CwqeQelI.js +2 -0
- package/dist/app/server/assets/PopoverPopup-CwqeQelI.js.map +1 -0
- package/dist/app/server/assets/Rows-CQKQwTI1.js +2 -0
- package/dist/app/server/assets/Rows-CQKQwTI1.js.map +1 -0
- package/dist/app/server/assets/Rows-DPGTUdA8.css +1 -0
- package/dist/app/server/assets/SBC-BTYkbkiD.svg +1 -0
- package/dist/app/server/assets/SEKAU-CB5Fc6o8.svg +5 -0
- package/dist/app/server/assets/Separator-DrI7KRAs.js +2 -0
- package/dist/app/server/assets/Separator-DrI7KRAs.js.map +1 -0
- package/dist/app/server/assets/SignIn-DfeeLEmN.js +2 -0
- package/dist/app/server/assets/SignIn-DfeeLEmN.js.map +1 -0
- package/dist/app/server/assets/SignIn-mlBTwUq4.css +1 -0
- package/dist/app/server/assets/Stack-D7Yt22Ed.css +1 -0
- package/dist/app/server/assets/Stack-DGktaLZc.js +2 -0
- package/dist/app/server/assets/Stack-DGktaLZc.js.map +1 -0
- package/dist/app/server/assets/Tag-9vSRBpph.js +2 -0
- package/dist/app/server/assets/Tag-9vSRBpph.js.map +1 -0
- package/dist/app/server/assets/Tag-BMh6ajrW.css +1 -0
- package/dist/app/server/assets/ToolbarRootContext-BH_CFSWd.js +2 -0
- package/dist/app/server/assets/ToolbarRootContext-BH_CFSWd.js.map +1 -0
- package/dist/app/server/assets/Tooltip-DLX41peb.js +2 -0
- package/dist/app/server/assets/Tooltip-DLX41peb.js.map +1 -0
- package/dist/app/server/assets/Tooltip-d3y9ESUJ.css +1 -0
- package/dist/app/server/assets/USD1-COVcO_PB.svg +30 -0
- package/dist/app/server/assets/USDB-Ckwb6UQw.svg +11 -0
- package/dist/app/server/assets/USDC.e-3nMsKAMC.svg +1 -0
- package/dist/app/server/assets/USDT0-ia60Dw0e.svg +1 -0
- package/dist/app/server/assets/USDY-CB4iet2q.svg +16 -0
- package/dist/app/server/assets/USDe-BXHQCd92.svg +1 -0
- package/dist/app/server/assets/Upgrade-Dbfm2nm0.js +2 -0
- package/dist/app/server/assets/Upgrade-Dbfm2nm0.js.map +1 -0
- package/dist/app/server/assets/WalletUpgrade-C04kW0Jl.css +1 -0
- package/dist/app/server/assets/WalletUpgrade-CWKc5fYt.js +2 -0
- package/dist/app/server/assets/WalletUpgrade-CWKc5fYt.js.map +1 -0
- package/dist/app/server/assets/YLDS-CRh8Zw0g.svg +3 -0
- package/dist/app/server/assets/___-I98bns4o.css +1 -0
- package/dist/app/server/assets/___.flows-Cc-awUoC.js +6 -0
- package/dist/app/server/assets/___.flows-Cc-awUoC.js.map +1 -0
- package/dist/app/server/assets/_tanstack-start-manifest_v-U7TrtUSA.js +1 -0
- package/dist/app/server/assets/_zyzz__2Fhome_2Frunner_2Fwork_2Fid_2Fid_2Fsrc_2Fapp_2Froutes_2F_5B__5D.flows-B1pPOqsM.css +1 -0
- package/dist/app/server/assets/_zyzz__2Fhome_2Frunner_2Fwork_2Fid_2Fid_2Fsrc_2Fapp_2Froutes_2F_5B__5D.flows.tsx-CKALi9oD.js +2 -0
- package/dist/app/server/assets/_zyzz__2Fhome_2Frunner_2Fwork_2Fid_2Fid_2Fsrc_2Fapp_2Froutes_2F_5B__5D.flows.tsx-CKALi9oD.js.map +1 -0
- package/dist/app/server/assets/_zyzz_shared-BbDIEd_j.css +1 -0
- package/dist/app/server/assets/alphaUSD-CpCFoVuh.svg +1 -0
- package/dist/app/server/assets/arktype-aI7TBD0R-D1R3Byo5.js +2 -0
- package/dist/app/server/assets/arktype-aI7TBD0R-D1R3Byo5.js.map +1 -0
- package/dist/app/server/assets/async-local-storage-DlOvwxXd.js +2 -0
- package/dist/app/server/assets/async-local-storage-DlOvwxXd.js.map +1 -0
- package/dist/app/server/assets/authorize-BXzTo2Pb.js +2 -0
- package/dist/app/server/assets/authorize-BXzTo2Pb.js.map +1 -0
- package/dist/app/server/assets/authorize-BzoUqOdA.js +2 -0
- package/dist/app/server/assets/authorize-BzoUqOdA.js.map +1 -0
- package/dist/app/server/assets/authorize-DXYo-K2n-rmQQ42x1.js +56 -0
- package/dist/app/server/assets/authorize-DXYo-K2n-rmQQ42x1.js.map +1 -0
- package/dist/app/server/assets/authorize-DfjPYJqS.css +1 -0
- package/dist/app/server/assets/betaUSD-9A8AjgYg.svg +1 -0
- package/dist/app/server/assets/bun-sqlite-dialect-DoOVo4ja-CtyBkVMb.js +2 -0
- package/dist/app/server/assets/bun-sqlite-dialect-DoOVo4ja-CtyBkVMb.js.map +1 -0
- package/dist/app/server/assets/cUSD-WsQfa-iC.svg +1 -0
- package/dist/app/server/assets/call-dzi0xjZd.js +31 -0
- package/dist/app/server/assets/call-dzi0xjZd.js.map +1 -0
- package/dist/app/server/assets/cbBTC-DvUDqrDi.svg +1 -0
- package/dist/app/server/assets/ccip-DjD8z_3B.js +2 -0
- package/dist/app/server/assets/ccip-DjD8z_3B.js.map +1 -0
- package/dist/app/server/assets/choose-account-_X7xq5am.js +2 -0
- package/dist/app/server/assets/choose-account-_X7xq5am.js.map +1 -0
- package/dist/app/server/assets/choose-account-lqA5SLx9.css +1 -0
- package/dist/app/server/assets/choose-account-zm-5Qs3e.js +2 -0
- package/dist/app/server/assets/choose-account-zm-5Qs3e.js.map +1 -0
- package/dist/app/server/assets/compiled-query-Ds2NVcFT.js +2 -0
- package/dist/app/server/assets/compiled-query-Ds2NVcFT.js.map +1 -0
- package/dist/app/server/assets/convert-BIC8n32x.js +2 -0
- package/dist/app/server/assets/convert-BIC8n32x.js.map +1 -0
- package/dist/app/server/assets/cookie-DxViaO10.js +2 -0
- package/dist/app/server/assets/cookie-DxViaO10.js.map +1 -0
- package/dist/app/server/assets/core-BBrZXfrB.js +56 -0
- package/dist/app/server/assets/core-BBrZXfrB.js.map +1 -0
- package/dist/app/server/assets/d1-sqlite-dialect-m0f01-4z-Cv-csE1u.js +2 -0
- package/dist/app/server/assets/d1-sqlite-dialect-m0f01-4z-Cv-csE1u.js.map +1 -0
- package/dist/app/server/assets/decodeFunctionData-bP6V2ai4.js +2 -0
- package/dist/app/server/assets/decodeFunctionData-bP6V2ai4.js.map +1 -0
- package/dist/app/server/assets/default-u_dwuiYb-DOrMMcg9.js +2 -0
- package/dist/app/server/assets/default-u_dwuiYb-DOrMMcg9.js.map +1 -0
- package/dist/app/server/assets/deposit-BBaNI01u.js +2 -0
- package/dist/app/server/assets/deposit-BBaNI01u.js.map +1 -0
- package/dist/app/server/assets/deposit-LWS6ZTuf.js +2 -0
- package/dist/app/server/assets/deposit-LWS6ZTuf.js.map +1 -0
- package/dist/app/server/assets/deposit-oFx15XCA.css +1 -0
- package/dist/app/server/assets/device-CrXlusjr.js +2 -0
- package/dist/app/server/assets/device-CrXlusjr.js.map +1 -0
- package/dist/app/server/assets/device-DdFMgYe2.js +2 -0
- package/dist/app/server/assets/device-DdFMgYe2.js.map +1 -0
- package/dist/app/server/assets/device-tMCpu5zk.css +1 -0
- package/dist/app/server/assets/dist-CR6FW-yS.js +1 -0
- package/dist/app/server/assets/dist-CoH0hD1s.js +15 -0
- package/dist/app/server/assets/dist-CoH0hD1s.js.map +1 -0
- package/dist/app/server/assets/dist-DcICI3fd.js +2 -0
- package/dist/app/server/assets/dist-DcICI3fd.js.map +1 -0
- package/dist/app/server/assets/effect-QlVUlMFu-mXPkuQTb.js +2 -0
- package/dist/app/server/assets/effect-QlVUlMFu-mXPkuQTb.js.map +1 -0
- package/dist/app/server/assets/empty-plugin-adapters-BaGcmrjC.js +2 -0
- package/dist/app/server/assets/empty-plugin-adapters-BaGcmrjC.js.map +1 -0
- package/dist/app/server/assets/error-CXy8gqdE.css +1 -0
- package/dist/app/server/assets/error-DXDT2AHy.js +2 -0
- package/dist/app/server/assets/error-DXDT2AHy.js.map +1 -0
- package/dist/app/server/assets/error-bnVzy9tx.js +2 -0
- package/dist/app/server/assets/error-bnVzy9tx.js.map +1 -0
- package/dist/app/server/assets/esm-C6SW--yt.js +4 -0
- package/dist/app/server/assets/esm-C6SW--yt.js.map +1 -0
- package/dist/app/server/assets/factory-dvoOlKer.js +5 -0
- package/dist/app/server/assets/factory-dvoOlKer.js.map +1 -0
- package/dist/app/server/assets/frxUSD-n9pTpinR.svg +1 -0
- package/dist/app/server/assets/get-id-field-OwLpfjj2.js +6 -0
- package/dist/app/server/assets/get-id-field-OwLpfjj2.js.map +1 -0
- package/dist/app/server/assets/getPseudoElementBounds-PkWU72kn.js +2 -0
- package/dist/app/server/assets/getPseudoElementBounds-PkWU72kn.js.map +1 -0
- package/dist/app/server/assets/goUSD-D9kUvHsF.svg +5 -0
- package/dist/app/server/assets/iUSD-CWCJAq9X.svg +1 -0
- package/dist/app/server/assets/index-CLddUTqr-CC33CMvS.js +2 -0
- package/dist/app/server/assets/index-CLddUTqr-CC33CMvS.js.map +1 -0
- package/dist/app/server/assets/index-DZEfthgZ-Ec2MMQ_e.js +2 -0
- package/dist/app/server/assets/index-DZEfthgZ-Ec2MMQ_e.js.map +1 -0
- package/dist/app/server/assets/kysely-adapter-B8tQwTcu.js +1 -0
- package/dist/app/server/assets/lazyRouteComponent-DPOOPZw_.js +2 -0
- package/dist/app/server/assets/lazyRouteComponent-DPOOPZw_.js.map +1 -0
- package/dist/app/server/assets/load-server-Dr0IlySo.js +3 -0
- package/dist/app/server/assets/load-server-Dr0IlySo.js.map +1 -0
- package/dist/app/server/assets/login-BLq3tEEr.js +2 -0
- package/dist/app/server/assets/login-BLq3tEEr.js.map +1 -0
- package/dist/app/server/assets/login-DCIrAbED.js +2 -0
- package/dist/app/server/assets/login-DCIrAbED.js.map +1 -0
- package/dist/app/server/assets/login-msDnt3yT.css +1 -0
- package/dist/app/server/assets/matchContext-ByrIUj4L.js +2 -0
- package/dist/app/server/assets/matchContext-ByrIUj4L.js.map +1 -0
- package/dist/app/server/assets/node-sqlite-dialect-BowWLXvK.js +2 -0
- package/dist/app/server/assets/node-sqlite-dialect-BowWLXvK.js.map +1 -0
- package/dist/app/server/assets/pathUSD-BvaO6ooS.svg +25 -0
- package/dist/app/server/assets/rUSD-B-xuFS7T.svg +1 -0
- package/dist/app/server/assets/reUSD-CkIUs0Lx.svg +1 -0
- package/dist/app/server/assets/react-dom-DYzj0HXX.js +2 -0
- package/dist/app/server/assets/react-dom-DYzj0HXX.js.map +1 -0
- package/dist/app/server/assets/removable-BCLTvPOx.js +2 -0
- package/dist/app/server/assets/removable-BCLTvPOx.js.map +1 -0
- package/dist/app/server/assets/reset-DZWeTZeQ.js +2 -0
- package/dist/app/server/assets/reset-DZWeTZeQ.js.map +1 -0
- package/dist/app/server/assets/reset-co-CHrff.css +1 -0
- package/dist/app/server/assets/resolveError-CZXXvCJR.js +2 -0
- package/dist/app/server/assets/resolveError-CZXXvCJR.js.map +1 -0
- package/dist/app/server/assets/rolldown-runtime-BWaJ3C7D.js +1 -0
- package/dist/app/server/assets/router-Bqz5X4Qw.js +2 -0
- package/dist/app/server/assets/router-Bqz5X4Qw.js.map +1 -0
- package/dist/app/server/assets/router-CclzitkY.css +1 -0
- package/dist/app/server/assets/routes-BLQdKqcZ.js +2 -0
- package/dist/app/server/assets/routes-BLQdKqcZ.js.map +1 -0
- package/dist/app/server/assets/routes-BgiIzrOt.css +1 -0
- package/dist/app/server/assets/routes-Bv6WzsSR.js +2 -0
- package/dist/app/server/assets/routes-Bv6WzsSR.js.map +1 -0
- package/dist/app/server/assets/sUSDe-j9GOnH7I.svg +1 -0
- package/dist/app/server/assets/schema-check-oLqvHR8y.js +9 -0
- package/dist/app/server/assets/schema-check-oLqvHR8y.js.map +1 -0
- package/dist/app/server/assets/secp256k1-yg8eeIYU.js +2 -0
- package/dist/app/server/assets/secp256k1-yg8eeIYU.js.map +1 -0
- package/dist/app/server/assets/server-RRUiOef2.js +76 -0
- package/dist/app/server/assets/server-RRUiOef2.js.map +1 -0
- package/dist/app/server/assets/siUSD-C10QUNYS.svg +1 -0
- package/dist/app/server/assets/sqlite-adapter-C_LAV4e3.js +2 -0
- package/dist/app/server/assets/sqlite-adapter-C_LAV4e3.js.map +1 -0
- package/dist/app/server/assets/sqlite-introspector-DSCAP82F-Crx7Ny14.js +6 -0
- package/dist/app/server/assets/sqlite-introspector-DSCAP82F-Crx7Ny14.js.map +1 -0
- package/dist/app/server/assets/standard-json-DY77efq0.js +2 -0
- package/dist/app/server/assets/standard-json-DY77efq0.js.map +1 -0
- package/dist/app/server/assets/standard-json-ig2t0wRt.js +2 -0
- package/dist/app/server/assets/standard-json-ig2t0wRt.js.map +1 -0
- package/dist/app/server/assets/standard-json-t-LNckA6.js +2 -0
- package/dist/app/server/assets/standard-json-t-LNckA6.js.map +1 -0
- package/dist/app/server/assets/standard-openapi-CONmVRl8.js +2 -0
- package/dist/app/server/assets/standard-openapi-CONmVRl8.js.map +1 -0
- package/dist/app/server/assets/start-C63VetBp.js +2 -0
- package/dist/app/server/assets/start-C63VetBp.js.map +1 -0
- package/dist/app/server/assets/stcUSD-DIntDJeh.svg +1 -0
- package/dist/app/server/assets/sury-CWZTCd75-CDaOS2oM.js +2 -0
- package/dist/app/server/assets/sury-CWZTCd75-CDaOS2oM.js.map +1 -0
- package/dist/app/server/assets/syrupUSDC-DhYwZqdM.svg +5 -0
- package/dist/app/server/assets/thetaUSD-Dgmksf6G.svg +1 -0
- package/dist/app/server/assets/typebox-Dei93FPO-D_lCVdAz.js +2 -0
- package/dist/app/server/assets/typebox-Dei93FPO-D_lCVdAz.js.map +1 -0
- package/dist/app/server/assets/useNavigate-D6d1w7KE.js +2 -0
- package/dist/app/server/assets/useNavigate-D6d1w7KE.js.map +1 -0
- package/dist/app/server/assets/useQuery-BRpCr5vj.js +2 -0
- package/dist/app/server/assets/useQuery-BRpCr5vj.js.map +1 -0
- package/dist/app/server/assets/useRegisterFieldControl-cJesrOWK.js +2 -0
- package/dist/app/server/assets/useRegisterFieldControl-cJesrOWK.js.map +1 -0
- package/dist/app/server/assets/utils-C4ZXZIZW.js +2 -0
- package/dist/app/server/assets/utils-C4ZXZIZW.js.map +1 -0
- package/dist/app/server/assets/utils-D57zfcg-.js +2 -0
- package/dist/app/server/assets/utils-D57zfcg-.js.map +1 -0
- package/dist/app/server/assets/valibot--1zFm7rT-CIKoH3MS.js +2 -0
- package/dist/app/server/assets/valibot--1zFm7rT-CIKoH3MS.js.map +1 -0
- package/dist/app/server/assets/valibot-D_HTw1Gn-BdPyLkXt.js +2 -0
- package/dist/app/server/assets/valibot-D_HTw1Gn-BdPyLkXt.js.map +1 -0
- package/dist/app/server/assets/wYLDS-CaCKDrO-.svg +10 -0
- package/dist/app/server/assets/wsrUSD-B2Ey0bDQ.svg +1 -0
- package/dist/app/server/assets/zod-Bwrt9trS-BezI5Gi1.js +2 -0
- package/dist/app/server/assets/zod-Bwrt9trS-BezI5Gi1.js.map +1 -0
- package/dist/app/server/assets/zod-DSgpEGAE-DZ7M_Q33.js +2 -0
- package/dist/app/server/assets/zod-DSgpEGAE-DZ7M_Q33.js.map +1 -0
- package/dist/app/server/assets/zyzz-6R2K52xP.css +1 -0
- package/dist/app/server/assets/zyzz.config-CJRpjn0E.js +3 -0
- package/dist/app/server/assets/zyzz.config-CJRpjn0E.js.map +1 -0
- package/dist/app/server/index.js +532 -0
- package/dist/app/server/index.js.map +1 -0
- package/dist/app/server/wrangler.json +1 -0
- package/dist/cli/Admin.d.ts +34 -0
- package/dist/cli/Admin.d.ts.map +1 -0
- package/dist/cli/Admin.js +61 -0
- package/dist/cli/Admin.js.map +1 -0
- package/dist/cli/Apps.d.ts +9 -0
- package/dist/cli/Apps.d.ts.map +1 -0
- package/dist/cli/Apps.js +34 -0
- package/dist/cli/Apps.js.map +1 -0
- package/dist/cli/Cli.d.ts +13 -0
- package/dist/cli/Cli.d.ts.map +1 -0
- package/dist/cli/Cli.js +26 -0
- package/dist/cli/Cli.js.map +1 -0
- package/dist/cli/Project.d.ts +17 -0
- package/dist/cli/Project.d.ts.map +1 -0
- package/dist/cli/Project.js +90 -0
- package/dist/cli/Project.js.map +1 -0
- package/dist/cli/Realms.d.ts +24 -0
- package/dist/cli/Realms.d.ts.map +1 -0
- package/dist/cli/Realms.js +48 -0
- package/dist/cli/Realms.js.map +1 -0
- package/dist/cli/Session.d.ts +79 -0
- package/dist/cli/Session.d.ts.map +1 -0
- package/dist/cli/Session.js +229 -0
- package/dist/cli/Session.js.map +1 -0
- package/dist/cli/User.d.ts +24 -0
- package/dist/cli/User.d.ts.map +1 -0
- package/dist/cli/User.js +34 -0
- package/dist/cli/User.js.map +1 -0
- package/dist/cli/V1.d.ts +32 -0
- package/dist/cli/V1.d.ts.map +1 -0
- package/dist/cli/V1.js +38 -0
- package/dist/cli/V1.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +46 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/commands/admin/apps/create.d.ts +23 -0
- package/dist/cli/commands/admin/apps/create.d.ts.map +1 -0
- package/dist/cli/commands/admin/apps/create.js +84 -0
- package/dist/cli/commands/admin/apps/create.js.map +1 -0
- package/dist/cli/commands/admin/apps/delete.d.ts +15 -0
- package/dist/cli/commands/admin/apps/delete.d.ts.map +1 -0
- package/dist/cli/commands/admin/apps/delete.js +41 -0
- package/dist/cli/commands/admin/apps/delete.js.map +1 -0
- package/dist/cli/commands/admin/apps/index.d.ts +5 -0
- package/dist/cli/commands/admin/apps/index.d.ts.map +1 -0
- package/dist/cli/commands/admin/apps/index.js +14 -0
- package/dist/cli/commands/admin/apps/index.js.map +1 -0
- package/dist/cli/commands/admin/apps/list.d.ts +10 -0
- package/dist/cli/commands/admin/apps/list.d.ts.map +1 -0
- package/dist/cli/commands/admin/apps/list.js +26 -0
- package/dist/cli/commands/admin/apps/list.js.map +1 -0
- package/dist/cli/commands/admin/apps/rotate-secret.d.ts +17 -0
- package/dist/cli/commands/admin/apps/rotate-secret.d.ts.map +1 -0
- package/dist/cli/commands/admin/apps/rotate-secret.js +47 -0
- package/dist/cli/commands/admin/apps/rotate-secret.js.map +1 -0
- package/dist/cli/commands/admin/apps/update.d.ts +20 -0
- package/dist/cli/commands/admin/apps/update.d.ts.map +1 -0
- package/dist/cli/commands/admin/apps/update.js +71 -0
- package/dist/cli/commands/admin/apps/update.js.map +1 -0
- package/dist/cli/commands/admin/index.d.ts +5 -0
- package/dist/cli/commands/admin/index.d.ts.map +1 -0
- package/dist/cli/commands/admin/index.js +13 -0
- package/dist/cli/commands/admin/index.js.map +1 -0
- package/dist/cli/commands/admin/migrate.d.ts +10 -0
- package/dist/cli/commands/admin/migrate.d.ts.map +1 -0
- package/dist/cli/commands/admin/migrate.js +20 -0
- package/dist/cli/commands/admin/migrate.js.map +1 -0
- package/dist/cli/commands/admin/realms/add.d.ts +13 -0
- package/dist/cli/commands/admin/realms/add.d.ts.map +1 -0
- package/dist/cli/commands/admin/realms/add.js +42 -0
- package/dist/cli/commands/admin/realms/add.js.map +1 -0
- package/dist/cli/commands/admin/realms/index.d.ts +5 -0
- package/dist/cli/commands/admin/realms/index.d.ts.map +1 -0
- package/dist/cli/commands/admin/realms/index.js +13 -0
- package/dist/cli/commands/admin/realms/index.js.map +1 -0
- package/dist/cli/commands/admin/realms/list.d.ts +14 -0
- package/dist/cli/commands/admin/realms/list.d.ts.map +1 -0
- package/dist/cli/commands/admin/realms/list.js +36 -0
- package/dist/cli/commands/admin/realms/list.js.map +1 -0
- package/dist/cli/commands/admin/realms/passkeys/index.d.ts +5 -0
- package/dist/cli/commands/admin/realms/passkeys/index.d.ts.map +1 -0
- package/dist/cli/commands/admin/realms/passkeys/index.js +13 -0
- package/dist/cli/commands/admin/realms/passkeys/index.js.map +1 -0
- package/dist/cli/commands/admin/realms/passkeys/set.d.ts +17 -0
- package/dist/cli/commands/admin/realms/passkeys/set.d.ts.map +1 -0
- package/dist/cli/commands/admin/realms/passkeys/set.js +46 -0
- package/dist/cli/commands/admin/realms/passkeys/set.js.map +1 -0
- package/dist/cli/commands/admin/realms/privy/index.d.ts +5 -0
- package/dist/cli/commands/admin/realms/privy/index.d.ts.map +1 -0
- package/dist/cli/commands/admin/realms/privy/index.js +13 -0
- package/dist/cli/commands/admin/realms/privy/index.js.map +1 -0
- package/dist/cli/commands/admin/realms/privy/set.d.ts +19 -0
- package/dist/cli/commands/admin/realms/privy/set.d.ts.map +1 -0
- package/dist/cli/commands/admin/realms/privy/set.js +66 -0
- package/dist/cli/commands/admin/realms/privy/set.js.map +1 -0
- package/dist/cli/commands/admin/realms/remove.d.ts +12 -0
- package/dist/cli/commands/admin/realms/remove.d.ts.map +1 -0
- package/dist/cli/commands/admin/realms/remove.js +35 -0
- package/dist/cli/commands/admin/realms/remove.js.map +1 -0
- package/dist/cli/commands/admin/realms/secp256k1/disable.d.ts +16 -0
- package/dist/cli/commands/admin/realms/secp256k1/disable.d.ts.map +1 -0
- package/dist/cli/commands/admin/realms/secp256k1/disable.js +42 -0
- package/dist/cli/commands/admin/realms/secp256k1/disable.js.map +1 -0
- package/dist/cli/commands/admin/realms/secp256k1/enable.d.ts +16 -0
- package/dist/cli/commands/admin/realms/secp256k1/enable.d.ts.map +1 -0
- package/dist/cli/commands/admin/realms/secp256k1/enable.js +42 -0
- package/dist/cli/commands/admin/realms/secp256k1/enable.js.map +1 -0
- package/dist/cli/commands/admin/realms/secp256k1/index.d.ts +9 -0
- package/dist/cli/commands/admin/realms/secp256k1/index.d.ts.map +1 -0
- package/dist/cli/commands/admin/realms/secp256k1/index.js +17 -0
- package/dist/cli/commands/admin/realms/secp256k1/index.js.map +1 -0
- package/dist/cli/commands/admin/realms/set.d.ts +17 -0
- package/dist/cli/commands/admin/realms/set.d.ts.map +1 -0
- package/dist/cli/commands/admin/realms/set.js +46 -0
- package/dist/cli/commands/admin/realms/set.js.map +1 -0
- package/dist/cli/commands/admin/rotate-key.d.ts +16 -0
- package/dist/cli/commands/admin/rotate-key.d.ts.map +1 -0
- package/dist/cli/commands/admin/rotate-key.js +46 -0
- package/dist/cli/commands/admin/rotate-key.js.map +1 -0
- package/dist/cli/commands/init.d.ts +11 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +34 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/login.d.ts +12 -0
- package/dist/cli/commands/login.d.ts.map +1 -0
- package/dist/cli/commands/login.js +29 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/commands/logout.d.ts +8 -0
- package/dist/cli/commands/logout.d.ts.map +1 -0
- package/dist/cli/commands/logout.js +19 -0
- package/dist/cli/commands/logout.js.map +1 -0
- package/dist/cli/commands/whoami.d.ts +12 -0
- package/dist/cli/commands/whoami.d.ts.map +1 -0
- package/dist/cli/commands/whoami.js +24 -0
- package/dist/cli/commands/whoami.js.map +1 -0
- package/dist/client/Actions.d.ts +108 -0
- package/dist/client/Actions.d.ts.map +1 -0
- package/dist/client/Actions.js +94 -0
- package/dist/client/Actions.js.map +1 -0
- package/dist/client/Auth.d.ts +143 -0
- package/dist/client/Auth.d.ts.map +1 -0
- package/dist/client/Auth.js +106 -0
- package/dist/client/Auth.js.map +1 -0
- package/dist/client/Authorization.d.ts +29 -0
- package/dist/client/Authorization.d.ts.map +1 -0
- package/dist/client/Authorization.js +12 -0
- package/dist/client/Authorization.js.map +1 -0
- package/dist/client/Decorator.d.ts +172 -0
- package/dist/client/Decorator.d.ts.map +1 -0
- package/dist/client/Decorator.js +241 -0
- package/dist/client/Decorator.js.map +1 -0
- package/dist/client/Key.d.ts +82 -0
- package/dist/client/Key.d.ts.map +1 -0
- package/dist/client/Key.js +81 -0
- package/dist/client/Key.js.map +1 -0
- package/dist/client/Pending.d.ts +38 -0
- package/dist/client/Pending.d.ts.map +1 -0
- package/dist/client/Pending.js +12 -0
- package/dist/client/Pending.js.map +1 -0
- package/dist/client/Permissions.d.ts +36 -0
- package/dist/client/Permissions.d.ts.map +1 -0
- package/dist/client/Permissions.js +16 -0
- package/dist/client/Permissions.js.map +1 -0
- package/dist/client/Storage.d.ts +27 -0
- package/dist/client/Storage.d.ts.map +1 -0
- package/dist/client/Storage.js +16 -0
- package/dist/client/Storage.js.map +1 -0
- package/dist/client/TempoAccount.d.ts +52 -0
- package/dist/client/TempoAccount.d.ts.map +1 -0
- package/dist/client/TempoAccount.js +17 -0
- package/dist/client/TempoAccount.js.map +1 -0
- package/dist/client/Tokens.d.ts +25 -0
- package/dist/client/Tokens.d.ts.map +1 -0
- package/dist/client/Tokens.js +12 -0
- package/dist/client/Tokens.js.map +1 -0
- package/dist/client/User.d.ts +21 -0
- package/dist/client/User.d.ts.map +1 -0
- package/dist/client/User.js +12 -0
- package/dist/client/User.js.map +1 -0
- package/dist/client/actions/account.d.ts +75 -0
- package/dist/client/actions/account.d.ts.map +1 -0
- package/dist/client/actions/account.js +102 -0
- package/dist/client/actions/account.js.map +1 -0
- package/dist/client/actions/device.d.ts +52 -0
- package/dist/client/actions/device.d.ts.map +1 -0
- package/dist/client/actions/device.js +103 -0
- package/dist/client/actions/device.js.map +1 -0
- package/dist/client/actions/email.d.ts +89 -0
- package/dist/client/actions/email.d.ts.map +1 -0
- package/dist/client/actions/email.js +86 -0
- package/dist/client/actions/email.js.map +1 -0
- package/dist/client/actions/oauth.d.ts +112 -0
- package/dist/client/actions/oauth.d.ts.map +1 -0
- package/dist/client/actions/oauth.js +103 -0
- package/dist/client/actions/oauth.js.map +1 -0
- package/dist/client/actions/passkey.d.ts +140 -0
- package/dist/client/actions/passkey.d.ts.map +1 -0
- package/dist/client/actions/passkey.js +170 -0
- package/dist/client/actions/passkey.js.map +1 -0
- package/dist/client/actions/realm.d.ts +25 -0
- package/dist/client/actions/realm.d.ts.map +1 -0
- package/dist/client/actions/realm.js +16 -0
- package/dist/client/actions/realm.js.map +1 -0
- package/dist/client/actions/redirect.d.ts +152 -0
- package/dist/client/actions/redirect.d.ts.map +1 -0
- package/dist/client/actions/redirect.js +311 -0
- package/dist/client/actions/redirect.js.map +1 -0
- package/dist/client/internal/accessKey.d.ts +21 -0
- package/dist/client/internal/accessKey.d.ts.map +1 -0
- package/dist/client/internal/accessKey.js +38 -0
- package/dist/client/internal/accessKey.js.map +1 -0
- package/dist/client/internal/account.d.ts +17 -0
- package/dist/client/internal/account.d.ts.map +1 -0
- package/dist/client/internal/account.js +3 -0
- package/dist/client/internal/account.js.map +1 -0
- package/dist/client/internal/api.d.ts +110 -0
- package/dist/client/internal/api.d.ts.map +1 -0
- package/dist/client/internal/api.js +136 -0
- package/dist/client/internal/api.js.map +1 -0
- package/dist/client/internal/headless.d.ts +159 -0
- package/dist/client/internal/headless.d.ts.map +1 -0
- package/dist/client/internal/headless.js +196 -0
- package/dist/client/internal/headless.js.map +1 -0
- package/dist/client/internal/indexedDb.d.ts +8 -0
- package/dist/client/internal/indexedDb.d.ts.map +1 -0
- package/dist/client/internal/indexedDb.js +32 -0
- package/dist/client/internal/indexedDb.js.map +1 -0
- package/dist/client/internal/request.d.ts +56 -0
- package/dist/client/internal/request.d.ts.map +1 -0
- package/dist/client/internal/request.js +44 -0
- package/dist/client/internal/request.js.map +1 -0
- package/dist/client/internal/store.d.ts +12 -0
- package/dist/client/internal/store.d.ts.map +1 -0
- package/dist/client/internal/store.js +15 -0
- package/dist/client/internal/store.js.map +1 -0
- package/dist/client/internal/verifier.d.ts +25 -0
- package/dist/client/internal/verifier.d.ts.map +1 -0
- package/dist/client/internal/verifier.js +52 -0
- package/dist/client/internal/verifier.js.map +1 -0
- package/dist/client.d.ts +18 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +18 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/Apps.d.ts +3169 -0
- package/dist/internal/Apps.d.ts.map +1 -0
- package/dist/internal/Apps.js +948 -0
- package/dist/internal/Apps.js.map +1 -0
- package/dist/internal/Auth.d.ts +4287 -0
- package/dist/internal/Auth.d.ts.map +1 -0
- package/dist/internal/Auth.js +185 -0
- package/dist/internal/Auth.js.map +1 -0
- package/dist/internal/Authorize.d.ts +526 -0
- package/dist/internal/Authorize.d.ts.map +1 -0
- package/dist/internal/Authorize.js +330 -0
- package/dist/internal/Authorize.js.map +1 -0
- package/dist/internal/Cache.d.ts +56 -0
- package/dist/internal/Cache.d.ts.map +1 -0
- package/dist/internal/Cache.js +65 -0
- package/dist/internal/Cache.js.map +1 -0
- package/dist/internal/Chain.d.ts +738 -0
- package/dist/internal/Chain.d.ts.map +1 -0
- package/dist/internal/Chain.js +65 -0
- package/dist/internal/Chain.js.map +1 -0
- package/dist/internal/ChainList.d.ts +117 -0
- package/dist/internal/ChainList.d.ts.map +1 -0
- package/dist/internal/ChainList.js +148 -0
- package/dist/internal/ChainList.js.map +1 -0
- package/dist/internal/ChainName.d.ts +11 -0
- package/dist/internal/ChainName.d.ts.map +1 -0
- package/dist/internal/ChainName.js +13 -0
- package/dist/internal/ChainName.js.map +1 -0
- package/dist/internal/CodeEmail.d.ts +38 -0
- package/dist/internal/CodeEmail.d.ts.map +1 -0
- package/dist/internal/CodeEmail.js +85 -0
- package/dist/internal/CodeEmail.js.map +1 -0
- package/dist/internal/ConfigurableAccount.d.ts +600 -0
- package/dist/internal/ConfigurableAccount.d.ts.map +1 -0
- package/dist/internal/ConfigurableAccount.js +795 -0
- package/dist/internal/ConfigurableAccount.js.map +1 -0
- package/dist/internal/Device.d.ts +246 -0
- package/dist/internal/Device.d.ts.map +1 -0
- package/dist/internal/Device.js +257 -0
- package/dist/internal/Device.js.map +1 -0
- package/dist/internal/Env.d.ts +97 -0
- package/dist/internal/Env.d.ts.map +1 -0
- package/dist/internal/Env.js +2 -0
- package/dist/internal/Env.js.map +1 -0
- package/dist/internal/ErrorResponse.d.ts +608 -0
- package/dist/internal/ErrorResponse.d.ts.map +1 -0
- package/dist/internal/ErrorResponse.js +701 -0
- package/dist/internal/ErrorResponse.js.map +1 -0
- package/dist/internal/Id.d.ts +10 -0
- package/dist/internal/Id.d.ts.map +1 -0
- package/dist/internal/Id.js +34 -0
- package/dist/internal/Id.js.map +1 -0
- package/dist/internal/KeyAuthorization.d.ts +1650 -0
- package/dist/internal/KeyAuthorization.d.ts.map +1 -0
- package/dist/internal/KeyAuthorization.js +1187 -0
- package/dist/internal/KeyAuthorization.js.map +1 -0
- package/dist/internal/Limits.d.ts +70 -0
- package/dist/internal/Limits.d.ts.map +1 -0
- package/dist/internal/Limits.js +36 -0
- package/dist/internal/Limits.js.map +1 -0
- package/dist/internal/Log.d.ts +34 -0
- package/dist/internal/Log.d.ts.map +1 -0
- package/dist/internal/Log.js +99 -0
- package/dist/internal/Log.js.map +1 -0
- package/dist/internal/LogLine.d.ts +29 -0
- package/dist/internal/LogLine.d.ts.map +1 -0
- package/dist/internal/LogLine.js +50 -0
- package/dist/internal/LogLine.js.map +1 -0
- package/dist/internal/Migrations.d.ts +28 -0
- package/dist/internal/Migrations.d.ts.map +1 -0
- package/dist/internal/Migrations.js +82 -0
- package/dist/internal/Migrations.js.map +1 -0
- package/dist/internal/Owner.d.ts +230 -0
- package/dist/internal/Owner.d.ts.map +1 -0
- package/dist/internal/Owner.js +101 -0
- package/dist/internal/Owner.js.map +1 -0
- package/dist/internal/Passkey.d.ts +901 -0
- package/dist/internal/Passkey.d.ts.map +1 -0
- package/dist/internal/Passkey.js +1185 -0
- package/dist/internal/Passkey.js.map +1 -0
- package/dist/internal/PendingKey.d.ts +49 -0
- package/dist/internal/PendingKey.d.ts.map +1 -0
- package/dist/internal/PendingKey.js +58 -0
- package/dist/internal/PendingKey.js.map +1 -0
- package/dist/internal/Privy.d.ts +138 -0
- package/dist/internal/Privy.d.ts.map +1 -0
- package/dist/internal/Privy.js +285 -0
- package/dist/internal/Privy.js.map +1 -0
- package/dist/internal/RateLimit.d.ts +218 -0
- package/dist/internal/RateLimit.d.ts.map +1 -0
- package/dist/internal/RateLimit.js +209 -0
- package/dist/internal/RateLimit.js.map +1 -0
- package/dist/internal/Realm.d.ts +290 -0
- package/dist/internal/Realm.d.ts.map +1 -0
- package/dist/internal/Realm.js +502 -0
- package/dist/internal/Realm.js.map +1 -0
- package/dist/internal/RequestId.d.ts +16 -0
- package/dist/internal/RequestId.d.ts.map +1 -0
- package/dist/internal/RequestId.js +78 -0
- package/dist/internal/RequestId.js.map +1 -0
- package/dist/internal/SigningKey.d.ts +126 -0
- package/dist/internal/SigningKey.d.ts.map +1 -0
- package/dist/internal/SigningKey.js +221 -0
- package/dist/internal/SigningKey.js.map +1 -0
- package/dist/internal/TempoAccount.d.ts +181 -0
- package/dist/internal/TempoAccount.d.ts.map +1 -0
- package/dist/internal/TempoAccount.js +369 -0
- package/dist/internal/TempoAccount.js.map +1 -0
- package/dist/internal/TestInbox.d.ts +42 -0
- package/dist/internal/TestInbox.d.ts.map +1 -0
- package/dist/internal/TestInbox.js +20 -0
- package/dist/internal/TestInbox.js.map +1 -0
- package/dist/internal/Timing.d.ts +52 -0
- package/dist/internal/Timing.d.ts.map +1 -0
- package/dist/internal/Timing.js +116 -0
- package/dist/internal/Timing.js.map +1 -0
- package/dist/internal/Validation.d.ts +52 -0
- package/dist/internal/Validation.d.ts.map +1 -0
- package/dist/internal/Validation.js +22 -0
- package/dist/internal/Validation.js.map +1 -0
- package/dist/internal/Verification.d.ts +24 -0
- package/dist/internal/Verification.d.ts.map +1 -0
- package/dist/internal/Verification.js +37 -0
- package/dist/internal/Verification.js.map +1 -0
- package/dist/internal/Wallet.d.ts +66 -0
- package/dist/internal/Wallet.d.ts.map +1 -0
- package/dist/internal/Wallet.js +59 -0
- package/dist/internal/Wallet.js.map +1 -0
- package/dist/internal/owners/Passkey.d.ts +9 -0
- package/dist/internal/owners/Passkey.d.ts.map +1 -0
- package/dist/internal/owners/Passkey.js +58 -0
- package/dist/internal/owners/Passkey.js.map +1 -0
- package/dist/internal/owners/Privy.d.ts +13 -0
- package/dist/internal/owners/Privy.d.ts.map +1 -0
- package/dist/internal/owners/Privy.js +59 -0
- package/dist/internal/owners/Privy.js.map +1 -0
- package/dist/internal/owners/Secp256k1.d.ts +12 -0
- package/dist/internal/owners/Secp256k1.d.ts.map +1 -0
- package/dist/internal/owners/Secp256k1.js +99 -0
- package/dist/internal/owners/Secp256k1.js.map +1 -0
- package/dist/worker.d.ts +18 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +18 -0
- package/dist/worker.js.map +1 -0
- package/package.json +83 -3
- package/src/Api.ts +157 -0
- package/src/App.ts +316 -0
- package/src/api/account.ts +2266 -0
- package/src/api/authenticate.ts +1662 -0
- package/src/api/authorize.ts +1086 -0
- package/src/api/device.ts +516 -0
- package/src/api/health.ts +72 -0
- package/src/api/lib/AccountStep.ts +485 -0
- package/src/api/lib/Auth.ts +164 -0
- package/src/api/lib/Configure.ts +921 -0
- package/src/api/lib/Consent.ts +388 -0
- package/src/api/lib/Cors.ts +160 -0
- package/src/api/lib/Database.ts +10 -0
- package/src/api/lib/DeviceRequest.ts +244 -0
- package/src/api/lib/FirstAccount.ts +136 -0
- package/src/api/lib/Headless.ts +616 -0
- package/src/api/lib/IsolateCache.ts +28 -0
- package/src/api/lib/KeySigner.ts +690 -0
- package/src/api/lib/PasskeyLink.ts +162 -0
- package/src/api/lib/Session.ts +64 -0
- package/src/api/lib/SignInConsent.ts +393 -0
- package/src/api/oauth.ts +1796 -0
- package/src/app/components/CodeInput.tsx +153 -0
- package/src/app/components/DevTools.tsx +1071 -0
- package/src/app/components/Grant.tsx +476 -0
- package/src/app/components/icons/BRLA.png +0 -0
- package/src/app/components/icons/BRZ.svg +1 -0
- package/src/app/components/icons/CHFAU.png +0 -0
- package/src/app/components/icons/DLUSD.png +0 -0
- package/src/app/components/icons/EURAU.svg +1 -0
- package/src/app/components/icons/EURC.e.svg +1 -0
- package/src/app/components/icons/GBPA.svg +4 -0
- package/src/app/components/icons/GUSD.svg +1 -0
- package/src/app/components/icons/MACH.svg +4 -0
- package/src/app/components/icons/OUSD.svg +5 -0
- package/src/app/components/icons/PRIME.svg +14 -0
- package/src/app/components/icons/SBC.svg +1 -0
- package/src/app/components/icons/SEKAU.svg +5 -0
- package/src/app/components/icons/USD1.svg +30 -0
- package/src/app/components/icons/USDB.svg +11 -0
- package/src/app/components/icons/USDC.e.svg +1 -0
- package/src/app/components/icons/USDT0.svg +1 -0
- package/src/app/components/icons/USDY.svg +16 -0
- package/src/app/components/icons/USDe.svg +1 -0
- package/src/app/components/icons/YLDS.svg +3 -0
- package/src/app/components/icons/alphaUSD.svg +1 -0
- package/src/app/components/icons/betaUSD.svg +1 -0
- package/src/app/components/icons/cUSD.svg +1 -0
- package/src/app/components/icons/cbBTC.svg +1 -0
- package/src/app/components/icons/frxUSD.svg +1 -0
- package/src/app/components/icons/goUSD.svg +5 -0
- package/src/app/components/icons/iUSD.svg +1 -0
- package/src/app/components/icons/pathUSD.svg +25 -0
- package/src/app/components/icons/rUSD.svg +1 -0
- package/src/app/components/icons/reUSD.svg +1 -0
- package/src/app/components/icons/sUSDe.svg +1 -0
- package/src/app/components/icons/siUSD.svg +1 -0
- package/src/app/components/icons/stcUSD.svg +1 -0
- package/src/app/components/icons/syrupUSDC.svg +5 -0
- package/src/app/components/icons/thetaUSD.svg +1 -0
- package/src/app/components/icons/wYLDS.svg +10 -0
- package/src/app/components/icons/wsrUSD.svg +1 -0
- package/src/app/components/logos/OUSD.dark.svg +17 -0
- package/src/app/components/logos/OUSD.light.svg +17 -0
- package/src/app/coverage.server.ts +73 -0
- package/src/app/coverage.ts +69 -0
- package/src/app/crons/cleanup.ts +104 -0
- package/src/app/db/auth.ts +440 -0
- package/src/app/db/migrations/0000_better_auth.sql +53 -0
- package/src/app/db/migrations/0001_oauth_provider.sql +162 -0
- package/src/app/db/migrations/0002_device_code.sql +17 -0
- package/src/app/db/migrations/0003_realms.sql +27 -0
- package/src/app/db/migrations/0004_default_realm.sql +2 -0
- package/src/app/db/migrations/0005_passkeys.sql +30 -0
- package/src/app/db/migrations/0006_first_accounts.sql +9 -0
- package/src/app/db/migrations/0007_pending_keys.sql +13 -0
- package/src/app/db/migrations/0008_secp256k1_owners.sql +18 -0
- package/src/app/db/migrations/0009_device_requests.sql +8 -0
- package/src/app/db/migrations/0010_device_keys.sql +4 -0
- package/src/app/db/migrations/0011_registered_passkeys.sql +2 -0
- package/src/app/db/migrations/0012_passkey_accounts.sql +7 -0
- package/src/app/db/migrations/0013_first_party_apps.sql +4 -0
- package/src/app/db/migrations/0014_logout_revokes_tokens.sql +17 -0
- package/src/app/db/migrations/0015_expiry_indexes.sql +9 -0
- package/src/app/db/migrations/0016_configurable_accounts.sql +15 -0
- package/src/app/db/migrations/0017_account_config_keys.sql +3 -0
- package/src/app/db/migrations/0018_better_auth_1_8.sql +9 -0
- package/src/app/db/migrations/0019_drop_provider_account_index.sql +1 -0
- package/src/app/db/migrations/meta/0000_snapshot.json +362 -0
- package/src/app/db/migrations/meta/0001_snapshot.json +1487 -0
- package/src/app/db/migrations/meta/0002_snapshot.json +1603 -0
- package/src/app/db/migrations/meta/0003_snapshot.json +1760 -0
- package/src/app/db/migrations/meta/0004_snapshot.json +1760 -0
- package/src/app/db/migrations/meta/0005_snapshot.json +1911 -0
- package/src/app/db/migrations/meta/0006_snapshot.json +1967 -0
- package/src/app/db/migrations/meta/0007_snapshot.json +2047 -0
- package/src/app/db/migrations/meta/0008_snapshot.json +2157 -0
- package/src/app/db/migrations/meta/0009_snapshot.json +2207 -0
- package/src/app/db/migrations/meta/0010_snapshot.json +2231 -0
- package/src/app/db/migrations/meta/0011_snapshot.json +2231 -0
- package/src/app/db/migrations/meta/0012_snapshot.json +2231 -0
- package/src/app/db/migrations/meta/0013_snapshot.json +2231 -0
- package/src/app/db/migrations/meta/0014_snapshot.json +2231 -0
- package/src/app/db/migrations/meta/0015_snapshot.json +2375 -0
- package/src/app/db/migrations/meta/0016_snapshot.json +2475 -0
- package/src/app/db/migrations/meta/0017_snapshot.json +2481 -0
- package/src/app/db/migrations/meta/0018_snapshot.json +2535 -0
- package/src/app/db/migrations/meta/0019_snapshot.json +2514 -0
- package/src/app/db/migrations/meta/_journal.json +146 -0
- package/src/app/db/schema.ts +241 -0
- package/src/app/db/table.ts +31 -0
- package/src/app/env.ts +57 -0
- package/src/app/inbox.ts +190 -0
- package/src/app/lib/Client.ts +82 -0
- package/src/app/lib/Inbox.ts +11 -0
- package/src/app/lib/Loader.ts +21 -0
- package/src/app/lib/Upgrade.ts +221 -0
- package/src/app/package.json +8 -0
- package/src/app/public/_headers +4 -0
- package/src/app/routeTree.gen.ts +219 -0
- package/src/app/router.tsx +47 -0
- package/src/app/routes/[_].flows.tsx +4254 -0
- package/src/app/routes/__root.tsx +172 -0
- package/src/app/routes/authorize.tsx +379 -0
- package/src/app/routes/choose-account.tsx +144 -0
- package/src/app/routes/deposit.tsx +107 -0
- package/src/app/routes/device.tsx +273 -0
- package/src/app/routes/error.tsx +62 -0
- package/src/app/routes/index.tsx +511 -0
- package/src/app/routes/login.tsx +577 -0
- package/src/app/screens/Account.tsx +559 -0
- package/src/app/screens/Approval.tsx +444 -0
- package/src/app/screens/ChooseAccount.tsx +206 -0
- package/src/app/screens/Deposit.tsx +523 -0
- package/src/app/screens/DeviceApproval.tsx +500 -0
- package/src/app/screens/NotFound.tsx +62 -0
- package/src/app/screens/SignIn.tsx +372 -0
- package/src/app/screens/WalletUpgrade.tsx +121 -0
- package/src/app/screens/chains/base.svg +3 -0
- package/src/app/screens/chains/ethereum.svg +11 -0
- package/src/app/screens/chains/solana.svg +4 -0
- package/src/app/screens/chains/tempo.svg +4 -0
- package/src/app/server.ts +342 -0
- package/src/app/vite.config.ts +128 -0
- package/src/app/void.config.ts +36 -0
- package/src/app/wallet.ts +69 -0
- package/src/app/zyzz.config.ts +52 -0
- package/src/cli/Admin.ts +74 -0
- package/src/cli/Apps.ts +27 -0
- package/src/cli/Cli.ts +41 -0
- package/src/cli/Project.ts +119 -0
- package/src/cli/Realms.ts +49 -0
- package/src/cli/Session.ts +290 -0
- package/src/cli/User.ts +40 -0
- package/src/cli/V1.ts +54 -0
- package/src/cli/bin.ts +44 -0
- package/src/cli/commands/admin/apps/create.ts +96 -0
- package/src/cli/commands/admin/apps/delete.ts +38 -0
- package/src/cli/commands/admin/apps/index.ts +15 -0
- package/src/cli/commands/admin/apps/list.ts +25 -0
- package/src/cli/commands/admin/apps/rotate-secret.ts +47 -0
- package/src/cli/commands/admin/apps/update.ts +79 -0
- package/src/cli/commands/admin/index.ts +14 -0
- package/src/cli/commands/admin/migrate.ts +19 -0
- package/src/cli/commands/admin/realms/add.ts +40 -0
- package/src/cli/commands/admin/realms/index.ts +14 -0
- package/src/cli/commands/admin/realms/list.ts +38 -0
- package/src/cli/commands/admin/realms/passkeys/index.ts +15 -0
- package/src/cli/commands/admin/realms/passkeys/set.ts +44 -0
- package/src/cli/commands/admin/realms/privy/index.ts +14 -0
- package/src/cli/commands/admin/realms/privy/set.ts +69 -0
- package/src/cli/commands/admin/realms/remove.ts +33 -0
- package/src/cli/commands/admin/realms/secp256k1/disable.ts +40 -0
- package/src/cli/commands/admin/realms/secp256k1/enable.ts +40 -0
- package/src/cli/commands/admin/realms/secp256k1/index.ts +19 -0
- package/src/cli/commands/admin/realms/set.ts +46 -0
- package/src/cli/commands/admin/rotate-key.ts +47 -0
- package/src/cli/commands/init.ts +42 -0
- package/src/cli/commands/login.ts +32 -0
- package/src/cli/commands/logout.ts +19 -0
- package/src/cli/commands/whoami.ts +23 -0
- package/src/client/Actions.ts +180 -0
- package/src/client/Auth.ts +267 -0
- package/src/client/Authorization.ts +34 -0
- package/src/client/Decorator.ts +444 -0
- package/src/client/Key.ts +137 -0
- package/src/client/Pending.ts +45 -0
- package/src/client/Permissions.ts +41 -0
- package/src/client/Storage.ts +29 -0
- package/src/client/TempoAccount.ts +55 -0
- package/src/client/Tokens.ts +27 -0
- package/src/client/User.ts +23 -0
- package/src/client/actions/account.ts +171 -0
- package/src/client/actions/device.ts +166 -0
- package/src/client/actions/email.ts +164 -0
- package/src/client/actions/oauth.ts +179 -0
- package/src/client/actions/passkey.ts +333 -0
- package/src/client/actions/realm.ts +37 -0
- package/src/client/actions/redirect.ts +502 -0
- package/src/client/internal/accessKey.ts +54 -0
- package/src/client/internal/account.ts +18 -0
- package/src/client/internal/api.ts +242 -0
- package/src/client/internal/headless.ts +308 -0
- package/src/client/internal/indexedDb.ts +44 -0
- package/src/client/internal/request.ts +70 -0
- package/src/client/internal/store.ts +20 -0
- package/src/client/internal/verifier.ts +75 -0
- package/src/client.ts +17 -0
- package/src/index.ts +5 -0
- package/src/internal/Apps.ts +1283 -0
- package/src/internal/Auth.ts +273 -0
- package/src/internal/Authorize.ts +433 -0
- package/src/internal/Balance.ts +144 -0
- package/src/internal/BodyLimit.ts +31 -0
- package/src/internal/Cache.ts +119 -0
- package/src/internal/Chain.ts +99 -0
- package/src/internal/ChainList.ts +270 -0
- package/src/internal/ChainName.ts +14 -0
- package/src/internal/ChainStatus.ts +65 -0
- package/src/internal/CodeEmail.ts +138 -0
- package/src/internal/ConfigurableAccount.ts +1315 -0
- package/src/internal/Device.ts +471 -0
- package/src/internal/Env.ts +92 -0
- package/src/internal/ErrorResponse.ts +896 -0
- package/src/internal/Faucet.ts +43 -0
- package/src/internal/Id.ts +41 -0
- package/src/internal/KeyAuthorization.ts +1536 -0
- package/src/internal/Limits.ts +40 -0
- package/src/internal/Log.ts +166 -0
- package/src/internal/LogLine.ts +81 -0
- package/src/internal/Logout.ts +356 -0
- package/src/internal/Migrations.ts +120 -0
- package/src/internal/OpenApi.ts +300 -0
- package/src/internal/Owner.ts +310 -0
- package/src/internal/Passkey.ts +1904 -0
- package/src/internal/PendingKey.ts +104 -0
- package/src/internal/Privy.ts +536 -0
- package/src/internal/RateLimit.ts +346 -0
- package/src/internal/Realm.ts +814 -0
- package/src/internal/RequestId.ts +118 -0
- package/src/internal/SignInGrant.ts +175 -0
- package/src/internal/SigningKey.ts +319 -0
- package/src/internal/TempoAccount.ts +545 -0
- package/src/internal/TestInbox.ts +51 -0
- package/src/internal/Timing.ts +172 -0
- package/src/internal/Validation.ts +52 -0
- package/src/internal/Verification.ts +58 -0
- package/src/internal/Wallet.ts +101 -0
- package/src/internal/owners/Passkey.ts +76 -0
- package/src/internal/owners/Privy.ts +82 -0
- package/src/internal/owners/Secp256k1.ts +139 -0
- package/src/types/app.d.ts +39 -0
- package/src/worker.ts +17 -0
|
@@ -0,0 +1,1662 @@
|
|
|
1
|
+
/** The sign-in routes under `/v1/auth`: email codes, passkeys, sign-out, and the session. @module */
|
|
2
|
+
import { verifyOAuthQueryParams } from '@better-auth/oauth-provider'
|
|
3
|
+
import { isAPIError } from 'better-auth/api'
|
|
4
|
+
import { eq } from 'drizzle-orm'
|
|
5
|
+
import type { Context } from 'hono'
|
|
6
|
+
import { Hono } from 'hono'
|
|
7
|
+
import { Hex } from 'ox'
|
|
8
|
+
import { Authentication } from 'webauthx/server'
|
|
9
|
+
import { env } from 'void/env'
|
|
10
|
+
import * as z from 'zod'
|
|
11
|
+
import * as Schema from '../app/db/schema.js'
|
|
12
|
+
import * as Apps from '../internal/Apps.js'
|
|
13
|
+
import * as core_Auth from '../internal/Auth.js'
|
|
14
|
+
import * as Authorize from '../internal/Authorize.js'
|
|
15
|
+
import * as BodyLimit from '../internal/BodyLimit.js'
|
|
16
|
+
import * as Cache from '../internal/Cache.js'
|
|
17
|
+
import * as Device from '../internal/Device.js'
|
|
18
|
+
import type { Env } from '../internal/Env.js'
|
|
19
|
+
import * as ErrorResponse from '../internal/ErrorResponse.js'
|
|
20
|
+
import * as KeyAuthorization from '../internal/KeyAuthorization.js'
|
|
21
|
+
import * as Log from '../internal/Log.js'
|
|
22
|
+
import * as Logout from '../internal/Logout.js'
|
|
23
|
+
import * as OpenApi from '../internal/OpenApi.js'
|
|
24
|
+
import * as Passkey from '../internal/Passkey.js'
|
|
25
|
+
import * as RateLimit from '../internal/RateLimit.js'
|
|
26
|
+
import * as Realm from '../internal/Realm.js'
|
|
27
|
+
import * as SignInGrant from '../internal/SignInGrant.js'
|
|
28
|
+
import * as TempoAccount from '../internal/TempoAccount.js'
|
|
29
|
+
import * as Timing from '../internal/Timing.js'
|
|
30
|
+
import * as Wallet from '../internal/Wallet.js'
|
|
31
|
+
import * as AccountStep from './lib/AccountStep.js'
|
|
32
|
+
import * as Auth from './lib/Auth.js'
|
|
33
|
+
import * as Cors from './lib/Cors.js'
|
|
34
|
+
import { db } from './lib/Database.js'
|
|
35
|
+
import * as FirstAccount from './lib/FirstAccount.js'
|
|
36
|
+
import * as Headless from './lib/Headless.js'
|
|
37
|
+
import * as PasskeyLink from './lib/PasskeyLink.js'
|
|
38
|
+
import * as Session from './lib/Session.js'
|
|
39
|
+
import * as SignInConsent from './lib/SignInConsent.js'
|
|
40
|
+
|
|
41
|
+
/** A hostname label: 1 to 63 letters, digits, and hyphens, with no hyphen at either end. */
|
|
42
|
+
const hostnameLabel = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/
|
|
43
|
+
|
|
44
|
+
namespace schema {
|
|
45
|
+
export namespace code {
|
|
46
|
+
export const json = z.object({
|
|
47
|
+
email: z
|
|
48
|
+
.string()
|
|
49
|
+
.trim()
|
|
50
|
+
.toLowerCase()
|
|
51
|
+
.pipe(
|
|
52
|
+
// A malformed or long address stops at its first failed check, so the refusal names it once.
|
|
53
|
+
z
|
|
54
|
+
.email({ abort: true })
|
|
55
|
+
.max(254, { abort: true, error: 'Invalid email address' })
|
|
56
|
+
// The email service refuses a local part over 64 characters, and a domain label that is
|
|
57
|
+
// not a hostname label: over 63 characters, or with a hyphen at either end.
|
|
58
|
+
.refine((value) => {
|
|
59
|
+
const [local = '', domain = ''] = value.split('@')
|
|
60
|
+
return (
|
|
61
|
+
local.length <= 64 &&
|
|
62
|
+
domain.split('.').every((label) => hostnameLabel.test(label))
|
|
63
|
+
)
|
|
64
|
+
}, 'Invalid email address'),
|
|
65
|
+
)
|
|
66
|
+
.meta({
|
|
67
|
+
description: "The user's email address.",
|
|
68
|
+
examples: ['ada@example.com'],
|
|
69
|
+
}),
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
export const response = z.object({
|
|
73
|
+
sent: z.literal(true).meta({
|
|
74
|
+
description:
|
|
75
|
+
'The request is accepted. The value is the same for every address.',
|
|
76
|
+
}),
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export namespace verify {
|
|
81
|
+
export const json = z
|
|
82
|
+
.object({
|
|
83
|
+
assertion: Passkey.schema.assertion.optional().meta({
|
|
84
|
+
description:
|
|
85
|
+
"The assertion of the email step's passkey, over a challenge from `POST /v1/auth/passkey/options` with `link`. Required with `link`, since only the person who holds the passkey can link it.",
|
|
86
|
+
}),
|
|
87
|
+
authorize: Authorize.schema.request.optional().meta({
|
|
88
|
+
description:
|
|
89
|
+
"The app's headless authorization request, as `POST /v1/authorize` takes it, from a first-party app's page. The sign-in then authorizes the app in the same request, creates a first Tempo Account for a user who holds none, and returns the authorization. A refusal that depends only on the app and the request, such as a key over the headless ceiling, comes before the sign-in and leaves the code unspent. A refusal of the authorization server after the sign-in, such as for a scope that the app's registration lacks, spends the code. Not with `link` or `oauth_query`.",
|
|
90
|
+
}),
|
|
91
|
+
code: z
|
|
92
|
+
.string()
|
|
93
|
+
.trim()
|
|
94
|
+
.regex(/^\d{6}$/)
|
|
95
|
+
.meta({
|
|
96
|
+
description: 'The six-digit emailed code.',
|
|
97
|
+
examples: ['123456'],
|
|
98
|
+
}),
|
|
99
|
+
email: code.json.shape.email,
|
|
100
|
+
link: z
|
|
101
|
+
.string()
|
|
102
|
+
.max(64)
|
|
103
|
+
.optional()
|
|
104
|
+
.meta({
|
|
105
|
+
description:
|
|
106
|
+
"The email step's `link`, which links the wallet.tempo passkey that asked for it to this user, with that passkey's `assertion`. An expired link refuses the sign-in with `passkey_link_expired`, and the code stays valid.",
|
|
107
|
+
examples: ['dGhlIG5vbmNlIHRoYXQgdGhlIHN0ZXAgY2Fycmllcw'],
|
|
108
|
+
}),
|
|
109
|
+
passkey: z.literal(true).optional().meta({
|
|
110
|
+
description:
|
|
111
|
+
"With `authorize`, requests the passkey step for an account that a passkey owns, in place of `webauthn_required`. The response's `authorization` is then the step, which the page signs, and `POST /v1/authorize` with `passkey: { assertion }` signs the key.",
|
|
112
|
+
}),
|
|
113
|
+
// The bound covers an authorization request's query, and limits what an unsigned query can
|
|
114
|
+
// write, since the passkey challenge stores its client ID before the signature is checked.
|
|
115
|
+
oauth_query: z
|
|
116
|
+
.string()
|
|
117
|
+
.max(8192)
|
|
118
|
+
.optional()
|
|
119
|
+
.meta({
|
|
120
|
+
description:
|
|
121
|
+
"The sign-in page's query string, which Better Auth signs when an app's authorization request sends the browser there. At most 8,192 characters.",
|
|
122
|
+
examples: [
|
|
123
|
+
'client_id=client_01JZ8Q6W3N1R5B4V7C2X9K0M3T&response_type=code&exp=1760000000&sig=Gx3kB0',
|
|
124
|
+
],
|
|
125
|
+
}),
|
|
126
|
+
})
|
|
127
|
+
.refine((value) => !value.link || value.assertion, {
|
|
128
|
+
message: 'Required with `link`.',
|
|
129
|
+
path: ['assertion'],
|
|
130
|
+
})
|
|
131
|
+
.refine(
|
|
132
|
+
(value) => !value.authorize || (!value.link && !value.oauth_query),
|
|
133
|
+
{
|
|
134
|
+
message: 'Not allowed with `link` or `oauth_query`.',
|
|
135
|
+
path: ['authorize'],
|
|
136
|
+
},
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
/** The user fields that the API returns. */
|
|
140
|
+
export const user = z
|
|
141
|
+
.object({
|
|
142
|
+
email: z.email().meta({
|
|
143
|
+
description: "The user's email address.",
|
|
144
|
+
examples: ['ada@example.com'],
|
|
145
|
+
}),
|
|
146
|
+
id: z.string().meta({
|
|
147
|
+
description:
|
|
148
|
+
"The user's ID, which is the `sub` of every token Tempo ID issues for the user.",
|
|
149
|
+
examples: ['user_01JZ8Q6W3N1R5B4V7C2X9K0M3T'],
|
|
150
|
+
}),
|
|
151
|
+
})
|
|
152
|
+
.meta({ description: 'The signed-in user.', id: 'User' })
|
|
153
|
+
|
|
154
|
+
export const response = z.object({
|
|
155
|
+
authorization: z
|
|
156
|
+
.union([Authorize.schema.response, Passkey.schema.headlessStep])
|
|
157
|
+
.optional()
|
|
158
|
+
.meta({
|
|
159
|
+
description:
|
|
160
|
+
"The app's headless authorization, for a sign-in that carried `authorize`, or a sign-in with key signature on the app's page whose options carried it. With `passkey`, the passkey step for a passkey-owned account. Absent for any other sign-in.",
|
|
161
|
+
}),
|
|
162
|
+
redirect: z
|
|
163
|
+
.url()
|
|
164
|
+
.nullable()
|
|
165
|
+
.meta({
|
|
166
|
+
description:
|
|
167
|
+
"Where the browser continues: the app's `redirect_uri` with an authorization code, or `null` for a sign-in that no app waits on.",
|
|
168
|
+
examples: [
|
|
169
|
+
'https://app.example.com/callback?code=SplxlOBeZQQYbYS6WxSbIA',
|
|
170
|
+
],
|
|
171
|
+
}),
|
|
172
|
+
user,
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
/** What Better Auth returns: the user, or the authorization that the sign-in continued. */
|
|
176
|
+
export const signedIn = z.union([
|
|
177
|
+
z.object({ url: z.string() }),
|
|
178
|
+
z.object({ user: z.object({ email: z.string(), id: z.string() }) }),
|
|
179
|
+
])
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export namespace passkeyOptions {
|
|
183
|
+
export const json = z
|
|
184
|
+
.object({
|
|
185
|
+
authorize: Authorize.schema.request.optional().meta({
|
|
186
|
+
description:
|
|
187
|
+
"The app's headless authorization request, as `POST /v1/authorize` takes it, from a first-party app's page. The challenge is then the digest of its key, so the passkey's one assertion signs the user in and signs the key, and `POST /v1/auth/passkey/verify` returns the authorization. Requires `client_id`, the same as its own.",
|
|
188
|
+
}),
|
|
189
|
+
client_id: z
|
|
190
|
+
.string()
|
|
191
|
+
.min(1)
|
|
192
|
+
.max(64)
|
|
193
|
+
.optional()
|
|
194
|
+
.meta({
|
|
195
|
+
description:
|
|
196
|
+
"The client ID of the first-party app whose page runs the ceremony, which selects the app's realms. Required from an app's page, whose `Origin` must be one of the app's redirect URI origins.",
|
|
197
|
+
examples: ['client_01JZ8Q6W3N1R5B4V7C2X9K0M3T'],
|
|
198
|
+
}),
|
|
199
|
+
credentialId: z
|
|
200
|
+
.union([
|
|
201
|
+
Passkey.schema.credentialId,
|
|
202
|
+
z.array(Passkey.schema.credentialId).max(16),
|
|
203
|
+
])
|
|
204
|
+
.optional()
|
|
205
|
+
.meta({
|
|
206
|
+
description:
|
|
207
|
+
'The credential ID, or up to 16 IDs, that the browser offers as `allowCredentials`, base64url. Defaults to every discoverable passkey of the relying party, as an empty list does.',
|
|
208
|
+
examples: ['KEbWNCc7NgaYnUyrNeFGX9_3Y-8'],
|
|
209
|
+
}),
|
|
210
|
+
link: verify.json.shape.link.meta({
|
|
211
|
+
description:
|
|
212
|
+
"The email step's `link`, which limits the options to that step's passkey, in place of `credentialId`. The emailed-code sign-in then sends the passkey's assertion with the `link`.",
|
|
213
|
+
examples: ['dGhlIG5vbmNlIHRoYXQgdGhlIHN0ZXAgY2Fycmllcw'],
|
|
214
|
+
}),
|
|
215
|
+
mediation: z
|
|
216
|
+
.enum(['conditional', 'optional', 'required', 'silent'])
|
|
217
|
+
.optional()
|
|
218
|
+
.meta({
|
|
219
|
+
description:
|
|
220
|
+
'Returned in the options for `navigator.credentials.get()`, such as `conditional` for passkey autofill.',
|
|
221
|
+
examples: ['conditional'],
|
|
222
|
+
}),
|
|
223
|
+
oauth_query: verify.json.shape.oauth_query,
|
|
224
|
+
user_code: z
|
|
225
|
+
.string()
|
|
226
|
+
.max(32)
|
|
227
|
+
.optional()
|
|
228
|
+
.meta({
|
|
229
|
+
description:
|
|
230
|
+
"The user code of the device grant that the sign-in returns to, ignored with `oauth_query` or `client_id`. A pending, unbound code selects its app's realms, and any other code selects the `default` realm.",
|
|
231
|
+
examples: ['WDJB-MJHT'],
|
|
232
|
+
}),
|
|
233
|
+
})
|
|
234
|
+
.refine(
|
|
235
|
+
(value) =>
|
|
236
|
+
!value.authorize || value.authorize.client_id === value.client_id,
|
|
237
|
+
{ message: 'Requires the same `client_id`.', path: ['authorize'] },
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
/** The `authorize` field as the page sent it, which the challenge stores with its expiry filled. */
|
|
241
|
+
export const sent = z.object({
|
|
242
|
+
authorize: z.looseObject({
|
|
243
|
+
key_authorization: z.looseObject({}).optional(),
|
|
244
|
+
}),
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
export const response = z.object({
|
|
248
|
+
options: z
|
|
249
|
+
.object({
|
|
250
|
+
mediation: passkeyOptions.json.shape.mediation,
|
|
251
|
+
publicKey: z
|
|
252
|
+
.object({
|
|
253
|
+
allowCredentials: z
|
|
254
|
+
.array(
|
|
255
|
+
z
|
|
256
|
+
.object({
|
|
257
|
+
id: z.string().meta({
|
|
258
|
+
description: 'A credential ID, base64url.',
|
|
259
|
+
examples: ['KEbWNCc7NgaYnUyrNeFGX9_3Y-8'],
|
|
260
|
+
}),
|
|
261
|
+
type: z.literal('public-key').meta({
|
|
262
|
+
description: 'The credential type.',
|
|
263
|
+
}),
|
|
264
|
+
})
|
|
265
|
+
.meta({ description: 'A passkey the browser offers.' }),
|
|
266
|
+
)
|
|
267
|
+
.optional()
|
|
268
|
+
.meta({
|
|
269
|
+
description:
|
|
270
|
+
'The passkeys the browser offers, from `credentialId`. Absent, it offers every discoverable passkey of the relying party.',
|
|
271
|
+
}),
|
|
272
|
+
challenge: z.templateLiteral(['0x', z.string()]).meta({
|
|
273
|
+
description:
|
|
274
|
+
'The single-use challenge, as hex. It expires after 5 minutes.',
|
|
275
|
+
examples: [
|
|
276
|
+
'0xf6310589abb21116acc6e123967ed0a125b5041c43f8e157237093e07eaa354c',
|
|
277
|
+
],
|
|
278
|
+
}),
|
|
279
|
+
rpId: z.string().meta({
|
|
280
|
+
description: 'The relying party ID of the passkeys.',
|
|
281
|
+
examples: ['tempo.xyz'],
|
|
282
|
+
}),
|
|
283
|
+
userVerification: z.literal('required').meta({
|
|
284
|
+
description:
|
|
285
|
+
'Every passkey sign-in requires user verification.',
|
|
286
|
+
}),
|
|
287
|
+
})
|
|
288
|
+
.meta({
|
|
289
|
+
description:
|
|
290
|
+
'The `PublicKeyCredentialRequestOptions`, serialized with the challenge as hex.',
|
|
291
|
+
}),
|
|
292
|
+
})
|
|
293
|
+
.meta({
|
|
294
|
+
description: 'The options for `navigator.credentials.get()`.',
|
|
295
|
+
}),
|
|
296
|
+
})
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export namespace passkeyVerify {
|
|
300
|
+
export const json = z.object({
|
|
301
|
+
assertion: Passkey.schema.assertion,
|
|
302
|
+
oauth_query: verify.json.shape.oauth_query,
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
export const response = z.union([
|
|
306
|
+
verify.response.meta({ description: 'The signed-in user.' }),
|
|
307
|
+
z
|
|
308
|
+
.object({
|
|
309
|
+
step: z
|
|
310
|
+
.object({
|
|
311
|
+
link: z.string().meta({
|
|
312
|
+
description:
|
|
313
|
+
'Send as `link` with the emailed-code sign-in, which links the passkey to that user.',
|
|
314
|
+
examples: ['dGhlIG5vbmNlIHRoYXQgdGhlIHN0ZXAgY2Fycmllcw'],
|
|
315
|
+
}),
|
|
316
|
+
signed: z.literal(false).meta({
|
|
317
|
+
description:
|
|
318
|
+
'Whether the assertion already signed a key authorization. This route signs none.',
|
|
319
|
+
}),
|
|
320
|
+
type: z.literal('email').meta({
|
|
321
|
+
description:
|
|
322
|
+
'Sign in with an emailed code, which links the passkey to that user.',
|
|
323
|
+
}),
|
|
324
|
+
})
|
|
325
|
+
.meta({
|
|
326
|
+
description:
|
|
327
|
+
'The email step, for a wallet.tempo passkey whose account has no email.',
|
|
328
|
+
}),
|
|
329
|
+
})
|
|
330
|
+
.meta({
|
|
331
|
+
description:
|
|
332
|
+
'A passkey sign-in that continues when the user verifies an email address.',
|
|
333
|
+
}),
|
|
334
|
+
])
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export namespace logout {
|
|
338
|
+
export const response = z.object({
|
|
339
|
+
signed_out: z.literal(true).meta({
|
|
340
|
+
description: 'The session is over, or there was none.',
|
|
341
|
+
}),
|
|
342
|
+
})
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export namespace session {
|
|
346
|
+
export const response = z.object({
|
|
347
|
+
user: verify.user
|
|
348
|
+
.extend({
|
|
349
|
+
name: z
|
|
350
|
+
.string()
|
|
351
|
+
.nullable()
|
|
352
|
+
.meta({
|
|
353
|
+
description:
|
|
354
|
+
"The user's name, or `null` for a user who signed up without one.",
|
|
355
|
+
examples: ['Ada Lovelace'],
|
|
356
|
+
}),
|
|
357
|
+
})
|
|
358
|
+
.nullable()
|
|
359
|
+
.meta({
|
|
360
|
+
description: 'The signed-in user, or `null` without a session.',
|
|
361
|
+
id: 'SessionUser',
|
|
362
|
+
}),
|
|
363
|
+
})
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export namespace assets {
|
|
367
|
+
export const response = z.object({
|
|
368
|
+
assets: z
|
|
369
|
+
.array(
|
|
370
|
+
z.string().meta({
|
|
371
|
+
description:
|
|
372
|
+
"The path of a script or style sheet on Tempo ID's origin.",
|
|
373
|
+
examples: ['/assets/index-QK4GWdO3.js'],
|
|
374
|
+
}),
|
|
375
|
+
)
|
|
376
|
+
.meta({
|
|
377
|
+
description:
|
|
378
|
+
"The sign-in page's scripts and style sheets, each once.",
|
|
379
|
+
examples: [
|
|
380
|
+
['/assets/index-QK4GWdO3.js', '/assets/zyzz-DYC-X0_B.css'],
|
|
381
|
+
],
|
|
382
|
+
}),
|
|
383
|
+
})
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export namespace methods {
|
|
387
|
+
export const query = z.object({
|
|
388
|
+
oauth_query: verify.json.shape.oauth_query,
|
|
389
|
+
return_to: z
|
|
390
|
+
.string()
|
|
391
|
+
.max(2048)
|
|
392
|
+
.optional()
|
|
393
|
+
.meta({
|
|
394
|
+
description:
|
|
395
|
+
"The page that sign-in returns to, from the sign-in page's query. Only a path on the device page passes, and any other value reads as absent.",
|
|
396
|
+
examples: ['/device?code=WDJB-MJHT'],
|
|
397
|
+
}),
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
export const response = z.object({
|
|
401
|
+
passkeys: z.boolean().meta({
|
|
402
|
+
description:
|
|
403
|
+
"Whether one of the sign-in's realms holds passkeys that sign in at Tempo ID's origin, so the page offers passkey sign-in.",
|
|
404
|
+
examples: [true],
|
|
405
|
+
}),
|
|
406
|
+
app: z
|
|
407
|
+
.string()
|
|
408
|
+
.nullable()
|
|
409
|
+
.meta({
|
|
410
|
+
description:
|
|
411
|
+
'The display name of the app of the request in `oauth_query`, or its client ID without a name. `null` for a forged or expired query, or an unknown app.',
|
|
412
|
+
examples: ['Tempo Cash'],
|
|
413
|
+
}),
|
|
414
|
+
host: z
|
|
415
|
+
.string()
|
|
416
|
+
.nullable()
|
|
417
|
+
.meta({
|
|
418
|
+
description:
|
|
419
|
+
"The host of a third-party app's client ID, which the page shows beside its name, or `null` for a registered app or without an app.",
|
|
420
|
+
examples: ['app.example'],
|
|
421
|
+
}),
|
|
422
|
+
request: z
|
|
423
|
+
.object({
|
|
424
|
+
app: z.string().meta({
|
|
425
|
+
description:
|
|
426
|
+
"The app's display name, or its client ID without one.",
|
|
427
|
+
examples: ['Tempo Cash'],
|
|
428
|
+
}),
|
|
429
|
+
key: KeyAuthorization.schema.description,
|
|
430
|
+
})
|
|
431
|
+
.nullable()
|
|
432
|
+
.meta({
|
|
433
|
+
description:
|
|
434
|
+
'The app and the key of the signed authorization request in `oauth_query`, which the sign-in grants. `null` when the sign-in grants no key, such as for an invalid query or a third-party app.',
|
|
435
|
+
}),
|
|
436
|
+
return_to: z
|
|
437
|
+
.string()
|
|
438
|
+
.nullable()
|
|
439
|
+
.meta({
|
|
440
|
+
description:
|
|
441
|
+
'The page that sign-in returns to, a path on the device page, or `null` for the account.',
|
|
442
|
+
examples: ['/device?code=WDJB-MJHT'],
|
|
443
|
+
}),
|
|
444
|
+
user_code: z
|
|
445
|
+
.string()
|
|
446
|
+
.nullable()
|
|
447
|
+
.meta({
|
|
448
|
+
description:
|
|
449
|
+
'The user code of the device page that sign-in returns to, or `null` without one.',
|
|
450
|
+
examples: ['WDJBMJHT'],
|
|
451
|
+
}),
|
|
452
|
+
verified: z.boolean().meta({
|
|
453
|
+
description:
|
|
454
|
+
'Whether `app` is a registered app, whose name the page marks as verified. `false` for a third-party app, which names itself in its metadata document, and without an app.',
|
|
455
|
+
examples: [true],
|
|
456
|
+
}),
|
|
457
|
+
})
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Returns the ID and stored address of the user with `email`, which wallet.tempo verified, and
|
|
463
|
+
* creates that user when none exists. A concurrent first sign-in can create the user first, so the
|
|
464
|
+
* read repeats.
|
|
465
|
+
*/
|
|
466
|
+
async function emailUser(c: Context<Env>, options: emailUser.Options) {
|
|
467
|
+
const email = options.email.trim().toLowerCase()
|
|
468
|
+
const context = await Timing.measure(c, c.var.auth.$context, 'auth.context')
|
|
469
|
+
const found = await Timing.measure(
|
|
470
|
+
c,
|
|
471
|
+
context.internalAdapter.findUserByEmail(email),
|
|
472
|
+
'db.user.findByEmail',
|
|
473
|
+
)
|
|
474
|
+
if (found) return { email: found.user.email, id: found.user.id }
|
|
475
|
+
|
|
476
|
+
try {
|
|
477
|
+
const created = await Timing.measure(
|
|
478
|
+
c,
|
|
479
|
+
context.internalAdapter.createUser(
|
|
480
|
+
{ email, emailVerified: true, name: '' },
|
|
481
|
+
{ method: 'passkey' },
|
|
482
|
+
),
|
|
483
|
+
'db.user.create',
|
|
484
|
+
)
|
|
485
|
+
return { email: created.email, id: created.id }
|
|
486
|
+
} catch (error) {
|
|
487
|
+
// The user hook refuses a new user outside `SIGN_UP_DOMAINS`.
|
|
488
|
+
if (
|
|
489
|
+
isAPIError(error) &&
|
|
490
|
+
error.body?.code === core_Auth.SignUpNotPermittedError.reason
|
|
491
|
+
)
|
|
492
|
+
throw new core_Auth.SignUpNotPermittedError({
|
|
493
|
+
domains: Auth.signUpDomains() ?? [],
|
|
494
|
+
})
|
|
495
|
+
const again = await Timing.measure(
|
|
496
|
+
c,
|
|
497
|
+
context.internalAdapter.findUserByEmail(email),
|
|
498
|
+
'db.user.findByEmail',
|
|
499
|
+
)
|
|
500
|
+
if (again) return { email: again.user.email, id: again.user.id }
|
|
501
|
+
throw error
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
declare namespace emailUser {
|
|
506
|
+
type Options = {
|
|
507
|
+
/** The email address that wallet.tempo verified. */
|
|
508
|
+
email: string
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
namespace emailUser {
|
|
513
|
+
/** The refusals `emailUser` throws. */
|
|
514
|
+
export const errors = [core_Auth.SignUpNotPermittedError] as const
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/** The sign-in routes under `/v1/auth`. */
|
|
518
|
+
export const authenticate = new Hono<Env>()
|
|
519
|
+
.post(
|
|
520
|
+
'/v1/auth/code',
|
|
521
|
+
OpenApi.describeRoute({
|
|
522
|
+
description:
|
|
523
|
+
'Emails a sign-in code. The response is the same for every address, so it reveals no account.',
|
|
524
|
+
responses: OpenApi.responses({
|
|
525
|
+
errors: [
|
|
526
|
+
...BodyLimit.errors,
|
|
527
|
+
...Auth.sendCode.errors,
|
|
528
|
+
...OpenApi.validate.errors,
|
|
529
|
+
...RateLimit.check.errors,
|
|
530
|
+
ErrorResponse.CrossSiteRequestError,
|
|
531
|
+
ErrorResponse.UpstreamUnavailableError,
|
|
532
|
+
],
|
|
533
|
+
success: {
|
|
534
|
+
description:
|
|
535
|
+
'The response for every address: the email service accepted the code, or does not deliver to the address.',
|
|
536
|
+
schema: schema.code.response,
|
|
537
|
+
},
|
|
538
|
+
}),
|
|
539
|
+
tags: ['Authenticate'],
|
|
540
|
+
}),
|
|
541
|
+
OpenApi.validate('json', schema.code.json),
|
|
542
|
+
async (c) => {
|
|
543
|
+
const { email } = c.req.valid('json')
|
|
544
|
+
// The IP address's limit counts first, so a refused flood spends no address's sends.
|
|
545
|
+
const refused = await Timing.measure(
|
|
546
|
+
c,
|
|
547
|
+
RateLimit.check(c, {
|
|
548
|
+
limits: [
|
|
549
|
+
{ binding: 'SIGN_IN_IP_LIMIT', key: RateLimit.ip(c) },
|
|
550
|
+
{ binding: 'SIGN_IN_CODE_LIMIT', key: RateLimit.email({ email }) },
|
|
551
|
+
],
|
|
552
|
+
}),
|
|
553
|
+
'RateLimit.check',
|
|
554
|
+
)
|
|
555
|
+
if (refused) return refused
|
|
556
|
+
|
|
557
|
+
// The Workers limits count each location on its own, so the database counts the address's
|
|
558
|
+
// codes across every location.
|
|
559
|
+
const within = await Timing.measure(
|
|
560
|
+
c,
|
|
561
|
+
RateLimit.countCode(db, { email }),
|
|
562
|
+
'RateLimit.countCode',
|
|
563
|
+
)
|
|
564
|
+
if (!within) {
|
|
565
|
+
RateLimit.describe(c, { policy: RateLimit.codePolicy })
|
|
566
|
+
return RateLimit.refuse(c, {
|
|
567
|
+
policy: RateLimit.codePolicy,
|
|
568
|
+
retryAfter: RateLimit.codePolicy.window,
|
|
569
|
+
})
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Every address receives a code, so the response reveals no account. The route waits for the
|
|
573
|
+
// email service, so a failed send refuses the request instead of a sign-in that never arrives.
|
|
574
|
+
const code = await Timing.measure(
|
|
575
|
+
c,
|
|
576
|
+
c.var.auth.api.createVerificationOTP({
|
|
577
|
+
body: { email, type: 'sign-in' },
|
|
578
|
+
}),
|
|
579
|
+
'auth.createVerificationOTP',
|
|
580
|
+
)
|
|
581
|
+
const failure = await Timing.measure(
|
|
582
|
+
c,
|
|
583
|
+
Auth.sendCode(c, { code, email: email.toLowerCase() }),
|
|
584
|
+
'Auth.sendCode',
|
|
585
|
+
).catch((error: unknown) => error)
|
|
586
|
+
if (failure) {
|
|
587
|
+
// The refusal is the email service's, as for any upstream service, so it is a warning.
|
|
588
|
+
Log.warn(c, {
|
|
589
|
+
error: failure,
|
|
590
|
+
message: 'The email service refused a sign-in code.',
|
|
591
|
+
})
|
|
592
|
+
// A refusal of the address itself responds as a sent code, so the response is the same for
|
|
593
|
+
// every address. The email service's rate limit is one the caller can wait out. Any other
|
|
594
|
+
// refusal is an outage.
|
|
595
|
+
if (!(failure instanceof Auth.UndeliverableError))
|
|
596
|
+
return ErrorResponse.json(
|
|
597
|
+
c,
|
|
598
|
+
failure instanceof ErrorResponse.RateLimitedError
|
|
599
|
+
? failure
|
|
600
|
+
: new ErrorResponse.UpstreamUnavailableError(),
|
|
601
|
+
)
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return c.json({ sent: true as const }, 200)
|
|
605
|
+
},
|
|
606
|
+
)
|
|
607
|
+
.post(
|
|
608
|
+
'/v1/auth/code/verify',
|
|
609
|
+
OpenApi.describeRoute({
|
|
610
|
+
description:
|
|
611
|
+
'Signs in with an emailed code, and sets the session cookie. For a user with no Tempo Account in the first realm of the app that `oauth_query` names, or in the `default` realm, it creates one when that realm creates accounts. A failed creation leaves the user signed in, and the next sign-in or `POST /v1/account` creates the account. With `authorize`, it also authorizes the app as `POST /v1/authorize` does, and a refusal after the sign-in carries `Tempo-Id-Signed-In: true`.',
|
|
612
|
+
responses: OpenApi.responses({
|
|
613
|
+
errors: [
|
|
614
|
+
...BodyLimit.errors,
|
|
615
|
+
...Cors.ownPage.errors,
|
|
616
|
+
...OpenApi.validate.errors,
|
|
617
|
+
...Headless.afterCode.errors,
|
|
618
|
+
...Headless.check.errors,
|
|
619
|
+
...Headless.keySignature.errors,
|
|
620
|
+
...RateLimit.check.errors,
|
|
621
|
+
core_Auth.AuthorizationExpiredError,
|
|
622
|
+
core_Auth.InvalidCodeError,
|
|
623
|
+
core_Auth.SignUpNotPermittedError,
|
|
624
|
+
Passkey.LinkExpiredError,
|
|
625
|
+
Passkey.WebAuthnRejectedError,
|
|
626
|
+
],
|
|
627
|
+
success: {
|
|
628
|
+
description: 'The signed-in user.',
|
|
629
|
+
schema: schema.verify.response,
|
|
630
|
+
},
|
|
631
|
+
}),
|
|
632
|
+
tags: ['Authenticate'],
|
|
633
|
+
}),
|
|
634
|
+
OpenApi.validate('json', schema.verify.json),
|
|
635
|
+
async (c) => {
|
|
636
|
+
const { assertion, authorize, code, email, link, oauth_query, passkey } =
|
|
637
|
+
c.req.valid('json')
|
|
638
|
+
const crossSite = Cors.ownPage(c, { oauth_query })
|
|
639
|
+
if (crossSite) return ErrorResponse.json(c, crossSite)
|
|
640
|
+
// Better Auth counts the guesses at each code. The IP address's limit bounds guesses across
|
|
641
|
+
// addresses.
|
|
642
|
+
const refused = await Timing.measure(
|
|
643
|
+
c,
|
|
644
|
+
RateLimit.check(c, {
|
|
645
|
+
limits: [{ binding: 'SIGN_IN_IP_LIMIT', key: RateLimit.ip(c) }],
|
|
646
|
+
}),
|
|
647
|
+
'RateLimit.check',
|
|
648
|
+
)
|
|
649
|
+
if (refused) return refused
|
|
650
|
+
|
|
651
|
+
// The app's request is checked as a headless one before the code, so a refused request leaves
|
|
652
|
+
// the code unspent.
|
|
653
|
+
const app = authorize
|
|
654
|
+
? await Timing.measure(
|
|
655
|
+
c,
|
|
656
|
+
Headless.app(c, { clientId: authorize.client_id }),
|
|
657
|
+
'Headless.app',
|
|
658
|
+
)
|
|
659
|
+
: undefined
|
|
660
|
+
const refusesAuthorize =
|
|
661
|
+
authorize &&
|
|
662
|
+
Headless.check(authorize, { app, origin: c.req.header('origin') })
|
|
663
|
+
if (refusesAuthorize) return ErrorResponse.json(c, refusesAuthorize)
|
|
664
|
+
|
|
665
|
+
// The email step's link is checked before the code, so an expired one refuses the sign-in and
|
|
666
|
+
// leaves the code unspent. The sign-in then never ends without the passkey it was meant to link.
|
|
667
|
+
const deferred = link
|
|
668
|
+
? await Timing.measure(
|
|
669
|
+
c,
|
|
670
|
+
PasskeyLink.claim(c, { nonce: link }),
|
|
671
|
+
'PasskeyLink.claim',
|
|
672
|
+
)
|
|
673
|
+
: undefined
|
|
674
|
+
if (link && !deferred)
|
|
675
|
+
return ErrorResponse.json(c, new Passkey.LinkExpiredError())
|
|
676
|
+
|
|
677
|
+
// The link's passkey signs again before the code is checked, so a page left at the email step
|
|
678
|
+
// links nothing for the next person, and a refusal leaves the code unspent. On an app's page,
|
|
679
|
+
// that assertion can sign the key of the request that the options carried, as a digest
|
|
680
|
+
// sign-in does.
|
|
681
|
+
const challenge =
|
|
682
|
+
deferred && assertion
|
|
683
|
+
? await Timing.measure(
|
|
684
|
+
c,
|
|
685
|
+
Passkey.take(db, {
|
|
686
|
+
clientDataJSON: assertion.metadata.clientDataJSON,
|
|
687
|
+
}),
|
|
688
|
+
'Passkey.take',
|
|
689
|
+
)
|
|
690
|
+
: undefined
|
|
691
|
+
if (deferred) {
|
|
692
|
+
// Only a challenge that the options issued for this link confirms it.
|
|
693
|
+
const bound =
|
|
694
|
+
challenge && link
|
|
695
|
+
? await Timing.measure(
|
|
696
|
+
c,
|
|
697
|
+
PasskeyLink.matches({ expected: challenge.link, nonce: link }),
|
|
698
|
+
'PasskeyLink.matches',
|
|
699
|
+
)
|
|
700
|
+
: false
|
|
701
|
+
if (
|
|
702
|
+
!assertion ||
|
|
703
|
+
!challenge ||
|
|
704
|
+
!bound ||
|
|
705
|
+
challenge.clientId !== client({ oauth_query }) ||
|
|
706
|
+
challenge.origin !== Cors.appOrigin(c) ||
|
|
707
|
+
challenge.rpId !== deferred.rpId ||
|
|
708
|
+
assertion.id !== deferred.id ||
|
|
709
|
+
!Passkey.verify(assertion, {
|
|
710
|
+
challenge: challenge.challenge,
|
|
711
|
+
origin: challenge.origin ?? new URL(env.URL).origin,
|
|
712
|
+
publicKey: deferred.publicKey,
|
|
713
|
+
rpId: deferred.rpId,
|
|
714
|
+
}) ||
|
|
715
|
+
!(await Timing.measure(
|
|
716
|
+
c,
|
|
717
|
+
Headless.spend(c, { assertion, challenge }),
|
|
718
|
+
'Headless.spend',
|
|
719
|
+
))
|
|
720
|
+
)
|
|
721
|
+
return ErrorResponse.json(c, new Passkey.WebAuthnRejectedError())
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const result = await Timing.measure(
|
|
725
|
+
c,
|
|
726
|
+
c.var.auth.api.signInEmailOTP({
|
|
727
|
+
// A call with a request returns a `Response` unless `asResponse` is false.
|
|
728
|
+
asResponse: false,
|
|
729
|
+
body: { email, otp: code, ...(oauth_query ? { oauth_query } : {}) },
|
|
730
|
+
// Better Auth sets `accept` to continue an authorization as JSON, and a request's own headers
|
|
731
|
+
// are immutable.
|
|
732
|
+
headers: new Headers(c.req.raw.headers),
|
|
733
|
+
// Better Auth continues an authorization only for a call that carries its request.
|
|
734
|
+
request: c.req.raw,
|
|
735
|
+
returnHeaders: true,
|
|
736
|
+
}),
|
|
737
|
+
'auth.signInEmailOTP',
|
|
738
|
+
).catch((error: unknown) => {
|
|
739
|
+
// Better Auth checks the authorization request's signature before the code, so a forged or
|
|
740
|
+
// expired one leaves the code unspent.
|
|
741
|
+
if (isAPIError(error) && error.body?.error === 'invalid_signature')
|
|
742
|
+
return new core_Auth.AuthorizationExpiredError()
|
|
743
|
+
// The user hook refuses a new user outside `SIGN_UP_DOMAINS`, after the code verifies.
|
|
744
|
+
if (
|
|
745
|
+
isAPIError(error) &&
|
|
746
|
+
error.body?.code === core_Auth.SignUpNotPermittedError.reason
|
|
747
|
+
)
|
|
748
|
+
return new core_Auth.SignUpNotPermittedError({
|
|
749
|
+
domains: Auth.signUpDomains() ?? [],
|
|
750
|
+
})
|
|
751
|
+
// Better Auth refuses a wrong, expired, or spent code, and a code past its guesses. Any other
|
|
752
|
+
// failure fails the request.
|
|
753
|
+
if (isAPIError(error)) return new core_Auth.InvalidCodeError()
|
|
754
|
+
throw error
|
|
755
|
+
})
|
|
756
|
+
if (result instanceof ErrorResponse.ResponseError)
|
|
757
|
+
return ErrorResponse.json(c, result)
|
|
758
|
+
|
|
759
|
+
for (const cookie of result.headers.getSetCookie())
|
|
760
|
+
c.header('set-cookie', cookie, { append: true })
|
|
761
|
+
c.header('cache-control', 'private, no-store')
|
|
762
|
+
|
|
763
|
+
// With `oauth_query`, Better Auth continues the app's authorization once the session exists, and
|
|
764
|
+
// returns its redirect in place of the user.
|
|
765
|
+
const signedIn = schema.verify.signedIn.parse(result.response)
|
|
766
|
+
const user = (() => {
|
|
767
|
+
if ('user' in signedIn)
|
|
768
|
+
return { email: signedIn.user.email, id: signedIn.user.id }
|
|
769
|
+
// Better Auth signs in the user whose address is the lowercase `email`, and the session
|
|
770
|
+
// that it created names that user.
|
|
771
|
+
const id = c.var.newSession?.user.id
|
|
772
|
+
if (!id) throw new Error('The signed-in user is missing.')
|
|
773
|
+
return { email, id }
|
|
774
|
+
})()
|
|
775
|
+
|
|
776
|
+
// The email step's passkey links to the user that the code signed in.
|
|
777
|
+
if (deferred) {
|
|
778
|
+
const linked = await Timing.measure(
|
|
779
|
+
c,
|
|
780
|
+
Passkey.link(db, {
|
|
781
|
+
credential: deferred,
|
|
782
|
+
id: deferred.id,
|
|
783
|
+
rpId: deferred.rpId,
|
|
784
|
+
userId: user.id,
|
|
785
|
+
}),
|
|
786
|
+
'Passkey.link',
|
|
787
|
+
)
|
|
788
|
+
// The account step of the sign-in listed the user's keys before the link.
|
|
789
|
+
TempoAccount.forget(c)
|
|
790
|
+
// A concurrent sign-in can link the passkey to another user first. Its row decides, so this
|
|
791
|
+
// sign-in links nothing, and the cookie stays until it expires.
|
|
792
|
+
if (linked === user.id) PasskeyLink.release(c)
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// A linked passkey owns its account, so the first account follows the link. The sign-in grant
|
|
796
|
+
// below can then sign the key with it.
|
|
797
|
+
await Timing.measure(
|
|
798
|
+
c,
|
|
799
|
+
FirstAccount.create(c, {
|
|
800
|
+
clientId: client({ oauth_query }),
|
|
801
|
+
// Better Auth signs in the user of the lowercase address.
|
|
802
|
+
email: user.email.toLowerCase(),
|
|
803
|
+
userId: user.id,
|
|
804
|
+
}),
|
|
805
|
+
'FirstAccount.create',
|
|
806
|
+
)
|
|
807
|
+
|
|
808
|
+
// The link's assertion signs the key of the request that its options carried.
|
|
809
|
+
if (challenge?.authorize !== undefined && assertion) {
|
|
810
|
+
// The session exists, so the page reads any refusal from here, thrown ones included, as
|
|
811
|
+
// one after the sign-in.
|
|
812
|
+
c.header(Headless.signedInHeader, 'true')
|
|
813
|
+
const authorization = await Timing.measure(
|
|
814
|
+
c,
|
|
815
|
+
Headless.keySignature(c, {
|
|
816
|
+
assertion,
|
|
817
|
+
challenge,
|
|
818
|
+
email: user.email,
|
|
819
|
+
setCookie: result.headers.getSetCookie(),
|
|
820
|
+
}),
|
|
821
|
+
'Headless.keySignature',
|
|
822
|
+
)
|
|
823
|
+
if (authorization instanceof Response) return authorization
|
|
824
|
+
if (authorization instanceof ErrorResponse.ResponseError)
|
|
825
|
+
return ErrorResponse.json(c, authorization)
|
|
826
|
+
return c.json({ authorization, redirect: null, user }, 200)
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// The app's request authorizes in the same request, after the code signed the user in.
|
|
830
|
+
if (authorize && app) {
|
|
831
|
+
// The session exists, so the page reads any refusal from here, thrown ones included, as one
|
|
832
|
+
// after the sign-in.
|
|
833
|
+
c.header(Headless.signedInHeader, 'true')
|
|
834
|
+
const authorization = await Timing.measure(
|
|
835
|
+
c,
|
|
836
|
+
Headless.afterCode(c, {
|
|
837
|
+
app,
|
|
838
|
+
email: user.email,
|
|
839
|
+
passkey,
|
|
840
|
+
request: authorize,
|
|
841
|
+
setCookie: result.headers.getSetCookie(),
|
|
842
|
+
}),
|
|
843
|
+
'Headless.afterCode',
|
|
844
|
+
)
|
|
845
|
+
if (authorization instanceof Response) return authorization
|
|
846
|
+
if (authorization instanceof ErrorResponse.ResponseError)
|
|
847
|
+
return ErrorResponse.json(c, authorization)
|
|
848
|
+
// A passkey signs only in the browser, so the page signs the digest and repeats the request
|
|
849
|
+
// at `POST /v1/authorize`.
|
|
850
|
+
if ('options' in authorization)
|
|
851
|
+
return c.json(
|
|
852
|
+
{
|
|
853
|
+
authorization: Passkey.schema.headlessStep.parse({
|
|
854
|
+
expiry: authorization.expiry,
|
|
855
|
+
step: { options: authorization.options, type: 'passkey' },
|
|
856
|
+
}),
|
|
857
|
+
redirect: null,
|
|
858
|
+
user,
|
|
859
|
+
},
|
|
860
|
+
200,
|
|
861
|
+
)
|
|
862
|
+
return c.json({ authorization, redirect: null, user }, 200)
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// The sign-in page showed the request's key, so the sign-in grants it.
|
|
866
|
+
const redirect =
|
|
867
|
+
'user' in signedIn
|
|
868
|
+
? null
|
|
869
|
+
: await Timing.measure(
|
|
870
|
+
c,
|
|
871
|
+
SignInConsent.afterSignIn(c, {
|
|
872
|
+
redirect: signedIn.url,
|
|
873
|
+
setCookie: result.headers.getSetCookie(),
|
|
874
|
+
}),
|
|
875
|
+
'SignInConsent.afterSignIn',
|
|
876
|
+
)
|
|
877
|
+
return c.json({ redirect, user }, 200)
|
|
878
|
+
},
|
|
879
|
+
)
|
|
880
|
+
.post(
|
|
881
|
+
'/v1/auth/passkey/options',
|
|
882
|
+
OpenApi.describeRoute({
|
|
883
|
+
description:
|
|
884
|
+
"Issues a single-use challenge, and returns request options for the passkeys of the first realm whose relying party covers Tempo ID's origin, and also the page's origin from an app's page. The realms are those of the app that `oauth_query`, `client_id`, or `user_code` names, or the `default` realm. With `link`, that realm must be the `default` realm at the link's relying party.",
|
|
885
|
+
responses: OpenApi.responses({
|
|
886
|
+
errors: [
|
|
887
|
+
...BodyLimit.errors,
|
|
888
|
+
...Cors.ownPage.errors,
|
|
889
|
+
...OpenApi.validate.errors,
|
|
890
|
+
...Authorize.check.errors,
|
|
891
|
+
...Passkey.checkPage.errors,
|
|
892
|
+
...RateLimit.check.errors,
|
|
893
|
+
Passkey.LinkExpiredError,
|
|
894
|
+
Passkey.NotPermittedError,
|
|
895
|
+
],
|
|
896
|
+
success: {
|
|
897
|
+
description: 'The request options.',
|
|
898
|
+
schema: schema.passkeyOptions.response,
|
|
899
|
+
},
|
|
900
|
+
}),
|
|
901
|
+
tags: ['Authenticate'],
|
|
902
|
+
}),
|
|
903
|
+
OpenApi.validate('json', schema.passkeyOptions.json),
|
|
904
|
+
async (c) => {
|
|
905
|
+
const {
|
|
906
|
+
authorize,
|
|
907
|
+
client_id,
|
|
908
|
+
credentialId,
|
|
909
|
+
link,
|
|
910
|
+
mediation,
|
|
911
|
+
oauth_query,
|
|
912
|
+
user_code,
|
|
913
|
+
} = c.req.valid('json')
|
|
914
|
+
const crossSite = Cors.ownPage(c, { oauth_query })
|
|
915
|
+
if (crossSite) return ErrorResponse.json(c, crossSite)
|
|
916
|
+
// Each view of the sign-in page requests options for its autofill, so the options requests
|
|
917
|
+
// count on their own key, and spend none of the address's quota for email sign-in.
|
|
918
|
+
const ip = RateLimit.ip(c)
|
|
919
|
+
const refused = await Timing.measure(
|
|
920
|
+
c,
|
|
921
|
+
RateLimit.check(c, {
|
|
922
|
+
limits: [
|
|
923
|
+
{
|
|
924
|
+
binding: 'SIGN_IN_IP_LIMIT',
|
|
925
|
+
key: ip ? `passkey-options:${ip}` : undefined,
|
|
926
|
+
},
|
|
927
|
+
],
|
|
928
|
+
}),
|
|
929
|
+
'RateLimit.check',
|
|
930
|
+
)
|
|
931
|
+
if (refused) return refused
|
|
932
|
+
|
|
933
|
+
const deferred = link
|
|
934
|
+
? await Timing.measure(
|
|
935
|
+
c,
|
|
936
|
+
PasskeyLink.claim(c, { nonce: link }),
|
|
937
|
+
'PasskeyLink.claim',
|
|
938
|
+
)
|
|
939
|
+
: undefined
|
|
940
|
+
if (link && !deferred)
|
|
941
|
+
return ErrorResponse.json(c, new Passkey.LinkExpiredError())
|
|
942
|
+
|
|
943
|
+
// A first-party app's page runs the ceremony at its own origin, which the challenge stores.
|
|
944
|
+
// Only an assertion for that origin, from a request that origin sends, spends it.
|
|
945
|
+
const page = Cors.appOrigin(c)
|
|
946
|
+
const app = client_id
|
|
947
|
+
? await Timing.measure(
|
|
948
|
+
c,
|
|
949
|
+
// A disabled app can share its origin with an enabled one, which CORS admits.
|
|
950
|
+
Headless.app(c, { clientId: client_id }),
|
|
951
|
+
'Headless.app',
|
|
952
|
+
)
|
|
953
|
+
: undefined
|
|
954
|
+
if (page || client_id) {
|
|
955
|
+
const refusal = Passkey.checkPage({
|
|
956
|
+
app,
|
|
957
|
+
clientId: client_id,
|
|
958
|
+
origin: c.req.header('origin'),
|
|
959
|
+
})
|
|
960
|
+
if (refusal) return ErrorResponse.json(c, refusal)
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// The signature of `oauth_query` is checked when the session is created. Until then, its app
|
|
964
|
+
// selects only the relying party. An app's page continues no authorization, so its challenge
|
|
965
|
+
// names no app.
|
|
966
|
+
const clientId = client({ oauth_query })
|
|
967
|
+
// A device app comes from the code's own row, never from the request, and selects only the
|
|
968
|
+
// relying party, so the challenge names no app.
|
|
969
|
+
const userCode = user_code ? Device.normalize(user_code) : undefined
|
|
970
|
+
const deviceApp =
|
|
971
|
+
!clientId && !client_id && userCode
|
|
972
|
+
? await Timing.measure(
|
|
973
|
+
c,
|
|
974
|
+
Device.client(db, { userCode }),
|
|
975
|
+
'Device.client',
|
|
976
|
+
)
|
|
977
|
+
: undefined
|
|
978
|
+
const appId = clientId ?? client_id ?? deviceApp
|
|
979
|
+
const stored = appId
|
|
980
|
+
? await Timing.measure(
|
|
981
|
+
c,
|
|
982
|
+
Cache.read(c, () => Realm.forApp(db, { clientId: appId }), [
|
|
983
|
+
'realm.forApp',
|
|
984
|
+
appId,
|
|
985
|
+
]),
|
|
986
|
+
'Realm.forApp',
|
|
987
|
+
)
|
|
988
|
+
: [
|
|
989
|
+
await Timing.measure(
|
|
990
|
+
c,
|
|
991
|
+
Cache.read(c, () => Realm.get(db, { id: Realm.defaultId }), [
|
|
992
|
+
'realm.get',
|
|
993
|
+
Realm.defaultId,
|
|
994
|
+
]),
|
|
995
|
+
'Realm.get',
|
|
996
|
+
),
|
|
997
|
+
]
|
|
998
|
+
const realms = await Timing.measure(
|
|
999
|
+
c,
|
|
1000
|
+
Realm.withDefaults(c, { db, origin: env.URL, realms: stored }),
|
|
1001
|
+
'Realm.withDefaults',
|
|
1002
|
+
)
|
|
1003
|
+
const relyingParty = Passkey.relyingParty(realms, {
|
|
1004
|
+
origin: env.URL,
|
|
1005
|
+
page,
|
|
1006
|
+
})
|
|
1007
|
+
// Only a challenge of the `default` realm reads wallet.tempo, so a link's passkey signs again
|
|
1008
|
+
// only for a sign-in whose realms select that realm, at the link's relying party.
|
|
1009
|
+
const refusesLink =
|
|
1010
|
+
deferred &&
|
|
1011
|
+
(relyingParty?.realmId !== Realm.defaultId ||
|
|
1012
|
+
relyingParty.rpId !== deferred.rpId)
|
|
1013
|
+
if (!relyingParty || refusesLink)
|
|
1014
|
+
return ErrorResponse.json(
|
|
1015
|
+
c,
|
|
1016
|
+
new Passkey.NotPermittedError({
|
|
1017
|
+
// A device app stays unnamed, so the refusal names no app that a code reveals.
|
|
1018
|
+
clientId: (clientId ?? client_id) || undefined,
|
|
1019
|
+
origin: page,
|
|
1020
|
+
}),
|
|
1021
|
+
)
|
|
1022
|
+
|
|
1023
|
+
// A challenge for a link stores the link's digest, so only that link's code sign-in takes it.
|
|
1024
|
+
const linked = link
|
|
1025
|
+
? await Timing.measure(
|
|
1026
|
+
c,
|
|
1027
|
+
PasskeyLink.digest({ nonce: link }),
|
|
1028
|
+
'PasskeyLink.digest',
|
|
1029
|
+
)
|
|
1030
|
+
: undefined
|
|
1031
|
+
// On an app's page, the request is checked as a headless one before the ceremony, so a key
|
|
1032
|
+
// over the headless ceiling signs nothing. Its key's expiry is filled now, so the request that
|
|
1033
|
+
// the sign-in runs has the digest that the passkey signs.
|
|
1034
|
+
const refusesAuthorize =
|
|
1035
|
+
authorize &&
|
|
1036
|
+
(page
|
|
1037
|
+
? Authorize.check(authorize, { app, origin: c.req.header('origin') })
|
|
1038
|
+
: new Authorize.InvalidOriginError({
|
|
1039
|
+
error_description:
|
|
1040
|
+
"Invalid `Origin`: none. A sign-in with `authorize` runs only on the app's page.",
|
|
1041
|
+
field: 'Origin',
|
|
1042
|
+
}))
|
|
1043
|
+
if (refusesAuthorize) return ErrorResponse.json(c, refusesAuthorize)
|
|
1044
|
+
const pinned = await (async () => {
|
|
1045
|
+
if (!authorize) return undefined
|
|
1046
|
+
const sent = schema.passkeyOptions.sent.parse(await c.req.json())
|
|
1047
|
+
const key = authorize.key_authorization
|
|
1048
|
+
return key
|
|
1049
|
+
? {
|
|
1050
|
+
...sent.authorize,
|
|
1051
|
+
key_authorization: {
|
|
1052
|
+
...sent.authorize.key_authorization,
|
|
1053
|
+
expiry: key.expiry,
|
|
1054
|
+
},
|
|
1055
|
+
}
|
|
1056
|
+
: sent.authorize
|
|
1057
|
+
})()
|
|
1058
|
+
|
|
1059
|
+
// The challenge of a request with a key is the key's digest, so one passkey assertion signs in
|
|
1060
|
+
// and signs the key: on the sign-in page for the request it continues, and on an app's page for
|
|
1061
|
+
// `authorize`. A link's step on the sign-in page signs in only. The witness is the request's
|
|
1062
|
+
// own, from its `challenge`, or a random one.
|
|
1063
|
+
const keyChallenge = await (async () => {
|
|
1064
|
+
if (page) {
|
|
1065
|
+
const key = authorize?.key_authorization
|
|
1066
|
+
if (!key) return undefined
|
|
1067
|
+
const witness =
|
|
1068
|
+
key.witness ??
|
|
1069
|
+
Hex.fromBytes(crypto.getRandomValues(new Uint8Array(32)))
|
|
1070
|
+
return {
|
|
1071
|
+
digest: KeyAuthorization.digest({ key, witness }),
|
|
1072
|
+
witness,
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
if (!oauth_query || link) return undefined
|
|
1076
|
+
return await Timing.measure(
|
|
1077
|
+
c,
|
|
1078
|
+
SignInConsent.keyChallenge(c, { oauthQuery: oauth_query }),
|
|
1079
|
+
'SignInConsent.keyChallenge',
|
|
1080
|
+
)
|
|
1081
|
+
})()
|
|
1082
|
+
const challenge = await Timing.measure(
|
|
1083
|
+
c,
|
|
1084
|
+
Passkey.challenge(db, {
|
|
1085
|
+
authorize: pinned,
|
|
1086
|
+
clientId,
|
|
1087
|
+
keyChallenge,
|
|
1088
|
+
link: linked,
|
|
1089
|
+
origin: page,
|
|
1090
|
+
...relyingParty,
|
|
1091
|
+
}),
|
|
1092
|
+
'Passkey.challenge',
|
|
1093
|
+
)
|
|
1094
|
+
// A pending challenge holds the same key challenge, which only an app's challenge repeats.
|
|
1095
|
+
if (!challenge)
|
|
1096
|
+
return ErrorResponse.json(c, new Passkey.WebAuthnRejectedError())
|
|
1097
|
+
// Without a credential ID, the browser offers every discoverable passkey of the relying party,
|
|
1098
|
+
// as WebAuthn defines for an empty list. The challenge stays Tempo ID's either way.
|
|
1099
|
+
const allowed = (() => {
|
|
1100
|
+
if (deferred) return [deferred.id]
|
|
1101
|
+
return credentialId === undefined ? [] : [credentialId].flat()
|
|
1102
|
+
})()
|
|
1103
|
+
const options = schema.passkeyOptions.response.shape.options.parse({
|
|
1104
|
+
...Authentication.getOptions({
|
|
1105
|
+
challenge,
|
|
1106
|
+
...(allowed.length > 0 ? { credentialId: allowed } : {}),
|
|
1107
|
+
rpId: relyingParty.rpId,
|
|
1108
|
+
userVerification: 'required',
|
|
1109
|
+
}).options,
|
|
1110
|
+
...(mediation ? { mediation } : {}),
|
|
1111
|
+
})
|
|
1112
|
+
c.header('cache-control', 'private, no-store')
|
|
1113
|
+
return c.json({ options }, 200)
|
|
1114
|
+
},
|
|
1115
|
+
)
|
|
1116
|
+
.post(
|
|
1117
|
+
'/v1/auth/passkey/verify',
|
|
1118
|
+
OpenApi.describeRoute({
|
|
1119
|
+
description:
|
|
1120
|
+
'Signs in with a passkey assertion over a challenge from `POST /v1/auth/passkey/options`, and sets the session cookie. For a user with no Tempo Account in the first realm of the app that `oauth_query` names, or in the `default` realm, it creates one when that realm creates accounts. A failed creation leaves the user signed in, and the next sign-in or `POST /v1/account` creates the account.',
|
|
1121
|
+
responses: OpenApi.responses({
|
|
1122
|
+
errors: [
|
|
1123
|
+
...BodyLimit.errors,
|
|
1124
|
+
...Cors.ownPage.errors,
|
|
1125
|
+
...OpenApi.validate.errors,
|
|
1126
|
+
...ErrorResponse.fromUpstream.errors,
|
|
1127
|
+
...Headless.keySignature.errors,
|
|
1128
|
+
...RateLimit.check.errors,
|
|
1129
|
+
...emailUser.errors,
|
|
1130
|
+
core_Auth.AuthorizationExpiredError,
|
|
1131
|
+
Passkey.WebAuthnRejectedError,
|
|
1132
|
+
],
|
|
1133
|
+
success: {
|
|
1134
|
+
description:
|
|
1135
|
+
'The signed-in user, with the authorization of a sign-in with key signature, or the email step for a wallet.tempo passkey without an email.',
|
|
1136
|
+
schema: schema.passkeyVerify.response,
|
|
1137
|
+
},
|
|
1138
|
+
}),
|
|
1139
|
+
tags: ['Authenticate'],
|
|
1140
|
+
}),
|
|
1141
|
+
OpenApi.validate('json', schema.passkeyVerify.json),
|
|
1142
|
+
async (c) => {
|
|
1143
|
+
const { assertion, oauth_query } = c.req.valid('json')
|
|
1144
|
+
const crossSite = Cors.ownPage(c, { oauth_query })
|
|
1145
|
+
if (crossSite) return ErrorResponse.json(c, crossSite)
|
|
1146
|
+
const refused = await Timing.measure(
|
|
1147
|
+
c,
|
|
1148
|
+
RateLimit.check(c, {
|
|
1149
|
+
limits: [{ binding: 'SIGN_IN_IP_LIMIT', key: RateLimit.ip(c) }],
|
|
1150
|
+
}),
|
|
1151
|
+
'RateLimit.check',
|
|
1152
|
+
)
|
|
1153
|
+
if (refused) return refused
|
|
1154
|
+
|
|
1155
|
+
// The challenge is spent before any other check, so a failed attempt cannot be retried. The
|
|
1156
|
+
// same statement reads the passkey and its user.
|
|
1157
|
+
const { id } = assertion
|
|
1158
|
+
const taken = await Timing.measure(
|
|
1159
|
+
c,
|
|
1160
|
+
Passkey.takeWithKey(db, {
|
|
1161
|
+
clientDataJSON: assertion.metadata.clientDataJSON,
|
|
1162
|
+
id,
|
|
1163
|
+
}),
|
|
1164
|
+
'Passkey.takeWithKey',
|
|
1165
|
+
)
|
|
1166
|
+
const challenge = taken?.challenge
|
|
1167
|
+
// A challenge serves only the sign-in it was issued for: the same app, or none, from the same
|
|
1168
|
+
// page. A challenge for an email step's link serves only that link's code sign-in.
|
|
1169
|
+
if (
|
|
1170
|
+
!challenge ||
|
|
1171
|
+
challenge.link !== undefined ||
|
|
1172
|
+
challenge.clientId !== client({ oauth_query }) ||
|
|
1173
|
+
challenge.origin !== Cors.appOrigin(c)
|
|
1174
|
+
)
|
|
1175
|
+
return ErrorResponse.json(c, new Passkey.WebAuthnRejectedError())
|
|
1176
|
+
|
|
1177
|
+
const key = taken.key
|
|
1178
|
+
// A passkey that Tempo ID does not store yet may be wallet.tempo's, which shares the `default`
|
|
1179
|
+
// realm's relying party, and links once it verifies.
|
|
1180
|
+
const credential = await (async () => {
|
|
1181
|
+
if (key || !c.env.WALLET || challenge.realmId !== Realm.defaultId)
|
|
1182
|
+
return undefined
|
|
1183
|
+
try {
|
|
1184
|
+
return await Timing.measure(
|
|
1185
|
+
c,
|
|
1186
|
+
Wallet.credential(c.env.WALLET, { id }),
|
|
1187
|
+
'Wallet.credential',
|
|
1188
|
+
)
|
|
1189
|
+
} catch (error) {
|
|
1190
|
+
// Only the binding call throws, so the failure is wallet.tempo's.
|
|
1191
|
+
Log.warn(c, {
|
|
1192
|
+
error,
|
|
1193
|
+
message: 'The wallet service failed a passkey lookup.',
|
|
1194
|
+
})
|
|
1195
|
+
return (
|
|
1196
|
+
(error instanceof Error && ErrorResponse.fromUpstream(error)) ||
|
|
1197
|
+
new ErrorResponse.UpstreamUnavailableError()
|
|
1198
|
+
)
|
|
1199
|
+
}
|
|
1200
|
+
})()
|
|
1201
|
+
if (credential instanceof ErrorResponse.ResponseError)
|
|
1202
|
+
return ErrorResponse.json(c, credential)
|
|
1203
|
+
const publicKey = key?.publicKey ?? credential?.publicKey
|
|
1204
|
+
if (
|
|
1205
|
+
!publicKey ||
|
|
1206
|
+
!Passkey.verify(assertion, {
|
|
1207
|
+
challenge: challenge.challenge,
|
|
1208
|
+
origin: challenge.origin ?? new URL(env.URL).origin,
|
|
1209
|
+
publicKey: publicKey as `0x${string}`,
|
|
1210
|
+
rpId: challenge.rpId,
|
|
1211
|
+
})
|
|
1212
|
+
)
|
|
1213
|
+
return ErrorResponse.json(c, new Passkey.WebAuthnRejectedError())
|
|
1214
|
+
// An assertion of a sign-in with key signature on an app's page signs in once, whatever then
|
|
1215
|
+
// signs the key.
|
|
1216
|
+
const unspent = await Timing.measure(
|
|
1217
|
+
c,
|
|
1218
|
+
Headless.spend(c, { assertion, challenge }),
|
|
1219
|
+
'Headless.spend',
|
|
1220
|
+
)
|
|
1221
|
+
if (!unspent)
|
|
1222
|
+
return ErrorResponse.json(c, new Passkey.WebAuthnRejectedError())
|
|
1223
|
+
|
|
1224
|
+
// A wallet.tempo account without an email links once the user verifies one with a code.
|
|
1225
|
+
if (credential && !credential.email) {
|
|
1226
|
+
const link = await Timing.measure(
|
|
1227
|
+
c,
|
|
1228
|
+
PasskeyLink.defer(c, { credential, id, rpId: challenge.rpId }),
|
|
1229
|
+
'PasskeyLink.defer',
|
|
1230
|
+
)
|
|
1231
|
+
c.header('cache-control', 'private, no-store')
|
|
1232
|
+
return c.json(
|
|
1233
|
+
{ step: { link, signed: false, type: 'email' as const } },
|
|
1234
|
+
200,
|
|
1235
|
+
)
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
// The user whom the passkey signs in, with the address that the response returns, when the
|
|
1239
|
+
// route already read it.
|
|
1240
|
+
const signer = await (async () => {
|
|
1241
|
+
if (key) return { user: key.user, userId: key.userId }
|
|
1242
|
+
// wallet.tempo verified the email, so its user is found or created by that address.
|
|
1243
|
+
const user = await Timing.measure(
|
|
1244
|
+
c,
|
|
1245
|
+
emailUser(c, { email: credential!.email! }),
|
|
1246
|
+
'authenticate.emailUser',
|
|
1247
|
+
)
|
|
1248
|
+
const linked = await Timing.measure(
|
|
1249
|
+
c,
|
|
1250
|
+
Passkey.link(db, {
|
|
1251
|
+
credential: credential!,
|
|
1252
|
+
id,
|
|
1253
|
+
rpId: challenge.rpId,
|
|
1254
|
+
userId: user.id,
|
|
1255
|
+
}),
|
|
1256
|
+
'Passkey.link',
|
|
1257
|
+
)
|
|
1258
|
+
TempoAccount.forget(c)
|
|
1259
|
+
// A concurrent first sign-in can link the passkey to another user, whose row decides.
|
|
1260
|
+
return linked
|
|
1261
|
+
? { user: linked === user.id ? user : undefined, userId: linked }
|
|
1262
|
+
: undefined
|
|
1263
|
+
})()
|
|
1264
|
+
// Another passkey holds the address that wallet.tempo returned.
|
|
1265
|
+
if (!signer)
|
|
1266
|
+
return ErrorResponse.json(c, new Passkey.WebAuthnRejectedError())
|
|
1267
|
+
const { userId } = signer
|
|
1268
|
+
|
|
1269
|
+
// A sign-in that continues an app's request reads the user's accounts in the app's realms, so
|
|
1270
|
+
// their Privy users load beside the session's creation, and so does the user's first account.
|
|
1271
|
+
// The assertion already verified, and its challenge is spent. Both start only for a query that
|
|
1272
|
+
// Better Auth signed and that has not expired, which the sign-in checks too, so a forged query
|
|
1273
|
+
// creates no account in its app's realm.
|
|
1274
|
+
const signed = await (async () => {
|
|
1275
|
+
if (!oauth_query) return true
|
|
1276
|
+
const context = await Timing.measure(
|
|
1277
|
+
c,
|
|
1278
|
+
c.var.auth.$context,
|
|
1279
|
+
'auth.context',
|
|
1280
|
+
)
|
|
1281
|
+
return await Timing.measure(
|
|
1282
|
+
c,
|
|
1283
|
+
verifyOAuthQueryParams(oauth_query, context.secret),
|
|
1284
|
+
'auth.verifyOAuthQueryParams',
|
|
1285
|
+
)
|
|
1286
|
+
})()
|
|
1287
|
+
const appId = signed
|
|
1288
|
+
? (client({ oauth_query }) ??
|
|
1289
|
+
Authorize.schema.request.safeParse(challenge.authorize).data
|
|
1290
|
+
?.client_id)
|
|
1291
|
+
: undefined
|
|
1292
|
+
// Every branch settles before the response, so a refused sign-in leaves no first account
|
|
1293
|
+
// running after it. The other branches catch their own failures.
|
|
1294
|
+
const [settled] = await Promise.allSettled([
|
|
1295
|
+
Timing.measure(
|
|
1296
|
+
c,
|
|
1297
|
+
c.var.auth.api.signInPasskey({
|
|
1298
|
+
asResponse: false,
|
|
1299
|
+
body: {
|
|
1300
|
+
userId,
|
|
1301
|
+
...(key ? { user: key.user } : {}),
|
|
1302
|
+
...(oauth_query ? { oauth_query } : {}),
|
|
1303
|
+
},
|
|
1304
|
+
// Better Auth sets `accept` to continue an authorization as JSON, and a request's own
|
|
1305
|
+
// headers are immutable.
|
|
1306
|
+
headers: new Headers(c.req.raw.headers),
|
|
1307
|
+
request: c.req.raw,
|
|
1308
|
+
returnHeaders: true,
|
|
1309
|
+
}),
|
|
1310
|
+
'auth.signInPasskey',
|
|
1311
|
+
).catch((error: unknown) => {
|
|
1312
|
+
if (isAPIError(error) && error.body?.error === 'invalid_signature')
|
|
1313
|
+
return new core_Auth.AuthorizationExpiredError()
|
|
1314
|
+
throw error
|
|
1315
|
+
}),
|
|
1316
|
+
appId
|
|
1317
|
+
? Timing.measure(
|
|
1318
|
+
c,
|
|
1319
|
+
AccountStep.prefetch(c, { clientId: appId, userId }),
|
|
1320
|
+
'AccountStep.prefetch',
|
|
1321
|
+
)
|
|
1322
|
+
: undefined,
|
|
1323
|
+
// A linked or registered passkey owns its account, so only a sign-in passkey's user can
|
|
1324
|
+
// receive one. The verified assertion signs the user in, so the account needs no session.
|
|
1325
|
+
// The account step inside the sign-in finds at most this one account either way.
|
|
1326
|
+
signed
|
|
1327
|
+
? Timing.measure(
|
|
1328
|
+
c,
|
|
1329
|
+
FirstAccount.create(c, {
|
|
1330
|
+
clientId: client({ oauth_query }),
|
|
1331
|
+
email: signer.user?.email,
|
|
1332
|
+
userId,
|
|
1333
|
+
}),
|
|
1334
|
+
'FirstAccount.create',
|
|
1335
|
+
)
|
|
1336
|
+
: undefined,
|
|
1337
|
+
])
|
|
1338
|
+
if (settled.status === 'rejected') throw settled.reason
|
|
1339
|
+
const result = settled.value
|
|
1340
|
+
if (result instanceof ErrorResponse.ResponseError)
|
|
1341
|
+
return ErrorResponse.json(c, result)
|
|
1342
|
+
|
|
1343
|
+
for (const cookie of result.headers.getSetCookie())
|
|
1344
|
+
c.header('set-cookie', cookie, { append: true })
|
|
1345
|
+
c.header('cache-control', 'private, no-store')
|
|
1346
|
+
|
|
1347
|
+
// With `oauth_query`, Better Auth continues the app's authorization once the session exists, and
|
|
1348
|
+
// returns its redirect in place of the user.
|
|
1349
|
+
const signedIn = schema.verify.signedIn.parse(result.response)
|
|
1350
|
+
if ('user' in signedIn) {
|
|
1351
|
+
const user = { email: signedIn.user.email, id: signedIn.user.id }
|
|
1352
|
+
if (challenge.authorize === undefined)
|
|
1353
|
+
return c.json({ redirect: null, user }, 200)
|
|
1354
|
+
// The session exists, so the page reads any refusal from here, thrown ones included, as
|
|
1355
|
+
// one after the sign-in.
|
|
1356
|
+
c.header(Headless.signedInHeader, 'true')
|
|
1357
|
+
const authorization = await Timing.measure(
|
|
1358
|
+
c,
|
|
1359
|
+
Headless.keySignature(c, {
|
|
1360
|
+
assertion,
|
|
1361
|
+
challenge,
|
|
1362
|
+
email: user.email,
|
|
1363
|
+
setCookie: result.headers.getSetCookie(),
|
|
1364
|
+
}),
|
|
1365
|
+
'Headless.keySignature',
|
|
1366
|
+
)
|
|
1367
|
+
if (authorization instanceof Response) return authorization
|
|
1368
|
+
if (authorization instanceof ErrorResponse.ResponseError)
|
|
1369
|
+
return ErrorResponse.json(c, authorization)
|
|
1370
|
+
return c.json({ authorization, redirect: null, user }, 200)
|
|
1371
|
+
}
|
|
1372
|
+
// The passkey's row or wallet.tempo's address named the user, so only a passkey that a
|
|
1373
|
+
// concurrent sign-in linked to another user reads it again.
|
|
1374
|
+
const user = await (async () => {
|
|
1375
|
+
if (signer.user) return { email: signer.user.email, id: signer.user.id }
|
|
1376
|
+
const [row] = await Timing.measure(
|
|
1377
|
+
c,
|
|
1378
|
+
db
|
|
1379
|
+
.select({ email: Schema.user.email, id: Schema.user.id })
|
|
1380
|
+
.from(Schema.user)
|
|
1381
|
+
.where(eq(Schema.user.id, userId)),
|
|
1382
|
+
'db.user.get',
|
|
1383
|
+
)
|
|
1384
|
+
return row
|
|
1385
|
+
})()
|
|
1386
|
+
if (!user) throw new Error('The signed-in user is missing.')
|
|
1387
|
+
|
|
1388
|
+
// The sign-in page showed the request's key, so the sign-in grants it. A key challenge was
|
|
1389
|
+
// the key's digest, so the same assertion signs it for an account that this passkey owns.
|
|
1390
|
+
const redirect = await Timing.measure(
|
|
1391
|
+
c,
|
|
1392
|
+
SignInConsent.afterSignIn(c, {
|
|
1393
|
+
keySignature: challenge.witness
|
|
1394
|
+
? { assertion, witness: challenge.witness }
|
|
1395
|
+
: undefined,
|
|
1396
|
+
redirect: signedIn.url,
|
|
1397
|
+
setCookie: result.headers.getSetCookie(),
|
|
1398
|
+
}),
|
|
1399
|
+
'SignInConsent.afterSignIn',
|
|
1400
|
+
)
|
|
1401
|
+
return c.json({ redirect, user }, 200)
|
|
1402
|
+
},
|
|
1403
|
+
)
|
|
1404
|
+
.post(
|
|
1405
|
+
'/v1/auth/logout',
|
|
1406
|
+
OpenApi.describeRoute({
|
|
1407
|
+
description:
|
|
1408
|
+
'Signs out: ends the session, revokes the access and refresh tokens that apps received through it, and clears its cookie.',
|
|
1409
|
+
responses: OpenApi.responses({
|
|
1410
|
+
errors: [...BodyLimit.errors, ErrorResponse.CrossSiteRequestError],
|
|
1411
|
+
success: {
|
|
1412
|
+
description: 'The session is over.',
|
|
1413
|
+
schema: schema.logout.response,
|
|
1414
|
+
},
|
|
1415
|
+
}),
|
|
1416
|
+
tags: ['Authenticate'],
|
|
1417
|
+
}),
|
|
1418
|
+
async (c) => {
|
|
1419
|
+
// One statement ends the session and its apps' tokens. Better Auth's sign-out takes six round
|
|
1420
|
+
// trips, and runs only to expire the cookies.
|
|
1421
|
+
const token = await Timing.measure(c, Session.token(c), 'Session.token')
|
|
1422
|
+
if (token)
|
|
1423
|
+
await Timing.measure(c, Logout.end(db, { token }), 'Logout.end')
|
|
1424
|
+
await Timing.measure(c, Session.clear(c), 'Session.clear')
|
|
1425
|
+
// The next sign-in chooses each app's account again.
|
|
1426
|
+
c.header('set-cookie', AccountStep.clear(), { append: true })
|
|
1427
|
+
c.header('cache-control', 'private, no-store')
|
|
1428
|
+
return c.json({ signed_out: true as const }, 200)
|
|
1429
|
+
},
|
|
1430
|
+
)
|
|
1431
|
+
.get(
|
|
1432
|
+
'/v1/auth/session',
|
|
1433
|
+
OpenApi.describeRoute({
|
|
1434
|
+
description: 'Reads the signed-in user, or `null` without a session.',
|
|
1435
|
+
responses: OpenApi.responses({
|
|
1436
|
+
success: {
|
|
1437
|
+
description: 'The session.',
|
|
1438
|
+
schema: schema.session.response,
|
|
1439
|
+
},
|
|
1440
|
+
}),
|
|
1441
|
+
tags: ['Authenticate'],
|
|
1442
|
+
}),
|
|
1443
|
+
async (c) => {
|
|
1444
|
+
const session = await Timing.measure(c, Session.get(c), 'Session.get')
|
|
1445
|
+
c.header('cache-control', 'private, no-store')
|
|
1446
|
+
if (!session) return c.json({ user: null }, 200)
|
|
1447
|
+
return c.json(
|
|
1448
|
+
{
|
|
1449
|
+
user: {
|
|
1450
|
+
email: session.user.email,
|
|
1451
|
+
id: session.user.id,
|
|
1452
|
+
// Better Auth stores an empty name for a user who signed up without one.
|
|
1453
|
+
name: session.user.name || null,
|
|
1454
|
+
},
|
|
1455
|
+
},
|
|
1456
|
+
200,
|
|
1457
|
+
)
|
|
1458
|
+
},
|
|
1459
|
+
)
|
|
1460
|
+
.get(
|
|
1461
|
+
'/v1/auth/assets',
|
|
1462
|
+
OpenApi.describeRoute({
|
|
1463
|
+
description:
|
|
1464
|
+
"Lists the sign-in page's scripts and style sheets, which a page on Tempo ID's site prefetches before it redirects. Browsers partition their caches by site, so a page on another site gains nothing.",
|
|
1465
|
+
responses: OpenApi.responses({
|
|
1466
|
+
success: {
|
|
1467
|
+
description: 'The assets.',
|
|
1468
|
+
schema: schema.assets.response,
|
|
1469
|
+
},
|
|
1470
|
+
}),
|
|
1471
|
+
tags: ['Authenticate'],
|
|
1472
|
+
}),
|
|
1473
|
+
async (c) => {
|
|
1474
|
+
const assets = await Timing.measure(
|
|
1475
|
+
c,
|
|
1476
|
+
c.env.signInAssets(),
|
|
1477
|
+
'pages.assets',
|
|
1478
|
+
)
|
|
1479
|
+
// A deploy changes the list, so a browser keeps it for a short time only.
|
|
1480
|
+
c.header('cache-control', 'public, max-age=300')
|
|
1481
|
+
return c.json({ assets }, 200)
|
|
1482
|
+
},
|
|
1483
|
+
)
|
|
1484
|
+
.get(
|
|
1485
|
+
'/v1/auth/methods',
|
|
1486
|
+
OpenApi.describeRoute({
|
|
1487
|
+
description:
|
|
1488
|
+
'Reads the sign-in methods that the sign-in page offers, the key that `oauth_query` grants, and the page that sign-in returns to. The app of `oauth_query`, or of a pending, unbound device code in `return_to`, selects the realms. The read binds nothing, and counts against the sign-in limit of the IP address.',
|
|
1489
|
+
responses: OpenApi.responses({
|
|
1490
|
+
errors: [...OpenApi.validate.errors],
|
|
1491
|
+
success: {
|
|
1492
|
+
description: 'The sign-in methods.',
|
|
1493
|
+
schema: schema.methods.response,
|
|
1494
|
+
},
|
|
1495
|
+
}),
|
|
1496
|
+
tags: ['Authenticate'],
|
|
1497
|
+
}),
|
|
1498
|
+
OpenApi.validate('query', schema.methods.query),
|
|
1499
|
+
async (c) => {
|
|
1500
|
+
const input = c.req.valid('query')
|
|
1501
|
+
// Sign-in returns only to an allowlisted path, so `return_to` never redirects to another site.
|
|
1502
|
+
const returnTo = Device.returnTo(input.return_to, { origin: env.URL })
|
|
1503
|
+
const query = (() => {
|
|
1504
|
+
if (!input.oauth_query) return undefined
|
|
1505
|
+
const query = new URLSearchParams(input.oauth_query)
|
|
1506
|
+
return query.has('sig') ? query : undefined
|
|
1507
|
+
})()
|
|
1508
|
+
|
|
1509
|
+
// The user code comes from `return_to`, but the app comes from the code's own row, so the
|
|
1510
|
+
// visitor selects no app by itself.
|
|
1511
|
+
const userCode = returnTo
|
|
1512
|
+
? Device.normalize(
|
|
1513
|
+
new URL(returnTo, env.URL).searchParams.get('code') ?? '',
|
|
1514
|
+
)
|
|
1515
|
+
: undefined
|
|
1516
|
+
const clientId = await (async () => {
|
|
1517
|
+
if (query) return query.get('client_id') ?? undefined
|
|
1518
|
+
if (!userCode) return undefined
|
|
1519
|
+
// A live code's realms change the page, so each IP address's lookups count, and keep a guess
|
|
1520
|
+
// of a live code out of reach (RFC 8628 §5.1). Past the limit, the page shows the `default`
|
|
1521
|
+
// realm.
|
|
1522
|
+
const ip = RateLimit.ip(c)
|
|
1523
|
+
if (!ip) return undefined
|
|
1524
|
+
const outcome = await Timing.measure(
|
|
1525
|
+
c,
|
|
1526
|
+
c.env.SIGN_IN_IP_LIMIT.limit({ key: `device-lookup:${ip}` }),
|
|
1527
|
+
'ratelimit.limit',
|
|
1528
|
+
)
|
|
1529
|
+
if (!outcome.success) return undefined
|
|
1530
|
+
return await Timing.measure(
|
|
1531
|
+
c,
|
|
1532
|
+
Device.client(db, { userCode }),
|
|
1533
|
+
'Device.client',
|
|
1534
|
+
)
|
|
1535
|
+
})()
|
|
1536
|
+
const stored = clientId
|
|
1537
|
+
? await Timing.measure(
|
|
1538
|
+
c,
|
|
1539
|
+
Cache.read(c, () => Realm.forApp(db, { clientId }), [
|
|
1540
|
+
'realm.forApp',
|
|
1541
|
+
clientId,
|
|
1542
|
+
]),
|
|
1543
|
+
'Realm.forApp',
|
|
1544
|
+
)
|
|
1545
|
+
: [
|
|
1546
|
+
await Timing.measure(
|
|
1547
|
+
c,
|
|
1548
|
+
Cache.read(c, () => Realm.get(db, { id: Realm.defaultId }), [
|
|
1549
|
+
'realm.get',
|
|
1550
|
+
Realm.defaultId,
|
|
1551
|
+
]),
|
|
1552
|
+
'Realm.get',
|
|
1553
|
+
),
|
|
1554
|
+
]
|
|
1555
|
+
const realms = await Timing.measure(
|
|
1556
|
+
c,
|
|
1557
|
+
Realm.withDefaults(c, { db, origin: env.URL, realms: stored }),
|
|
1558
|
+
'Realm.withDefaults',
|
|
1559
|
+
)
|
|
1560
|
+
const request = query
|
|
1561
|
+
? await Timing.measure(c, granted(c, { query }), 'authenticate.granted')
|
|
1562
|
+
: null
|
|
1563
|
+
return c.json(
|
|
1564
|
+
{
|
|
1565
|
+
app: request?.app ?? null,
|
|
1566
|
+
host: request?.host ?? null,
|
|
1567
|
+
passkeys: Boolean(Passkey.relyingParty(realms, { origin: env.URL })),
|
|
1568
|
+
// `request` keeps its `/v1` shape: it is `null` without a key that the sign-in grants.
|
|
1569
|
+
request: request?.key ? { app: request.app, key: request.key } : null,
|
|
1570
|
+
return_to: returnTo ?? null,
|
|
1571
|
+
user_code: userCode || null,
|
|
1572
|
+
verified: request?.verified ?? false,
|
|
1573
|
+
},
|
|
1574
|
+
200,
|
|
1575
|
+
)
|
|
1576
|
+
},
|
|
1577
|
+
)
|
|
1578
|
+
|
|
1579
|
+
/** Returns the client ID of the app that `oauth_query` names, or `null` without one. */
|
|
1580
|
+
function client(options: client.Options) {
|
|
1581
|
+
if (!options.oauth_query) return null
|
|
1582
|
+
return new URLSearchParams(options.oauth_query).get('client_id')
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
declare namespace client {
|
|
1586
|
+
type Options = {
|
|
1587
|
+
/** The sign-in page's query string, as the request sends it. */
|
|
1588
|
+
oauth_query?: string | undefined
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
/**
|
|
1593
|
+
* Returns the app of the signed authorization request `query`, whether it is first-party, and the
|
|
1594
|
+
* key that the sign-in grants, or `null` for a forged or expired query, or an unknown app. The key is
|
|
1595
|
+
* `null` for a request without one, with a `challenge`, whose key the approval page shows beside its
|
|
1596
|
+
* message, or from a third-party app, whose key the approval page shows beside the host that serves
|
|
1597
|
+
* its metadata document.
|
|
1598
|
+
*/
|
|
1599
|
+
async function granted(c: Context<Env>, options: granted.Options) {
|
|
1600
|
+
const { query } = options
|
|
1601
|
+
const context = await Timing.measure(c, c.var.auth.$context, 'auth.context')
|
|
1602
|
+
const verified = await Timing.measure(
|
|
1603
|
+
c,
|
|
1604
|
+
verifyOAuthQueryParams(query.toString(), context.secret),
|
|
1605
|
+
'auth.verifyOAuthQueryParams',
|
|
1606
|
+
)
|
|
1607
|
+
const clientId = query.get('client_id')
|
|
1608
|
+
if (!verified || !clientId) return null
|
|
1609
|
+
|
|
1610
|
+
// The sign-in page shows only the app's name, so the cache holds the name alone.
|
|
1611
|
+
const app = await Timing.measure(
|
|
1612
|
+
c,
|
|
1613
|
+
Cache.read(
|
|
1614
|
+
c,
|
|
1615
|
+
async () => {
|
|
1616
|
+
const found = await Apps.get(db, { clientId }).catch(
|
|
1617
|
+
(error: unknown) => {
|
|
1618
|
+
if (error instanceof Apps.NotFoundError) return undefined
|
|
1619
|
+
throw error
|
|
1620
|
+
},
|
|
1621
|
+
)
|
|
1622
|
+
return found ? { name: found.name } : undefined
|
|
1623
|
+
},
|
|
1624
|
+
['apps.name', clientId],
|
|
1625
|
+
{ keep: (found) => found !== undefined },
|
|
1626
|
+
),
|
|
1627
|
+
'Apps.get',
|
|
1628
|
+
)
|
|
1629
|
+
if (!app) return null
|
|
1630
|
+
|
|
1631
|
+
// A third-party app names itself in its metadata document, so only the approval page grants its key.
|
|
1632
|
+
const thirdParty = Apps.thirdParty(clientId)
|
|
1633
|
+
// A request that a sign-in already spent grants nothing at sign-in, so only the approval page
|
|
1634
|
+
// shows its key.
|
|
1635
|
+
const key =
|
|
1636
|
+
query.has('challenge') ||
|
|
1637
|
+
thirdParty ||
|
|
1638
|
+
!query.has('key_authorization') ||
|
|
1639
|
+
(await Timing.measure(
|
|
1640
|
+
c,
|
|
1641
|
+
SignInGrant.spent(db, { query }),
|
|
1642
|
+
'SignInGrant.spent',
|
|
1643
|
+
))
|
|
1644
|
+
? undefined
|
|
1645
|
+
: Authorize.fromQuery(query)
|
|
1646
|
+
return {
|
|
1647
|
+
app: app.name ?? clientId,
|
|
1648
|
+
host: thirdParty ? new URL(clientId).host : null,
|
|
1649
|
+
key:
|
|
1650
|
+
key && !(key instanceof ErrorResponse.ResponseError)
|
|
1651
|
+
? KeyAuthorization.describe(key)
|
|
1652
|
+
: null,
|
|
1653
|
+
verified: !thirdParty,
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
declare namespace granted {
|
|
1658
|
+
type Options = {
|
|
1659
|
+
/** The sign-in page's query. */
|
|
1660
|
+
query: URLSearchParams
|
|
1661
|
+
}
|
|
1662
|
+
}
|