@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
package/src/api/oauth.ts
ADDED
|
@@ -0,0 +1,1796 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The OAuth and OpenID protocol paths under `/oauth2` and `/.well-known`, and the device grant's
|
|
3
|
+
* `/device/code`, which discovery advertises. Only the listed paths reach
|
|
4
|
+
* Better Auth, since its handler also serves client and consent management that Tempo ID calls only in
|
|
5
|
+
* process. Bodies and responses pass through, except that an error response gains `request_id`.
|
|
6
|
+
* A browser's authorization receives Tempo ID's page of its next step in place of a redirect to it.
|
|
7
|
+
* The device grant carries Tempo ID's key authorization through `/device/code` and `/oauth2/token`.
|
|
8
|
+
* Tempo ID serves `/oauth2/end-session` itself, since Better Auth's logout fetches the signing keys
|
|
9
|
+
* over HTTP and renders its own page.
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import { decodeBasicCredentials } from '@better-auth/core/oauth2'
|
|
13
|
+
import { isAPIError } from 'better-auth/api'
|
|
14
|
+
import type { Context } from 'hono'
|
|
15
|
+
import { Hono } from 'hono'
|
|
16
|
+
import { Base64 } from 'ox'
|
|
17
|
+
import { env } from 'void/env'
|
|
18
|
+
import * as z from 'zod'
|
|
19
|
+
import * as Apps from '../internal/Apps.js'
|
|
20
|
+
import * as Authorize from '../internal/Authorize.js'
|
|
21
|
+
import * as BodyLimit from '../internal/BodyLimit.js'
|
|
22
|
+
import * as Device from '../internal/Device.js'
|
|
23
|
+
import type { Env } from '../internal/Env.js'
|
|
24
|
+
import * as ErrorResponse from '../internal/ErrorResponse.js'
|
|
25
|
+
import * as KeyAuthorization from '../internal/KeyAuthorization.js'
|
|
26
|
+
import * as Log from '../internal/Log.js'
|
|
27
|
+
import * as Logout from '../internal/Logout.js'
|
|
28
|
+
import * as OpenApi from '../internal/OpenApi.js'
|
|
29
|
+
import * as RateLimit from '../internal/RateLimit.js'
|
|
30
|
+
import * as SigningKey from '../internal/SigningKey.js'
|
|
31
|
+
import * as Timing from '../internal/Timing.js'
|
|
32
|
+
import * as Validation from '../internal/Validation.js'
|
|
33
|
+
import * as AccountStep from './lib/AccountStep.js'
|
|
34
|
+
import { db } from './lib/Database.js'
|
|
35
|
+
import * as DeviceRequest from './lib/DeviceRequest.js'
|
|
36
|
+
import * as Session from './lib/Session.js'
|
|
37
|
+
import * as SignInConsent from './lib/SignInConsent.js'
|
|
38
|
+
|
|
39
|
+
namespace schema {
|
|
40
|
+
export namespace openidConfiguration {
|
|
41
|
+
export const response = z
|
|
42
|
+
.looseObject({
|
|
43
|
+
authorization_endpoint: z.url().meta({
|
|
44
|
+
description: 'The authorization endpoint.',
|
|
45
|
+
examples: ['https://id.tempo.xyz/oauth2/authorize'],
|
|
46
|
+
}),
|
|
47
|
+
issuer: z.url().meta({
|
|
48
|
+
description: "The issuer's identifier, every token's `iss`.",
|
|
49
|
+
examples: ['https://id.tempo.xyz'],
|
|
50
|
+
}),
|
|
51
|
+
jwks_uri: z.url().meta({
|
|
52
|
+
description: "The issuer's signing keys.",
|
|
53
|
+
examples: ['https://id.tempo.xyz/oauth2/jwks'],
|
|
54
|
+
}),
|
|
55
|
+
token_endpoint: z.url().meta({
|
|
56
|
+
description: 'The token endpoint.',
|
|
57
|
+
examples: ['https://id.tempo.xyz/oauth2/token'],
|
|
58
|
+
}),
|
|
59
|
+
})
|
|
60
|
+
.meta({
|
|
61
|
+
description:
|
|
62
|
+
"The issuer's metadata (RFC 8414 §2, OpenID Connect Discovery §3), with the members that RFC 8414 defines.",
|
|
63
|
+
id: 'AuthorizationServerMetadata',
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export namespace oauthAuthorizationServer {
|
|
68
|
+
export const response = openidConfiguration.response
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export namespace authorize {
|
|
72
|
+
export const query = z.object({
|
|
73
|
+
challenge: z
|
|
74
|
+
.string()
|
|
75
|
+
.optional()
|
|
76
|
+
.meta({
|
|
77
|
+
description:
|
|
78
|
+
"A message, such as a sign-in challenge from the app's backend, whose EIP-191 hash becomes the key authorization's `witness` (TIP-1053). Requires `key_authorization`, and the app's `tempo-id:sign` scope.",
|
|
79
|
+
examples: ['Sign in to app.example.com\nNonce: 7f3a9c2e5b1d4f60'],
|
|
80
|
+
}),
|
|
81
|
+
client_id: z.string().meta({
|
|
82
|
+
description: "The app's client ID.",
|
|
83
|
+
examples: ['tempo-cli'],
|
|
84
|
+
}),
|
|
85
|
+
code_challenge: z.string().meta({
|
|
86
|
+
description:
|
|
87
|
+
'The PKCE challenge: the base64url SHA-256 hash of the code verifier (RFC 7636 §4.2).',
|
|
88
|
+
examples: ['E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM'],
|
|
89
|
+
}),
|
|
90
|
+
code_challenge_method: z.literal('S256').meta({
|
|
91
|
+
description: 'The PKCE challenge method.',
|
|
92
|
+
}),
|
|
93
|
+
key_authorization: z
|
|
94
|
+
.string()
|
|
95
|
+
.optional()
|
|
96
|
+
.meta({
|
|
97
|
+
description:
|
|
98
|
+
"A key authorization for the app's own key, as base64url JSON in the shape of `KeyAuthorizationRequest`. The approval page shows the key, and `/oauth2/token` returns it beside the tokens once approval signs it. A key that breaks a rule returns to the redirect URI with the refusal.",
|
|
99
|
+
examples: [
|
|
100
|
+
'eyJhZGRyZXNzIjoiMHg4MWMwZjIyYmQxNzNjMTE1ZDE4ZWVlYTBmNmEzOGU5MzVlY2Y1MmZiIn0',
|
|
101
|
+
],
|
|
102
|
+
}),
|
|
103
|
+
prompt: z
|
|
104
|
+
.enum(['consent', 'login', 'none', 'select_account'])
|
|
105
|
+
.optional()
|
|
106
|
+
.meta({
|
|
107
|
+
description:
|
|
108
|
+
'Whether the user signs in or consents again (OIDC Core §3.1.2.1). Absent to reuse the session and stored consent.',
|
|
109
|
+
examples: ['consent'],
|
|
110
|
+
}),
|
|
111
|
+
redirect_uri: z.url().meta({
|
|
112
|
+
description: "One of the app's registered redirect URIs.",
|
|
113
|
+
examples: ['https://app.example/callback'],
|
|
114
|
+
}),
|
|
115
|
+
resource: z
|
|
116
|
+
.url()
|
|
117
|
+
.optional()
|
|
118
|
+
.meta({
|
|
119
|
+
description:
|
|
120
|
+
"The API the access token is for (RFC 8707). Absent for Tempo ID's own origin.",
|
|
121
|
+
examples: ['https://id.tempo.xyz'],
|
|
122
|
+
}),
|
|
123
|
+
response_type: z.literal('code').meta({
|
|
124
|
+
description: 'The authorization code flow.',
|
|
125
|
+
}),
|
|
126
|
+
scope: z.string().meta({
|
|
127
|
+
description: 'Space-separated OAuth scopes.',
|
|
128
|
+
examples: ['openid profile email'],
|
|
129
|
+
}),
|
|
130
|
+
state: z
|
|
131
|
+
.string()
|
|
132
|
+
.optional()
|
|
133
|
+
.meta({
|
|
134
|
+
description:
|
|
135
|
+
'An opaque value that the redirect returns to the app unchanged.',
|
|
136
|
+
examples: ['af0ifjsldkj'],
|
|
137
|
+
}),
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
export const response = z
|
|
141
|
+
.object({
|
|
142
|
+
redirect: z.boolean().meta({
|
|
143
|
+
description: 'Whether the app sends the browser to `url`.',
|
|
144
|
+
examples: [true],
|
|
145
|
+
}),
|
|
146
|
+
url: z.string().meta({
|
|
147
|
+
description:
|
|
148
|
+
'Where the browser goes next: the sign-in page, the consent page, or the redirect URI with a code.',
|
|
149
|
+
examples: [
|
|
150
|
+
'https://app.example/callback?code=SplxlOBeZQQYbYS6WxSbIA',
|
|
151
|
+
],
|
|
152
|
+
}),
|
|
153
|
+
})
|
|
154
|
+
.meta({
|
|
155
|
+
description:
|
|
156
|
+
'The next step of the authorization, for a request that a script sends.',
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export namespace token {
|
|
161
|
+
/** An OAuth error response from the issuer, passed through. It carries no `reason`. */
|
|
162
|
+
export const error = z
|
|
163
|
+
.object({
|
|
164
|
+
error: z.string().meta({
|
|
165
|
+
description:
|
|
166
|
+
'The RFC 6749 error code, such as `invalid_grant` or `invalid_client`.',
|
|
167
|
+
examples: ['invalid_grant'],
|
|
168
|
+
}),
|
|
169
|
+
error_description: z
|
|
170
|
+
.string()
|
|
171
|
+
.optional()
|
|
172
|
+
.meta({
|
|
173
|
+
description:
|
|
174
|
+
"The issuer's description of the error. Absent when the issuer sends none.",
|
|
175
|
+
examples: ['invalid code'],
|
|
176
|
+
}),
|
|
177
|
+
error_uri: z
|
|
178
|
+
.url()
|
|
179
|
+
.optional()
|
|
180
|
+
.meta({
|
|
181
|
+
description:
|
|
182
|
+
'A page about the error. Absent when the issuer sends none.',
|
|
183
|
+
examples: ['https://example.com/docs/errors#invalid_grant'],
|
|
184
|
+
}),
|
|
185
|
+
request_id: z
|
|
186
|
+
.string()
|
|
187
|
+
.optional()
|
|
188
|
+
.meta({
|
|
189
|
+
description:
|
|
190
|
+
'The request ID, which Tempo ID adds and `X-Request-Id` also carries.',
|
|
191
|
+
examples: ['5b3e1f2a-8c4d-4e6f-9a0b-7c2d1e3f4a5b'],
|
|
192
|
+
}),
|
|
193
|
+
})
|
|
194
|
+
.meta({
|
|
195
|
+
description:
|
|
196
|
+
"The issuer's OAuth error response (RFC 6749 §5.2), passed through.",
|
|
197
|
+
id: 'OAuthErrorResponse',
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
export const form = z.object({
|
|
201
|
+
client_id: z
|
|
202
|
+
.string()
|
|
203
|
+
.optional()
|
|
204
|
+
.meta({
|
|
205
|
+
description:
|
|
206
|
+
"The app's client ID. Absent when the app authenticates with HTTP Basic `Authorization`.",
|
|
207
|
+
examples: ['tempo-cli'],
|
|
208
|
+
}),
|
|
209
|
+
client_secret: z
|
|
210
|
+
.string()
|
|
211
|
+
.optional()
|
|
212
|
+
.meta({
|
|
213
|
+
description: "A confidential app's secret.",
|
|
214
|
+
examples: ['3bTm9qXc2Lr7WvN5kYp8Hs4Df6Ga1Je0'],
|
|
215
|
+
}),
|
|
216
|
+
code: z
|
|
217
|
+
.string()
|
|
218
|
+
.optional()
|
|
219
|
+
.meta({
|
|
220
|
+
description:
|
|
221
|
+
'The authorization code to redeem, for the `authorization_code` grant.',
|
|
222
|
+
examples: ['SplxlOBeZQQYbYS6WxSbIA'],
|
|
223
|
+
}),
|
|
224
|
+
code_verifier: z
|
|
225
|
+
.string()
|
|
226
|
+
.optional()
|
|
227
|
+
.meta({
|
|
228
|
+
description:
|
|
229
|
+
"The PKCE verifier whose S256 hash is the authorization request's `code_challenge` (RFC 7636 §4.5).",
|
|
230
|
+
examples: ['dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'],
|
|
231
|
+
}),
|
|
232
|
+
device_code: z
|
|
233
|
+
.string()
|
|
234
|
+
.optional()
|
|
235
|
+
.meta({
|
|
236
|
+
description:
|
|
237
|
+
'The device code, for the device code grant (RFC 8628 §3.4).',
|
|
238
|
+
examples: ['GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS'],
|
|
239
|
+
}),
|
|
240
|
+
grant_type: z
|
|
241
|
+
.enum([
|
|
242
|
+
'authorization_code',
|
|
243
|
+
'client_credentials',
|
|
244
|
+
'refresh_token',
|
|
245
|
+
'urn:ietf:params:oauth:grant-type:device_code',
|
|
246
|
+
])
|
|
247
|
+
.meta({
|
|
248
|
+
description: 'The OAuth grant that the request redeems.',
|
|
249
|
+
examples: ['authorization_code'],
|
|
250
|
+
}),
|
|
251
|
+
redirect_uri: z
|
|
252
|
+
.string()
|
|
253
|
+
.optional()
|
|
254
|
+
.meta({
|
|
255
|
+
description:
|
|
256
|
+
'The redirect URI of the authorization request, for the `authorization_code` grant.',
|
|
257
|
+
examples: ['https://app.example/callback'],
|
|
258
|
+
}),
|
|
259
|
+
refresh_token: z
|
|
260
|
+
.string()
|
|
261
|
+
.optional()
|
|
262
|
+
.meta({
|
|
263
|
+
description:
|
|
264
|
+
'The refresh token to redeem, for the `refresh_token` grant.',
|
|
265
|
+
examples: ['yAjhKk7QnRZ3vD9pLm2Xc8Ws5Tf1Ub4N'],
|
|
266
|
+
}),
|
|
267
|
+
resource: z
|
|
268
|
+
.url()
|
|
269
|
+
.optional()
|
|
270
|
+
.meta({
|
|
271
|
+
description:
|
|
272
|
+
"The API the access token is for (RFC 8707). Absent for Tempo ID's own origin.",
|
|
273
|
+
examples: ['https://id.tempo.xyz'],
|
|
274
|
+
}),
|
|
275
|
+
scope: z
|
|
276
|
+
.string()
|
|
277
|
+
.optional()
|
|
278
|
+
.meta({
|
|
279
|
+
description:
|
|
280
|
+
'Space-separated OAuth scopes to request. Absent to keep the scopes of the grant.',
|
|
281
|
+
examples: ['openid profile email'],
|
|
282
|
+
}),
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
export const response = z
|
|
286
|
+
.looseObject({
|
|
287
|
+
access_token: z.string().meta({
|
|
288
|
+
description:
|
|
289
|
+
'The access token, an RS256 JWT for the requested resource.',
|
|
290
|
+
examples: [
|
|
291
|
+
'eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCJ9.e30.c2lnbmF0dXJl',
|
|
292
|
+
],
|
|
293
|
+
}),
|
|
294
|
+
account: KeyAuthorization.schema.account.optional().meta({
|
|
295
|
+
description:
|
|
296
|
+
'The Tempo Account that the key acts for. Present beside `key_authorization`.',
|
|
297
|
+
examples: ['0x9a073914db0d149375ac2b7fbb72c68c0780fb5f'],
|
|
298
|
+
}),
|
|
299
|
+
expires_in: z.number().meta({
|
|
300
|
+
description: 'How long the access token lasts, in seconds.',
|
|
301
|
+
examples: [300],
|
|
302
|
+
}),
|
|
303
|
+
id_token: z
|
|
304
|
+
.string()
|
|
305
|
+
.optional()
|
|
306
|
+
.meta({
|
|
307
|
+
description:
|
|
308
|
+
'The ID token, an RS256 JWT. Absent without the `openid` scope.',
|
|
309
|
+
examples: ['eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.e30.c2lnbmF0dXJl'],
|
|
310
|
+
}),
|
|
311
|
+
key_authorization: KeyAuthorization.schema.rpc.optional().meta({
|
|
312
|
+
description:
|
|
313
|
+
'The key authorization that the approval page or the device page signed, for the first redemption of a code or device code whose request carried `key_authorization`. Absent for every other code, a refresh, and a key that expired.',
|
|
314
|
+
}),
|
|
315
|
+
refresh_token: z
|
|
316
|
+
.string()
|
|
317
|
+
.optional()
|
|
318
|
+
.meta({
|
|
319
|
+
description:
|
|
320
|
+
'The refresh token. Absent without the `offline_access` scope.',
|
|
321
|
+
examples: ['yAjhKk7QnRZ3vD9pLm2Xc8Ws5Tf1Ub4N'],
|
|
322
|
+
}),
|
|
323
|
+
scope: z
|
|
324
|
+
.string()
|
|
325
|
+
.optional()
|
|
326
|
+
.meta({
|
|
327
|
+
description: 'Space-separated OAuth scopes of the tokens.',
|
|
328
|
+
examples: ['openid profile email'],
|
|
329
|
+
}),
|
|
330
|
+
token_type: z.string().meta({
|
|
331
|
+
description: 'The type of the access token (RFC 6749 §7.1).',
|
|
332
|
+
examples: ['Bearer'],
|
|
333
|
+
}),
|
|
334
|
+
})
|
|
335
|
+
.meta({
|
|
336
|
+
description: "The issuer's token response (RFC 6749 §5.1).",
|
|
337
|
+
id: 'TokenResponse',
|
|
338
|
+
})
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export namespace deviceCode {
|
|
342
|
+
export const form = z.object({
|
|
343
|
+
client_id: z
|
|
344
|
+
.string()
|
|
345
|
+
.optional()
|
|
346
|
+
.meta({
|
|
347
|
+
description:
|
|
348
|
+
'The client ID of an app that holds the device grant. Absent when the app authenticates with HTTP Basic `Authorization`.',
|
|
349
|
+
examples: ['tempo-cli'],
|
|
350
|
+
}),
|
|
351
|
+
client_secret: z
|
|
352
|
+
.string()
|
|
353
|
+
.optional()
|
|
354
|
+
.meta({
|
|
355
|
+
description: "A confidential app's secret.",
|
|
356
|
+
examples: ['3bTm9qXc2Lr7WvN5kYp8Hs4Df6Ga1Je0'],
|
|
357
|
+
}),
|
|
358
|
+
resource: z
|
|
359
|
+
.url()
|
|
360
|
+
.optional()
|
|
361
|
+
.meta({
|
|
362
|
+
description:
|
|
363
|
+
"The API the access token is for (RFC 8707). Absent for Tempo ID's own origin.",
|
|
364
|
+
examples: ['https://id.tempo.xyz'],
|
|
365
|
+
}),
|
|
366
|
+
scope: z
|
|
367
|
+
.string()
|
|
368
|
+
.optional()
|
|
369
|
+
.meta({
|
|
370
|
+
description:
|
|
371
|
+
'Space-separated OAuth scopes. Defaults to `openid email offline_access`.',
|
|
372
|
+
examples: ['openid email offline_access'],
|
|
373
|
+
}),
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
/** The parameters that Tempo ID adds to the device authorization request. */
|
|
377
|
+
export const extension = z.object({
|
|
378
|
+
// The device page shows the challenge as the message that the key signs, so control and format
|
|
379
|
+
// characters, such as bidi overrides, cannot reorder it. Newlines and tabs stay.
|
|
380
|
+
challenge: Authorize.schema.challenge.refine(
|
|
381
|
+
(value) =>
|
|
382
|
+
value === undefined || /^(?:[\t\n]|[^\p{Cc}\p{Cf}])*$/u.test(value),
|
|
383
|
+
{
|
|
384
|
+
message:
|
|
385
|
+
'Remove control and format characters, such as bidi overrides',
|
|
386
|
+
},
|
|
387
|
+
),
|
|
388
|
+
device: z
|
|
389
|
+
.string()
|
|
390
|
+
.trim()
|
|
391
|
+
.min(1)
|
|
392
|
+
.max(64)
|
|
393
|
+
// The device page shows the name as the device reports it, so control and format characters,
|
|
394
|
+
// such as bidi overrides, cannot reorder the text around it.
|
|
395
|
+
.regex(/^[^\p{Cc}\p{Cf}]*$/u, {
|
|
396
|
+
message:
|
|
397
|
+
'Remove control and format characters, such as bidi overrides',
|
|
398
|
+
})
|
|
399
|
+
.optional()
|
|
400
|
+
.meta({
|
|
401
|
+
description:
|
|
402
|
+
"The name that the device page shows, such as the machine's hostname. It holds no control or format characters.",
|
|
403
|
+
examples: ['ada-macbook'],
|
|
404
|
+
}),
|
|
405
|
+
key_authorization: authorize.query.shape.key_authorization.meta({
|
|
406
|
+
description:
|
|
407
|
+
"A key authorization for the app's own key, as base64url JSON in the shape of `KeyAuthorizationRequest`, from a public first-party app only. The device page shows it, approval signs it, and `/oauth2/token` returns it beside the tokens.",
|
|
408
|
+
examples: [
|
|
409
|
+
'eyJhZGRyZXNzIjoiMHg4MWMwZjIyYmQxNzNjMTE1ZDE4ZWVlYTBmNmEzOGU5MzVlY2Y1MmZiIn0',
|
|
410
|
+
],
|
|
411
|
+
}),
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
export const response = z
|
|
415
|
+
.object({
|
|
416
|
+
device_code: z.string().meta({
|
|
417
|
+
description:
|
|
418
|
+
'The secret code that the device polls `/oauth2/token` with (RFC 8628 §3.2). The device keeps it private.',
|
|
419
|
+
examples: ['GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS'],
|
|
420
|
+
}),
|
|
421
|
+
expires_in: z.number().meta({
|
|
422
|
+
description: 'Seconds until the device code and user code expire.',
|
|
423
|
+
examples: [600],
|
|
424
|
+
}),
|
|
425
|
+
interval: z.number().meta({
|
|
426
|
+
description: 'Seconds between polls.',
|
|
427
|
+
examples: [5],
|
|
428
|
+
}),
|
|
429
|
+
user_code: z.string().meta({
|
|
430
|
+
description:
|
|
431
|
+
'The code that the user enters on the verification page: eight consonants.',
|
|
432
|
+
examples: ['WDJBMJHT'],
|
|
433
|
+
}),
|
|
434
|
+
verification_uri: z.url().meta({
|
|
435
|
+
description: 'The page where the user enters the user code.',
|
|
436
|
+
examples: ['https://id.tempo.xyz/device'],
|
|
437
|
+
}),
|
|
438
|
+
verification_uri_complete: z.url().meta({
|
|
439
|
+
description:
|
|
440
|
+
'The verification page with the user code filled in, for a link or QR code.',
|
|
441
|
+
examples: ['https://id.tempo.xyz/device?user_code=WDJBMJHT'],
|
|
442
|
+
}),
|
|
443
|
+
})
|
|
444
|
+
.meta({
|
|
445
|
+
description:
|
|
446
|
+
"The issuer's device authorization response (RFC 8628 §3.2).",
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
/** Better Auth's response, with the members that the device page's record reads. */
|
|
450
|
+
export const issued = z.looseObject({
|
|
451
|
+
device_code: z.string(),
|
|
452
|
+
expires_in: z.number(),
|
|
453
|
+
user_code: z.string(),
|
|
454
|
+
})
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export namespace introspect {
|
|
458
|
+
export const form = z.object({
|
|
459
|
+
client_id: token.form.shape.client_id,
|
|
460
|
+
client_secret: token.form.shape.client_secret,
|
|
461
|
+
token: z.string().meta({
|
|
462
|
+
description: 'The token to introspect.',
|
|
463
|
+
examples: ['yAjhKk7QnRZ3vD9pLm2Xc8Ws5Tf1Ub4N'],
|
|
464
|
+
}),
|
|
465
|
+
token_type_hint: z
|
|
466
|
+
.enum(['access_token', 'refresh_token'])
|
|
467
|
+
.optional()
|
|
468
|
+
.meta({
|
|
469
|
+
description:
|
|
470
|
+
'The type of `token`, which helps the issuer find it (RFC 7662 §2.1).',
|
|
471
|
+
examples: ['refresh_token'],
|
|
472
|
+
}),
|
|
473
|
+
})
|
|
474
|
+
|
|
475
|
+
export const response = z
|
|
476
|
+
.looseObject({
|
|
477
|
+
active: z.boolean().meta({
|
|
478
|
+
description:
|
|
479
|
+
'Whether the token is active. An inactive token has no other members.',
|
|
480
|
+
examples: [true],
|
|
481
|
+
}),
|
|
482
|
+
client_id: z
|
|
483
|
+
.string()
|
|
484
|
+
.optional()
|
|
485
|
+
.meta({
|
|
486
|
+
description: 'The client ID of the app that the token belongs to.',
|
|
487
|
+
examples: ['tempo-cli'],
|
|
488
|
+
}),
|
|
489
|
+
exp: z
|
|
490
|
+
.number()
|
|
491
|
+
.optional()
|
|
492
|
+
.meta({
|
|
493
|
+
description: 'When the token expires, in Unix seconds.',
|
|
494
|
+
examples: [1792076100],
|
|
495
|
+
}),
|
|
496
|
+
iat: z
|
|
497
|
+
.number()
|
|
498
|
+
.optional()
|
|
499
|
+
.meta({
|
|
500
|
+
description: 'When the issuer issued the token, in Unix seconds.',
|
|
501
|
+
examples: [1792075800],
|
|
502
|
+
}),
|
|
503
|
+
iss: z
|
|
504
|
+
.string()
|
|
505
|
+
.optional()
|
|
506
|
+
.meta({
|
|
507
|
+
description: "The issuer's identifier.",
|
|
508
|
+
examples: ['https://id.tempo.xyz'],
|
|
509
|
+
}),
|
|
510
|
+
scope: z
|
|
511
|
+
.string()
|
|
512
|
+
.optional()
|
|
513
|
+
.meta({
|
|
514
|
+
description: 'Space-separated OAuth scopes of the token.',
|
|
515
|
+
examples: ['openid profile email'],
|
|
516
|
+
}),
|
|
517
|
+
sub: z
|
|
518
|
+
.string()
|
|
519
|
+
.optional()
|
|
520
|
+
.meta({
|
|
521
|
+
description: "The user ID of the token's subject.",
|
|
522
|
+
examples: ['user_01JZ8Q6W3N1R5B4V7C2X9K0M3T'],
|
|
523
|
+
}),
|
|
524
|
+
})
|
|
525
|
+
.meta({
|
|
526
|
+
description: "The issuer's introspection response (RFC 7662 §2.2).",
|
|
527
|
+
id: 'IntrospectionResponse',
|
|
528
|
+
})
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export namespace revoke {
|
|
532
|
+
export const form = z.object({
|
|
533
|
+
client_id: token.form.shape.client_id,
|
|
534
|
+
client_secret: token.form.shape.client_secret,
|
|
535
|
+
token: z.string().meta({
|
|
536
|
+
description: 'The access or refresh token to revoke.',
|
|
537
|
+
examples: ['yAjhKk7QnRZ3vD9pLm2Xc8Ws5Tf1Ub4N'],
|
|
538
|
+
}),
|
|
539
|
+
token_type_hint: introspect.form.shape.token_type_hint,
|
|
540
|
+
})
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
export namespace userinfo {
|
|
544
|
+
export const response = z
|
|
545
|
+
.looseObject({
|
|
546
|
+
email: z
|
|
547
|
+
.string()
|
|
548
|
+
.optional()
|
|
549
|
+
.meta({
|
|
550
|
+
description:
|
|
551
|
+
"The user's email address. Absent without the `email` scope.",
|
|
552
|
+
examples: ['ada@example.com'],
|
|
553
|
+
}),
|
|
554
|
+
email_verified: z
|
|
555
|
+
.boolean()
|
|
556
|
+
.optional()
|
|
557
|
+
.meta({
|
|
558
|
+
description:
|
|
559
|
+
'Whether the issuer verified the email address. Absent without the `email` scope.',
|
|
560
|
+
examples: [true],
|
|
561
|
+
}),
|
|
562
|
+
sub: z.string().meta({
|
|
563
|
+
description: 'The user ID.',
|
|
564
|
+
examples: ['user_01JZ8Q6W3N1R5B4V7C2X9K0M3T'],
|
|
565
|
+
}),
|
|
566
|
+
})
|
|
567
|
+
.meta({
|
|
568
|
+
description: "The issuer's UserInfo response (OIDC Core §5.3.2).",
|
|
569
|
+
id: 'UserInfoResponse',
|
|
570
|
+
})
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
export namespace endSession {
|
|
574
|
+
export const query = z.object({
|
|
575
|
+
client_id: z
|
|
576
|
+
.string()
|
|
577
|
+
.optional()
|
|
578
|
+
.meta({
|
|
579
|
+
description:
|
|
580
|
+
"The app's client ID. Absent when `id_token_hint` names the app.",
|
|
581
|
+
examples: ['tempo-cli'],
|
|
582
|
+
}),
|
|
583
|
+
continuation: z
|
|
584
|
+
.string()
|
|
585
|
+
.optional()
|
|
586
|
+
.meta({
|
|
587
|
+
description:
|
|
588
|
+
'The sealed parameters of a form POST that carried no session cookie, which its 303 sends here. Absent otherwise.',
|
|
589
|
+
examples: ['a3f1c2…'],
|
|
590
|
+
}),
|
|
591
|
+
id_token_hint: z
|
|
592
|
+
.string()
|
|
593
|
+
.optional()
|
|
594
|
+
.meta({
|
|
595
|
+
description:
|
|
596
|
+
'An ID token that Tempo ID issued to the app, even an expired one, which names the session to end. Required without `continuation`.',
|
|
597
|
+
examples: ['eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.e30.c2lnbmF0dXJl'],
|
|
598
|
+
}),
|
|
599
|
+
post_logout_redirect_uri: z
|
|
600
|
+
.url()
|
|
601
|
+
.optional()
|
|
602
|
+
.meta({
|
|
603
|
+
description:
|
|
604
|
+
"Where the browser goes after sign-out: one of the app's registered post-logout redirect URIs.",
|
|
605
|
+
examples: ['https://app.example/signed-out'],
|
|
606
|
+
}),
|
|
607
|
+
state: authorize.query.shape.state,
|
|
608
|
+
})
|
|
609
|
+
|
|
610
|
+
export const form = query.omit({ continuation: true }).extend({
|
|
611
|
+
id_token_hint: z.string().meta({
|
|
612
|
+
description:
|
|
613
|
+
'An ID token that Tempo ID issued to the app, which names the session to end. It may be expired.',
|
|
614
|
+
examples: ['eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.e30.c2lnbmF0dXJl'],
|
|
615
|
+
}),
|
|
616
|
+
})
|
|
617
|
+
|
|
618
|
+
export const response = z.object({
|
|
619
|
+
signed_out: z.literal(true).meta({
|
|
620
|
+
description:
|
|
621
|
+
'The browser holds no session that the hint names, since it ended or never held one.',
|
|
622
|
+
}),
|
|
623
|
+
})
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
export namespace jwks {
|
|
627
|
+
export const response = z
|
|
628
|
+
.object({
|
|
629
|
+
keys: z
|
|
630
|
+
.array(
|
|
631
|
+
z.looseObject({
|
|
632
|
+
alg: z
|
|
633
|
+
.string()
|
|
634
|
+
.optional()
|
|
635
|
+
.meta({
|
|
636
|
+
description: 'The algorithm that the key signs with.',
|
|
637
|
+
examples: ['RS256'],
|
|
638
|
+
}),
|
|
639
|
+
kid: z
|
|
640
|
+
.string()
|
|
641
|
+
.optional()
|
|
642
|
+
.meta({
|
|
643
|
+
description:
|
|
644
|
+
'The key ID, which matches the `kid` header of a token that the key signs.',
|
|
645
|
+
examples: ['Qq0BN2lPx8VzTRsaGgMjc5HbEFKY4Wty'],
|
|
646
|
+
}),
|
|
647
|
+
kty: z.string().meta({
|
|
648
|
+
description: 'The key family (RFC 7518 §6.1).',
|
|
649
|
+
examples: ['RSA'],
|
|
650
|
+
}),
|
|
651
|
+
}),
|
|
652
|
+
)
|
|
653
|
+
.meta({
|
|
654
|
+
description:
|
|
655
|
+
"The issuer's public signing keys, with the members of each key that RFC 7517 defines.",
|
|
656
|
+
}),
|
|
657
|
+
})
|
|
658
|
+
.meta({
|
|
659
|
+
description: "The issuer's JSON Web Key Set (RFC 7517 §5).",
|
|
660
|
+
id: 'JsonWebKeySet',
|
|
661
|
+
})
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* The issuer's refusals, which pass through with their own status, and the `unexpected` error
|
|
667
|
+
* response that Tempo ID returns when the issuer fails.
|
|
668
|
+
*/
|
|
669
|
+
const passthrough = {
|
|
670
|
+
...OpenApi.errors([]),
|
|
671
|
+
400: {
|
|
672
|
+
content: {
|
|
673
|
+
'application/json': { schema: OpenApi.resolver(schema.token.error) },
|
|
674
|
+
},
|
|
675
|
+
description: "The issuer's refusal, passed through.",
|
|
676
|
+
},
|
|
677
|
+
401: {
|
|
678
|
+
content: {
|
|
679
|
+
'application/json': { schema: OpenApi.resolver(schema.token.error) },
|
|
680
|
+
},
|
|
681
|
+
description: "The issuer's refusal of the client or token, passed through.",
|
|
682
|
+
},
|
|
683
|
+
} as const
|
|
684
|
+
|
|
685
|
+
/** The responses of `POST /oauth2/introspect` in the OpenAPI document. */
|
|
686
|
+
const introspectRoute = {
|
|
687
|
+
responses: {
|
|
688
|
+
200: {
|
|
689
|
+
content: {
|
|
690
|
+
'application/json': {
|
|
691
|
+
schema: OpenApi.resolver(schema.introspect.response),
|
|
692
|
+
},
|
|
693
|
+
},
|
|
694
|
+
description: "The issuer's introspection response.",
|
|
695
|
+
},
|
|
696
|
+
...passthrough,
|
|
697
|
+
...OpenApi.errors([...BodyLimit.errors, ...RateLimit.check.errors]),
|
|
698
|
+
},
|
|
699
|
+
security: [{}, { clientBasic: [] }],
|
|
700
|
+
tags: ['OAuth'],
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/** The description and responses of `GET` and `POST /oauth2/end-session` in the OpenAPI document. */
|
|
704
|
+
const endSessionRoute = {
|
|
705
|
+
description:
|
|
706
|
+
"Signs the browser out of the session that the app's ID token names, if the browser holds it, and revokes the tokens that apps received through it (OpenID Connect RP-Initiated Logout). Any other session stays signed in, and Tempo ID redirects the browser to the app's post-logout redirect URI.",
|
|
707
|
+
responses: {
|
|
708
|
+
302: {
|
|
709
|
+
description:
|
|
710
|
+
"Redirects the browser to the app's post-logout redirect URI, with `state`.",
|
|
711
|
+
},
|
|
712
|
+
...OpenApi.responses({
|
|
713
|
+
errors: [...OpenApi.validate.errors, ...Logout.check.errors],
|
|
714
|
+
success: {
|
|
715
|
+
description:
|
|
716
|
+
'The browser is signed out of the session that the hint names, for a request without `post_logout_redirect_uri`.',
|
|
717
|
+
schema: schema.endSession.response,
|
|
718
|
+
},
|
|
719
|
+
}),
|
|
720
|
+
},
|
|
721
|
+
tags: ['OAuth'],
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Returns the refusal of the device authorization request `form`'s `key`, or `undefined`. Only a
|
|
726
|
+
* public first-party device app receives a key, since its poll sends no secret, and a third-party
|
|
727
|
+
* app authorizes keys through a redirect. The device page shows the key, so a key that approval
|
|
728
|
+
* would refuse is refused here, before the device shows a code.
|
|
729
|
+
*/
|
|
730
|
+
async function refuseDeviceKey(
|
|
731
|
+
c: Context<Env>,
|
|
732
|
+
options: refuseDeviceKey.Options,
|
|
733
|
+
): Promise<ErrorResponse.Instance<typeof refuseDeviceKey.errors> | undefined> {
|
|
734
|
+
const { form, key } = options
|
|
735
|
+
const clientId = basicClientId(c.req.raw) ?? form.get('client_id')
|
|
736
|
+
const app = clientId
|
|
737
|
+
? await Timing.measure(
|
|
738
|
+
c,
|
|
739
|
+
Apps.get(db, { clientId, enabled: true }).catch((error: unknown) => {
|
|
740
|
+
if (error instanceof Apps.NotFoundError) return undefined
|
|
741
|
+
throw error
|
|
742
|
+
}),
|
|
743
|
+
'Apps.get',
|
|
744
|
+
)
|
|
745
|
+
: undefined
|
|
746
|
+
if (!clientId || !app?.device || app.secret || !app.firstParty)
|
|
747
|
+
return new Authorize.RejectedError({
|
|
748
|
+
description: `Device grant refused: \`client_id\` ${clientId ?? '(none)'} names no enabled public first-party device app. Send the client ID of a registered device app, or leave out \`key_authorization\`.`,
|
|
749
|
+
})
|
|
750
|
+
return (
|
|
751
|
+
KeyAuthorization.check(key) ??
|
|
752
|
+
KeyAuthorization.checkChallenge({ app, clientId, key })
|
|
753
|
+
)
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
declare namespace refuseDeviceKey {
|
|
757
|
+
type Options = {
|
|
758
|
+
/** The device authorization request's form. */
|
|
759
|
+
form: URLSearchParams
|
|
760
|
+
/** The key that the form requests, with its challenge bound. */
|
|
761
|
+
key: KeyAuthorization.Key
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
namespace refuseDeviceKey {
|
|
766
|
+
/** The refusals `refuseDeviceKey` returns. Its return type admits no other. */
|
|
767
|
+
export const errors = [
|
|
768
|
+
...KeyAuthorization.check.errors,
|
|
769
|
+
...KeyAuthorization.checkChallenge.errors,
|
|
770
|
+
Authorize.RejectedError,
|
|
771
|
+
] as const
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* Returns the refusal of a `form` that repeats one of `names`, or `undefined`. A request parameter
|
|
776
|
+
* appears at most once (RFC 6749 §3.1).
|
|
777
|
+
*/
|
|
778
|
+
function repeated(form: URLSearchParams, options: repeated.Options) {
|
|
779
|
+
const name = options.names.find((entry) => form.getAll(entry).length > 1)
|
|
780
|
+
if (!name) return undefined
|
|
781
|
+
return new Validation.InvalidFieldError({
|
|
782
|
+
issues: [{ message: 'Repeated parameter. Send it once', path: [name] }],
|
|
783
|
+
})
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
declare namespace repeated {
|
|
787
|
+
type Options = {
|
|
788
|
+
/** The parameters that may appear once. */
|
|
789
|
+
names: readonly string[]
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/** The protocol paths that Better Auth serves over HTTP, with any method it accepts. */
|
|
794
|
+
const paths = [
|
|
795
|
+
'/.well-known/oauth-authorization-server',
|
|
796
|
+
'/.well-known/openid-configuration',
|
|
797
|
+
'/oauth2/authorize',
|
|
798
|
+
'/oauth2/introspect',
|
|
799
|
+
'/oauth2/jwks',
|
|
800
|
+
'/oauth2/revoke',
|
|
801
|
+
'/oauth2/token',
|
|
802
|
+
'/oauth2/userinfo',
|
|
803
|
+
] as const
|
|
804
|
+
|
|
805
|
+
/** Tempo ID's pages that an authorization redirects to, which `GET /oauth2/authorize` renders. */
|
|
806
|
+
const pages: ReadonlySet<string> = new Set([
|
|
807
|
+
'/authorize',
|
|
808
|
+
'/choose-account',
|
|
809
|
+
'/error',
|
|
810
|
+
'/login',
|
|
811
|
+
])
|
|
812
|
+
|
|
813
|
+
/** The paths whose handler resolves the client that the request names, which `limitDiscovery` counts. */
|
|
814
|
+
const discoveryPaths: ReadonlySet<string> = new Set([
|
|
815
|
+
'/device/code',
|
|
816
|
+
'/oauth2/authorize',
|
|
817
|
+
'/oauth2/introspect',
|
|
818
|
+
'/oauth2/revoke',
|
|
819
|
+
'/oauth2/token',
|
|
820
|
+
])
|
|
821
|
+
|
|
822
|
+
/** The OAuth and OpenID protocol routes. */
|
|
823
|
+
export const oauth = new Hono<Env>()
|
|
824
|
+
.get(
|
|
825
|
+
'/.well-known/openid-configuration',
|
|
826
|
+
OpenApi.describeRoute({
|
|
827
|
+
description: "Reads the issuer's OpenID Connect discovery document.",
|
|
828
|
+
responses: OpenApi.responses({
|
|
829
|
+
success: {
|
|
830
|
+
description: 'The discovery document.',
|
|
831
|
+
schema: schema.openidConfiguration.response,
|
|
832
|
+
},
|
|
833
|
+
}),
|
|
834
|
+
tags: ['OAuth'],
|
|
835
|
+
}),
|
|
836
|
+
(c) => discovery(c),
|
|
837
|
+
)
|
|
838
|
+
.get(
|
|
839
|
+
'/.well-known/oauth-authorization-server',
|
|
840
|
+
OpenApi.describeRoute({
|
|
841
|
+
description: "Reads the issuer's authorization server metadata.",
|
|
842
|
+
responses: OpenApi.responses({
|
|
843
|
+
success: {
|
|
844
|
+
description: 'The metadata.',
|
|
845
|
+
schema: schema.oauthAuthorizationServer.response,
|
|
846
|
+
},
|
|
847
|
+
}),
|
|
848
|
+
tags: ['OAuth'],
|
|
849
|
+
}),
|
|
850
|
+
(c) => discovery(c),
|
|
851
|
+
)
|
|
852
|
+
.get(
|
|
853
|
+
'/oauth2/authorize',
|
|
854
|
+
OpenApi.describeRoute({
|
|
855
|
+
description:
|
|
856
|
+
'Starts an authorization. A signed-out browser receives the sign-in page, and a signed-in one the consent page or a redirect to the redirect URI with a code.',
|
|
857
|
+
responses: {
|
|
858
|
+
200: {
|
|
859
|
+
content: {
|
|
860
|
+
'application/json': {
|
|
861
|
+
schema: OpenApi.resolver(schema.authorize.response),
|
|
862
|
+
},
|
|
863
|
+
'text/html': { schema: { type: 'string' } },
|
|
864
|
+
},
|
|
865
|
+
description:
|
|
866
|
+
"The next step as JSON, for a request from a script. A browser navigation receives Tempo ID's page of the next step, such as the sign-in page or the error page.",
|
|
867
|
+
},
|
|
868
|
+
302: {
|
|
869
|
+
description:
|
|
870
|
+
'Redirects the browser to the redirect URI with a code. A refused `key_authorization` returns to the redirect URI with `error` and `error_description`.',
|
|
871
|
+
},
|
|
872
|
+
...passthrough,
|
|
873
|
+
// A third-party app's requests count per address, before the redirect.
|
|
874
|
+
...OpenApi.errors(RateLimit.check.errors),
|
|
875
|
+
},
|
|
876
|
+
tags: ['OAuth'],
|
|
877
|
+
}),
|
|
878
|
+
OpenApi.input('query', schema.authorize.query),
|
|
879
|
+
async (c) => {
|
|
880
|
+
const response = await Timing.measure(c, forward(c), 'oauth2.forward')
|
|
881
|
+
// A redirect to one of Tempo ID's own pages renders that page in this response, so the
|
|
882
|
+
// browser loads the page without a second request. Its URL keeps the signed query.
|
|
883
|
+
const location = URL.parse(
|
|
884
|
+
response.headers.get('location') ?? '',
|
|
885
|
+
env.URL,
|
|
886
|
+
)
|
|
887
|
+
if (
|
|
888
|
+
response.status !== 302 ||
|
|
889
|
+
location?.origin !== new URL(env.URL).origin ||
|
|
890
|
+
!pages.has(location.pathname)
|
|
891
|
+
)
|
|
892
|
+
return response
|
|
893
|
+
|
|
894
|
+
const setCookie = response.headers.getSetCookie()
|
|
895
|
+
for (const cookie of setCookie)
|
|
896
|
+
c.header('set-cookie', cookie, { append: true })
|
|
897
|
+
const request = new Request(location, {
|
|
898
|
+
headers: SignInConsent.sessionHeaders(c, { setCookie }),
|
|
899
|
+
})
|
|
900
|
+
return Timing.measure(c, c.env.pages(c, { request }), 'page.render')
|
|
901
|
+
},
|
|
902
|
+
)
|
|
903
|
+
.post(
|
|
904
|
+
'/oauth2/token',
|
|
905
|
+
OpenApi.describeRoute({
|
|
906
|
+
description:
|
|
907
|
+
'Redeems an authorization code, a refresh token, a device code, or client credentials for tokens.',
|
|
908
|
+
responses: {
|
|
909
|
+
200: {
|
|
910
|
+
content: {
|
|
911
|
+
'application/json': {
|
|
912
|
+
schema: OpenApi.resolver(schema.token.response),
|
|
913
|
+
},
|
|
914
|
+
},
|
|
915
|
+
description: 'The tokens.',
|
|
916
|
+
},
|
|
917
|
+
...passthrough,
|
|
918
|
+
...OpenApi.errors([
|
|
919
|
+
...BodyLimit.errors,
|
|
920
|
+
...OpenApi.validate.errors,
|
|
921
|
+
...RateLimit.check.errors,
|
|
922
|
+
]),
|
|
923
|
+
},
|
|
924
|
+
// Body credentials or HTTP Basic, so either requirement satisfies it.
|
|
925
|
+
security: [{}, { clientBasic: [] }],
|
|
926
|
+
tags: ['OAuth'],
|
|
927
|
+
}),
|
|
928
|
+
OpenApi.input('form', schema.token.form),
|
|
929
|
+
async (c) => {
|
|
930
|
+
const limited = await Timing.measure(
|
|
931
|
+
c,
|
|
932
|
+
RateLimit.check(c, {
|
|
933
|
+
limits: [{ binding: 'TOKEN_LIMIT', key: RateLimit.ip(c) }],
|
|
934
|
+
}),
|
|
935
|
+
'RateLimit.check',
|
|
936
|
+
)
|
|
937
|
+
if (limited) return limited
|
|
938
|
+
|
|
939
|
+
// A client ID is the caller's to choose, so an app's limit counts only a request whose client
|
|
940
|
+
// secret matches. A public app has no secret, and only the IP address bounds its callers.
|
|
941
|
+
const form = new URLSearchParams(
|
|
942
|
+
await Timing.measure(c, c.req.text(), 'request.text'),
|
|
943
|
+
)
|
|
944
|
+
const credentials = clientCredentials({
|
|
945
|
+
authorization: c.req.header('authorization'),
|
|
946
|
+
form,
|
|
947
|
+
})
|
|
948
|
+
const authenticated = credentials
|
|
949
|
+
? await Timing.measure(
|
|
950
|
+
c,
|
|
951
|
+
Apps.authenticate(db, credentials),
|
|
952
|
+
'Apps.authenticate',
|
|
953
|
+
)
|
|
954
|
+
: false
|
|
955
|
+
const limitedApp = await Timing.measure(
|
|
956
|
+
c,
|
|
957
|
+
RateLimit.check(c, {
|
|
958
|
+
limits: [
|
|
959
|
+
{
|
|
960
|
+
binding: 'TOKEN_APP_LIMIT',
|
|
961
|
+
key: authenticated ? credentials?.clientId : undefined,
|
|
962
|
+
},
|
|
963
|
+
],
|
|
964
|
+
}),
|
|
965
|
+
'RateLimit.check',
|
|
966
|
+
)
|
|
967
|
+
if (limitedApp) return limitedApp
|
|
968
|
+
|
|
969
|
+
// Better Auth reads the last value of a parameter, so the row is read for the code it redeems.
|
|
970
|
+
const twice = repeated(form, { names: ['device_code', 'grant_type'] })
|
|
971
|
+
if (twice) return ErrorResponse.json(c, twice)
|
|
972
|
+
// The redemption deletes the device code's row, so the row that names its held key is read
|
|
973
|
+
// first.
|
|
974
|
+
const deviceCode =
|
|
975
|
+
form.get('grant_type') === Apps.deviceGrant
|
|
976
|
+
? form.get('device_code')
|
|
977
|
+
: null
|
|
978
|
+
const row = deviceCode
|
|
979
|
+
? await Timing.measure(
|
|
980
|
+
c,
|
|
981
|
+
Device.find(db, { deviceCode }),
|
|
982
|
+
'Device.find',
|
|
983
|
+
)
|
|
984
|
+
: undefined
|
|
985
|
+
const response = await Timing.measure(c, forward(c), 'oauth2.forward')
|
|
986
|
+
if (!row || !response.ok) return response
|
|
987
|
+
return Timing.measure(
|
|
988
|
+
c,
|
|
989
|
+
withDeviceKey(c, { id: row.id, response }),
|
|
990
|
+
'oauth2.withDeviceKey',
|
|
991
|
+
)
|
|
992
|
+
},
|
|
993
|
+
)
|
|
994
|
+
.post(
|
|
995
|
+
'/device/code',
|
|
996
|
+
OpenApi.describeRoute({
|
|
997
|
+
description:
|
|
998
|
+
'Starts the device grant (RFC 8628 §3.1). The user approves the user code on the device page, and the device then redeems the device code at `/oauth2/token`.',
|
|
999
|
+
responses: {
|
|
1000
|
+
200: {
|
|
1001
|
+
content: {
|
|
1002
|
+
'application/json': {
|
|
1003
|
+
schema: OpenApi.resolver(schema.deviceCode.response),
|
|
1004
|
+
},
|
|
1005
|
+
},
|
|
1006
|
+
description: 'The device code.',
|
|
1007
|
+
},
|
|
1008
|
+
...passthrough,
|
|
1009
|
+
...OpenApi.errors([
|
|
1010
|
+
...BodyLimit.errors,
|
|
1011
|
+
...OpenApi.validate.errors,
|
|
1012
|
+
...RateLimit.check.errors,
|
|
1013
|
+
...refuseDeviceKey.errors,
|
|
1014
|
+
]),
|
|
1015
|
+
},
|
|
1016
|
+
security: [{}, { clientBasic: [] }],
|
|
1017
|
+
tags: ['OAuth'],
|
|
1018
|
+
}),
|
|
1019
|
+
OpenApi.input(
|
|
1020
|
+
'form',
|
|
1021
|
+
schema.deviceCode.form.extend(schema.deviceCode.extension.shape),
|
|
1022
|
+
),
|
|
1023
|
+
async (c) => {
|
|
1024
|
+
// A client ID in the body is the caller's to choose, so only the IP address bounds a caller. Any
|
|
1025
|
+
// page can post this form from a visitor's browser, which sends `Origin` and a CLI does not, so
|
|
1026
|
+
// a page's requests count apart and spend none of the quota of a CLI at the same address.
|
|
1027
|
+
const ip = RateLimit.ip(c)
|
|
1028
|
+
const limited = await Timing.measure(
|
|
1029
|
+
c,
|
|
1030
|
+
RateLimit.check(c, {
|
|
1031
|
+
limits: [
|
|
1032
|
+
{
|
|
1033
|
+
binding: 'DEVICE_LIMIT',
|
|
1034
|
+
key:
|
|
1035
|
+
ip !== undefined && c.req.header('origin') !== undefined
|
|
1036
|
+
? `${ip}:browser`
|
|
1037
|
+
: ip,
|
|
1038
|
+
},
|
|
1039
|
+
],
|
|
1040
|
+
}),
|
|
1041
|
+
'RateLimit.check',
|
|
1042
|
+
)
|
|
1043
|
+
if (limited) return limited
|
|
1044
|
+
|
|
1045
|
+
const form = new URLSearchParams(
|
|
1046
|
+
await Timing.measure(c, c.req.text(), 'request.text'),
|
|
1047
|
+
)
|
|
1048
|
+
// Better Auth reads the last value of a parameter, and Tempo ID's checks the first.
|
|
1049
|
+
const twice = repeated(form, { names: ['client_id', 'device'] })
|
|
1050
|
+
if (twice) return ErrorResponse.json(c, twice)
|
|
1051
|
+
// The issuer prefers Basic credentials to the form's `client_id`, so the two must name one app.
|
|
1052
|
+
const basic = basicClientId(c.req.raw)
|
|
1053
|
+
if (basic && form.has('client_id') && form.get('client_id') !== basic)
|
|
1054
|
+
return ErrorResponse.json(
|
|
1055
|
+
c,
|
|
1056
|
+
new Validation.InvalidFieldError({
|
|
1057
|
+
issues: [
|
|
1058
|
+
{
|
|
1059
|
+
message:
|
|
1060
|
+
'Conflicting client ID. Send the one of the Basic credentials, or leave it out',
|
|
1061
|
+
path: ['client_id'],
|
|
1062
|
+
},
|
|
1063
|
+
],
|
|
1064
|
+
}),
|
|
1065
|
+
)
|
|
1066
|
+
const extension = schema.deviceCode.extension.safeParse({
|
|
1067
|
+
challenge: form.get('challenge') ?? undefined,
|
|
1068
|
+
device: form.get('device') ?? undefined,
|
|
1069
|
+
key_authorization: form.get('key_authorization') ?? undefined,
|
|
1070
|
+
})
|
|
1071
|
+
if (!extension.success)
|
|
1072
|
+
return ErrorResponse.json(
|
|
1073
|
+
c,
|
|
1074
|
+
new Validation.InvalidFieldError({ issues: extension.error.issues }),
|
|
1075
|
+
)
|
|
1076
|
+
const { challenge, device } = extension.data
|
|
1077
|
+
const key = Authorize.fromQuery(form)
|
|
1078
|
+
if (key instanceof ErrorResponse.ResponseError)
|
|
1079
|
+
return ErrorResponse.json(c, key)
|
|
1080
|
+
|
|
1081
|
+
if (key) {
|
|
1082
|
+
const refused = await Timing.measure(
|
|
1083
|
+
c,
|
|
1084
|
+
refuseDeviceKey(c, { form, key }),
|
|
1085
|
+
'oauth2.refuseDeviceKey',
|
|
1086
|
+
)
|
|
1087
|
+
if (refused) return ErrorResponse.json(c, refused)
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
const response = await Timing.measure(c, forward(c), 'oauth2.forward')
|
|
1091
|
+
if (!response.ok) return response
|
|
1092
|
+
// The device page shows where each code came from and the key it requests.
|
|
1093
|
+
const code = schema.deviceCode.issued.parse(
|
|
1094
|
+
await Timing.measure(c, response.json(), 'response.json'),
|
|
1095
|
+
)
|
|
1096
|
+
await Timing.measure(
|
|
1097
|
+
c,
|
|
1098
|
+
DeviceRequest.create(c, {
|
|
1099
|
+
challenge,
|
|
1100
|
+
device: device ?? null,
|
|
1101
|
+
deviceCode: code.device_code,
|
|
1102
|
+
expiresIn: code.expires_in,
|
|
1103
|
+
key: key && unbound(form),
|
|
1104
|
+
}),
|
|
1105
|
+
'DeviceRequest.create',
|
|
1106
|
+
)
|
|
1107
|
+
|
|
1108
|
+
// The user code reads in two groups of four, as the device page shows it.
|
|
1109
|
+
const userCode = Device.format(code.user_code)
|
|
1110
|
+
const verification = new URL(Device.page, env.URL)
|
|
1111
|
+
const headers = new Headers(response.headers)
|
|
1112
|
+
headers.delete('content-length')
|
|
1113
|
+
return new Response(
|
|
1114
|
+
JSON.stringify({
|
|
1115
|
+
...code,
|
|
1116
|
+
user_code: userCode,
|
|
1117
|
+
verification_uri: verification.href,
|
|
1118
|
+
verification_uri_complete: `${verification.href}?${new URLSearchParams({ code: userCode })}`,
|
|
1119
|
+
}),
|
|
1120
|
+
{ headers, status: response.status },
|
|
1121
|
+
)
|
|
1122
|
+
},
|
|
1123
|
+
)
|
|
1124
|
+
.post(
|
|
1125
|
+
'/oauth2/introspect',
|
|
1126
|
+
OpenApi.describeRoute({
|
|
1127
|
+
description:
|
|
1128
|
+
'Introspects a token (RFC 7662). The app authenticates with its client credentials, in the body or as HTTP Basic `Authorization`.',
|
|
1129
|
+
...introspectRoute,
|
|
1130
|
+
}),
|
|
1131
|
+
OpenApi.input('form', schema.introspect.form),
|
|
1132
|
+
(c) => forward(c),
|
|
1133
|
+
)
|
|
1134
|
+
.post(
|
|
1135
|
+
'/oauth2/revoke',
|
|
1136
|
+
OpenApi.describeRoute({
|
|
1137
|
+
description:
|
|
1138
|
+
'Revokes an access or refresh token (RFC 7009). The app authenticates with its client credentials, in the body or as HTTP Basic `Authorization`.',
|
|
1139
|
+
responses: {
|
|
1140
|
+
200: {
|
|
1141
|
+
content: {
|
|
1142
|
+
'application/json': {
|
|
1143
|
+
schema: {
|
|
1144
|
+
description:
|
|
1145
|
+
'Empty: the token is revoked, or was already invalid (RFC 7009 §2.2).',
|
|
1146
|
+
},
|
|
1147
|
+
},
|
|
1148
|
+
},
|
|
1149
|
+
description: 'The token is revoked.',
|
|
1150
|
+
},
|
|
1151
|
+
...passthrough,
|
|
1152
|
+
...OpenApi.errors([...BodyLimit.errors, ...RateLimit.check.errors]),
|
|
1153
|
+
},
|
|
1154
|
+
security: [{}, { clientBasic: [] }],
|
|
1155
|
+
tags: ['OAuth'],
|
|
1156
|
+
}),
|
|
1157
|
+
OpenApi.input('form', schema.revoke.form),
|
|
1158
|
+
(c) => forward(c),
|
|
1159
|
+
)
|
|
1160
|
+
.get(
|
|
1161
|
+
'/oauth2/userinfo',
|
|
1162
|
+
OpenApi.describeRoute({
|
|
1163
|
+
description: "Reads the token subject's claims (OIDC Core §5.3).",
|
|
1164
|
+
responses: {
|
|
1165
|
+
200: {
|
|
1166
|
+
content: {
|
|
1167
|
+
'application/json': {
|
|
1168
|
+
schema: OpenApi.resolver(schema.userinfo.response),
|
|
1169
|
+
},
|
|
1170
|
+
},
|
|
1171
|
+
description: "The issuer's UserInfo response.",
|
|
1172
|
+
},
|
|
1173
|
+
...passthrough,
|
|
1174
|
+
},
|
|
1175
|
+
security: [{ accessToken: [] }],
|
|
1176
|
+
tags: ['OAuth'],
|
|
1177
|
+
}),
|
|
1178
|
+
(c) => forward(c),
|
|
1179
|
+
)
|
|
1180
|
+
.get(
|
|
1181
|
+
'/oauth2/end-session',
|
|
1182
|
+
OpenApi.describeRoute(endSessionRoute),
|
|
1183
|
+
OpenApi.validate('query', schema.endSession.query),
|
|
1184
|
+
async (c) => endSession(c, c.req.valid('query')),
|
|
1185
|
+
)
|
|
1186
|
+
.post(
|
|
1187
|
+
'/oauth2/end-session',
|
|
1188
|
+
OpenApi.describeRoute({
|
|
1189
|
+
...endSessionRoute,
|
|
1190
|
+
responses: {
|
|
1191
|
+
...endSessionRoute.responses,
|
|
1192
|
+
...OpenApi.errors(BodyLimit.errors),
|
|
1193
|
+
303: {
|
|
1194
|
+
description:
|
|
1195
|
+
'Continues a form POST without the session cookie as `GET /oauth2/end-session` with the sealed parameters in `continuation`, which the browser sends with the cookie.',
|
|
1196
|
+
},
|
|
1197
|
+
},
|
|
1198
|
+
}),
|
|
1199
|
+
OpenApi.validate('form', schema.endSession.form),
|
|
1200
|
+
async (c) => endSession(c, c.req.valid('form')),
|
|
1201
|
+
)
|
|
1202
|
+
.get(
|
|
1203
|
+
'/oauth2/jwks',
|
|
1204
|
+
OpenApi.describeRoute({
|
|
1205
|
+
description:
|
|
1206
|
+
"Reads the issuer's signing keys. A verifier may cache the response for 60 seconds, since a new key signs 5 minutes after its rotation.",
|
|
1207
|
+
responses: OpenApi.responses({
|
|
1208
|
+
success: {
|
|
1209
|
+
description: 'The JSON Web Key Set.',
|
|
1210
|
+
schema: schema.jwks.response,
|
|
1211
|
+
},
|
|
1212
|
+
}),
|
|
1213
|
+
tags: ['OAuth'],
|
|
1214
|
+
}),
|
|
1215
|
+
async (c) => {
|
|
1216
|
+
const response = await Timing.measure(c, forward(c), 'oauth2.forward')
|
|
1217
|
+
if (!response.ok) return response
|
|
1218
|
+
// A verifier caches the key set a fifth of the publication delay, so it holds a rotation's new
|
|
1219
|
+
// key well before the key signs. An empty set, before the first token creates the first key,
|
|
1220
|
+
// stays uncached, so a verifier reads that key once it signs.
|
|
1221
|
+
const keys = schema.jwks.response.safeParse(
|
|
1222
|
+
await Timing.measure(c, response.clone().json(), 'response.json'),
|
|
1223
|
+
)
|
|
1224
|
+
if (keys.success && keys.data.keys.length > 0)
|
|
1225
|
+
response.headers.set(
|
|
1226
|
+
'cache-control',
|
|
1227
|
+
`public, max-age=${SigningKey.publication / 5}`,
|
|
1228
|
+
)
|
|
1229
|
+
return response
|
|
1230
|
+
},
|
|
1231
|
+
)
|
|
1232
|
+
// The other method of each path that Better Auth accepts, such as a `POST` to `/oauth2/userinfo`.
|
|
1233
|
+
.on(['GET', 'POST'], [...paths], (c) => forward(c))
|
|
1234
|
+
|
|
1235
|
+
/**
|
|
1236
|
+
* Returns the client ID and secret of a token request: from HTTP Basic `Authorization`, whose parts
|
|
1237
|
+
* RFC 6749 §2.3.1 form-encodes, or else from the form body, as Better Auth reads them. Returns
|
|
1238
|
+
* `undefined` for a request without a client secret, with a repeated one, or with an
|
|
1239
|
+
* `Authorization` header that does not decode.
|
|
1240
|
+
*/
|
|
1241
|
+
function clientCredentials(options: clientCredentials.Options) {
|
|
1242
|
+
const { authorization, form } = options
|
|
1243
|
+
// The issuer decodes with the same function, so both read the same credentials.
|
|
1244
|
+
if (authorization && /^Basic +/i.test(authorization))
|
|
1245
|
+
try {
|
|
1246
|
+
return decodeBasicCredentials(authorization)
|
|
1247
|
+
} catch {
|
|
1248
|
+
return undefined
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
// Better Auth drops empty values and refuses a repeated one, so only a single value counts.
|
|
1252
|
+
const [clientId, ...otherIds] = form
|
|
1253
|
+
.getAll('client_id')
|
|
1254
|
+
.filter((value) => value.length > 0)
|
|
1255
|
+
const [clientSecret, ...otherSecrets] = form
|
|
1256
|
+
.getAll('client_secret')
|
|
1257
|
+
.filter((value) => value.length > 0)
|
|
1258
|
+
if (!clientId || !clientSecret || otherIds.length || otherSecrets.length)
|
|
1259
|
+
return undefined
|
|
1260
|
+
return { clientId, clientSecret }
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
declare namespace clientCredentials {
|
|
1264
|
+
type Options = {
|
|
1265
|
+
/** The request's `Authorization` header. */
|
|
1266
|
+
authorization: string | undefined
|
|
1267
|
+
/** The request's form body. */
|
|
1268
|
+
form: URLSearchParams
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
/**
|
|
1273
|
+
* Serves RP-initiated logout for `GET` and `POST /oauth2/end-session`, with the request's parameters.
|
|
1274
|
+
* It ends the browser's session only when the hint names that session, since RP-Initiated Logout §2
|
|
1275
|
+
* asks the user to confirm any other logout. Every valid request redirects to the app.
|
|
1276
|
+
*/
|
|
1277
|
+
async function endSession(
|
|
1278
|
+
c: Context<Env>,
|
|
1279
|
+
input: z.output<typeof schema.endSession.query>,
|
|
1280
|
+
) {
|
|
1281
|
+
const parameters =
|
|
1282
|
+
input.continuation === undefined
|
|
1283
|
+
? input
|
|
1284
|
+
: await Timing.measure(
|
|
1285
|
+
c,
|
|
1286
|
+
Logout.unseal(input.continuation, { secret: env.SECRET }),
|
|
1287
|
+
'Logout.unseal',
|
|
1288
|
+
)
|
|
1289
|
+
if (!parameters)
|
|
1290
|
+
return ErrorResponse.json(
|
|
1291
|
+
c,
|
|
1292
|
+
new Validation.InvalidFieldError({
|
|
1293
|
+
issues: [
|
|
1294
|
+
{
|
|
1295
|
+
message:
|
|
1296
|
+
'Expired or invalid continuation. Send the logout request again',
|
|
1297
|
+
path: ['continuation'],
|
|
1298
|
+
},
|
|
1299
|
+
],
|
|
1300
|
+
}),
|
|
1301
|
+
)
|
|
1302
|
+
const hint = parameters.id_token_hint
|
|
1303
|
+
if (!hint)
|
|
1304
|
+
return ErrorResponse.json(
|
|
1305
|
+
c,
|
|
1306
|
+
new Validation.InvalidFieldError({
|
|
1307
|
+
issues: [{ message: 'Required', path: ['id_token_hint'] }],
|
|
1308
|
+
}),
|
|
1309
|
+
)
|
|
1310
|
+
|
|
1311
|
+
const jwks = await Timing.measure(
|
|
1312
|
+
c,
|
|
1313
|
+
SigningKey.hintKeys(db, { hint }),
|
|
1314
|
+
'SigningKey.hintKeys',
|
|
1315
|
+
)
|
|
1316
|
+
const logout = await Timing.measure(
|
|
1317
|
+
c,
|
|
1318
|
+
Logout.check(db, {
|
|
1319
|
+
clientId: parameters.client_id,
|
|
1320
|
+
hint,
|
|
1321
|
+
issuer: env.URL,
|
|
1322
|
+
jwks,
|
|
1323
|
+
postLogoutRedirectUri: parameters.post_logout_redirect_uri,
|
|
1324
|
+
state: parameters.state,
|
|
1325
|
+
}),
|
|
1326
|
+
'Logout.check',
|
|
1327
|
+
)
|
|
1328
|
+
if (logout instanceof ErrorResponse.ResponseError)
|
|
1329
|
+
return ErrorResponse.json(c, logout)
|
|
1330
|
+
|
|
1331
|
+
const token = await Timing.measure(c, Session.token(c), 'Session.token')
|
|
1332
|
+
// One statement ends the session that the hint names, when it is the browser's own session.
|
|
1333
|
+
const ended = token
|
|
1334
|
+
? await Timing.measure(
|
|
1335
|
+
c,
|
|
1336
|
+
Logout.end(db, { sessionId: logout.sessionId, token }),
|
|
1337
|
+
'Logout.end',
|
|
1338
|
+
)
|
|
1339
|
+
: undefined
|
|
1340
|
+
const holds = ended !== undefined
|
|
1341
|
+
// A browser sends the `SameSite=Lax` session cookie on a top-level GET, but not on a cross-site
|
|
1342
|
+
// form POST, so a POST without the session continues as that GET. The GET never redirects again,
|
|
1343
|
+
// and its URL holds the parameters sealed, so the ID token stays out of history and logs.
|
|
1344
|
+
if (!holds && c.req.method === 'POST') {
|
|
1345
|
+
const continuation = await Timing.measure(
|
|
1346
|
+
c,
|
|
1347
|
+
Logout.seal(
|
|
1348
|
+
{
|
|
1349
|
+
client_id: parameters.client_id,
|
|
1350
|
+
id_token_hint: hint,
|
|
1351
|
+
post_logout_redirect_uri: parameters.post_logout_redirect_uri,
|
|
1352
|
+
state: parameters.state,
|
|
1353
|
+
},
|
|
1354
|
+
{ secret: env.SECRET },
|
|
1355
|
+
),
|
|
1356
|
+
'Logout.seal',
|
|
1357
|
+
)
|
|
1358
|
+
const url = new URL('/oauth2/end-session', env.URL)
|
|
1359
|
+
url.searchParams.set('continuation', continuation)
|
|
1360
|
+
c.header('cache-control', 'no-store')
|
|
1361
|
+
return c.redirect(url.href, 303)
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
if (holds) {
|
|
1365
|
+
await Timing.measure(c, Session.clear(c), 'Session.clear')
|
|
1366
|
+
// The next sign-in chooses each app's account again, as after `POST /v1/auth/logout`.
|
|
1367
|
+
c.header('set-cookie', AccountStep.clear(), { append: true })
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
c.header('cache-control', 'no-store')
|
|
1371
|
+
if (logout.redirect) return c.redirect(logout.redirect, 302)
|
|
1372
|
+
return c.json({ signed_out: true as const }, 200)
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/** The discovery members of front-channel logout, which Tempo ID's end-session does not deliver. */
|
|
1376
|
+
const frontchannel = [
|
|
1377
|
+
'frontchannel_logout_session_supported',
|
|
1378
|
+
'frontchannel_logout_supported',
|
|
1379
|
+
] as const
|
|
1380
|
+
|
|
1381
|
+
/**
|
|
1382
|
+
* Forwards the discovery request `c` to Better Auth, and returns its document without the
|
|
1383
|
+
* `frontchannel` members. The issuer advertises front-channel logout unconditionally, but
|
|
1384
|
+
* `Logout.end` ends the session without the issuer's end-session page, which delivers it.
|
|
1385
|
+
*/
|
|
1386
|
+
async function discovery(c: Context<Env>) {
|
|
1387
|
+
const response = await Timing.measure(c, forward(c), 'oauth2.forward')
|
|
1388
|
+
// Hono serves HEAD through this GET route, and the issuer responds to HEAD without a body.
|
|
1389
|
+
if (!response.ok || c.req.method === 'HEAD') return response
|
|
1390
|
+
|
|
1391
|
+
const document = z
|
|
1392
|
+
.record(z.string(), z.unknown())
|
|
1393
|
+
.parse(await Timing.measure(c, response.json(), 'response.json'))
|
|
1394
|
+
for (const member of frontchannel) delete document[member]
|
|
1395
|
+
|
|
1396
|
+
const headers = new Headers(response.headers)
|
|
1397
|
+
headers.delete('content-length')
|
|
1398
|
+
return new Response(JSON.stringify(document), {
|
|
1399
|
+
headers,
|
|
1400
|
+
status: response.status,
|
|
1401
|
+
})
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
/**
|
|
1405
|
+
* Forwards the request `c` to Better Auth, and returns its response. An authorization request with
|
|
1406
|
+
* a key that Tempo ID refuses returns to the app's redirect URI instead, as `refuseKey` describes.
|
|
1407
|
+
* A request that names a third-party app past the address's `APP_DISCOVERY_LIMIT` responds with
|
|
1408
|
+
* `rate_limited`.
|
|
1409
|
+
*/
|
|
1410
|
+
async function forward(c: Context<Env>) {
|
|
1411
|
+
const request = await Timing.measure(
|
|
1412
|
+
c,
|
|
1413
|
+
withResource(c),
|
|
1414
|
+
'oauth2.withResource',
|
|
1415
|
+
)
|
|
1416
|
+
const body =
|
|
1417
|
+
request.method === 'POST'
|
|
1418
|
+
? await Timing.measure(c, request.clone().text(), 'request.text')
|
|
1419
|
+
: undefined
|
|
1420
|
+
// Better Auth reads a `POST` request's parameters from its form body, and only from there.
|
|
1421
|
+
const form =
|
|
1422
|
+
body !== undefined &&
|
|
1423
|
+
request.headers
|
|
1424
|
+
.get('content-type')
|
|
1425
|
+
?.toLowerCase()
|
|
1426
|
+
.includes('application/x-www-form-urlencoded')
|
|
1427
|
+
const query = form
|
|
1428
|
+
? new URLSearchParams(body)
|
|
1429
|
+
: new URL(request.url).searchParams
|
|
1430
|
+
|
|
1431
|
+
const limited = await Timing.measure(
|
|
1432
|
+
c,
|
|
1433
|
+
limitDiscovery(c, { body, request }),
|
|
1434
|
+
'oauth2.limitDiscovery',
|
|
1435
|
+
)
|
|
1436
|
+
if (limited) return limited
|
|
1437
|
+
|
|
1438
|
+
// Better Auth reads the last `client_id` of a form, and Tempo ID's checks the first, so a form
|
|
1439
|
+
// that names two apps is refused before either reads it.
|
|
1440
|
+
if (
|
|
1441
|
+
c.req.path === '/oauth2/authorize' &&
|
|
1442
|
+
query.getAll('client_id').length > 1
|
|
1443
|
+
)
|
|
1444
|
+
return ErrorResponse.json(
|
|
1445
|
+
c,
|
|
1446
|
+
new Validation.InvalidFieldError({
|
|
1447
|
+
issues: [
|
|
1448
|
+
{ message: 'Repeated parameter. Send it once', path: ['client_id'] },
|
|
1449
|
+
],
|
|
1450
|
+
}),
|
|
1451
|
+
)
|
|
1452
|
+
|
|
1453
|
+
// The issuer approves a device code through `/oauth2/authorize` with its `user_code`, and responds
|
|
1454
|
+
// whether the code is live without counting the request. The `/device` page approves device codes,
|
|
1455
|
+
// and counts each user's lookups.
|
|
1456
|
+
if (c.req.path === '/oauth2/authorize' && query.has('user_code'))
|
|
1457
|
+
return ErrorResponse.json(
|
|
1458
|
+
c,
|
|
1459
|
+
new Validation.InvalidFieldError({
|
|
1460
|
+
issues: [
|
|
1461
|
+
{
|
|
1462
|
+
message: 'Unsupported parameter. Approve a device code at /device',
|
|
1463
|
+
path: ['user_code'],
|
|
1464
|
+
},
|
|
1465
|
+
],
|
|
1466
|
+
}),
|
|
1467
|
+
)
|
|
1468
|
+
|
|
1469
|
+
// The token endpoint runs as Better Auth's endpoint, without the router that `auth.handler` builds
|
|
1470
|
+
// from every endpoint for each request. The router's origin check and request hooks serve cookie
|
|
1471
|
+
// sessions and discovery, and the token endpoint reads neither. The form reads as the router reads
|
|
1472
|
+
// it, with the last value of a repeated parameter.
|
|
1473
|
+
if (c.req.path === '/oauth2/token' && form) {
|
|
1474
|
+
const params: Record<string, string> = {}
|
|
1475
|
+
for (const [name, value] of query) params[name] = value
|
|
1476
|
+
return Timing.measure(
|
|
1477
|
+
c,
|
|
1478
|
+
c.var.auth.api.oauth2Token({
|
|
1479
|
+
asResponse: true,
|
|
1480
|
+
// The endpoint validates the form against its schema, as for the router, so a form without
|
|
1481
|
+
// `grant_type` is refused there.
|
|
1482
|
+
body: params as typeof params & { grant_type: string },
|
|
1483
|
+
headers: request.headers,
|
|
1484
|
+
request,
|
|
1485
|
+
}),
|
|
1486
|
+
'auth.oauth2Token',
|
|
1487
|
+
)
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
if (c.req.path !== '/oauth2/authorize')
|
|
1491
|
+
return Timing.measure(c, c.var.auth.handler(request), 'auth.handler')
|
|
1492
|
+
|
|
1493
|
+
const refused = await Timing.measure(
|
|
1494
|
+
c,
|
|
1495
|
+
refuseKey(c, { query }),
|
|
1496
|
+
'oauth2.refuseKey',
|
|
1497
|
+
)
|
|
1498
|
+
if (refused) return refused
|
|
1499
|
+
return Timing.measure(c, c.var.auth.handler(request), 'auth.handler')
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
/**
|
|
1503
|
+
* Returns the `rate_limited` refusal when `request` names a third-party app whose row is missing or
|
|
1504
|
+
* stale and its address spent `APP_DISCOVERY_LIMIT`, or `undefined`. Resolving such an app fetches
|
|
1505
|
+
* its metadata document and writes its row before any sign-in, so each address starts a bounded
|
|
1506
|
+
* number of them. A request for an app with a fresh row, such as a refresh, counts only against the
|
|
1507
|
+
* path's own limit. The issuer prefers Basic credentials to the parameters and checks no match, so the limit
|
|
1508
|
+
* reads both.
|
|
1509
|
+
*/
|
|
1510
|
+
async function limitDiscovery(
|
|
1511
|
+
c: Context<Env>,
|
|
1512
|
+
options: { body: string | undefined; request: Request },
|
|
1513
|
+
) {
|
|
1514
|
+
const { body, request } = options
|
|
1515
|
+
const url = new URL(request.url)
|
|
1516
|
+
if (!discoveryPaths.has(url.pathname)) return undefined
|
|
1517
|
+
const clientIds = [
|
|
1518
|
+
...url.searchParams.getAll('client_id'),
|
|
1519
|
+
...bodyClientIds(body),
|
|
1520
|
+
basicClientId(request),
|
|
1521
|
+
].filter((clientId) => clientId !== undefined)
|
|
1522
|
+
if (!clientIds.some(Apps.thirdParty)) return undefined
|
|
1523
|
+
|
|
1524
|
+
const stale = await Timing.measure(
|
|
1525
|
+
c,
|
|
1526
|
+
Apps.stale(db, { clientIds }),
|
|
1527
|
+
'Apps.stale',
|
|
1528
|
+
)
|
|
1529
|
+
if (stale.length === 0) return undefined
|
|
1530
|
+
return Timing.measure(
|
|
1531
|
+
c,
|
|
1532
|
+
RateLimit.check(c, {
|
|
1533
|
+
limits: [{ binding: 'APP_DISCOVERY_LIMIT', key: RateLimit.ip(c) }],
|
|
1534
|
+
}),
|
|
1535
|
+
'RateLimit.check',
|
|
1536
|
+
)
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
/**
|
|
1540
|
+
* Returns every client ID in `body`, read as a form and as JSON: each `client_id`, and the `sub` and
|
|
1541
|
+
* `iss` of each `client_assertion`. The check counts each one, since the issuer drops an empty
|
|
1542
|
+
* value, reads the client of an assertion from its claims before it verifies the signature, and
|
|
1543
|
+
* `/device/code` also accepts JSON.
|
|
1544
|
+
*/
|
|
1545
|
+
function bodyClientIds(body: string | undefined): readonly string[] {
|
|
1546
|
+
if (!body) return []
|
|
1547
|
+
const form = new URLSearchParams(body)
|
|
1548
|
+
const json = (() => {
|
|
1549
|
+
try {
|
|
1550
|
+
const value: unknown = JSON.parse(body)
|
|
1551
|
+
if (typeof value !== 'object' || value === null) return []
|
|
1552
|
+
const clientId = (value as Record<string, unknown>).client_id
|
|
1553
|
+
return typeof clientId === 'string' ? [clientId] : []
|
|
1554
|
+
} catch {
|
|
1555
|
+
// A form body is no JSON.
|
|
1556
|
+
return []
|
|
1557
|
+
}
|
|
1558
|
+
})()
|
|
1559
|
+
const assertions = form.getAll('client_assertion').flatMap((assertion) => {
|
|
1560
|
+
try {
|
|
1561
|
+
const [, payload] = assertion.split('.')
|
|
1562
|
+
const claims: unknown = JSON.parse(
|
|
1563
|
+
new TextDecoder().decode(Base64.toBytes(payload ?? '')),
|
|
1564
|
+
)
|
|
1565
|
+
if (typeof claims !== 'object' || claims === null) return []
|
|
1566
|
+
const { iss, sub } = claims as Record<string, unknown>
|
|
1567
|
+
return [iss, sub].filter((claim) => typeof claim === 'string')
|
|
1568
|
+
} catch {
|
|
1569
|
+
// An assertion that does not decode names no client, and the issuer refuses it.
|
|
1570
|
+
return []
|
|
1571
|
+
}
|
|
1572
|
+
})
|
|
1573
|
+
return [...form.getAll('client_id'), ...json, ...assertions]
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
/** Returns the client ID of `request`'s Basic credentials, if any. */
|
|
1577
|
+
function basicClientId(request: Request) {
|
|
1578
|
+
const authorization = request.headers.get('authorization')
|
|
1579
|
+
if (!authorization || !/^Basic +/i.test(authorization)) return undefined
|
|
1580
|
+
// The issuer decodes with the same function, so both read the same client ID.
|
|
1581
|
+
try {
|
|
1582
|
+
return decodeBasicCredentials(authorization).clientId
|
|
1583
|
+
} catch {
|
|
1584
|
+
return undefined
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
/**
|
|
1589
|
+
* Returns the redirect to the app's redirect URI with the refusal (RFC 6749 §4.1.2.1) when the
|
|
1590
|
+
* authorization request's `query` carries a key or a challenge that Tempo ID refuses, before the
|
|
1591
|
+
* browser signs in.
|
|
1592
|
+
* Returns `undefined` for a request without a key, and for an unknown app or an unregistered
|
|
1593
|
+
* redirect URI, which Better Auth refuses without the redirect.
|
|
1594
|
+
*/
|
|
1595
|
+
async function refuseKey(c: Context<Env>, options: { query: URLSearchParams }) {
|
|
1596
|
+
const { query } = options
|
|
1597
|
+
// A challenge without a key is refused too, since no witness would bind it.
|
|
1598
|
+
if (!query.has('key_authorization') && !query.has('challenge'))
|
|
1599
|
+
return undefined
|
|
1600
|
+
const clientId = query.get('client_id')
|
|
1601
|
+
const redirectUri = query.get('redirect_uri')
|
|
1602
|
+
if (!clientId || !redirectUri) return undefined
|
|
1603
|
+
|
|
1604
|
+
// A third-party app has a row only once Better Auth resolves its metadata document. A document
|
|
1605
|
+
// that fails to resolve leaves no app here, and Better Auth refuses the request itself.
|
|
1606
|
+
if (Apps.thirdParty(clientId)) {
|
|
1607
|
+
const resolved = await Timing.measure(
|
|
1608
|
+
c,
|
|
1609
|
+
c.var.auth.api
|
|
1610
|
+
.resolveOAuthClient({ body: { client_id: clientId } })
|
|
1611
|
+
.catch((error: unknown) => {
|
|
1612
|
+
if (isAPIError(error)) return { resolved: false }
|
|
1613
|
+
throw error
|
|
1614
|
+
}),
|
|
1615
|
+
'auth.resolveOAuthClient',
|
|
1616
|
+
)
|
|
1617
|
+
if (!resolved.resolved) return undefined
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
const app = await Timing.measure(
|
|
1621
|
+
c,
|
|
1622
|
+
Apps.get(db, { clientId }).catch((error: unknown) => {
|
|
1623
|
+
if (error instanceof Apps.NotFoundError) return undefined
|
|
1624
|
+
throw error
|
|
1625
|
+
}),
|
|
1626
|
+
'Apps.get',
|
|
1627
|
+
)
|
|
1628
|
+
if (!app || !app.redirectUris.includes(redirectUri)) return undefined
|
|
1629
|
+
|
|
1630
|
+
const key = Authorize.fromQuery(query)
|
|
1631
|
+
const refused =
|
|
1632
|
+
key instanceof ErrorResponse.ResponseError
|
|
1633
|
+
? key
|
|
1634
|
+
: key &&
|
|
1635
|
+
Authorize.checkKey({
|
|
1636
|
+
app,
|
|
1637
|
+
clientId,
|
|
1638
|
+
codeChallenge: query.get('code_challenge'),
|
|
1639
|
+
key,
|
|
1640
|
+
query,
|
|
1641
|
+
})
|
|
1642
|
+
if (!refused) return undefined
|
|
1643
|
+
|
|
1644
|
+
// Each parameter follows the registered URI's own, as Better Auth appends its refusals.
|
|
1645
|
+
const url = new URL(redirectUri)
|
|
1646
|
+
url.searchParams.append('error', refused.response.error)
|
|
1647
|
+
url.searchParams.append(
|
|
1648
|
+
'error_description',
|
|
1649
|
+
refused.response.error_description,
|
|
1650
|
+
)
|
|
1651
|
+
const state = query.get('state')
|
|
1652
|
+
if (state) url.searchParams.append('state', state)
|
|
1653
|
+
url.searchParams.append('iss', env.URL)
|
|
1654
|
+
c.header('cache-control', 'no-store')
|
|
1655
|
+
return c.redirect(url.href, 302)
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
/**
|
|
1659
|
+
* Returns the token `response` of the redeemed device code with row `id`, with the key that its
|
|
1660
|
+
* approval signed as `account` and `key_authorization`. Only the one redemption that succeeds
|
|
1661
|
+
* reaches here, so only it takes the key. The code is spent, so a failed take returns the tokens
|
|
1662
|
+
* alone.
|
|
1663
|
+
*/
|
|
1664
|
+
async function withDeviceKey(c: Context<Env>, options: withDeviceKey.Options) {
|
|
1665
|
+
const { id, response } = options
|
|
1666
|
+
const key = await Timing.measure(
|
|
1667
|
+
c,
|
|
1668
|
+
Device.take(db, { id }),
|
|
1669
|
+
'Device.take',
|
|
1670
|
+
).catch((error: unknown) => {
|
|
1671
|
+
Log.error(c, {
|
|
1672
|
+
error,
|
|
1673
|
+
message: 'A device code redemption failed to take the held key.',
|
|
1674
|
+
})
|
|
1675
|
+
return undefined
|
|
1676
|
+
})
|
|
1677
|
+
if (!key) return response
|
|
1678
|
+
|
|
1679
|
+
const body = (await Timing.measure(
|
|
1680
|
+
c,
|
|
1681
|
+
response.json(),
|
|
1682
|
+
'response.json',
|
|
1683
|
+
)) as Record<string, unknown>
|
|
1684
|
+
const headers = new Headers(response.headers)
|
|
1685
|
+
headers.delete('content-length')
|
|
1686
|
+
return new Response(
|
|
1687
|
+
JSON.stringify({
|
|
1688
|
+
...body,
|
|
1689
|
+
account: key.account,
|
|
1690
|
+
key_authorization: key.keyAuthorization,
|
|
1691
|
+
}),
|
|
1692
|
+
{ headers, status: response.status },
|
|
1693
|
+
)
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
declare namespace withDeviceKey {
|
|
1697
|
+
type Options = {
|
|
1698
|
+
/** The `device_code` row ID of the redeemed code. */
|
|
1699
|
+
id: string
|
|
1700
|
+
/** The issuer's token response. */
|
|
1701
|
+
response: Response
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
/**
|
|
1706
|
+
* Returns the key of the device authorization request `form` without its challenge bound, as the
|
|
1707
|
+
* device page stores it and binds the challenge when it reads it.
|
|
1708
|
+
*/
|
|
1709
|
+
function unbound(form: URLSearchParams) {
|
|
1710
|
+
const query = new URLSearchParams(form)
|
|
1711
|
+
query.delete('challenge')
|
|
1712
|
+
const key = Authorize.fromQuery(query)
|
|
1713
|
+
// The form's key already passed the same parse.
|
|
1714
|
+
return key instanceof ErrorResponse.ResponseError ? undefined : key
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
/**
|
|
1718
|
+
* Returns the request with Tempo ID's origin as its `resource` when an authorization, a device
|
|
1719
|
+
* authorization, or a token request names none. Better Auth issues a JWT access token only for a `resource`, and every API
|
|
1720
|
+
* verifies the one audience. An authorization request with a key also asks for consent, and names
|
|
1721
|
+
* the key's expiry, as `Authorize.prepare` describes.
|
|
1722
|
+
*/
|
|
1723
|
+
async function withResource(c: Context<Env>) {
|
|
1724
|
+
const request = c.req.raw
|
|
1725
|
+
|
|
1726
|
+
if (c.req.path === '/oauth2/authorize' && c.req.method === 'GET') {
|
|
1727
|
+
const url = new URL(request.url)
|
|
1728
|
+
if (!url.searchParams.has('resource'))
|
|
1729
|
+
url.searchParams.set('resource', env.URL)
|
|
1730
|
+
url.search = Authorize.prepare(url.searchParams).toString()
|
|
1731
|
+
return new Request(url, request)
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
// The `POST` form of an authorization request carries its parameters in the body, which Better
|
|
1735
|
+
// Auth accepts only as a form. A key in that body gains the same consent and expiry. Better Auth
|
|
1736
|
+
// accepts any media type that contains the form's, in any case, so each one is read here as a
|
|
1737
|
+
// form and forwarded as exactly one.
|
|
1738
|
+
if (
|
|
1739
|
+
c.req.path === '/oauth2/authorize' &&
|
|
1740
|
+
c.req.method === 'POST' &&
|
|
1741
|
+
c.req
|
|
1742
|
+
.header('content-type')
|
|
1743
|
+
?.split(';')[0]
|
|
1744
|
+
?.toLowerCase()
|
|
1745
|
+
.includes('application/x-www-form-urlencoded')
|
|
1746
|
+
) {
|
|
1747
|
+
const form = new URLSearchParams(
|
|
1748
|
+
await Timing.measure(c, c.req.text(), 'request.text'),
|
|
1749
|
+
)
|
|
1750
|
+
if (!form.has('resource')) form.set('resource', env.URL)
|
|
1751
|
+
const headers = new Headers(request.headers)
|
|
1752
|
+
headers.delete('content-length')
|
|
1753
|
+
headers.set('content-type', 'application/x-www-form-urlencoded')
|
|
1754
|
+
return new Request(request.url, {
|
|
1755
|
+
body: Authorize.prepare(form),
|
|
1756
|
+
headers,
|
|
1757
|
+
method: 'POST',
|
|
1758
|
+
})
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
if (c.req.path === '/oauth2/token' && c.req.method === 'POST') {
|
|
1762
|
+
const form = new URLSearchParams(
|
|
1763
|
+
await Timing.measure(c, c.req.text(), 'request.text'),
|
|
1764
|
+
)
|
|
1765
|
+
if (!form.has('resource')) form.set('resource', env.URL)
|
|
1766
|
+
const headers = new Headers(request.headers)
|
|
1767
|
+
headers.delete('content-length')
|
|
1768
|
+
// The client authenticates, never a session. The issuer refuses a cookie without a trusted
|
|
1769
|
+
// `Origin`, such as a device's poll from a browser that holds Tempo ID's session.
|
|
1770
|
+
headers.delete('cookie')
|
|
1771
|
+
return new Request(request.url, { body: form, headers, method: 'POST' })
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
// A device authorization request is a form (RFC 8628 §3.1), whose resource every redemption must
|
|
1775
|
+
// match. A device code binds to the user who approves it, so the request cannot bind one with
|
|
1776
|
+
// `user_id`.
|
|
1777
|
+
if (c.req.path === '/device/code' && c.req.method === 'POST') {
|
|
1778
|
+
const form = new URLSearchParams(
|
|
1779
|
+
await Timing.measure(c, c.req.text(), 'request.text'),
|
|
1780
|
+
)
|
|
1781
|
+
if (!form.has('resource')) form.set('resource', env.URL)
|
|
1782
|
+
if (!form.has('scope')) form.set('scope', Device.scope)
|
|
1783
|
+
form.delete('user_id')
|
|
1784
|
+
// Tempo ID reads its own parameters, which the issuer does not take.
|
|
1785
|
+
for (const name of ['challenge', 'device', 'key_authorization'])
|
|
1786
|
+
form.delete(name)
|
|
1787
|
+
const headers = new Headers(request.headers)
|
|
1788
|
+
headers.delete('content-length')
|
|
1789
|
+
// As for `/oauth2/token`, the client authenticates, and a session's cookie plays no part.
|
|
1790
|
+
headers.delete('cookie')
|
|
1791
|
+
headers.set('content-type', 'application/x-www-form-urlencoded')
|
|
1792
|
+
return new Request(request.url, { body: form, headers, method: 'POST' })
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
return request
|
|
1796
|
+
}
|