@tempoxyz/id 0.0.0-pr-187.sha-eed04d3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -0
- 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-DjQNeRZp.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 +82 -0
- 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,718 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth apps: the Better Auth plugins that serve them, and the operations of `tempo-id admin apps`
|
|
3
|
+
* on the `oauth_client` table. Better Auth registers clients only for a signed-in owner, so the
|
|
4
|
+
* operations write the rows that it reads, with its client ID format and its secret hash. A client
|
|
5
|
+
* secret is stored only as its hash.
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { isLoopbackHost } from '@better-auth/core/utils/host'
|
|
9
|
+
import { isNativePrivateUseRedirectUri } from '@better-auth/core/utils/redirect-uri'
|
|
10
|
+
import {
|
|
11
|
+
getOAuthProviderState,
|
|
12
|
+
oauthProvider,
|
|
13
|
+
} from '@better-auth/oauth-provider'
|
|
14
|
+
import { generateRandomString } from 'better-auth/crypto'
|
|
15
|
+
import { jwt } from 'better-auth/plugins/jwt'
|
|
16
|
+
import { and, asc, eq, isNull, or } from 'drizzle-orm'
|
|
17
|
+
import { Base64 } from 'ox'
|
|
18
|
+
import * as Schema from '../app/db/schema.js'
|
|
19
|
+
import type * as PendingKey from './PendingKey.js'
|
|
20
|
+
import type * as Realm from './Realm.js'
|
|
21
|
+
import * as SigningKey from './SigningKey.js'
|
|
22
|
+
|
|
23
|
+
/** Another app already has the client ID. */
|
|
24
|
+
export class ExistsError extends Error {
|
|
25
|
+
override readonly name = 'Apps.ExistsError'
|
|
26
|
+
|
|
27
|
+
constructor(options: { clientId: string }) {
|
|
28
|
+
super(
|
|
29
|
+
`App ${options.clientId} already exists. Update it, or choose another client ID.`,
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** A name, a redirect URI, or another value of an app is not valid. */
|
|
35
|
+
export class InvalidError extends Error {
|
|
36
|
+
override readonly name = 'Apps.InvalidError'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** No OAuth app has the client ID. */
|
|
40
|
+
export class NotFoundError extends Error {
|
|
41
|
+
override readonly name = 'Apps.NotFoundError'
|
|
42
|
+
|
|
43
|
+
constructor(options: { clientId: string }) {
|
|
44
|
+
super(`Unknown app ${options.clientId}. Name a registered OAuth app.`)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The app redeems codes with PKCE alone, so it has no client secret to rotate. */
|
|
49
|
+
export class PublicError extends Error {
|
|
50
|
+
override readonly name = 'Apps.PublicError'
|
|
51
|
+
|
|
52
|
+
constructor(options: { clientId: string }) {
|
|
53
|
+
super(
|
|
54
|
+
`App ${options.clientId} has no client secret. Rotate the secret of an app with a backend.`,
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** An OAuth app, as `tempo-id admin apps` returns it. */
|
|
60
|
+
export type App = {
|
|
61
|
+
/** The client ID, which the app sends as `client_id`. */
|
|
62
|
+
clientId: string
|
|
63
|
+
/** When the app was created. */
|
|
64
|
+
createdAt: Date | null
|
|
65
|
+
/** Whether the app signs in through the device grant, in place of a redirect, as a CLI does. */
|
|
66
|
+
device: boolean
|
|
67
|
+
/** The display name, which the approval page shows. */
|
|
68
|
+
name: string | null
|
|
69
|
+
/** Every URI that `GET /oauth2/end-session` may return to after it ends the session. */
|
|
70
|
+
postLogoutRedirectUris: readonly string[]
|
|
71
|
+
/** Every URI that the app returns to. */
|
|
72
|
+
redirectUris: readonly string[]
|
|
73
|
+
/** Whether the app holds a client secret, which its backend sends to the token endpoint. */
|
|
74
|
+
secret: boolean
|
|
75
|
+
/** Whether the app holds the `tempo-id:sign` scope, which permits a challenge. */
|
|
76
|
+
sign: boolean
|
|
77
|
+
/** Whether a request without a key skips the approval page. */
|
|
78
|
+
skipConsent: boolean
|
|
79
|
+
/** `web` for a browser app or an app with a backend, and `native` for a mobile app. */
|
|
80
|
+
type: 'native' | 'web'
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The grant type of the device grant (RFC 8628 §3.4). */
|
|
84
|
+
export const deviceGrant = 'urn:ietf:params:oauth:grant-type:device_code'
|
|
85
|
+
|
|
86
|
+
/** The scope that lets an app bind its own challenge into a key authorization. */
|
|
87
|
+
export const signScope = 'tempo-id:sign'
|
|
88
|
+
|
|
89
|
+
/** The scopes that every app may request. */
|
|
90
|
+
const scopes = ['openid', 'profile', 'email', 'offline_access'] as const
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Returns whether `clientSecret` is the client secret of the enabled app with `clientId`. An
|
|
94
|
+
* unknown app, a disabled app, and an app without a client secret return `false`. Better Auth still
|
|
95
|
+
* authenticates the request itself, so a match only decides which app a rate limit counts.
|
|
96
|
+
*/
|
|
97
|
+
export async function authenticate(
|
|
98
|
+
db: Realm.Database,
|
|
99
|
+
options: authenticate.Options,
|
|
100
|
+
) {
|
|
101
|
+
const [row] = await db
|
|
102
|
+
.select({
|
|
103
|
+
clientSecret: Schema.oauthClient.clientSecret,
|
|
104
|
+
disabled: Schema.oauthClient.disabled,
|
|
105
|
+
})
|
|
106
|
+
.from(Schema.oauthClient)
|
|
107
|
+
.where(eq(Schema.oauthClient.clientId, options.clientId))
|
|
108
|
+
.limit(1)
|
|
109
|
+
if (!row?.clientSecret || row.disabled) return false
|
|
110
|
+
return equal(await hashSecret(options.clientSecret), row.clientSecret)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export declare namespace authenticate {
|
|
114
|
+
type Options = {
|
|
115
|
+
/** The client ID that the request names. */
|
|
116
|
+
clientId: string
|
|
117
|
+
/** The client secret that the request sends. */
|
|
118
|
+
clientSecret: string
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Registers an app, and returns it with its client secret. The secret appears only in this result,
|
|
124
|
+
* since the row holds only its hash. A `clientId` or `clientSecret` keeps the values of an app that
|
|
125
|
+
* another deployment registered.
|
|
126
|
+
*
|
|
127
|
+
* Throws `InvalidError` for an invalid name, redirect URI, client ID, or client secret, or a device
|
|
128
|
+
* app with a secret, and `ExistsError` when another app has the client ID.
|
|
129
|
+
*/
|
|
130
|
+
export async function create(db: Realm.Database, options: create.Options) {
|
|
131
|
+
const type = options.type ?? 'web'
|
|
132
|
+
const name = parseName(options.name)
|
|
133
|
+
const postLogoutRedirectUris = parsePostLogoutRedirectUris(
|
|
134
|
+
options.postLogoutRedirectUris ?? [],
|
|
135
|
+
{ type },
|
|
136
|
+
)
|
|
137
|
+
const device = options.device ?? false
|
|
138
|
+
// A CLI cannot keep a secret, and the device grant's poll sends none, so a device app is public.
|
|
139
|
+
if (device && (options.secret || options.clientSecret !== undefined))
|
|
140
|
+
throw new InvalidError(
|
|
141
|
+
'Invalid app: a device app has no client secret, since a CLI cannot keep one. Leave out the secret.',
|
|
142
|
+
)
|
|
143
|
+
// A device app redirects nowhere, so it needs no redirect URI.
|
|
144
|
+
const redirectUris =
|
|
145
|
+
device && (options.redirectUris ?? []).length === 0
|
|
146
|
+
? []
|
|
147
|
+
: parseRedirectUris(options.redirectUris ?? [], { type })
|
|
148
|
+
const clientId =
|
|
149
|
+
options.clientId === undefined
|
|
150
|
+
? // Better Auth's format: 32 random letters.
|
|
151
|
+
generateRandomString(32, 'a-z', 'A-Z')
|
|
152
|
+
: parseClientId(options.clientId)
|
|
153
|
+
const clientSecret = (() => {
|
|
154
|
+
if (options.clientSecret !== undefined)
|
|
155
|
+
return parseClientSecret(options.clientSecret)
|
|
156
|
+
return options.secret ? secret() : undefined
|
|
157
|
+
})()
|
|
158
|
+
|
|
159
|
+
const now = new Date()
|
|
160
|
+
const [row] = await db
|
|
161
|
+
.insert(Schema.oauthClient)
|
|
162
|
+
.values({
|
|
163
|
+
applicationType: type,
|
|
164
|
+
clientId,
|
|
165
|
+
clientSecret: clientSecret ? await hashSecret(clientSecret) : null,
|
|
166
|
+
createdAt: now,
|
|
167
|
+
// Lets the app end the session at `GET /oauth2/end-session`, and puts `sid` in its ID tokens.
|
|
168
|
+
enableEndSession: true,
|
|
169
|
+
// A device app has no authorization code grant, so `/oauth2/authorize` refuses it.
|
|
170
|
+
grantTypes: device
|
|
171
|
+
? [deviceGrant, 'refresh_token']
|
|
172
|
+
: ['authorization_code', 'refresh_token'],
|
|
173
|
+
id: crypto.randomUUID(),
|
|
174
|
+
name,
|
|
175
|
+
postLogoutRedirectUris: [...postLogoutRedirectUris],
|
|
176
|
+
redirectUris: [...redirectUris],
|
|
177
|
+
responseTypes: ['code'],
|
|
178
|
+
scopes: scopesOf({ sign: options.sign ?? false }),
|
|
179
|
+
skipConsent: options.skipConsent ?? false,
|
|
180
|
+
// The client package sends the secret in the form body.
|
|
181
|
+
tokenEndpointAuthMethod: clientSecret ? 'client_secret_post' : 'none',
|
|
182
|
+
updatedAt: now,
|
|
183
|
+
})
|
|
184
|
+
.onConflictDoNothing({ target: Schema.oauthClient.clientId })
|
|
185
|
+
.returning()
|
|
186
|
+
if (!row) throw new ExistsError({ clientId })
|
|
187
|
+
|
|
188
|
+
return { app: toApp(row), ...(clientSecret ? { clientSecret } : {}) }
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export declare namespace create {
|
|
192
|
+
type Options = {
|
|
193
|
+
/**
|
|
194
|
+
* The client ID, such as the one that another deployment issued. Defaults to 32 random letters,
|
|
195
|
+
* as Better Auth issues.
|
|
196
|
+
*/
|
|
197
|
+
clientId?: string | undefined
|
|
198
|
+
/**
|
|
199
|
+
* The client secret, such as the one that another deployment issued. It gives the app a secret
|
|
200
|
+
* without `secret`. Defaults to a new secret when `secret` is set.
|
|
201
|
+
*/
|
|
202
|
+
clientSecret?: string | undefined
|
|
203
|
+
/**
|
|
204
|
+
* Whether the app signs in through the device grant, in place of a redirect, as a CLI does.
|
|
205
|
+
* Defaults to `false`.
|
|
206
|
+
*/
|
|
207
|
+
device?: boolean | undefined
|
|
208
|
+
/** The display name, which the approval page shows. */
|
|
209
|
+
name: string
|
|
210
|
+
/** Every URI that `GET /oauth2/end-session` may return to. Defaults to none. */
|
|
211
|
+
postLogoutRedirectUris?: readonly string[] | undefined
|
|
212
|
+
/** Every URI that the app returns to, in each environment. A device app may have none. */
|
|
213
|
+
redirectUris?: readonly string[] | undefined
|
|
214
|
+
/** Whether the app receives a client secret, for its backend. Defaults to `false`. */
|
|
215
|
+
secret?: boolean | undefined
|
|
216
|
+
/** Whether the app holds the `tempo-id:sign` scope. Defaults to `false`. */
|
|
217
|
+
sign?: boolean | undefined
|
|
218
|
+
/** Whether a request without a key skips the approval page. Defaults to `false`. */
|
|
219
|
+
skipConsent?: boolean | undefined
|
|
220
|
+
/** The application type. Defaults to `web`. */
|
|
221
|
+
type?: App['type'] | undefined
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Returns whether `origin` is the origin of a redirect URI of an enabled first-party app, one that
|
|
227
|
+
* skips consent. Pages on such an origin call the API with the session cookie. A disabled app,
|
|
228
|
+
* which Better Auth refuses, gives no origin. The registration rules keep production redirect URIs
|
|
229
|
+
* on `https`, so the check filters no scheme.
|
|
230
|
+
*/
|
|
231
|
+
export async function firstPartyOrigin(
|
|
232
|
+
db: Realm.Database,
|
|
233
|
+
options: firstPartyOrigin.Options,
|
|
234
|
+
) {
|
|
235
|
+
const rows = await db
|
|
236
|
+
.select({ redirectUris: Schema.oauthClient.redirectUris })
|
|
237
|
+
.from(Schema.oauthClient)
|
|
238
|
+
.where(
|
|
239
|
+
and(
|
|
240
|
+
eq(Schema.oauthClient.skipConsent, true),
|
|
241
|
+
or(
|
|
242
|
+
isNull(Schema.oauthClient.disabled),
|
|
243
|
+
eq(Schema.oauthClient.disabled, false),
|
|
244
|
+
),
|
|
245
|
+
),
|
|
246
|
+
)
|
|
247
|
+
return rows.some((row) =>
|
|
248
|
+
row.redirectUris.some((uri) => {
|
|
249
|
+
const url = URL.parse(uri)
|
|
250
|
+
return (
|
|
251
|
+
(url?.protocol === 'https:' || url?.protocol === 'http:') &&
|
|
252
|
+
url.origin === options.origin
|
|
253
|
+
)
|
|
254
|
+
}),
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export declare namespace firstPartyOrigin {
|
|
259
|
+
type Options = {
|
|
260
|
+
/** The request's `Origin`. */
|
|
261
|
+
origin: string
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Returns the app with `clientId`. Throws `NotFoundError` when no app has it, or with `enabled`,
|
|
267
|
+
* when the app is disabled.
|
|
268
|
+
*/
|
|
269
|
+
export async function get(db: Realm.Database, options: get.Options) {
|
|
270
|
+
const row = await find(db, options)
|
|
271
|
+
if (options.enabled && row.disabled)
|
|
272
|
+
throw new NotFoundError({ clientId: options.clientId })
|
|
273
|
+
return toApp(row)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export declare namespace get {
|
|
277
|
+
type Options = {
|
|
278
|
+
/** The app's client ID. */
|
|
279
|
+
clientId: string
|
|
280
|
+
/** Whether a disabled app counts as no app. Defaults to `false`. */
|
|
281
|
+
enabled?: boolean | undefined
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Returns the SHA-256 hash of a client secret, as base64url without padding, which the row holds.
|
|
287
|
+
* Better Auth verifies a client secret against it.
|
|
288
|
+
*/
|
|
289
|
+
export async function hashSecret(secret: string) {
|
|
290
|
+
const digest = await crypto.subtle.digest(
|
|
291
|
+
'SHA-256',
|
|
292
|
+
new TextEncoder().encode(secret),
|
|
293
|
+
)
|
|
294
|
+
return Base64.fromBytes(new Uint8Array(digest), { pad: false, url: true })
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Compares two hashes from `hashSecret` in constant time. Node's `crypto.subtle` has no
|
|
299
|
+
* `timingSafeEqual`, and the CLI runs this module in Node, so the loop reads every byte of both.
|
|
300
|
+
*/
|
|
301
|
+
function equal(a: string, b: string) {
|
|
302
|
+
const left = Base64.toBytes(a)
|
|
303
|
+
const right = Base64.toBytes(b)
|
|
304
|
+
if (left.length !== right.length) return false
|
|
305
|
+
let difference = 0
|
|
306
|
+
for (let index = 0; index < left.length; index++)
|
|
307
|
+
difference |= left[index]! ^ right[index]!
|
|
308
|
+
return difference === 0
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** Returns every app, oldest first. */
|
|
312
|
+
export async function list(db: Realm.Database): Promise<readonly App[]> {
|
|
313
|
+
const rows = await db
|
|
314
|
+
.select()
|
|
315
|
+
.from(Schema.oauthClient)
|
|
316
|
+
.orderBy(
|
|
317
|
+
asc(Schema.oauthClient.createdAt),
|
|
318
|
+
asc(Schema.oauthClient.clientId),
|
|
319
|
+
)
|
|
320
|
+
return rows.map(toApp)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Returns the Better Auth plugins that serve OAuth apps: the JWT plugin, which signs tokens with
|
|
325
|
+
* RS256, and the OAuth provider. `baseURL` is Tempo ID's origin, which is `iss` and the one resource.
|
|
326
|
+
*/
|
|
327
|
+
export function plugins(options: plugins.Options) {
|
|
328
|
+
const { accountStep, baseURL, pendingKey } = options
|
|
329
|
+
return [
|
|
330
|
+
// `iss` is the configured origin, never the request's host.
|
|
331
|
+
jwt({
|
|
332
|
+
adapter: SigningKey.adapter,
|
|
333
|
+
jwks: SigningKey.jwks,
|
|
334
|
+
jwt: { issuer: baseURL },
|
|
335
|
+
}),
|
|
336
|
+
oauthProvider({
|
|
337
|
+
allowDynamicClientRegistration: false,
|
|
338
|
+
consentPage: '/authorize',
|
|
339
|
+
// Apps read the user's address from the ID token, so a grant of `email` puts it there as well
|
|
340
|
+
// as in userinfo, as OIDC Core §5.4 allows.
|
|
341
|
+
customIdTokenClaims: (info) =>
|
|
342
|
+
info.scopes.includes('email')
|
|
343
|
+
? {
|
|
344
|
+
email: info.user.email,
|
|
345
|
+
email_verified: info.user.emailVerified,
|
|
346
|
+
}
|
|
347
|
+
: {},
|
|
348
|
+
// Every app may request the one resource.
|
|
349
|
+
enforcePerClientResources: false,
|
|
350
|
+
loginPage: '/login',
|
|
351
|
+
// The account step runs after sign-in and before consent, and `oauth2Continue` resumes the
|
|
352
|
+
// authorization with `postLogin`, which checks again.
|
|
353
|
+
...(accountStep
|
|
354
|
+
? {
|
|
355
|
+
postLogin: {
|
|
356
|
+
consentReferenceId: () => undefined,
|
|
357
|
+
page: '/choose-account',
|
|
358
|
+
shouldRedirect: async (context: {
|
|
359
|
+
headers: Headers
|
|
360
|
+
session: { id: string }
|
|
361
|
+
user: { id: string }
|
|
362
|
+
}) => {
|
|
363
|
+
// The authorization stores its query before it checks, so the hook reads the app.
|
|
364
|
+
const query = (await getOAuthProviderState())?.query
|
|
365
|
+
const clientId = query
|
|
366
|
+
? new URLSearchParams(query).get('client_id')
|
|
367
|
+
: null
|
|
368
|
+
if (!clientId) return false
|
|
369
|
+
return await accountStep({
|
|
370
|
+
clientId,
|
|
371
|
+
headers: context.headers,
|
|
372
|
+
sessionId: context.session.id,
|
|
373
|
+
userId: context.user.id,
|
|
374
|
+
})
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
}
|
|
378
|
+
: {}),
|
|
379
|
+
// A code's redemption returns the key that its approval signed, once. Better Auth calls this
|
|
380
|
+
// after it checks the verifier against the code's challenge.
|
|
381
|
+
...(pendingKey
|
|
382
|
+
? {
|
|
383
|
+
customTokenResponseFields: async (info: {
|
|
384
|
+
grantType: string
|
|
385
|
+
user?: { id: string } | null | undefined
|
|
386
|
+
verificationValue?: {
|
|
387
|
+
query: {
|
|
388
|
+
client_id: string
|
|
389
|
+
code_challenge?: string | undefined
|
|
390
|
+
key_authorization?: unknown
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}) => {
|
|
394
|
+
const query = info.verificationValue?.query
|
|
395
|
+
if (
|
|
396
|
+
info.grantType !== 'authorization_code' ||
|
|
397
|
+
!query?.code_challenge ||
|
|
398
|
+
// Only a code whose request carried the key takes it, so a keyless code for a reused
|
|
399
|
+
// challenge leaves it for the keyed one.
|
|
400
|
+
query.key_authorization === undefined ||
|
|
401
|
+
!info.user
|
|
402
|
+
)
|
|
403
|
+
return {}
|
|
404
|
+
const key = await pendingKey({
|
|
405
|
+
clientId: query.client_id,
|
|
406
|
+
codeChallenge: query.code_challenge,
|
|
407
|
+
userId: info.user.id,
|
|
408
|
+
})
|
|
409
|
+
if (!key) return {}
|
|
410
|
+
return {
|
|
411
|
+
account: key.account,
|
|
412
|
+
key_authorization: key.keyAuthorization,
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
}
|
|
416
|
+
: {}),
|
|
417
|
+
// Every access token names one audience, so each API verifies tokens for every app the same way.
|
|
418
|
+
resources: [
|
|
419
|
+
{ accessTokenTtl: 300, identifier: baseURL, name: 'Tempo ID' },
|
|
420
|
+
],
|
|
421
|
+
// Better Auth checks a registered app's scopes against its own row, so `tempo-id:sign` stays out
|
|
422
|
+
// of this list. A row without scopes falls back to this list, and never receives the scope.
|
|
423
|
+
scopes: [...scopes],
|
|
424
|
+
// `create` and `rotateSecret` store each secret with the same hash.
|
|
425
|
+
storeClientSecret: { hash: hashSecret },
|
|
426
|
+
}),
|
|
427
|
+
]
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export declare namespace plugins {
|
|
431
|
+
type Options = {
|
|
432
|
+
/**
|
|
433
|
+
* Returns whether the authorization of the app with `clientId` needs the account step, which
|
|
434
|
+
* the `/choose-account` page shows. Absent, no authorization shows it.
|
|
435
|
+
*/
|
|
436
|
+
accountStep?:
|
|
437
|
+
| ((options: {
|
|
438
|
+
clientId: string
|
|
439
|
+
headers: Headers
|
|
440
|
+
sessionId: string
|
|
441
|
+
userId: string
|
|
442
|
+
}) => Promise<boolean>)
|
|
443
|
+
| undefined
|
|
444
|
+
/** Tempo ID's origin, such as `https://id.tempo.xyz`. */
|
|
445
|
+
baseURL: string
|
|
446
|
+
/**
|
|
447
|
+
* Takes the key authorization held for the app with `clientId`, the PKCE challenge
|
|
448
|
+
* `codeChallenge`, and the code's user, as `PendingKey.take` does. Absent, a code's redemption returns no key.
|
|
449
|
+
*/
|
|
450
|
+
pendingKey?:
|
|
451
|
+
| ((options: {
|
|
452
|
+
clientId: string
|
|
453
|
+
codeChallenge: string
|
|
454
|
+
userId: string
|
|
455
|
+
}) => Promise<PendingKey.PendingKey | undefined>)
|
|
456
|
+
| undefined
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Removes the app with `clientId`, with its consents, tokens, and realms. Throws `NotFoundError`
|
|
462
|
+
* when no app has it.
|
|
463
|
+
*/
|
|
464
|
+
export async function remove(db: Realm.Database, options: remove.Options) {
|
|
465
|
+
const removed = await db
|
|
466
|
+
.delete(Schema.oauthClient)
|
|
467
|
+
.where(eq(Schema.oauthClient.clientId, options.clientId))
|
|
468
|
+
.returning()
|
|
469
|
+
const [row] = removed
|
|
470
|
+
if (!row) throw new NotFoundError({ clientId: options.clientId })
|
|
471
|
+
return toApp(row)
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export declare namespace remove {
|
|
475
|
+
type Options = {
|
|
476
|
+
/** The app's client ID. */
|
|
477
|
+
clientId: string
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Replaces the client secret of the app with `clientId`, and returns the app with the new secret.
|
|
483
|
+
* The earlier secret stops working at once.
|
|
484
|
+
*
|
|
485
|
+
* Throws `NotFoundError` when no app has the client ID, `PublicError` when the app has no client
|
|
486
|
+
* secret, and `InvalidError` for an invalid `clientSecret` or one that matches the current secret.
|
|
487
|
+
*/
|
|
488
|
+
export async function rotateSecret(
|
|
489
|
+
db: Realm.Database,
|
|
490
|
+
options: rotateSecret.Options,
|
|
491
|
+
) {
|
|
492
|
+
const clientSecret =
|
|
493
|
+
options.clientSecret === undefined
|
|
494
|
+
? secret()
|
|
495
|
+
: parseClientSecret(options.clientSecret)
|
|
496
|
+
const current = await find(db, options)
|
|
497
|
+
if (!current.clientSecret) throw new PublicError(options)
|
|
498
|
+
// A rotation to the same secret would leave a leaked secret working while it reports success.
|
|
499
|
+
if (equal(current.clientSecret, await hashSecret(clientSecret)))
|
|
500
|
+
throw new InvalidError(
|
|
501
|
+
'Invalid client secret: it matches the current secret. Use a new secret.',
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
const [row] = await db
|
|
505
|
+
.update(Schema.oauthClient)
|
|
506
|
+
.set({
|
|
507
|
+
clientSecret: await hashSecret(clientSecret),
|
|
508
|
+
updatedAt: new Date(),
|
|
509
|
+
})
|
|
510
|
+
.where(eq(Schema.oauthClient.clientId, options.clientId))
|
|
511
|
+
.returning()
|
|
512
|
+
// A concurrent `remove` deleted the app after the read.
|
|
513
|
+
if (!row) throw new NotFoundError(options)
|
|
514
|
+
// The row, not the earlier read, since a concurrent `update` can change the app between them.
|
|
515
|
+
return { app: toApp(row), clientSecret }
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
export declare namespace rotateSecret {
|
|
519
|
+
type Options = {
|
|
520
|
+
/** The app's client ID. */
|
|
521
|
+
clientId: string
|
|
522
|
+
/** The new client secret, such as the one that another deployment issued. Defaults to a new secret. */
|
|
523
|
+
clientSecret?: string | undefined
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Updates the app with `clientId`, and returns it. A value left out stays.
|
|
529
|
+
*
|
|
530
|
+
* Throws `NotFoundError` when no app has the client ID, and `InvalidError` for an invalid name or
|
|
531
|
+
* redirect URI.
|
|
532
|
+
*/
|
|
533
|
+
export async function update(db: Realm.Database, options: update.Options) {
|
|
534
|
+
const current = await find(db, options)
|
|
535
|
+
const app = toApp(current)
|
|
536
|
+
const name = options.name === undefined ? undefined : parseName(options.name)
|
|
537
|
+
const postLogoutRedirectUris =
|
|
538
|
+
options.postLogoutRedirectUris === undefined
|
|
539
|
+
? undefined
|
|
540
|
+
: parsePostLogoutRedirectUris(options.postLogoutRedirectUris, {
|
|
541
|
+
type: app.type,
|
|
542
|
+
})
|
|
543
|
+
const redirectUris =
|
|
544
|
+
options.redirectUris === undefined
|
|
545
|
+
? undefined
|
|
546
|
+
: parseRedirectUris(options.redirectUris, { type: app.type })
|
|
547
|
+
|
|
548
|
+
const [row] = await db
|
|
549
|
+
.update(Schema.oauthClient)
|
|
550
|
+
.set({
|
|
551
|
+
...(name === undefined ? {} : { name }),
|
|
552
|
+
...(postLogoutRedirectUris === undefined
|
|
553
|
+
? {}
|
|
554
|
+
: { postLogoutRedirectUris: [...postLogoutRedirectUris] }),
|
|
555
|
+
// An app registered before logout existed gains it with its post-logout redirect URIs.
|
|
556
|
+
...(postLogoutRedirectUris?.length ? { enableEndSession: true } : {}),
|
|
557
|
+
...(redirectUris === undefined
|
|
558
|
+
? {}
|
|
559
|
+
: { redirectUris: [...redirectUris] }),
|
|
560
|
+
...(options.sign === undefined
|
|
561
|
+
? {}
|
|
562
|
+
: {
|
|
563
|
+
scopes: scopesOf({
|
|
564
|
+
scopes: current.scopes ?? undefined,
|
|
565
|
+
sign: options.sign,
|
|
566
|
+
}),
|
|
567
|
+
}),
|
|
568
|
+
...(options.skipConsent === undefined
|
|
569
|
+
? {}
|
|
570
|
+
: { skipConsent: options.skipConsent }),
|
|
571
|
+
updatedAt: new Date(),
|
|
572
|
+
})
|
|
573
|
+
.where(eq(Schema.oauthClient.clientId, options.clientId))
|
|
574
|
+
.returning()
|
|
575
|
+
// A concurrent `remove` deleted the app after the read.
|
|
576
|
+
if (!row) throw new NotFoundError(options)
|
|
577
|
+
return toApp(row)
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
export declare namespace update {
|
|
581
|
+
type Options = {
|
|
582
|
+
/** The app's client ID. */
|
|
583
|
+
clientId: string
|
|
584
|
+
/** The display name. */
|
|
585
|
+
name?: string | undefined
|
|
586
|
+
/** Every URI that `GET /oauth2/end-session` may return to. They replace the earlier ones, and none clears them. */
|
|
587
|
+
postLogoutRedirectUris?: readonly string[] | undefined
|
|
588
|
+
/** Every URI that the app returns to. They replace the earlier ones. */
|
|
589
|
+
redirectUris?: readonly string[] | undefined
|
|
590
|
+
/** Whether the app holds the `tempo-id:sign` scope. */
|
|
591
|
+
sign?: boolean | undefined
|
|
592
|
+
/** Whether a request without a key skips the approval page. */
|
|
593
|
+
skipConsent?: boolean | undefined
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/** Returns the `oauth_client` row with `clientId`. Throws `NotFoundError` when no app has it. */
|
|
598
|
+
async function find(db: Realm.Database, options: { clientId: string }) {
|
|
599
|
+
const [row] = await db
|
|
600
|
+
.select()
|
|
601
|
+
.from(Schema.oauthClient)
|
|
602
|
+
.where(eq(Schema.oauthClient.clientId, options.clientId))
|
|
603
|
+
if (!row) throw new NotFoundError({ clientId: options.clientId })
|
|
604
|
+
return row
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Returns `clientId`. Throws `InvalidError` unless it holds 8 to 64 letters, digits, `-`, or `_`,
|
|
609
|
+
* which every URL and form carries unescaped.
|
|
610
|
+
*/
|
|
611
|
+
function parseClientId(clientId: string) {
|
|
612
|
+
if (!/^[A-Za-z0-9_-]{8,64}$/.test(clientId))
|
|
613
|
+
throw new InvalidError(
|
|
614
|
+
`Invalid client ID: ${clientId}. Use 8 to 64 letters, digits, -, or _.`,
|
|
615
|
+
)
|
|
616
|
+
return clientId
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/** Returns `clientSecret`. Throws `InvalidError` when it holds fewer than 32 characters. */
|
|
620
|
+
function parseClientSecret(clientSecret: string) {
|
|
621
|
+
// The message leaves the secret out, since errors never hold one.
|
|
622
|
+
if (clientSecret.length < 32)
|
|
623
|
+
throw new InvalidError(
|
|
624
|
+
'Invalid client secret: fewer than 32 characters. Use a longer secret.',
|
|
625
|
+
)
|
|
626
|
+
return clientSecret
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/** Returns `name` trimmed. Throws `InvalidError` when it is empty. */
|
|
630
|
+
function parseName(name: string) {
|
|
631
|
+
const trimmed = name.trim()
|
|
632
|
+
if (!trimmed)
|
|
633
|
+
throw new InvalidError('Invalid app: the name is empty. Set a name.')
|
|
634
|
+
return trimmed
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Returns the post-logout redirect URIs `uris`, each named once. They follow the rules of
|
|
639
|
+
* `parseRedirectUris`, and may be empty. Throws `InvalidError` for an invalid URI.
|
|
640
|
+
*/
|
|
641
|
+
function parsePostLogoutRedirectUris(
|
|
642
|
+
uris: readonly string[],
|
|
643
|
+
options: { type: App['type'] },
|
|
644
|
+
): readonly string[] {
|
|
645
|
+
if (uris.length === 0) return []
|
|
646
|
+
return parseRedirectUris(uris, options)
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Returns `uris`, each named once. A web app returns to `https` URIs, and a native app to `https` or
|
|
651
|
+
* a custom scheme, such as `com.example.app:/callback`. Loopback and other `http` redirects are
|
|
652
|
+
* refused, so a CLI uses the device grant. Throws `InvalidError` for an invalid URI.
|
|
653
|
+
*/
|
|
654
|
+
function parseRedirectUris(
|
|
655
|
+
uris: readonly string[],
|
|
656
|
+
options: { type: App['type'] },
|
|
657
|
+
): readonly string[] {
|
|
658
|
+
if (uris.length === 0)
|
|
659
|
+
throw new InvalidError(
|
|
660
|
+
'Invalid app: no redirect URI. Set at least one redirect URI.',
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
for (const uri of uris) {
|
|
664
|
+
const url = URL.parse(uri)
|
|
665
|
+
const reason = (() => {
|
|
666
|
+
if (!url) return 'is not an absolute URI'
|
|
667
|
+
// RFC 6749 §3.1.2 bars a fragment from a redirect URI.
|
|
668
|
+
if (url.hash || uri.includes('#')) return 'has a fragment'
|
|
669
|
+
if (url.username || url.password) return 'holds credentials'
|
|
670
|
+
if (url.protocol === 'https:')
|
|
671
|
+
return isLoopbackHost(url.hostname) ? 'has a loopback host' : undefined
|
|
672
|
+
if (url.protocol === 'http:') return 'uses http'
|
|
673
|
+
if (options.type === 'web') return 'is not https'
|
|
674
|
+
// Refuses reserved schemes, such as `javascript:` and `file:`, as Better Auth's registration does.
|
|
675
|
+
if (!isNativePrivateUseRedirectUri(url))
|
|
676
|
+
return 'is not a valid custom scheme'
|
|
677
|
+
return undefined
|
|
678
|
+
})()
|
|
679
|
+
if (reason)
|
|
680
|
+
throw new InvalidError(
|
|
681
|
+
`Invalid redirect URI: ${uri} ${reason}. Use an https URI${options.type === 'native' ? ', or a custom scheme such as com.example.app:/callback' : ''}.`,
|
|
682
|
+
)
|
|
683
|
+
}
|
|
684
|
+
return [...new Set(uris)]
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Returns `scopes`, or the scopes that every app may request, with `tempo-id:sign` only when `sign`
|
|
689
|
+
* is set. Every other scope stays as it is.
|
|
690
|
+
*/
|
|
691
|
+
function scopesOf(options: {
|
|
692
|
+
scopes?: readonly string[] | undefined
|
|
693
|
+
sign: boolean
|
|
694
|
+
}): string[] {
|
|
695
|
+
const kept = (options.scopes ?? scopes).filter((scope) => scope !== signScope)
|
|
696
|
+
return [...kept, ...(options.sign ? [signScope] : [])]
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/** Returns a new client secret: 32 random letters, as Better Auth creates by default. */
|
|
700
|
+
function secret() {
|
|
701
|
+
return generateRandomString(32, 'a-z', 'A-Z')
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/** Returns the app of an `oauth_client` row. */
|
|
705
|
+
function toApp(row: typeof Schema.oauthClient.$inferSelect): App {
|
|
706
|
+
return {
|
|
707
|
+
clientId: row.clientId,
|
|
708
|
+
createdAt: row.createdAt,
|
|
709
|
+
device: (row.grantTypes ?? []).includes(deviceGrant),
|
|
710
|
+
name: row.name,
|
|
711
|
+
postLogoutRedirectUris: row.postLogoutRedirectUris ?? [],
|
|
712
|
+
redirectUris: row.redirectUris,
|
|
713
|
+
secret: Boolean(row.clientSecret),
|
|
714
|
+
sign: (row.scopes ?? []).includes(signScope),
|
|
715
|
+
skipConsent: row.skipConsent ?? false,
|
|
716
|
+
type: row.applicationType === 'native' ? 'native' : 'web',
|
|
717
|
+
}
|
|
718
|
+
}
|