@tempoxyz/id 0.0.0-stage → 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -2
- package/dist/App.d.ts +20 -0
- package/dist/App.d.ts.map +1 -0
- package/dist/App.js +26 -0
- package/dist/App.js.map +1 -0
- package/dist/app/client/.assetsignore +2 -0
- package/dist/app/client/assets/BRLA-CZvVDNYr.png +0 -0
- package/dist/app/client/assets/CHFAU-DCedc4aH.png +0 -0
- package/dist/app/client/assets/DLUSD-BnRXm3EI.png +0 -0
- package/dist/app/client/assets/PRIME-CiFp2-3e.svg +14 -0
- package/dist/app/client/assets/Pilat-Bold-C1t1eIrF.woff2 +0 -0
- package/dist/app/client/assets/Pilat-Book-CbS8ofT8.woff2 +0 -0
- package/dist/app/client/assets/Pilat-Demi-CADSbPAt.woff2 +0 -0
- package/dist/app/client/assets/Pilat-Regular-Bx8vzE7q.woff2 +0 -0
- package/dist/app/client/assets/USD1-COVcO_PB.svg +30 -0
- package/dist/app/client/assets/YLDS-CRh8Zw0g.svg +3 -0
- package/dist/app/client/assets/ccip-Cq1mMiVl.js +1 -0
- package/dist/app/client/assets/frxUSD-n9pTpinR.svg +1 -0
- package/dist/app/client/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/app/client/assets/index-B7YJVP00.css +1 -0
- package/dist/app/client/assets/index-UXTxXCLk.js +55 -0
- package/dist/app/client/assets/pathUSD-BvaO6ooS.svg +25 -0
- package/dist/app/client/assets/reset-co-CHrff.css +1 -0
- package/dist/app/client/assets/wYLDS-CaCKDrO-.svg +10 -0
- package/dist/app/db/auth.d.ts +3207 -0
- package/dist/app/db/auth.d.ts.map +1 -0
- package/dist/app/db/auth.js +330 -0
- package/dist/app/db/auth.js.map +1 -0
- package/dist/app/db/schema.d.ts +932 -0
- package/dist/app/db/schema.d.ts.map +1 -0
- package/dist/app/db/schema.js +171 -0
- package/dist/app/db/schema.js.map +1 -0
- package/dist/app/server/.vite/manifest.json +489 -0
- package/dist/app/server/assets/BRLA-CZvVDNYr.png +0 -0
- package/dist/app/server/assets/CHFAU-DCedc4aH.png +0 -0
- package/dist/app/server/assets/DLUSD-BnRXm3EI.png +0 -0
- package/dist/app/server/assets/PRIME-CiFp2-3e.svg +14 -0
- package/dist/app/server/assets/Pilat-Bold-C1t1eIrF.woff2 +0 -0
- package/dist/app/server/assets/Pilat-Book-CbS8ofT8.woff2 +0 -0
- package/dist/app/server/assets/Pilat-Demi-CADSbPAt.woff2 +0 -0
- package/dist/app/server/assets/Pilat-Regular-Bx8vzE7q.woff2 +0 -0
- package/dist/app/server/assets/USD1-COVcO_PB.svg +30 -0
- package/dist/app/server/assets/YLDS-CRh8Zw0g.svg +3 -0
- package/dist/app/server/assets/_tanstack-start-manifest_v-11GtFGrq.js +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/authorize-C95F-9E1-CbESJKGU.js +31 -0
- package/dist/app/server/assets/authorize-C95F-9E1-CbESJKGU.js.map +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/call-UQamaCKN.js +32 -0
- package/dist/app/server/assets/call-UQamaCKN.js.map +1 -0
- package/dist/app/server/assets/ccip-CbNKFO4n.js +2 -0
- package/dist/app/server/assets/ccip-CbNKFO4n.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-CS900nSP.css +1 -0
- package/dist/app/server/assets/cookie-Cce9CrIP.js +10 -0
- package/dist/app/server/assets/cookie-Cce9CrIP.js.map +1 -0
- package/dist/app/server/assets/core-B1HCXUmx.js +56 -0
- package/dist/app/server/assets/core-B1HCXUmx.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/default-u_dwuiYb-BJJ9PwCi.js +2 -0
- package/dist/app/server/assets/default-u_dwuiYb-BJJ9PwCi.js.map +1 -0
- package/dist/app/server/assets/dist-BxSwugSY.js +1 -0
- package/dist/app/server/assets/dist-CZGFhmQd.js +2 -0
- package/dist/app/server/assets/dist-CZGFhmQd.js.map +1 -0
- package/dist/app/server/assets/dist-Dg3PQqiW.js +15 -0
- package/dist/app/server/assets/dist-Dg3PQqiW.js.map +1 -0
- package/dist/app/server/assets/effect-QlVUlMFu-2tSPDT2b.js +2 -0
- package/dist/app/server/assets/effect-QlVUlMFu-2tSPDT2b.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/factory-ipHlJXZY.js +5 -0
- package/dist/app/server/assets/factory-ipHlJXZY.js.map +1 -0
- package/dist/app/server/assets/frxUSD-n9pTpinR.svg +1 -0
- package/dist/app/server/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/app/server/assets/get-id-field-B0x109VU.js +6 -0
- package/dist/app/server/assets/get-id-field-B0x109VU.js.map +1 -0
- package/dist/app/server/assets/index-CLddUTqr-xpSuLpBt.js +2 -0
- package/dist/app/server/assets/index-CLddUTqr-xpSuLpBt.js.map +1 -0
- package/dist/app/server/assets/index-DZEfthgZ-8Uq5s5Pt.js +2 -0
- package/dist/app/server/assets/index-DZEfthgZ-8Uq5s5Pt.js.map +1 -0
- package/dist/app/server/assets/kysely-adapter-MkHAI9Jk.js +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/reset-co-CHrff.css +1 -0
- package/dist/app/server/assets/rolldown-runtime-B0Z9INg1.js +1 -0
- package/dist/app/server/assets/router-CcbyBCI4.css +1 -0
- package/dist/app/server/assets/router-xjZtEeaI.js +7 -0
- package/dist/app/server/assets/router-xjZtEeaI.js.map +1 -0
- package/dist/app/server/assets/schema-check-Cf7QOZZD.js +9 -0
- package/dist/app/server/assets/schema-check-Cf7QOZZD.js.map +1 -0
- package/dist/app/server/assets/schemas-C2lCYmR5.js +2 -0
- package/dist/app/server/assets/schemas-C2lCYmR5.js.map +1 -0
- package/dist/app/server/assets/secp256k1-buM5g4AL.js +2 -0
- package/dist/app/server/assets/secp256k1-buM5g4AL.js.map +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-Cg1N07hM.js +2 -0
- package/dist/app/server/assets/standard-json-Cg1N07hM.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/sury-CWZTCd75-xt1mWc9f.js +2 -0
- package/dist/app/server/assets/sury-CWZTCd75-xt1mWc9f.js.map +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/utils-C4ZXZIZW.js +2 -0
- package/dist/app/server/assets/utils-C4ZXZIZW.js.map +1 -0
- package/dist/app/server/assets/valibot--1zFm7rT-NSoY2xTp.js +2 -0
- package/dist/app/server/assets/valibot--1zFm7rT-NSoY2xTp.js.map +1 -0
- package/dist/app/server/assets/valibot-D_HTw1Gn-CJwWeOUE.js +2 -0
- package/dist/app/server/assets/valibot-D_HTw1Gn-CJwWeOUE.js.map +1 -0
- package/dist/app/server/assets/wYLDS-CaCKDrO-.svg +10 -0
- package/dist/app/server/assets/zod-Bwrt9trS-CV38yPnQ.js +2 -0
- package/dist/app/server/assets/zod-Bwrt9trS-CV38yPnQ.js.map +1 -0
- package/dist/app/server/assets/zod-DSgpEGAE-BvlnBE2L.js +2 -0
- package/dist/app/server/assets/zod-DSgpEGAE-BvlnBE2L.js.map +1 -0
- package/dist/app/server/index.js +574 -0
- package/dist/app/server/index.js.map +1 -0
- package/dist/app/server/wrangler.json +1 -0
- package/dist/cli/Admin.d.ts +38 -0
- package/dist/cli/Admin.d.ts.map +1 -0
- package/dist/cli/Admin.js +62 -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 +36 -0
- package/dist/cli/Apps.js.map +1 -0
- package/dist/cli/Cli.d.ts +14 -0
- package/dist/cli/Cli.d.ts.map +1 -0
- package/dist/cli/Cli.js +27 -0
- package/dist/cli/Cli.js.map +1 -0
- package/dist/cli/Project.d.ts +20 -0
- package/dist/cli/Project.d.ts.map +1 -0
- package/dist/cli/Project.js +88 -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 +49 -0
- package/dist/cli/Realms.js.map +1 -0
- package/dist/cli/Session.d.ts +85 -0
- package/dist/cli/Session.d.ts.map +1 -0
- package/dist/cli/Session.js +178 -0
- package/dist/cli/Session.js.map +1 -0
- package/dist/cli/User.d.ts +29 -0
- package/dist/cli/User.d.ts.map +1 -0
- package/dist/cli/User.js +39 -0
- package/dist/cli/User.js.map +1 -0
- package/dist/cli/V1.d.ts +35 -0
- package/dist/cli/V1.d.ts.map +1 -0
- package/dist/cli/V1.js +41 -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 +89 -0
- package/dist/cli/commands/admin/apps/create.js.map +1 -0
- package/dist/cli/commands/admin/apps/delete.d.ts +14 -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 +9 -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 +16 -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 +76 -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 +9 -0
- package/dist/cli/commands/admin/migrate.d.ts.map +1 -0
- package/dist/cli/commands/admin/migrate.js +17 -0
- package/dist/cli/commands/admin/migrate.js.map +1 -0
- package/dist/cli/commands/admin/realms/add.d.ts +12 -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 +13 -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 +16 -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 +18 -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 +11 -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 +15 -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 +15 -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 +16 -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 +15 -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 +101 -0
- package/dist/client/Actions.d.ts.map +1 -0
- package/dist/client/Actions.js +90 -0
- package/dist/client/Actions.js.map +1 -0
- package/dist/client/Auth.d.ts +161 -0
- package/dist/client/Auth.d.ts.map +1 -0
- package/dist/client/Auth.js +113 -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 +170 -0
- package/dist/client/Decorator.d.ts.map +1 -0
- package/dist/client/Decorator.js +227 -0
- package/dist/client/Decorator.js.map +1 -0
- package/dist/client/Key.d.ts +85 -0
- package/dist/client/Key.d.ts.map +1 -0
- package/dist/client/Key.js +83 -0
- package/dist/client/Key.js.map +1 -0
- package/dist/client/Pending.d.ts +39 -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 +34 -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/Provider.d.ts +16 -0
- package/dist/client/Provider.d.ts.map +1 -0
- package/dist/client/Provider.js +12 -0
- package/dist/client/Provider.js.map +1 -0
- package/dist/client/Step.d.ts +21 -0
- package/dist/client/Step.d.ts.map +1 -0
- package/dist/client/Step.js +16 -0
- package/dist/client/Step.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 +40 -0
- package/dist/client/TempoAccount.d.ts.map +1 -0
- package/dist/client/TempoAccount.js +16 -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 +25 -0
- package/dist/client/actions/account.d.ts.map +1 -0
- package/dist/client/actions/account.js +34 -0
- package/dist/client/actions/account.js.map +1 -0
- package/dist/client/actions/device.d.ts +53 -0
- package/dist/client/actions/device.d.ts.map +1 -0
- package/dist/client/actions/device.js +79 -0
- package/dist/client/actions/device.js.map +1 -0
- package/dist/client/actions/email.d.ts +84 -0
- package/dist/client/actions/email.d.ts.map +1 -0
- package/dist/client/actions/email.js +63 -0
- package/dist/client/actions/email.js.map +1 -0
- package/dist/client/actions/oauth.d.ts +115 -0
- package/dist/client/actions/oauth.d.ts.map +1 -0
- package/dist/client/actions/oauth.js +105 -0
- package/dist/client/actions/oauth.js.map +1 -0
- package/dist/client/actions/passkey.d.ts +88 -0
- package/dist/client/actions/passkey.d.ts.map +1 -0
- package/dist/client/actions/passkey.js +60 -0
- package/dist/client/actions/passkey.js.map +1 -0
- package/dist/client/actions/provider.d.ts +49 -0
- package/dist/client/actions/provider.d.ts.map +1 -0
- package/dist/client/actions/provider.js +34 -0
- package/dist/client/actions/provider.js.map +1 -0
- package/dist/client/actions/realm.d.ts +24 -0
- package/dist/client/actions/realm.d.ts.map +1 -0
- package/dist/client/actions/realm.js +15 -0
- package/dist/client/actions/realm.js.map +1 -0
- package/dist/client/actions/redirect.d.ts +153 -0
- package/dist/client/actions/redirect.d.ts.map +1 -0
- package/dist/client/actions/redirect.js +236 -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 +14 -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 +106 -0
- package/dist/client/internal/api.d.ts.map +1 -0
- package/dist/client/internal/api.js +129 -0
- package/dist/client/internal/api.js.map +1 -0
- package/dist/client/internal/headless.d.ts +33 -0
- package/dist/client/internal/headless.d.ts.map +1 -0
- package/dist/client/internal/headless.js +46 -0
- package/dist/client/internal/headless.js.map +1 -0
- package/dist/client/internal/indexedDb.d.ts +9 -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 +58 -0
- package/dist/client/internal/request.d.ts.map +1 -0
- package/dist/client/internal/request.js +45 -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.d.ts +20 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +20 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/Apps.d.ts +3051 -0
- package/dist/internal/Apps.d.ts.map +1 -0
- package/dist/internal/Apps.js +472 -0
- package/dist/internal/Apps.js.map +1 -0
- package/dist/internal/Auth.d.ts +4058 -0
- package/dist/internal/Auth.d.ts.map +1 -0
- package/dist/internal/Auth.js +137 -0
- package/dist/internal/Auth.js.map +1 -0
- package/dist/internal/Authorize.d.ts +532 -0
- package/dist/internal/Authorize.d.ts.map +1 -0
- package/dist/internal/Authorize.js +326 -0
- package/dist/internal/Authorize.js.map +1 -0
- package/dist/internal/Device.d.ts +371 -0
- package/dist/internal/Device.d.ts.map +1 -0
- package/dist/internal/Device.js +271 -0
- package/dist/internal/Device.js.map +1 -0
- package/dist/internal/Env.d.ts +57 -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 +609 -0
- package/dist/internal/ErrorResponse.d.ts.map +1 -0
- package/dist/internal/ErrorResponse.js +679 -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 +1456 -0
- package/dist/internal/KeyAuthorization.d.ts.map +1 -0
- package/dist/internal/KeyAuthorization.js +1030 -0
- package/dist/internal/KeyAuthorization.js.map +1 -0
- package/dist/internal/Limits.d.ts +65 -0
- package/dist/internal/Limits.d.ts.map +1 -0
- package/dist/internal/Limits.js +35 -0
- package/dist/internal/Limits.js.map +1 -0
- package/dist/internal/Log.d.ts +49 -0
- package/dist/internal/Log.d.ts.map +1 -0
- package/dist/internal/Log.js +101 -0
- package/dist/internal/Log.js.map +1 -0
- package/dist/internal/LogLine.d.ts +33 -0
- package/dist/internal/LogLine.d.ts.map +1 -0
- package/dist/internal/LogLine.js +56 -0
- package/dist/internal/LogLine.js.map +1 -0
- package/dist/internal/Migrations.d.ts +19 -0
- package/dist/internal/Migrations.d.ts.map +1 -0
- package/dist/internal/Migrations.js +57 -0
- package/dist/internal/Migrations.js.map +1 -0
- package/dist/internal/Owner.d.ts +146 -0
- package/dist/internal/Owner.d.ts.map +1 -0
- package/dist/internal/Owner.js +65 -0
- package/dist/internal/Owner.js.map +1 -0
- package/dist/internal/Passkey.d.ts +528 -0
- package/dist/internal/Passkey.d.ts.map +1 -0
- package/dist/internal/Passkey.js +658 -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 +105 -0
- package/dist/internal/Privy.d.ts.map +1 -0
- package/dist/internal/Privy.js +224 -0
- package/dist/internal/Privy.js.map +1 -0
- package/dist/internal/RateLimit.d.ts +190 -0
- package/dist/internal/RateLimit.d.ts.map +1 -0
- package/dist/internal/RateLimit.js +148 -0
- package/dist/internal/RateLimit.js.map +1 -0
- package/dist/internal/Realm.d.ts +281 -0
- package/dist/internal/Realm.d.ts.map +1 -0
- package/dist/internal/Realm.js +477 -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 +115 -0
- package/dist/internal/SigningKey.d.ts.map +1 -0
- package/dist/internal/SigningKey.js +197 -0
- package/dist/internal/SigningKey.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 +51 -0
- package/dist/internal/Timing.d.ts.map +1 -0
- package/dist/internal/Timing.js +103 -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/Wallet.d.ts +37 -0
- package/dist/internal/Wallet.d.ts.map +1 -0
- package/dist/internal/Wallet.js +43 -0
- package/dist/internal/Wallet.js.map +1 -0
- package/dist/worker.d.ts +12 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +12 -0
- package/dist/worker.js.map +1 -0
- package/package.json +79 -3
- package/src/Api.ts +151 -0
- package/src/App.ts +56 -0
- package/src/api/account.ts +311 -0
- package/src/api/authenticate.ts +1290 -0
- package/src/api/authorize.ts +1030 -0
- package/src/api/device.ts +874 -0
- package/src/api/health.ts +72 -0
- package/src/api/lib/AccountStep.ts +335 -0
- package/src/api/lib/Auth.ts +134 -0
- package/src/api/lib/Consent.ts +339 -0
- package/src/api/lib/Cors.ts +107 -0
- package/src/api/lib/Database.ts +10 -0
- package/src/api/lib/DeviceRequest.ts +265 -0
- package/src/api/lib/FirstAccount.ts +90 -0
- package/src/api/lib/KeySigner.ts +393 -0
- package/src/api/lib/PasskeyLink.ts +162 -0
- package/src/api/lib/Session.ts +34 -0
- package/src/api/lib/SignInConsent.ts +316 -0
- package/src/api/oauth.ts +1282 -0
- package/src/api/provider.ts +441 -0
- package/src/app/components/Button.tsx +128 -0
- package/src/app/components/CodeInput.tsx +186 -0
- package/src/app/components/ErrorMessage.tsx +66 -0
- package/src/app/components/Grant.tsx +323 -0
- package/src/app/components/Input.tsx +48 -0
- package/src/app/components/Tooltip.tsx +45 -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/crons/cleanup.ts +42 -0
- package/src/app/db/auth.ts +416 -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/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/_journal.json +83 -0
- package/src/app/db/schema.ts +218 -0
- package/src/app/env.ts +49 -0
- package/src/app/lib/Client.ts +82 -0
- package/src/app/lib/Loader.ts +24 -0
- package/src/app/package.json +8 -0
- package/src/app/routeTree.gen.ts +198 -0
- package/src/app/router.tsx +48 -0
- package/src/app/routes/[_].flows.tsx +3001 -0
- package/src/app/routes/__root.tsx +91 -0
- package/src/app/routes/authorize.tsx +197 -0
- package/src/app/routes/choose-account.tsx +111 -0
- package/src/app/routes/deposit.tsx +105 -0
- package/src/app/routes/device.tsx +227 -0
- package/src/app/routes/index.tsx +115 -0
- package/src/app/routes/login.tsx +506 -0
- package/src/app/screens/Account.tsx +347 -0
- package/src/app/screens/Approval.tsx +276 -0
- package/src/app/screens/ChooseAccount.tsx +240 -0
- package/src/app/screens/Deposit.tsx +762 -0
- package/src/app/screens/DeviceApproval.tsx +480 -0
- package/src/app/screens/SignIn.tsx +362 -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 +204 -0
- package/src/app/vite.config.ts +95 -0
- package/src/app/void.config.ts +29 -0
- package/src/app/zyzz.config.ts +274 -0
- package/src/cli/Admin.ts +73 -0
- package/src/cli/Apps.ts +29 -0
- package/src/cli/Cli.ts +42 -0
- package/src/cli/Project.ts +117 -0
- package/src/cli/Realms.ts +50 -0
- package/src/cli/Session.ts +239 -0
- package/src/cli/User.ts +45 -0
- package/src/cli/V1.ts +57 -0
- package/src/cli/bin.ts +44 -0
- package/src/cli/commands/admin/apps/create.ts +101 -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 +86 -0
- package/src/cli/commands/admin/index.ts +14 -0
- package/src/cli/commands/admin/migrate.ts +16 -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 +168 -0
- package/src/client/Auth.ts +285 -0
- package/src/client/Authorization.ts +34 -0
- package/src/client/Decorator.ts +410 -0
- package/src/client/Key.ts +140 -0
- package/src/client/Pending.ts +46 -0
- package/src/client/Permissions.ts +39 -0
- package/src/client/Provider.ts +18 -0
- package/src/client/Step.ts +24 -0
- package/src/client/Storage.ts +29 -0
- package/src/client/TempoAccount.ts +42 -0
- package/src/client/Tokens.ts +27 -0
- package/src/client/User.ts +23 -0
- package/src/client/actions/account.ts +57 -0
- package/src/client/actions/device.ts +151 -0
- package/src/client/actions/email.ts +141 -0
- package/src/client/actions/oauth.ts +182 -0
- package/src/client/actions/passkey.ts +141 -0
- package/src/client/actions/provider.ts +70 -0
- package/src/client/actions/realm.ts +36 -0
- package/src/client/actions/redirect.ts +408 -0
- package/src/client/internal/accessKey.ts +54 -0
- package/src/client/internal/account.ts +15 -0
- package/src/client/internal/api.ts +224 -0
- package/src/client/internal/headless.ts +77 -0
- package/src/client/internal/indexedDb.ts +44 -0
- package/src/client/internal/request.ts +71 -0
- package/src/client/internal/store.ts +20 -0
- package/src/client.ts +19 -0
- package/src/index.ts +6 -0
- package/src/internal/Apps.ts +718 -0
- package/src/internal/Auth.ts +203 -0
- package/src/internal/Authorize.ts +424 -0
- package/src/internal/Balance.ts +168 -0
- package/src/internal/BodyLimit.ts +36 -0
- package/src/internal/Chain.ts +16 -0
- package/src/internal/Device.ts +469 -0
- package/src/internal/Env.ts +58 -0
- package/src/internal/ErrorResponse.ts +854 -0
- package/src/internal/Id.ts +41 -0
- package/src/internal/KeyAuthorization.ts +1339 -0
- package/src/internal/Limits.ts +39 -0
- package/src/internal/Log.ts +178 -0
- package/src/internal/LogLine.ts +87 -0
- package/src/internal/Logout.ts +336 -0
- package/src/internal/Migrations.ts +86 -0
- package/src/internal/OpenApi.ts +300 -0
- package/src/internal/Owner.ts +211 -0
- package/src/internal/Passkey.ts +1052 -0
- package/src/internal/PendingKey.ts +104 -0
- package/src/internal/Privy.ts +402 -0
- package/src/internal/Provider.ts +125 -0
- package/src/internal/RateLimit.ts +259 -0
- package/src/internal/Realm.ts +768 -0
- package/src/internal/RequestId.ts +118 -0
- package/src/internal/SignInGrant.ts +168 -0
- package/src/internal/SigningKey.ts +286 -0
- package/src/internal/TempoAccount.ts +342 -0
- package/src/internal/TestInbox.ts +51 -0
- package/src/internal/Timing.ts +158 -0
- package/src/internal/Validation.ts +52 -0
- package/src/internal/Wallet.ts +62 -0
- package/src/internal/owners/Passkey.ts +72 -0
- package/src/internal/owners/Privy.ts +81 -0
- package/src/internal/owners/Secp256k1.ts +143 -0
- package/src/types/app.d.ts +19 -0
- package/src/worker.ts +11 -0
|
@@ -0,0 +1,1030 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authorizations under `/v1/authorize`: an app's headless authorization, with the key authorization
|
|
3
|
+
* that the account's owner signs, and the requests of the pages that an app's authorization passes
|
|
4
|
+
* through, the account step's selection and the approval page's decision. Better Auth issues every
|
|
5
|
+
* code and token, in process.
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { verifyOAuthQueryParams } from '@better-auth/oauth-provider'
|
|
9
|
+
import { isAPIError } from 'better-auth/api'
|
|
10
|
+
import { Hono } from 'hono'
|
|
11
|
+
import { Base64, Bytes, Hash } from 'ox'
|
|
12
|
+
import { env } from 'void/env'
|
|
13
|
+
import * as z from 'zod'
|
|
14
|
+
import * as Apps from '../internal/Apps.js'
|
|
15
|
+
import * as core_Auth from '../internal/Auth.js'
|
|
16
|
+
import * as Authorize from '../internal/Authorize.js'
|
|
17
|
+
import * as Balance from '../internal/Balance.js'
|
|
18
|
+
import * as BodyLimit from '../internal/BodyLimit.js'
|
|
19
|
+
import type { Env } from '../internal/Env.js'
|
|
20
|
+
import * as ErrorResponse from '../internal/ErrorResponse.js'
|
|
21
|
+
import * as KeyAuthorization from '../internal/KeyAuthorization.js'
|
|
22
|
+
import * as Log from '../internal/Log.js'
|
|
23
|
+
import * as OpenApi from '../internal/OpenApi.js'
|
|
24
|
+
import * as Passkey from '../internal/Passkey.js'
|
|
25
|
+
import * as RateLimit from '../internal/RateLimit.js'
|
|
26
|
+
import * as Realm from '../internal/Realm.js'
|
|
27
|
+
import * as TempoAccount from '../internal/TempoAccount.js'
|
|
28
|
+
import * as Timing from '../internal/Timing.js'
|
|
29
|
+
import * as Validation from '../internal/Validation.js'
|
|
30
|
+
import * as AccountStep from './lib/AccountStep.js'
|
|
31
|
+
import * as Consent from './lib/Consent.js'
|
|
32
|
+
import * as Cors from './lib/Cors.js'
|
|
33
|
+
import { db } from './lib/Database.js'
|
|
34
|
+
import * as KeySigner from './lib/KeySigner.js'
|
|
35
|
+
import * as Session from './lib/Session.js'
|
|
36
|
+
import * as SignInConsent from './lib/SignInConsent.js'
|
|
37
|
+
|
|
38
|
+
namespace schema {
|
|
39
|
+
export namespace authorize {
|
|
40
|
+
export const json = Authorize.schema.request
|
|
41
|
+
|
|
42
|
+
export const response = Authorize.schema.response
|
|
43
|
+
|
|
44
|
+
/** Better Auth's authorization result, which names the redirect with the code. */
|
|
45
|
+
export const redirect = z.object({ url: z.string().min(1) })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export namespace account {
|
|
49
|
+
export const json = z.object({
|
|
50
|
+
account: TempoAccount.schema.tempoAccount.shape.address.meta({
|
|
51
|
+
description:
|
|
52
|
+
"The address of the account that the user selects, one of the user's accounts in the app's realms.",
|
|
53
|
+
examples: ['0x8C3b5d0e2A4f6B1c9D7e3F5a0B2c4D6e8F1a3B5c'],
|
|
54
|
+
}),
|
|
55
|
+
oauth_query: z
|
|
56
|
+
.string()
|
|
57
|
+
.min(1)
|
|
58
|
+
.max(8192)
|
|
59
|
+
.meta({
|
|
60
|
+
description:
|
|
61
|
+
"The account step's query string, which Better Auth signs when it sends the browser there. At most 8,192 characters.",
|
|
62
|
+
examples: [
|
|
63
|
+
'client_id=Qk3vT9xZbLmN2pRw8YcHjF6sDaE4uGtK&response_type=code&exp=1760000000&sig=Gx3kB0',
|
|
64
|
+
],
|
|
65
|
+
}),
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
export const response = z.object({
|
|
69
|
+
redirect: z.string().meta({
|
|
70
|
+
description:
|
|
71
|
+
"Where the browser continues: the approval page's path on Tempo ID, or the app's `redirect_uri` with an authorization code and `state`.",
|
|
72
|
+
examples: [
|
|
73
|
+
'https://app.example.com/callback?code=Vb7mQ2xLkP9sTzR4&state=af0ifjsldkj',
|
|
74
|
+
],
|
|
75
|
+
}),
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
/** Better Auth's result, which names the redirect: a path on Tempo ID, or the app's URL. */
|
|
79
|
+
export const result = z.object({ url: z.string().min(1) })
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export namespace consent {
|
|
83
|
+
export const json = z.object({
|
|
84
|
+
accept: z.boolean().meta({
|
|
85
|
+
description:
|
|
86
|
+
'Whether the user approves the request. `false` denies it, and the app receives `access_denied`.',
|
|
87
|
+
examples: [true],
|
|
88
|
+
}),
|
|
89
|
+
account: TempoAccount.schema.tempoAccount.shape.address.optional().meta({
|
|
90
|
+
description:
|
|
91
|
+
"The Tempo Account that the approval page showed for the request's key authorization, one of the user's accounts in the app's realms. Ignored when the key names its account, and absent for a request without a key.",
|
|
92
|
+
examples: ['0x8C3b5d0e2A4f6B1c9D7e3F5a0B2c4D6e8F1a3B5c'],
|
|
93
|
+
}),
|
|
94
|
+
oauth_query: z
|
|
95
|
+
.string()
|
|
96
|
+
.min(1)
|
|
97
|
+
.max(8192)
|
|
98
|
+
.meta({
|
|
99
|
+
description:
|
|
100
|
+
"The approval page's query string, which Better Auth signs when it sends the browser there. At most 8,192 characters.",
|
|
101
|
+
examples: [
|
|
102
|
+
'client_id=Qk3vT9xZbLmN2pRw8YcHjF6sDaE4uGtK&response_type=code&exp=1760000000&sig=Gx3kB0',
|
|
103
|
+
],
|
|
104
|
+
}),
|
|
105
|
+
passkey: z
|
|
106
|
+
.object({ assertion: Passkey.schema.assertion })
|
|
107
|
+
.optional()
|
|
108
|
+
.meta({
|
|
109
|
+
description:
|
|
110
|
+
"The passkey's assertion over the options of the passkey step, which a passkey-owned account's approval returns. The repeated approval signs the key with it.",
|
|
111
|
+
}),
|
|
112
|
+
session_id: z
|
|
113
|
+
.string()
|
|
114
|
+
.min(1)
|
|
115
|
+
.max(256)
|
|
116
|
+
.meta({
|
|
117
|
+
description:
|
|
118
|
+
'The ID of the session that the approval page showed. The decision applies only while that session is the signed-in one.',
|
|
119
|
+
examples: ['8nVq3kZ0bT5wXy2mLp7sR4dA'],
|
|
120
|
+
}),
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
export const step = Passkey.schema.step
|
|
124
|
+
|
|
125
|
+
export const response = z.union([
|
|
126
|
+
z
|
|
127
|
+
.object({
|
|
128
|
+
redirect: z.url().meta({
|
|
129
|
+
description:
|
|
130
|
+
"The app's `redirect_uri`, with an authorization code and `state`, or with `error=access_denied` when the user denied the request.",
|
|
131
|
+
examples: [
|
|
132
|
+
'https://app.example.com/callback?code=Vb7mQ2xLkP9sTzR4&state=af0ifjsldkj',
|
|
133
|
+
],
|
|
134
|
+
}),
|
|
135
|
+
})
|
|
136
|
+
.meta({ description: 'A decision that returns to the app.' }),
|
|
137
|
+
step,
|
|
138
|
+
])
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export namespace request {
|
|
142
|
+
export const query = z.object({
|
|
143
|
+
oauth_query: z
|
|
144
|
+
.string()
|
|
145
|
+
.min(1)
|
|
146
|
+
.max(8192)
|
|
147
|
+
.meta({
|
|
148
|
+
description:
|
|
149
|
+
"The approval page's query string, which Better Auth signs when it sends the browser there. At most 8,192 characters.",
|
|
150
|
+
examples: [
|
|
151
|
+
'client_id=Qk3vT9xZbLmN2pRw8YcHjF6sDaE4uGtK&response_type=code&exp=1760000000&sig=Gx3kB0',
|
|
152
|
+
],
|
|
153
|
+
}),
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
export const response = z.union([
|
|
157
|
+
z
|
|
158
|
+
.object({
|
|
159
|
+
redirect: z.url().meta({
|
|
160
|
+
description:
|
|
161
|
+
"The app's `redirect_uri`, with the refusal of the request's key as `error` and `error_description`, `state`, and `iss`.",
|
|
162
|
+
examples: [
|
|
163
|
+
'https://app.example.com/callback?error=invalid_request&state=af0ifjsldkj',
|
|
164
|
+
],
|
|
165
|
+
}),
|
|
166
|
+
})
|
|
167
|
+
.meta({
|
|
168
|
+
description:
|
|
169
|
+
'A key that Tempo ID refuses, which returns the browser to the app.',
|
|
170
|
+
}),
|
|
171
|
+
z
|
|
172
|
+
.object({
|
|
173
|
+
request: z
|
|
174
|
+
.object({
|
|
175
|
+
app: z.string().meta({
|
|
176
|
+
description:
|
|
177
|
+
"The app's display name, or the host of its redirect URI without one.",
|
|
178
|
+
examples: ['Tempo Cash'],
|
|
179
|
+
}),
|
|
180
|
+
destination: z.string().meta({
|
|
181
|
+
description:
|
|
182
|
+
"The host of the app's redirect URI, or its scheme for a native app.",
|
|
183
|
+
examples: ['cash.tempo.xyz'],
|
|
184
|
+
}),
|
|
185
|
+
email: z.email().meta({
|
|
186
|
+
description: "The signed-in user's email address.",
|
|
187
|
+
examples: ['ada@example.com'],
|
|
188
|
+
}),
|
|
189
|
+
key: KeyAuthorization.schema.description
|
|
190
|
+
.extend({
|
|
191
|
+
challenge: z
|
|
192
|
+
.string()
|
|
193
|
+
.optional()
|
|
194
|
+
.meta({
|
|
195
|
+
description:
|
|
196
|
+
"The message that the key's witness binds, such as a sign-in challenge from the app. Absent when the request carries none.",
|
|
197
|
+
examples: [
|
|
198
|
+
'Sign in to app.example.com\nNonce: 7f3a9c2e5b1d4f60',
|
|
199
|
+
],
|
|
200
|
+
}),
|
|
201
|
+
// A key that names a foreign account returns to the app instead.
|
|
202
|
+
refusal: KeySigner.schema.refusal
|
|
203
|
+
.exclude(['foreign_account'])
|
|
204
|
+
.optional()
|
|
205
|
+
.meta({
|
|
206
|
+
description:
|
|
207
|
+
'Why approval cannot sign the key: the user holds no account and none can be created (`no_account`), a passkey that this page cannot use owns the account (`passkey`), or the accounts could not be read (`unavailable`). Absent when approval can sign it.',
|
|
208
|
+
examples: ['passkey'],
|
|
209
|
+
}),
|
|
210
|
+
})
|
|
211
|
+
.optional()
|
|
212
|
+
.meta({
|
|
213
|
+
description:
|
|
214
|
+
'What the key that the request carries may do, and the account that approval signs for. Absent for a request without a key.',
|
|
215
|
+
}),
|
|
216
|
+
session: z.string().meta({
|
|
217
|
+
description:
|
|
218
|
+
'The ID of the signed-in session, which `POST /v1/authorize/consent` names.',
|
|
219
|
+
examples: ['8nVq3kZ0bT5wXy2mLp7sR4dA'],
|
|
220
|
+
}),
|
|
221
|
+
})
|
|
222
|
+
.nullable()
|
|
223
|
+
.meta({
|
|
224
|
+
description:
|
|
225
|
+
'The request that the approval page shows, or `null` for a forged or expired query, or a request without a known app.',
|
|
226
|
+
}),
|
|
227
|
+
})
|
|
228
|
+
.meta({ description: 'The request that the approval page shows.' }),
|
|
229
|
+
])
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export namespace accounts {
|
|
233
|
+
export const query = request.query
|
|
234
|
+
|
|
235
|
+
export const response = z.object({
|
|
236
|
+
request: z
|
|
237
|
+
.object({
|
|
238
|
+
accounts: z
|
|
239
|
+
.array(
|
|
240
|
+
z.object({
|
|
241
|
+
address: TempoAccount.schema.tempoAccount.shape.address.meta({
|
|
242
|
+
description: 'The account address.',
|
|
243
|
+
examples: ['0x8C3b5d0e2A4f6B1c9D7e3F5a0B2c4D6e8F1a3B5c'],
|
|
244
|
+
}),
|
|
245
|
+
balance: Balance.schema.balance.nullable().meta({
|
|
246
|
+
description:
|
|
247
|
+
"The account's holdings of USD tokens on the chain of the request's key, or of Tempo mainnet without one. `null` when the read fails or takes longer than 3 seconds.",
|
|
248
|
+
}),
|
|
249
|
+
owner:
|
|
250
|
+
TempoAccount.schema.tempoAccount.shape.config.shape.owners.unwrap()
|
|
251
|
+
.element.shape.type,
|
|
252
|
+
realm: z
|
|
253
|
+
.string()
|
|
254
|
+
.nullable()
|
|
255
|
+
.meta({
|
|
256
|
+
description:
|
|
257
|
+
"The name of the account's realm, or `null` for the `default` realm.",
|
|
258
|
+
examples: ['Tempo Cash'],
|
|
259
|
+
}),
|
|
260
|
+
}),
|
|
261
|
+
)
|
|
262
|
+
.readonly()
|
|
263
|
+
.meta({
|
|
264
|
+
description:
|
|
265
|
+
"The user's accounts in the app's realms, oldest first.",
|
|
266
|
+
}),
|
|
267
|
+
app: z.string().meta({
|
|
268
|
+
description: "The app's display name.",
|
|
269
|
+
examples: ['Tempo Cash'],
|
|
270
|
+
}),
|
|
271
|
+
})
|
|
272
|
+
.nullable()
|
|
273
|
+
.meta({
|
|
274
|
+
description:
|
|
275
|
+
'The account step, or `null` for a forged or expired query, or a request without a known app.',
|
|
276
|
+
}),
|
|
277
|
+
})
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** The authorization routes. */
|
|
282
|
+
export const authorize = new Hono<Env>()
|
|
283
|
+
.post(
|
|
284
|
+
'/v1/authorize',
|
|
285
|
+
OpenApi.describeRoute({
|
|
286
|
+
description:
|
|
287
|
+
"Authorizes the app for the signed-in user without a redirect. Only a first-party app's page on the redirect URI's origin calls it. A native app authorizes through `/oauth2/authorize` instead. With `code_challenge`, the app's backend receives a code. Without one, a public app receives tokens. A key authorization is signed by the account's owner, and must fit the headless ceiling. The account becomes the app's account for its later requests.",
|
|
288
|
+
responses: OpenApi.responses({
|
|
289
|
+
errors: [
|
|
290
|
+
...Authorize.check.errors,
|
|
291
|
+
...AccountStep.held.errors,
|
|
292
|
+
...BodyLimit.errors,
|
|
293
|
+
...OpenApi.validate.errors,
|
|
294
|
+
...RateLimit.check.errors,
|
|
295
|
+
core_Auth.SessionRequiredError,
|
|
296
|
+
ErrorResponse.CrossSiteRequestError,
|
|
297
|
+
KeyAuthorization.WebAuthnRequiredError,
|
|
298
|
+
TempoAccount.ForeignAccountError,
|
|
299
|
+
],
|
|
300
|
+
success: {
|
|
301
|
+
description: 'The authorization.',
|
|
302
|
+
schema: schema.authorize.response,
|
|
303
|
+
},
|
|
304
|
+
}),
|
|
305
|
+
security: [{ sessionCookie: [] }],
|
|
306
|
+
tags: ['Authorization'],
|
|
307
|
+
}),
|
|
308
|
+
OpenApi.validate('json', schema.authorize.json),
|
|
309
|
+
async (c) => {
|
|
310
|
+
const request = c.req.valid('json')
|
|
311
|
+
const clientId = request.client_id
|
|
312
|
+
|
|
313
|
+
const app = await Timing.measure(
|
|
314
|
+
c,
|
|
315
|
+
Apps.get(db, { clientId }).catch((error: unknown) => {
|
|
316
|
+
if (error instanceof Apps.NotFoundError) return undefined
|
|
317
|
+
throw error
|
|
318
|
+
}),
|
|
319
|
+
'Apps.get',
|
|
320
|
+
)
|
|
321
|
+
const refused = Authorize.check(request, {
|
|
322
|
+
app,
|
|
323
|
+
origin: c.req.header('origin'),
|
|
324
|
+
})
|
|
325
|
+
if (refused) return ErrorResponse.json(c, refused)
|
|
326
|
+
|
|
327
|
+
// Without a session the app shows its sign-in.
|
|
328
|
+
const session = await Timing.measure(c, Session.get(c), 'Session.get')
|
|
329
|
+
if (!session)
|
|
330
|
+
return ErrorResponse.json(c, new core_Auth.SessionRequiredError())
|
|
331
|
+
|
|
332
|
+
// A backend redeems its code with the app's secret, which a page never holds.
|
|
333
|
+
if (!request.code_challenge && app!.secret)
|
|
334
|
+
return ErrorResponse.json(
|
|
335
|
+
c,
|
|
336
|
+
new Authorize.RejectedError({
|
|
337
|
+
description:
|
|
338
|
+
'Tokens refused: the app holds a client secret, so its backend redeems a code. Send `code_challenge`, then redeem the code with the secret.',
|
|
339
|
+
}),
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
// Counted per user once the request can authorize, so no other caller spends the user's quota.
|
|
343
|
+
const limited = await Timing.measure(
|
|
344
|
+
c,
|
|
345
|
+
RateLimit.check(c, {
|
|
346
|
+
limits: [{ binding: 'AUTHORIZE_LIMIT', key: session.user.id }],
|
|
347
|
+
}),
|
|
348
|
+
'RateLimit.check',
|
|
349
|
+
)
|
|
350
|
+
if (limited) return limited
|
|
351
|
+
|
|
352
|
+
// A headless request creates no account, so it reads the accounts the user holds. Only
|
|
353
|
+
// `POST /v1/account` and an approval on the approval page create one. A key needs the owner
|
|
354
|
+
// of its account, to sign it.
|
|
355
|
+
const key = request.key_authorization
|
|
356
|
+
const signing = key
|
|
357
|
+
? await Timing.measure(
|
|
358
|
+
c,
|
|
359
|
+
KeySigner.resolve(c, {
|
|
360
|
+
account: key.account,
|
|
361
|
+
clientId,
|
|
362
|
+
cookie: c.req.header('cookie'),
|
|
363
|
+
sessionId: session.session.id,
|
|
364
|
+
userId: session.user.id,
|
|
365
|
+
}),
|
|
366
|
+
'KeySigner.resolve',
|
|
367
|
+
)
|
|
368
|
+
: undefined
|
|
369
|
+
if (signing instanceof ErrorResponse.ResponseError)
|
|
370
|
+
return ErrorResponse.json(c, signing)
|
|
371
|
+
|
|
372
|
+
// Without a key, the accounts only pick the selection, so a Privy outage skips it, as the
|
|
373
|
+
// account step does.
|
|
374
|
+
const account = await (async () => {
|
|
375
|
+
if (signing) return signing.account
|
|
376
|
+
const listed = await Timing.measure(
|
|
377
|
+
c,
|
|
378
|
+
AccountStep.accounts(c, { clientId, userId: session.user.id }),
|
|
379
|
+
'AccountStep.accounts',
|
|
380
|
+
).catch((error: unknown) => {
|
|
381
|
+
Log.warn(c, {
|
|
382
|
+
error,
|
|
383
|
+
fields: { clientId },
|
|
384
|
+
message:
|
|
385
|
+
'The headless authorization skipped an account selection whose accounts failed to load.',
|
|
386
|
+
})
|
|
387
|
+
return []
|
|
388
|
+
})
|
|
389
|
+
const selected = AccountStep.selected({
|
|
390
|
+
clientId,
|
|
391
|
+
cookie: c.req.header('cookie'),
|
|
392
|
+
sessionId: session.session.id,
|
|
393
|
+
})
|
|
394
|
+
return AccountStep.find(listed, selected)?.address ?? listed[0]?.address
|
|
395
|
+
})()
|
|
396
|
+
|
|
397
|
+
// The in-process authorization reads the account, so the account step passes. Only an account
|
|
398
|
+
// that the key names becomes the app's account for later requests.
|
|
399
|
+
const selection = account
|
|
400
|
+
? AccountStep.select({
|
|
401
|
+
account: account.toLowerCase(),
|
|
402
|
+
clientId,
|
|
403
|
+
cookie: c.req.header('cookie'),
|
|
404
|
+
sessionId: session.session.id,
|
|
405
|
+
})
|
|
406
|
+
: undefined
|
|
407
|
+
const headers = new Headers(c.req.raw.headers)
|
|
408
|
+
if (selection) headers.set('cookie', selection.header)
|
|
409
|
+
// The route read the accounts and chose one, so the account step reads them no second time.
|
|
410
|
+
c.set('accountChosen', true)
|
|
411
|
+
|
|
412
|
+
// Without the app's challenge, Tempo ID holds the verifier and redeems the code itself.
|
|
413
|
+
const verifier = request.code_challenge
|
|
414
|
+
? undefined
|
|
415
|
+
: Base64.fromBytes(Bytes.random(32), base64url)
|
|
416
|
+
const query = {
|
|
417
|
+
client_id: clientId,
|
|
418
|
+
code_challenge:
|
|
419
|
+
request.code_challenge ??
|
|
420
|
+
Base64.fromBytes(Hash.sha256(Bytes.fromString(verifier!)), base64url),
|
|
421
|
+
code_challenge_method: 'S256',
|
|
422
|
+
redirect_uri: request.redirect_uri,
|
|
423
|
+
// In-process calls skip `withResource`, so each names the one audience that APIs verify.
|
|
424
|
+
resource: env.URL,
|
|
425
|
+
response_type: 'code',
|
|
426
|
+
scope: request.scope ?? 'openid',
|
|
427
|
+
...(request.state ? { state: request.state } : {}),
|
|
428
|
+
}
|
|
429
|
+
const url = new URL('/oauth2/authorize', env.URL)
|
|
430
|
+
url.search = new URLSearchParams(query).toString()
|
|
431
|
+
// Better Auth returns the redirect as JSON for a request that accepts it.
|
|
432
|
+
headers.set('accept', 'application/json')
|
|
433
|
+
const authorized = await Timing.measure(
|
|
434
|
+
c,
|
|
435
|
+
c.var.auth.api.oauth2Authorize({
|
|
436
|
+
asResponse: false,
|
|
437
|
+
// A `GET` request reads `query`. The endpoint types a body for its `POST` form.
|
|
438
|
+
body: {},
|
|
439
|
+
headers,
|
|
440
|
+
query,
|
|
441
|
+
request: new Request(url, { headers }),
|
|
442
|
+
}),
|
|
443
|
+
'auth.oauth2Authorize',
|
|
444
|
+
)
|
|
445
|
+
const location = schema.authorize.redirect.safeParse(authorized)
|
|
446
|
+
const redirect = location.success
|
|
447
|
+
? URL.parse(location.data.url, env.URL)
|
|
448
|
+
: null
|
|
449
|
+
// A request that needs the approval page, or that Better Auth refuses, returns no code. Better
|
|
450
|
+
// Auth sets its `code` in place of a registered one, and appends its `error` to the registered
|
|
451
|
+
// URI's query, which may hold its own.
|
|
452
|
+
const registered = new URL(request.redirect_uri).searchParams
|
|
453
|
+
const code = redirect?.searchParams.get('code')
|
|
454
|
+
if (
|
|
455
|
+
!redirect ||
|
|
456
|
+
!code ||
|
|
457
|
+
redirect.searchParams.getAll('error').length >
|
|
458
|
+
registered.getAll('error').length ||
|
|
459
|
+
!`${redirect.origin}${redirect.pathname}`.startsWith(
|
|
460
|
+
new URL(request.redirect_uri).origin,
|
|
461
|
+
)
|
|
462
|
+
)
|
|
463
|
+
return ErrorResponse.json(
|
|
464
|
+
c,
|
|
465
|
+
new Authorize.RejectedError({
|
|
466
|
+
description:
|
|
467
|
+
"Authorization refused: the app's request needs the user's approval, which no page shows here. Authorize through a redirect, or register the app to skip consent.",
|
|
468
|
+
}),
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
// The owner signs only once the authorization returns a code, so a refused request signs nothing.
|
|
472
|
+
const signed =
|
|
473
|
+
key && signing
|
|
474
|
+
? await Timing.measure(
|
|
475
|
+
c,
|
|
476
|
+
KeySigner.sign(c, {
|
|
477
|
+
account: signing.account,
|
|
478
|
+
clientId,
|
|
479
|
+
key,
|
|
480
|
+
owner: signing.owner,
|
|
481
|
+
userId: session.user.id,
|
|
482
|
+
}),
|
|
483
|
+
'KeySigner.sign',
|
|
484
|
+
)
|
|
485
|
+
: undefined
|
|
486
|
+
if (signed instanceof ErrorResponse.ResponseError)
|
|
487
|
+
return ErrorResponse.json(c, signed)
|
|
488
|
+
|
|
489
|
+
const result = {
|
|
490
|
+
...(account && signed ? { account, key_authorization: signed } : {}),
|
|
491
|
+
...(request.state ? { state: request.state } : {}),
|
|
492
|
+
}
|
|
493
|
+
if (selection && key?.account)
|
|
494
|
+
c.header('set-cookie', selection.setCookie, { append: true })
|
|
495
|
+
c.header('cache-control', 'no-store')
|
|
496
|
+
if (!verifier) return c.json({ ...result, code }, 200)
|
|
497
|
+
|
|
498
|
+
const tokens = await Timing.measure(
|
|
499
|
+
c,
|
|
500
|
+
c.var.auth.api.oauth2Token({
|
|
501
|
+
body: {
|
|
502
|
+
client_id: clientId,
|
|
503
|
+
code,
|
|
504
|
+
code_verifier: verifier,
|
|
505
|
+
grant_type: 'authorization_code',
|
|
506
|
+
redirect_uri: request.redirect_uri,
|
|
507
|
+
resource: env.URL,
|
|
508
|
+
},
|
|
509
|
+
headers: new Headers({
|
|
510
|
+
'content-type': 'application/x-www-form-urlencoded',
|
|
511
|
+
}),
|
|
512
|
+
}),
|
|
513
|
+
'auth.oauth2Token',
|
|
514
|
+
)
|
|
515
|
+
return c.json(
|
|
516
|
+
{ ...result, tokens: Authorize.schema.tokens.parse(tokens) },
|
|
517
|
+
200,
|
|
518
|
+
)
|
|
519
|
+
},
|
|
520
|
+
)
|
|
521
|
+
.post(
|
|
522
|
+
'/v1/authorize/account',
|
|
523
|
+
OpenApi.describeRoute({
|
|
524
|
+
description:
|
|
525
|
+
"Selects the account that the app of `oauth_query` uses, then continues its authorization, and returns the next redirect. The selection holds for the app's later authorizations in the same session, and `GET /v1/account` lists it first.",
|
|
526
|
+
responses: OpenApi.responses({
|
|
527
|
+
errors: [
|
|
528
|
+
...BodyLimit.errors,
|
|
529
|
+
...Cors.ownPage.errors,
|
|
530
|
+
...OpenApi.validate.errors,
|
|
531
|
+
...AccountStep.accounts.errors,
|
|
532
|
+
...RateLimit.check.errors,
|
|
533
|
+
core_Auth.AuthorizationExpiredError,
|
|
534
|
+
core_Auth.SessionRequiredError,
|
|
535
|
+
TempoAccount.ForeignAccountError,
|
|
536
|
+
],
|
|
537
|
+
success: {
|
|
538
|
+
description: 'The next redirect.',
|
|
539
|
+
schema: schema.account.response,
|
|
540
|
+
},
|
|
541
|
+
}),
|
|
542
|
+
security: [{ sessionCookie: [] }],
|
|
543
|
+
tags: ['Authorization'],
|
|
544
|
+
}),
|
|
545
|
+
OpenApi.validate('json', schema.account.json),
|
|
546
|
+
async (c) => {
|
|
547
|
+
const { account, oauth_query } = c.req.valid('json')
|
|
548
|
+
const crossSite = Cors.ownPage(c, { oauth_query })
|
|
549
|
+
if (crossSite) return ErrorResponse.json(c, crossSite)
|
|
550
|
+
|
|
551
|
+
const session = await Timing.measure(c, Session.get(c), 'Session.get')
|
|
552
|
+
if (!session)
|
|
553
|
+
return ErrorResponse.json(c, new core_Auth.SessionRequiredError())
|
|
554
|
+
|
|
555
|
+
// The account lookup can call every realm's Privy app, so it counts against the user's
|
|
556
|
+
// account reads, as `GET /v1/account` does.
|
|
557
|
+
const refused = await Timing.measure(
|
|
558
|
+
c,
|
|
559
|
+
RateLimit.check(c, {
|
|
560
|
+
limits: [{ binding: 'ACCOUNT_LIMIT', key: session.user.id }],
|
|
561
|
+
}),
|
|
562
|
+
'RateLimit.check',
|
|
563
|
+
)
|
|
564
|
+
if (refused) return refused
|
|
565
|
+
|
|
566
|
+
// The account lookup reads only a signed query, so a forged one learns nothing about the user.
|
|
567
|
+
const context = await Timing.measure(
|
|
568
|
+
c,
|
|
569
|
+
c.var.auth.$context,
|
|
570
|
+
'auth.context',
|
|
571
|
+
)
|
|
572
|
+
const verified = await Timing.measure(
|
|
573
|
+
c,
|
|
574
|
+
verifyOAuthQueryParams(oauth_query, context.secret),
|
|
575
|
+
'auth.verifyOAuthQueryParams',
|
|
576
|
+
)
|
|
577
|
+
const query = new URLSearchParams(oauth_query)
|
|
578
|
+
const clientId = query.get('client_id')
|
|
579
|
+
if (!verified || !clientId)
|
|
580
|
+
return ErrorResponse.json(c, new core_Auth.AuthorizationExpiredError())
|
|
581
|
+
// The continued authorization can store a code for the redirect URI, so it is checked first.
|
|
582
|
+
Consent.checkRedirect(query)
|
|
583
|
+
|
|
584
|
+
const accounts = await Timing.measure(
|
|
585
|
+
c,
|
|
586
|
+
AccountStep.accounts(c, { clientId, userId: session.user.id }),
|
|
587
|
+
'AccountStep.accounts',
|
|
588
|
+
)
|
|
589
|
+
if (!AccountStep.find(accounts, account))
|
|
590
|
+
return ErrorResponse.json(
|
|
591
|
+
c,
|
|
592
|
+
new TempoAccount.ForeignAccountError({ account }),
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
// The continued authorization checks the account step again, so it reads the new selection.
|
|
596
|
+
const selection = AccountStep.select({
|
|
597
|
+
account: account.toLowerCase(),
|
|
598
|
+
clientId,
|
|
599
|
+
cookie: c.req.header('cookie'),
|
|
600
|
+
sessionId: session.session.id,
|
|
601
|
+
})
|
|
602
|
+
const headers = new Headers(c.req.raw.headers)
|
|
603
|
+
headers.set('cookie', selection.header)
|
|
604
|
+
const result = await Timing.measure(
|
|
605
|
+
c,
|
|
606
|
+
c.var.auth.api.oauth2Continue({
|
|
607
|
+
// A call with a request returns a `Response` unless `asResponse` is false.
|
|
608
|
+
asResponse: false,
|
|
609
|
+
body: { oauth_query, postLogin: true },
|
|
610
|
+
headers,
|
|
611
|
+
request: new Request(c.req.url, { headers, method: 'POST' }),
|
|
612
|
+
}),
|
|
613
|
+
'auth.oauth2Continue',
|
|
614
|
+
).catch((error: unknown) => {
|
|
615
|
+
// Better Auth refuses a forged or expired request before it continues the authorization.
|
|
616
|
+
if (isAPIError(error) && error.body?.error === 'invalid_signature')
|
|
617
|
+
return new core_Auth.AuthorizationExpiredError()
|
|
618
|
+
throw error
|
|
619
|
+
})
|
|
620
|
+
if (result instanceof ErrorResponse.ResponseError)
|
|
621
|
+
return ErrorResponse.json(c, result)
|
|
622
|
+
|
|
623
|
+
// A sign-in that showed the request's key held its grant for this step, which takes it.
|
|
624
|
+
const redirect = await Timing.measure(
|
|
625
|
+
c,
|
|
626
|
+
SignInConsent.afterAccountStep(c, {
|
|
627
|
+
account,
|
|
628
|
+
headers,
|
|
629
|
+
redirect: schema.account.result.parse(result).url,
|
|
630
|
+
session,
|
|
631
|
+
}),
|
|
632
|
+
'SignInConsent.afterAccountStep',
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
c.header('set-cookie', selection.setCookie, { append: true })
|
|
636
|
+
c.header('cache-control', 'private, no-store')
|
|
637
|
+
return c.json({ redirect }, 200)
|
|
638
|
+
},
|
|
639
|
+
)
|
|
640
|
+
.post(
|
|
641
|
+
'/v1/authorize/consent',
|
|
642
|
+
OpenApi.describeRoute({
|
|
643
|
+
description:
|
|
644
|
+
"Records the signed-in user's decision on the app's authorization request that `oauth_query` carries, and returns the app's redirect. The decision applies only while `session_id` names the signed-in session. An approval stores the consent, so the app's next request with the same scopes skips the approval page. An approval of a request with a key authorization signs the key as the account's owner, and the first redemption of the code returns it from `/oauth2/token`. For a passkey-owned account, the approval first returns the passkey step, and the repeated approval with `passkey` signs the key. A user who holds no account in the app's realms, and whose key names none, first receives a Privy-owned account, as `POST /v1/account` creates it.",
|
|
645
|
+
responses: OpenApi.responses({
|
|
646
|
+
errors: [
|
|
647
|
+
...Authorize.checkKey.errors,
|
|
648
|
+
...BodyLimit.errors,
|
|
649
|
+
...Cors.ownPage.errors,
|
|
650
|
+
...KeySigner.resolve.errors,
|
|
651
|
+
...KeySigner.sign.errors,
|
|
652
|
+
...OpenApi.validate.errors,
|
|
653
|
+
...RateLimit.check.errors,
|
|
654
|
+
Authorize.RejectedError,
|
|
655
|
+
core_Auth.AuthorizationExpiredError,
|
|
656
|
+
core_Auth.SessionChangedError,
|
|
657
|
+
core_Auth.SessionRequiredError,
|
|
658
|
+
Validation.InvalidFieldError,
|
|
659
|
+
],
|
|
660
|
+
success: {
|
|
661
|
+
description: "The app's redirect, or the passkey step.",
|
|
662
|
+
schema: schema.consent.response,
|
|
663
|
+
},
|
|
664
|
+
}),
|
|
665
|
+
security: [{ sessionCookie: [] }],
|
|
666
|
+
tags: ['Authorization'],
|
|
667
|
+
}),
|
|
668
|
+
OpenApi.validate('json', schema.consent.json),
|
|
669
|
+
async (c) => {
|
|
670
|
+
const { accept, account, oauth_query, passkey, session_id } =
|
|
671
|
+
c.req.valid('json')
|
|
672
|
+
const crossSite = Cors.ownPage(c, { oauth_query })
|
|
673
|
+
if (crossSite) return ErrorResponse.json(c, crossSite)
|
|
674
|
+
|
|
675
|
+
const session = await Timing.measure(c, Session.get(c), 'Session.get')
|
|
676
|
+
if (!session)
|
|
677
|
+
return ErrorResponse.json(c, new core_Auth.SessionRequiredError())
|
|
678
|
+
// A sign-in in another tab replaces the session, and the page still names the earlier account.
|
|
679
|
+
if (session.session.id !== session_id)
|
|
680
|
+
return ErrorResponse.json(c, new core_Auth.SessionChangedError())
|
|
681
|
+
|
|
682
|
+
// Better Auth stores the consent before it returns the redirect, so the signed redirect URI is
|
|
683
|
+
// checked first, or an approval would let the app's next request reach it without this page.
|
|
684
|
+
const context = await Timing.measure(
|
|
685
|
+
c,
|
|
686
|
+
c.var.auth.$context,
|
|
687
|
+
'auth.context',
|
|
688
|
+
)
|
|
689
|
+
const verified = await Timing.measure(
|
|
690
|
+
c,
|
|
691
|
+
verifyOAuthQueryParams(oauth_query, context.secret),
|
|
692
|
+
'auth.verifyOAuthQueryParams',
|
|
693
|
+
)
|
|
694
|
+
if (!verified)
|
|
695
|
+
return ErrorResponse.json(c, new core_Auth.AuthorizationExpiredError())
|
|
696
|
+
const query = new URLSearchParams(oauth_query)
|
|
697
|
+
Consent.checkRedirect(query)
|
|
698
|
+
|
|
699
|
+
// Each approval of a key reads the user's accounts and calls Privy, as a headless
|
|
700
|
+
// authorization does. The repeated approval of the passkey step counts too, since it reads
|
|
701
|
+
// the same owners before its assertion is checked.
|
|
702
|
+
const keyed =
|
|
703
|
+
accept && (query.has('key_authorization') || query.has('challenge'))
|
|
704
|
+
const limited = keyed
|
|
705
|
+
? await Timing.measure(
|
|
706
|
+
c,
|
|
707
|
+
RateLimit.check(c, {
|
|
708
|
+
limits: [{ binding: 'AUTHORIZE_LIMIT', key: session.user.id }],
|
|
709
|
+
}),
|
|
710
|
+
'RateLimit.check',
|
|
711
|
+
)
|
|
712
|
+
: undefined
|
|
713
|
+
if (limited) return limited
|
|
714
|
+
|
|
715
|
+
// An approval signs the key before Better Auth issues the code, so a refused key issues none.
|
|
716
|
+
if (accept) {
|
|
717
|
+
const approved = await Timing.measure(
|
|
718
|
+
c,
|
|
719
|
+
Consent.approve(c, {
|
|
720
|
+
account,
|
|
721
|
+
assertion: passkey?.assertion,
|
|
722
|
+
headers: new Headers(c.req.raw.headers),
|
|
723
|
+
oauthQuery: oauth_query,
|
|
724
|
+
session,
|
|
725
|
+
}),
|
|
726
|
+
'Consent.approve',
|
|
727
|
+
)
|
|
728
|
+
if (approved instanceof ErrorResponse.ResponseError)
|
|
729
|
+
return ErrorResponse.json(c, approved)
|
|
730
|
+
c.header('cache-control', 'private, no-store')
|
|
731
|
+
// A passkey signs only in this browser, so the page signs the digest and repeats the approval.
|
|
732
|
+
if ('options' in approved)
|
|
733
|
+
return c.json(
|
|
734
|
+
schema.consent.step.parse({
|
|
735
|
+
step: { options: approved.options, type: 'passkey' },
|
|
736
|
+
}),
|
|
737
|
+
200,
|
|
738
|
+
)
|
|
739
|
+
return c.json({ redirect: approved.redirect }, 200)
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const result = await Timing.measure(
|
|
743
|
+
c,
|
|
744
|
+
c.var.auth.api.oauth2Consent({
|
|
745
|
+
// A call with a request returns a `Response` unless `asResponse` is false.
|
|
746
|
+
asResponse: false,
|
|
747
|
+
body: { accept, oauth_query },
|
|
748
|
+
// Better Auth sets `accept` to continue an authorization as JSON, and a request's own headers
|
|
749
|
+
// are immutable.
|
|
750
|
+
headers: new Headers(c.req.raw.headers),
|
|
751
|
+
// An authorization that asks to sign in again continues through the request.
|
|
752
|
+
request: c.req.raw,
|
|
753
|
+
}),
|
|
754
|
+
'auth.oauth2Consent',
|
|
755
|
+
).catch((error: unknown) => {
|
|
756
|
+
// Better Auth refuses a forged or expired request before it stores anything.
|
|
757
|
+
if (isAPIError(error) && error.body?.error === 'invalid_signature')
|
|
758
|
+
return new core_Auth.AuthorizationExpiredError()
|
|
759
|
+
throw error
|
|
760
|
+
})
|
|
761
|
+
if (result instanceof ErrorResponse.ResponseError)
|
|
762
|
+
return ErrorResponse.json(c, result)
|
|
763
|
+
|
|
764
|
+
c.header('cache-control', 'private, no-store')
|
|
765
|
+
return c.json({ redirect: schema.account.result.parse(result).url }, 200)
|
|
766
|
+
},
|
|
767
|
+
)
|
|
768
|
+
.get(
|
|
769
|
+
'/v1/authorize/request',
|
|
770
|
+
OpenApi.describeRoute({
|
|
771
|
+
description:
|
|
772
|
+
"Reads the app's authorization request that `oauth_query` carries, as the approval page shows it: the app, where the browser returns, and any key, with the account that approval signs for and why approval cannot sign it, if it cannot. A user who holds no account shows no account, and approval creates the first one. Creates no account. Returns the app's redirect with the refusal for a key that Tempo ID refuses.",
|
|
773
|
+
responses: OpenApi.responses({
|
|
774
|
+
errors: [
|
|
775
|
+
...OpenApi.validate.errors,
|
|
776
|
+
core_Auth.SessionRequiredError,
|
|
777
|
+
ErrorResponse.CrossSiteRequestError,
|
|
778
|
+
],
|
|
779
|
+
success: {
|
|
780
|
+
description: "The request, or the app's redirect with a refusal.",
|
|
781
|
+
schema: schema.request.response,
|
|
782
|
+
},
|
|
783
|
+
}),
|
|
784
|
+
security: [{ sessionCookie: [] }],
|
|
785
|
+
tags: ['Authorization'],
|
|
786
|
+
}),
|
|
787
|
+
OpenApi.validate('query', schema.request.query),
|
|
788
|
+
async (c) => {
|
|
789
|
+
// The read can spend the user's `ACCOUNT_LIMIT`, which a page on another site could otherwise
|
|
790
|
+
// spend by navigating a window here.
|
|
791
|
+
if (c.req.header('sec-fetch-site') === 'cross-site')
|
|
792
|
+
return ErrorResponse.json(c, new ErrorResponse.CrossSiteRequestError())
|
|
793
|
+
|
|
794
|
+
const session = await Timing.measure(c, Session.get(c), 'Session.get')
|
|
795
|
+
if (!session)
|
|
796
|
+
return ErrorResponse.json(c, new core_Auth.SessionRequiredError())
|
|
797
|
+
c.header('cache-control', 'private, no-store')
|
|
798
|
+
|
|
799
|
+
// Better Auth reads the query that the page posts with `URLSearchParams`, so the route reads it
|
|
800
|
+
// the same way, and a malformed escape cannot show one request and approve another.
|
|
801
|
+
const { oauth_query } = c.req.valid('query')
|
|
802
|
+
const query = new URLSearchParams(oauth_query)
|
|
803
|
+
const context = await Timing.measure(
|
|
804
|
+
c,
|
|
805
|
+
c.var.auth.$context,
|
|
806
|
+
'auth.context',
|
|
807
|
+
)
|
|
808
|
+
const verified = await Timing.measure(
|
|
809
|
+
c,
|
|
810
|
+
verifyOAuthQueryParams(oauth_query, context.secret),
|
|
811
|
+
'auth.verifyOAuthQueryParams',
|
|
812
|
+
)
|
|
813
|
+
if (!verified) return c.json({ request: null }, 200)
|
|
814
|
+
|
|
815
|
+
const clientId = query.get('client_id')
|
|
816
|
+
const uri = query.get('redirect_uri')
|
|
817
|
+
const redirectUri = URL.parse(uri ?? '')
|
|
818
|
+
if (!clientId || !uri || !redirectUri)
|
|
819
|
+
return c.json({ request: null }, 200)
|
|
820
|
+
|
|
821
|
+
const app = await Timing.measure(
|
|
822
|
+
c,
|
|
823
|
+
Apps.get(db, { clientId }).catch((error: unknown) => {
|
|
824
|
+
if (error instanceof Apps.NotFoundError) return undefined
|
|
825
|
+
throw error
|
|
826
|
+
}),
|
|
827
|
+
'Apps.get',
|
|
828
|
+
)
|
|
829
|
+
// Better Auth redirects only to a registered URI, compared as a string.
|
|
830
|
+
if (!app || !app.redirectUris.includes(uri))
|
|
831
|
+
return c.json({ request: null }, 200)
|
|
832
|
+
|
|
833
|
+
// A refused key returns to the app with the refusal (RFC 6749 §4.1.2.1), as `/oauth2/authorize`
|
|
834
|
+
// returns one before sign-in, since a refusal page would leave the app without a callback. Each
|
|
835
|
+
// parameter follows the registered URI's own, as Better Auth appends its refusals.
|
|
836
|
+
const refuse = (error: ErrorResponse.ResponseError) => {
|
|
837
|
+
const url = new URL(uri)
|
|
838
|
+
url.searchParams.append('error', error.response.error)
|
|
839
|
+
url.searchParams.append(
|
|
840
|
+
'error_description',
|
|
841
|
+
error.response.error_description,
|
|
842
|
+
)
|
|
843
|
+
const state = query.get('state')
|
|
844
|
+
if (state) url.searchParams.append('state', state)
|
|
845
|
+
url.searchParams.append('iss', env.URL)
|
|
846
|
+
return c.json({ redirect: url.href }, 200)
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// The page shows the key that approval signs, so the route refuses one that approval would
|
|
850
|
+
// refuse.
|
|
851
|
+
const key = Authorize.fromQuery(query)
|
|
852
|
+
if (key instanceof ErrorResponse.ResponseError) return refuse(key)
|
|
853
|
+
const refused =
|
|
854
|
+
key &&
|
|
855
|
+
Authorize.checkKey({
|
|
856
|
+
app,
|
|
857
|
+
clientId,
|
|
858
|
+
codeChallenge: query.get('code_challenge'),
|
|
859
|
+
key,
|
|
860
|
+
})
|
|
861
|
+
if (refused) return refuse(refused)
|
|
862
|
+
|
|
863
|
+
// A custom scheme can carry an authority, as in `com.example.app://oauth/callback`, but the
|
|
864
|
+
// browser returns to whichever native app claims the scheme, so only a web URI names its host.
|
|
865
|
+
const destination = ['http:', 'https:'].includes(redirectUri.protocol)
|
|
866
|
+
? redirectUri.host
|
|
867
|
+
: redirectUri.protocol.slice(0, -1)
|
|
868
|
+
|
|
869
|
+
const preview = key
|
|
870
|
+
? await Timing.measure(
|
|
871
|
+
c,
|
|
872
|
+
KeySigner.preview(c, {
|
|
873
|
+
account: key.account,
|
|
874
|
+
clientId,
|
|
875
|
+
sessionId: session.session.id,
|
|
876
|
+
userId: session.user.id,
|
|
877
|
+
}),
|
|
878
|
+
'KeySigner.preview',
|
|
879
|
+
)
|
|
880
|
+
: undefined
|
|
881
|
+
if (preview?.foreign) return refuse(preview.foreign)
|
|
882
|
+
|
|
883
|
+
const shown = (() => {
|
|
884
|
+
if (!key || !preview) return undefined
|
|
885
|
+
const challenge = query.get('challenge')
|
|
886
|
+
return {
|
|
887
|
+
...KeyAuthorization.describe(key),
|
|
888
|
+
...(preview.account ? { account: preview.account } : {}),
|
|
889
|
+
...(challenge ? { challenge } : {}),
|
|
890
|
+
...(preview.refusal && preview.refusal !== 'foreign_account'
|
|
891
|
+
? { refusal: preview.refusal }
|
|
892
|
+
: {}),
|
|
893
|
+
}
|
|
894
|
+
})()
|
|
895
|
+
|
|
896
|
+
return c.json(
|
|
897
|
+
{
|
|
898
|
+
request: {
|
|
899
|
+
app: app.name ?? destination,
|
|
900
|
+
destination,
|
|
901
|
+
email: session.user.email,
|
|
902
|
+
...(shown ? { key: shown } : {}),
|
|
903
|
+
session: session.session.id,
|
|
904
|
+
},
|
|
905
|
+
},
|
|
906
|
+
200,
|
|
907
|
+
)
|
|
908
|
+
},
|
|
909
|
+
)
|
|
910
|
+
.get(
|
|
911
|
+
'/v1/authorize/accounts',
|
|
912
|
+
OpenApi.describeRoute({
|
|
913
|
+
description:
|
|
914
|
+
"Reads the account step of the app's authorization request that `oauth_query` carries: the app, and the user's accounts in its realms, oldest first, each with its balance on the chain of the request's key, or of Tempo mainnet without one. The read counts against the user's `ACCOUNT_LIMIT`, as `GET /v1/account` does.",
|
|
915
|
+
responses: OpenApi.responses({
|
|
916
|
+
errors: [
|
|
917
|
+
...AccountStep.accounts.errors,
|
|
918
|
+
...OpenApi.validate.errors,
|
|
919
|
+
...RateLimit.check.errors,
|
|
920
|
+
core_Auth.SessionRequiredError,
|
|
921
|
+
ErrorResponse.CrossSiteRequestError,
|
|
922
|
+
],
|
|
923
|
+
success: {
|
|
924
|
+
description: 'The account step.',
|
|
925
|
+
schema: schema.accounts.response,
|
|
926
|
+
},
|
|
927
|
+
}),
|
|
928
|
+
security: [{ sessionCookie: [] }],
|
|
929
|
+
tags: ['Authorization'],
|
|
930
|
+
}),
|
|
931
|
+
OpenApi.validate('query', schema.accounts.query),
|
|
932
|
+
async (c) => {
|
|
933
|
+
// A page on another site could otherwise spend the user's `ACCOUNT_LIMIT` by navigating a
|
|
934
|
+
// window here.
|
|
935
|
+
if (c.req.header('sec-fetch-site') === 'cross-site')
|
|
936
|
+
return ErrorResponse.json(c, new ErrorResponse.CrossSiteRequestError())
|
|
937
|
+
|
|
938
|
+
const session = await Timing.measure(c, Session.get(c), 'Session.get')
|
|
939
|
+
if (!session)
|
|
940
|
+
return ErrorResponse.json(c, new core_Auth.SessionRequiredError())
|
|
941
|
+
c.header('cache-control', 'private, no-store')
|
|
942
|
+
|
|
943
|
+
const { oauth_query } = c.req.valid('query')
|
|
944
|
+
const query = new URLSearchParams(oauth_query)
|
|
945
|
+
const context = await Timing.measure(
|
|
946
|
+
c,
|
|
947
|
+
c.var.auth.$context,
|
|
948
|
+
'auth.context',
|
|
949
|
+
)
|
|
950
|
+
const verified = await Timing.measure(
|
|
951
|
+
c,
|
|
952
|
+
verifyOAuthQueryParams(oauth_query, context.secret),
|
|
953
|
+
'auth.verifyOAuthQueryParams',
|
|
954
|
+
)
|
|
955
|
+
const clientId = query.get('client_id')
|
|
956
|
+
if (!verified || !clientId) return c.json({ request: null }, 200)
|
|
957
|
+
|
|
958
|
+
const app = await Timing.measure(
|
|
959
|
+
c,
|
|
960
|
+
Apps.get(db, { clientId }).catch((error: unknown) => {
|
|
961
|
+
if (error instanceof Apps.NotFoundError) return undefined
|
|
962
|
+
throw error
|
|
963
|
+
}),
|
|
964
|
+
'Apps.get',
|
|
965
|
+
)
|
|
966
|
+
if (!app) return c.json({ request: null }, 200)
|
|
967
|
+
|
|
968
|
+
// The account lookup can call every realm's Privy app, so it counts against the user's account
|
|
969
|
+
// reads, as `GET /v1/account` does.
|
|
970
|
+
const refused = await Timing.measure(
|
|
971
|
+
c,
|
|
972
|
+
RateLimit.check(c, {
|
|
973
|
+
limits: [{ binding: 'ACCOUNT_LIMIT', key: session.user.id }],
|
|
974
|
+
}),
|
|
975
|
+
'RateLimit.check',
|
|
976
|
+
)
|
|
977
|
+
if (refused) return refused
|
|
978
|
+
|
|
979
|
+
const accounts = await Timing.measure(
|
|
980
|
+
c,
|
|
981
|
+
AccountStep.accounts(c, { clientId, userId: session.user.id }),
|
|
982
|
+
'AccountStep.accounts',
|
|
983
|
+
)
|
|
984
|
+
// A key that does not parse falls back to the default chain, and the approval page refuses it.
|
|
985
|
+
const key = Authorize.fromQuery(query)
|
|
986
|
+
const chainId =
|
|
987
|
+
key && !(key instanceof ErrorResponse.ResponseError)
|
|
988
|
+
? Number(key.chainId)
|
|
989
|
+
: KeyAuthorization.defaultChainId
|
|
990
|
+
|
|
991
|
+
// Each Tempo Account has one owner, so the guard only narrows the read.
|
|
992
|
+
const owned = accounts.flatMap((account) => {
|
|
993
|
+
const owner = account.config.owners[0]
|
|
994
|
+
if (!owner) return []
|
|
995
|
+
return [
|
|
996
|
+
{
|
|
997
|
+
address: account.address,
|
|
998
|
+
owner: owner.type,
|
|
999
|
+
// The `default` realm goes unnamed, so a row names only a realm such as Tempo Cash.
|
|
1000
|
+
realm:
|
|
1001
|
+
account.realm.id === Realm.defaultId ? null : account.realm.name,
|
|
1002
|
+
},
|
|
1003
|
+
]
|
|
1004
|
+
})
|
|
1005
|
+
const balances = await Timing.measure(
|
|
1006
|
+
c,
|
|
1007
|
+
Balance.list(c, {
|
|
1008
|
+
accounts: owned,
|
|
1009
|
+
chainId,
|
|
1010
|
+
rpcUrl: env.TEMPO_RPC_URL,
|
|
1011
|
+
}),
|
|
1012
|
+
'Balance.list',
|
|
1013
|
+
)
|
|
1014
|
+
return c.json(
|
|
1015
|
+
{
|
|
1016
|
+
request: {
|
|
1017
|
+
accounts: owned.map((account, index) => ({
|
|
1018
|
+
...account,
|
|
1019
|
+
balance: balances[index] ?? null,
|
|
1020
|
+
})),
|
|
1021
|
+
app: app.name ?? 'The app',
|
|
1022
|
+
},
|
|
1023
|
+
},
|
|
1024
|
+
200,
|
|
1025
|
+
)
|
|
1026
|
+
},
|
|
1027
|
+
)
|
|
1028
|
+
|
|
1029
|
+
/** Unpadded URL-safe Base64, as PKCE encodes verifiers and challenges. */
|
|
1030
|
+
const base64url = { pad: false, url: true } as const
|