Add Better Auth worker route
This commit is contained in:
Generated
+360
-1
@@ -7,6 +7,9 @@
|
||||
"": {
|
||||
"name": "@elydora/cloudflare-worker",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"better-auth": "^1.6.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "4.20260508.1",
|
||||
"@types/node": "24.12.3",
|
||||
@@ -14,6 +17,138 @@
|
||||
"wrangler": "4.90.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@better-auth/core": {
|
||||
"version": "1.6.9",
|
||||
"resolved": "https://registry.npmjs.org/@better-auth/core/-/core-1.6.9.tgz",
|
||||
"integrity": "sha512-ADFk5pwmLybmc+LvYvXJ6M1x2oY/EyYLkwLuH0x28FUq12DfjL0wnE7g+WRDf3yozDO+qIxTpFGXDGwLKbfz0w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@opentelemetry/semantic-conventions": "^1.39.0",
|
||||
"@standard-schema/spec": "^1.1.0",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@better-auth/utils": "0.4.0",
|
||||
"@better-fetch/fetch": "1.1.21",
|
||||
"@cloudflare/workers-types": ">=4",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"better-call": "1.3.5",
|
||||
"jose": "^6.1.0",
|
||||
"kysely": "^0.28.5",
|
||||
"nanostores": "^1.0.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@cloudflare/workers-types": {
|
||||
"optional": true
|
||||
},
|
||||
"@opentelemetry/api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@better-auth/drizzle-adapter": {
|
||||
"version": "1.6.9",
|
||||
"resolved": "https://registry.npmjs.org/@better-auth/drizzle-adapter/-/drizzle-adapter-1.6.9.tgz",
|
||||
"integrity": "sha512-Lcco5hOGrMgc4XKAkvB6x72eQm4wCcya8IevMg4wBHY9W9GVg8pu23rpRX6VsVQSO4Ux13S7lFwUWtF7/r9aKw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@better-auth/core": "^1.6.9",
|
||||
"@better-auth/utils": "0.4.0",
|
||||
"drizzle-orm": "^0.45.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"drizzle-orm": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@better-auth/kysely-adapter": {
|
||||
"version": "1.6.9",
|
||||
"resolved": "https://registry.npmjs.org/@better-auth/kysely-adapter/-/kysely-adapter-1.6.9.tgz",
|
||||
"integrity": "sha512-gyjuuxJtZ4o9G9z9q4kqn24X2kvMSp7F+KHogYxF03SnXY/2WleAcuj57iC4wP3e9mGDbjPOrnM5K6Kr3Ktdpw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@better-auth/core": "^1.6.9",
|
||||
"@better-auth/utils": "0.4.0",
|
||||
"kysely": "^0.28.14"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"kysely": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@better-auth/memory-adapter": {
|
||||
"version": "1.6.9",
|
||||
"resolved": "https://registry.npmjs.org/@better-auth/memory-adapter/-/memory-adapter-1.6.9.tgz",
|
||||
"integrity": "sha512-XmIG4tUnOXZ+KEcWjHUjOI9Z5donD09dC2t/AQTXifAUIqx7cySg86w0KTM09ArzAxRx1fCqO36Wkt5nULnrkQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@better-auth/core": "^1.6.9",
|
||||
"@better-auth/utils": "0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@better-auth/mongo-adapter": {
|
||||
"version": "1.6.9",
|
||||
"resolved": "https://registry.npmjs.org/@better-auth/mongo-adapter/-/mongo-adapter-1.6.9.tgz",
|
||||
"integrity": "sha512-h+AiRJ/TsBSi+ZDjySASBpbJ/9QCXBre34PSKgCz7QmTHrFM9Cg2EM4AM7LjR5lPXipEE+2rWPBc9wfnUBjhcw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@better-auth/core": "^1.6.9",
|
||||
"@better-auth/utils": "0.4.0",
|
||||
"mongodb": "^6.0.0 || ^7.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"mongodb": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@better-auth/prisma-adapter": {
|
||||
"version": "1.6.9",
|
||||
"resolved": "https://registry.npmjs.org/@better-auth/prisma-adapter/-/prisma-adapter-1.6.9.tgz",
|
||||
"integrity": "sha512-XHks01ntK20orqK/jICq8wmEbJ/zT6dct49Fk8zTQKN9QNGDc+Ix5+7z/Kvui0DXGFf790GfvRozquzaLtXa8Q==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@better-auth/core": "^1.6.9",
|
||||
"@better-auth/utils": "0.4.0",
|
||||
"@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
||||
"prisma": "^5.0.0 || ^6.0.0 || ^7.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@prisma/client": {
|
||||
"optional": true
|
||||
},
|
||||
"prisma": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@better-auth/telemetry": {
|
||||
"version": "1.6.9",
|
||||
"resolved": "https://registry.npmjs.org/@better-auth/telemetry/-/telemetry-1.6.9.tgz",
|
||||
"integrity": "sha512-0u5zkhSCAQFoN3DHvUkLHOF6MBbVTDAa6mU8mhPwiysdz1x21vMzhzfaAKN/ZGWaQ09v91/F+2qu42G/bhUV4A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@better-auth/core": "^1.6.9",
|
||||
"@better-auth/utils": "0.4.0",
|
||||
"@better-fetch/fetch": "1.1.21"
|
||||
}
|
||||
},
|
||||
"node_modules/@better-auth/utils": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.4.0.tgz",
|
||||
"integrity": "sha512-RpMtLUIQAEWMgdPLNVbIF5ON2mm+CH0U3rCdUCU1VyeAUui4m38DyK7/aXMLZov2YDjG684pS1D0MBllrmgjQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@better-fetch/fetch": {
|
||||
"version": "1.1.21",
|
||||
"resolved": "https://registry.npmjs.org/@better-fetch/fetch/-/fetch-1.1.21.tgz",
|
||||
"integrity": "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A=="
|
||||
},
|
||||
"node_modules/@cloudflare/kv-asset-handler": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.5.0.tgz",
|
||||
@@ -129,7 +264,7 @@
|
||||
"version": "4.20260508.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20260508.1.tgz",
|
||||
"integrity": "sha512-0KNR+UkrYJYmtyQ5tOjUT/wt/U34FuE4Y8FLSbPMwFrGQQpmvR9wKghwRkL4d28y5t9jI9cvGqeAoo/cerTnCQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT OR Apache-2.0"
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
@@ -1164,6 +1299,39 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/ciphers": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-2.2.0.tgz",
|
||||
"integrity": "sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 20.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz",
|
||||
"integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 20.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/semantic-conventions": {
|
||||
"version": "1.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz",
|
||||
"integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@poppinss/colors": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz",
|
||||
@@ -1213,6 +1381,12 @@
|
||||
"dev": true,
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/@standard-schema/spec": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
|
||||
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.12.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.3.tgz",
|
||||
@@ -1223,6 +1397,131 @@
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/better-auth": {
|
||||
"version": "1.6.9",
|
||||
"resolved": "https://registry.npmjs.org/better-auth/-/better-auth-1.6.9.tgz",
|
||||
"integrity": "sha512-EBFURtglyiEZxbx4NJBoqUD8J65dX24yC+6I9AUbIXNgUkt76mshzGbHkxZ3n/lB7Dwq3kBC+hHt0hUQsnL7HA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@better-auth/core": "1.6.9",
|
||||
"@better-auth/drizzle-adapter": "1.6.9",
|
||||
"@better-auth/kysely-adapter": "1.6.9",
|
||||
"@better-auth/memory-adapter": "1.6.9",
|
||||
"@better-auth/mongo-adapter": "1.6.9",
|
||||
"@better-auth/prisma-adapter": "1.6.9",
|
||||
"@better-auth/telemetry": "1.6.9",
|
||||
"@better-auth/utils": "0.4.0",
|
||||
"@better-fetch/fetch": "1.1.21",
|
||||
"@noble/ciphers": "^2.1.1",
|
||||
"@noble/hashes": "^2.0.1",
|
||||
"better-call": "1.3.5",
|
||||
"defu": "^6.1.4",
|
||||
"jose": "^6.1.3",
|
||||
"kysely": "^0.28.14",
|
||||
"nanostores": "^1.1.1",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@lynx-js/react": "*",
|
||||
"@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@tanstack/react-start": "^1.0.0",
|
||||
"@tanstack/solid-start": "^1.0.0",
|
||||
"better-sqlite3": "^12.0.0",
|
||||
"drizzle-kit": ">=0.31.4",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"mongodb": "^6.0.0 || ^7.0.0",
|
||||
"mysql2": "^3.0.0",
|
||||
"next": "^14.0.0 || ^15.0.0 || ^16.0.0",
|
||||
"pg": "^8.0.0",
|
||||
"prisma": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0",
|
||||
"solid-js": "^1.0.0",
|
||||
"svelte": "^4.0.0 || ^5.0.0",
|
||||
"vitest": "^2.0.0 || ^3.0.0 || ^4.0.0",
|
||||
"vue": "^3.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@lynx-js/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@prisma/client": {
|
||||
"optional": true
|
||||
},
|
||||
"@sveltejs/kit": {
|
||||
"optional": true
|
||||
},
|
||||
"@tanstack/react-start": {
|
||||
"optional": true
|
||||
},
|
||||
"@tanstack/solid-start": {
|
||||
"optional": true
|
||||
},
|
||||
"better-sqlite3": {
|
||||
"optional": true
|
||||
},
|
||||
"drizzle-kit": {
|
||||
"optional": true
|
||||
},
|
||||
"drizzle-orm": {
|
||||
"optional": true
|
||||
},
|
||||
"mongodb": {
|
||||
"optional": true
|
||||
},
|
||||
"mysql2": {
|
||||
"optional": true
|
||||
},
|
||||
"next": {
|
||||
"optional": true
|
||||
},
|
||||
"pg": {
|
||||
"optional": true
|
||||
},
|
||||
"prisma": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"solid-js": {
|
||||
"optional": true
|
||||
},
|
||||
"svelte": {
|
||||
"optional": true
|
||||
},
|
||||
"vitest": {
|
||||
"optional": true
|
||||
},
|
||||
"vue": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/better-call": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/better-call/-/better-call-1.3.5.tgz",
|
||||
"integrity": "sha512-kOFJkBP7utAQLEYrobZm3vkTH8mXq5GNgvjc5/XEST1ilVHaxXUXfeDeFlqoETMtyqS4+3/h4ONX2i++ebZrvA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@better-auth/utils": "^0.4.0",
|
||||
"@better-fetch/fetch": "^1.1.21",
|
||||
"rou3": "^0.7.12",
|
||||
"set-cookie-parser": "^3.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"zod": "^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"zod": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/blake3-wasm": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz",
|
||||
@@ -1244,6 +1543,12 @@
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/defu": {
|
||||
"version": "6.1.7",
|
||||
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz",
|
||||
"integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
@@ -1321,6 +1626,15 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz",
|
||||
"integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/kleur": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
|
||||
@@ -1331,6 +1645,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/kysely": {
|
||||
"version": "0.28.17",
|
||||
"resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.17.tgz",
|
||||
"integrity": "sha512-nbD8lB9EB3wNdMhOCdx5Li8DxnLbvKByylRLcJ1h+4SkrowVeECAyZlyiKMThF7xFdRz0jSQ2MoJr+wXux2y0Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare": {
|
||||
"version": "4.20260507.1",
|
||||
"resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260507.1.tgz",
|
||||
@@ -1352,6 +1675,21 @@
|
||||
"node": ">=22.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nanostores": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/nanostores/-/nanostores-1.3.0.tgz",
|
||||
"integrity": "sha512-XPUa/jz+P1oJvN9VBxw4L9MtdFfaH3DAryqPssqhb2kXjmb9npz0dly6rCsgFWOPr4Yg9mTfM3MDZgZZ+7A3lA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^20.0.0 || >=22.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
|
||||
@@ -1366,6 +1704,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/rou3": {
|
||||
"version": "0.7.12",
|
||||
"resolved": "https://registry.npmjs.org/rou3/-/rou3-0.7.12.tgz",
|
||||
"integrity": "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
@@ -1379,6 +1723,12 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/set-cookie-parser": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz",
|
||||
"integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
||||
@@ -1588,6 +1938,15 @@
|
||||
"@poppinss/exception": "^1.2.2",
|
||||
"error-stack-parser-es": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
|
||||
"integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,5 +12,8 @@
|
||||
"@types/node": "24.12.3",
|
||||
"typescript": "6.0.3",
|
||||
"wrangler": "4.90.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"better-auth": "^1.6.9"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
import { betterAuth, type BetterAuthOptions } from "better-auth";
|
||||
import { emailOTP } from "better-auth/plugins/email-otp";
|
||||
|
||||
import type { Env } from "./bindings.js";
|
||||
import { jsonResponse } from "./responses.js";
|
||||
|
||||
const APP_NAME = "ELY Browser";
|
||||
const AUTH_BASE_PATH = "/api/auth";
|
||||
const AUTH_CALLBACK_URL = "ely://auth/callback";
|
||||
const OTP_REQUEST_TIMEOUT_MS = 10_000;
|
||||
|
||||
type BetterAuthDatabase = NonNullable<BetterAuthOptions["database"]>;
|
||||
type BetterAuthSocialProviders = NonNullable<BetterAuthOptions["socialProviders"]>;
|
||||
|
||||
export async function handleBetterAuthRoute(request: Request, env: Env): Promise<Response> {
|
||||
try {
|
||||
return createElyAuth(env).handler(request);
|
||||
} catch (error) {
|
||||
if (error instanceof BetterAuthConfigError) {
|
||||
return jsonResponse({ error: "auth_unconfigured" }, 500, {
|
||||
"Cache-Control": "no-store",
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function createElyAuth(env: Env) {
|
||||
return betterAuth({
|
||||
appName: APP_NAME,
|
||||
basePath: AUTH_BASE_PATH,
|
||||
baseURL: requiredBinding(env.ELY_AUTH_BASE_URL, "ELY_AUTH_BASE_URL"),
|
||||
secret: requiredBinding(env.ELY_AUTH_SECRET, "ELY_AUTH_SECRET"),
|
||||
database: env.ELY_DB as BetterAuthDatabase,
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
minPasswordLength: 12,
|
||||
maxPasswordLength: 128,
|
||||
revokeSessionsOnPasswordReset: true,
|
||||
},
|
||||
socialProviders: socialProviders(env),
|
||||
trustedOrigins: [requiredBinding(env.ELY_AUTH_BASE_URL, "ELY_AUTH_BASE_URL"), AUTH_CALLBACK_URL],
|
||||
plugins: [
|
||||
emailOTP({
|
||||
expiresIn: 300,
|
||||
allowedAttempts: 3,
|
||||
storeOTP: "encrypted",
|
||||
resendStrategy: "rotate",
|
||||
sendVerificationOTP: (data) => sendVerificationOtp(env, data),
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function socialProviders(env: Env): BetterAuthSocialProviders {
|
||||
const providers: BetterAuthSocialProviders = {};
|
||||
const google = bindingPair(env.ELY_AUTH_GOOGLE_CLIENT_ID, env.ELY_AUTH_GOOGLE_CLIENT_SECRET);
|
||||
if (google !== null) {
|
||||
providers.google = {
|
||||
clientId: google.clientId,
|
||||
clientSecret: google.clientSecret,
|
||||
};
|
||||
}
|
||||
const github = bindingPair(env.ELY_AUTH_GITHUB_CLIENT_ID, env.ELY_AUTH_GITHUB_CLIENT_SECRET);
|
||||
if (github !== null) {
|
||||
providers.github = {
|
||||
clientId: github.clientId,
|
||||
clientSecret: github.clientSecret,
|
||||
};
|
||||
}
|
||||
return providers;
|
||||
}
|
||||
|
||||
function bindingPair(
|
||||
clientId: string | undefined,
|
||||
clientSecret: string | undefined,
|
||||
): { clientId: string; clientSecret: string } | null {
|
||||
if (!isPresent(clientId) || !isPresent(clientSecret)) {
|
||||
return null;
|
||||
}
|
||||
return { clientId: clientId.trim(), clientSecret: clientSecret.trim() };
|
||||
}
|
||||
|
||||
async function sendVerificationOtp(
|
||||
env: Env,
|
||||
data: {
|
||||
email: string;
|
||||
otp: string;
|
||||
type: "sign-in" | "email-verification" | "forget-password" | "change-email";
|
||||
},
|
||||
): Promise<void> {
|
||||
const endpoint = requiredBinding(env.ELY_AUTH_EMAIL_OTP_ENDPOINT, "ELY_AUTH_EMAIL_OTP_ENDPOINT");
|
||||
const token = requiredBinding(env.ELY_AUTH_EMAIL_OTP_TOKEN, "ELY_AUTH_EMAIL_OTP_TOKEN");
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), OTP_REQUEST_TIMEOUT_MS);
|
||||
try {
|
||||
const response = await fetch(endpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
signal: controller.signal,
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new BetterAuthEmailDeliveryError(response.status);
|
||||
}
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
function requiredBinding(value: string | undefined, name: string): string {
|
||||
if (isPresent(value)) {
|
||||
return value.trim();
|
||||
}
|
||||
throw new BetterAuthConfigError(name);
|
||||
}
|
||||
|
||||
function isPresent(value: string | undefined): value is string {
|
||||
return value !== undefined && value.trim() !== "";
|
||||
}
|
||||
|
||||
class BetterAuthConfigError extends Error {
|
||||
constructor(readonly binding: string) {
|
||||
super(`missing auth binding: ${binding}`);
|
||||
this.name = "BetterAuthConfigError";
|
||||
}
|
||||
}
|
||||
|
||||
class BetterAuthEmailDeliveryError extends Error {
|
||||
constructor(readonly status: number) {
|
||||
super(`auth otp delivery failed with status ${status}`);
|
||||
this.name = "BetterAuthEmailDeliveryError";
|
||||
}
|
||||
}
|
||||
@@ -52,4 +52,12 @@ export interface Env {
|
||||
ELY_RATE_LIMITER: ElyRateLimit;
|
||||
ELY_API_AUDIT: ElyAnalyticsDataset;
|
||||
ELY_ENVIRONMENT: string;
|
||||
ELY_AUTH_BASE_URL: string;
|
||||
ELY_AUTH_SECRET: string;
|
||||
ELY_AUTH_GOOGLE_CLIENT_ID?: string;
|
||||
ELY_AUTH_GOOGLE_CLIENT_SECRET?: string;
|
||||
ELY_AUTH_GITHUB_CLIENT_ID?: string;
|
||||
ELY_AUTH_GITHUB_CLIENT_SECRET?: string;
|
||||
ELY_AUTH_EMAIL_OTP_ENDPOINT?: string;
|
||||
ELY_AUTH_EMAIL_OTP_TOKEN?: string;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Env } from "./bindings.js";
|
||||
import { withAuthenticatedApiControls, withPublicApiControls } from "./api_controls.js";
|
||||
import { handleBetterAuthRoute } from "./better_auth.js";
|
||||
import {
|
||||
DevicePermissionError,
|
||||
DevicePersistenceError,
|
||||
@@ -42,6 +43,9 @@ export default {
|
||||
|
||||
export async function handleRequest(request: Request, env: Env): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname === "/api/auth" || url.pathname.startsWith("/api/auth/")) {
|
||||
return handleBetterAuthRoute(request, env);
|
||||
}
|
||||
if (url.pathname === "/api/devices") {
|
||||
return withAuthenticatedApiControls(request, env, "devices.list", ["GET"], async (context) => {
|
||||
try {
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare module "bun:sqlite" {
|
||||
export class Database {}
|
||||
}
|
||||
@@ -244,6 +244,8 @@ function testEnv(options: TestEnvOptions = {}): Env {
|
||||
|
||||
return {
|
||||
ELY_ENVIRONMENT: "local",
|
||||
ELY_AUTH_BASE_URL: "https://elydora.test",
|
||||
ELY_AUTH_SECRET: "test-auth-secret-for-api-controls",
|
||||
ELY_DB: testD1Database(),
|
||||
ELY_KV: {
|
||||
get(key: string): Promise<string | null> {
|
||||
|
||||
@@ -41,6 +41,8 @@ export function testEnv(options: TestEnvOptions): Env {
|
||||
const values = new Map(options.kvEntries ?? []);
|
||||
return {
|
||||
ELY_ENVIRONMENT: "local",
|
||||
ELY_AUTH_BASE_URL: "https://elydora.test",
|
||||
ELY_AUTH_SECRET: "test-auth-secret-for-worker-routes",
|
||||
ELY_DB: options.d1 ?? testD1Database([]),
|
||||
ELY_KV: {
|
||||
get(key: string): Promise<string | null> {
|
||||
|
||||
@@ -82,6 +82,17 @@ describe("worker routes", () => {
|
||||
assert.deepEqual(await response.json(), { error: "not_found" });
|
||||
});
|
||||
|
||||
it("routes Better Auth session requests under api auth", async () => {
|
||||
const response = await handleRequest(
|
||||
new Request("https://elydora.test/api/auth/get-session"),
|
||||
testEnv(null),
|
||||
);
|
||||
|
||||
assert.equal(response.status, 200);
|
||||
assert.equal(response.headers.get("content-type"), "application/json");
|
||||
assert.equal(await response.text(), "null");
|
||||
});
|
||||
|
||||
it("returns public plugin catalog from KV", async () => {
|
||||
const response = await handleRequest(
|
||||
new Request("https://elydora.test/api/plugins"),
|
||||
@@ -341,6 +352,8 @@ function testEnv(
|
||||
|
||||
return {
|
||||
ELY_ENVIRONMENT: "local",
|
||||
ELY_AUTH_BASE_URL: "https://elydora.test",
|
||||
ELY_AUTH_SECRET: "test-auth-secret-for-worker-routes",
|
||||
ELY_DB: testD1Database(),
|
||||
ELY_KV: {
|
||||
get(key: string): Promise<string | null> {
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"lib": ["ES2022"],
|
||||
"types": ["@cloudflare/workers-types"],
|
||||
"types": ["@cloudflare/workers-types", "node"],
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": false,
|
||||
"skipLibCheck": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"rootDir": "."
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": false,
|
||||
"skipLibCheck": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"rootDir": "."
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
name = "ely-browser-cloud"
|
||||
main = "src/index.ts"
|
||||
compatibility_date = "2026-05-08"
|
||||
compatibility_flags = ["nodejs_als"]
|
||||
|
||||
[[d1_databases]]
|
||||
binding = "ELY_DB"
|
||||
@@ -18,6 +19,7 @@ id = "5eff92ba31c94fcf83e1b6d5e79ce070"
|
||||
|
||||
[vars]
|
||||
ELY_ENVIRONMENT = "local"
|
||||
ELY_AUTH_BASE_URL = "http://localhost:8787"
|
||||
|
||||
[[analytics_engine_datasets]]
|
||||
binding = "ELY_API_AUDIT"
|
||||
|
||||
Reference in New Issue
Block a user