{"_id":"@auth-craft/pages-auth-proxy","_rev":"3-dd3ce889001d58e00f56b6f685d16baf","name":"@auth-craft/pages-auth-proxy","dist-tags":{"latest":"1.1.1"},"versions":{"1.0.0":{"name":"@auth-craft/pages-auth-proxy","version":"1.0.0","keywords":["auth-craft","cloudflare","pages","proxy","bff","first-party-cookies"],"author":{"name":"Auth Craft Contributors"},"license":"MIT","_id":"@auth-craft/pages-auth-proxy@1.0.0","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"dist":{"shasum":"8c38b1f5386c22cd5e0509e2f0d225e90b652ac2","tarball":"https://registry.npmjs.org/@auth-craft/pages-auth-proxy/-/pages-auth-proxy-1.0.0.tgz","fileCount":14,"integrity":"sha512-3kPHeyv5K1p7QwXhycriK3smcplUbRPoVcxg1P74MLFwq0wfzhZOhXnO0N/JHgEulXEt8jFCn0aAB5Y75mgHSA==","signatures":[{"sig":"MEQCIEK2ogruKst+3TjytvhBH+Tu6+KIpdjnIAgXwacG5fB4AiB0zgmHRAVwnyTf/P5IWTOpDjct3CnaaJj2T1TBSGQwHQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":21127},"main":"./dist/index.js","type":"module","_from":"file:auth-craft-pages-auth-proxy-1.0.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./pages":{"types":"./dist/pages.d.ts","import":"./dist/pages.js"}},"scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest","type-check":"tsc --noEmit"},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"_resolved":"/tmp/28f0fc9062de4c1e6f9107a733df1a86/auth-craft-pages-auth-proxy-1.0.0.tgz","_integrity":"sha512-3kPHeyv5K1p7QwXhycriK3smcplUbRPoVcxg1P74MLFwq0wfzhZOhXnO0N/JHgEulXEt8jFCn0aAB5Y75mgHSA==","_npmVersion":"11.12.1","description":"Thin Cloudflare Pages reverse-proxy for Auth Craft — serves /{scope}/auth/* same-origin (first-party cookies) and forwards to the gateway worker (Service Binding or HTTP + X-Edge-Gate). Keeps all trust-critical logic in the gateway.","directories":{},"_nodeVersion":"24.15.0","_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.1.8","typescript":"^6.0.3","@auth-craft/tsconfig":"0.1.0","@cloudflare/workers-types":"^4.20260610.1"},"_npmOperationalInternal":{"tmp":"tmp/pages-auth-proxy_1.0.0_1781846484660_0.1601380221987052","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@auth-craft/pages-auth-proxy","version":"1.1.0","keywords":["auth-craft","cloudflare","pages","proxy","bff","first-party-cookies"],"author":{"name":"Auth Craft Contributors"},"license":"MIT","_id":"@auth-craft/pages-auth-proxy@1.1.0","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"dist":{"shasum":"a735401d0a4734aa8e5e6acbf8e1c1802b9ab8bc","tarball":"https://registry.npmjs.org/@auth-craft/pages-auth-proxy/-/pages-auth-proxy-1.1.0.tgz","fileCount":14,"integrity":"sha512-HjOqEKfCqGpLkH8y+WpuMGteDZdYIWD25PqFUx+ASNd39JiG5wTFUhf4BmPy1IBsB09HhpSGJ9BzSbTYT2ib6A==","signatures":[{"sig":"MEYCIQDCNWfkf6iDvW0m7V/wyc2esCOb9uzO9TO2mFk1RmbOsAIhAMuu2LdA/yEUevEWkOVQPiPH7gOq7Qp9SCk5OoWfeqjM","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25277},"main":"./dist/index.js","type":"module","_from":"file:auth-craft-pages-auth-proxy-1.1.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./pages":{"types":"./dist/pages.d.ts","import":"./dist/pages.js"}},"scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest","type-check":"tsc --noEmit"},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"_resolved":"/tmp/3b06d8593982a7eef643c8b36b6ce90f/auth-craft-pages-auth-proxy-1.1.0.tgz","_integrity":"sha512-HjOqEKfCqGpLkH8y+WpuMGteDZdYIWD25PqFUx+ASNd39JiG5wTFUhf4BmPy1IBsB09HhpSGJ9BzSbTYT2ib6A==","_npmVersion":"11.12.1","description":"Thin Cloudflare Pages reverse-proxy for Auth Craft — serves /{scope}/auth/* same-origin (first-party cookies) and forwards to the gateway worker (Service Binding or HTTP + X-Edge-Gate). Keeps all trust-critical logic in the gateway.","directories":{},"_nodeVersion":"24.15.0","_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.1.8","typescript":"^6.0.3","@auth-craft/tsconfig":"0.1.0","@cloudflare/workers-types":"^4.20260610.1"},"_npmOperationalInternal":{"tmp":"tmp/pages-auth-proxy_1.1.0_1781853423373_0.8233904220822383","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@auth-craft/pages-auth-proxy","version":"1.1.1","description":"Thin Cloudflare Pages reverse-proxy for Auth Craft — serves /{scope}/auth/* same-origin (first-party cookies) and forwards to the gateway worker (Service Binding or HTTP + X-Edge-Gate). Keeps all trust-critical logic in the gateway.","type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","devDependencies":{"@cloudflare/workers-types":"^5.20260814.1","typescript":"^7.0.2","vitest":"^4.1.10","@auth-craft/tsconfig":"0.1.0"},"keywords":["auth-craft","cloudflare","pages","proxy","bff","first-party-cookies"],"author":{"name":"Auth Craft Contributors"},"license":"MIT","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./pages":{"types":"./dist/pages.d.ts","import":"./dist/pages.js"}},"scripts":{"build":"tsc","dev":"tsc --watch","type-check":"tsc --noEmit","test":"vitest run","test:watch":"vitest","clean":"rm -rf dist"},"_id":"@auth-craft/pages-auth-proxy@1.1.1","_integrity":"sha512-4azCF3FVAmobwEY2PfRd8speHKBABJ0uwuUolWCpUzrPpByGGonl1gce2HuHTVi0lw0xaYF5xqIvrNs9zAsZ+A==","_resolved":"/tmp/aea4fe639a7a727760c4075c4c007546/auth-craft-pages-auth-proxy-1.1.1.tgz","_from":"file:auth-craft-pages-auth-proxy-1.1.1.tgz","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-4azCF3FVAmobwEY2PfRd8speHKBABJ0uwuUolWCpUzrPpByGGonl1gce2HuHTVi0lw0xaYF5xqIvrNs9zAsZ+A==","shasum":"363c84dc131d1fe390f1aa7cfa707dd5e0e80156","tarball":"https://registry.npmjs.org/@auth-craft/pages-auth-proxy/-/pages-auth-proxy-1.1.1.tgz","fileCount":14,"unpackedSize":25299,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCuNA2vCbzxgkbTz13+CfEr/jcdUhz0cT3sF6I18P6zTAIgNZOX2ium7+xdGm6LTQgTZ8mK57DoTl5aFu+tFL+NgwE="}]},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"directories":{},"maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pages-auth-proxy_1.1.1_1786706881441_0.7800234399270358"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-19T05:21:24.432Z","modified":"2026-08-14T11:28:01.797Z","1.0.0":"2026-06-19T05:21:24.793Z","1.1.0":"2026-06-19T07:17:03.519Z","1.1.1":"2026-08-14T11:28:01.576Z"},"author":{"name":"Auth Craft Contributors"},"license":"MIT","keywords":["auth-craft","cloudflare","pages","proxy","bff","first-party-cookies"],"description":"Thin Cloudflare Pages reverse-proxy for Auth Craft — serves /{scope}/auth/* same-origin (first-party cookies) and forwards to the gateway worker (Service Binding or HTTP + X-Edge-Gate). Keeps all trust-critical logic in the gateway.","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"readme":"# @auth-craft/pages-auth-proxy\n\nThin reverse-proxy so an app on **Cloudflare Pages** can serve `/{scope}/auth/*` on its\n**own origin**, then forward to the Auth Craft **gateway worker**.\n\nWhy: when the browser only ever sees the app's own origin, the refresh cookie the\nbackend sets is **first-party** (same-site) — `SameSite=Lax/Strict` works, there is **no\ncross-site CORS**, and it is **immune to Safari ITP**. This proxy is intentionally\n**thin**: it never holds backend secrets. All trust-critical logic (`X-Gateway-Secret`,\nJWT verify, rate-limit, stealth 404) stays in the gateway.\n\nThe one thing it *can* enforce is **scope**: lock the proxy to a single scope (`scope:\n'customer'`) and the frontend calls scope-less `/auth/...` while the proxy prepends the\nscope segment. The client never picks the scope, so a customer app **cannot** reach\nsystem/tenant context through it — the lock holds even against a single shared gateway\nworker that derives scope from the path. (Leave `scope` unset for the legacy behaviour\nwhere the client carries its own `/{scope}/...` segment.)\n\n> Optional: use this only for apps you deploy on Cloudflare Pages. Apps that can't (e.g.\n> a store you don't host) keep calling the gateway cross-origin and use **body** token\n> transport instead — both models work against the same gateway + backend.\n\n## Install\n\n```bash\npnpm add @auth-craft/pages-auth-proxy\n```\n\n## Cloudflare Pages usage\n\nAdd a catch-all Pages Function under `/auth` and lock it to this app's scope:\n\n```ts\n// functions/auth/[[path]].ts\nimport { createPagesAuthProxy } from '@auth-craft/pages-auth-proxy/pages';\n\nexport const onRequest = createPagesAuthProxy({ scope: 'customer' });\n```\n\nConfigure the Pages project bindings:\n\n| Binding | Purpose |\n|---------|---------|\n| `AUTH_GATEWAY` | **Service Binding** to the gateway worker (preferred — in-network, lowest latency) |\n| `AUTH_GATEWAY_URL` | Gateway public URL (fallback when no Service Binding) |\n| `EDGE_GATE_SECRET` | Client→gateway gate, sent as `X-Edge-Gate` (Pages **secret**) |\n\nThe frontend then calls auth on its **own origin**, scope-less, e.g.\n`fetch('/auth/authenticate', { method: 'POST', credentials: 'include', ... })`.\nThe proxy stamps the scope; the cookie set by the backend is first-party to the app.\n\n### Pairs with `@auth-craft/client` cookie mode\n\nBecause cookies are first-party, use the cookie provider — no IndexedDB, refresh handled\nby the `HttpOnly` cookie:\n\n```ts\nimport { createCookieProvider } from '@auth-craft/client';\n\nconst provider = createCookieProvider({\n  loginUrl: '/auth/authenticate',\n  refreshUrl: '/auth/refresh',\n  logoutUrl: '/auth/logout',\n});\n```\n\n## Framework-agnostic core\n\n```ts\nimport { createAuthProxy } from '@auth-craft/pages-auth-proxy';\n\nconst proxy = createAuthProxy({\n  scope: 'customer',\n  gatewayUrl: 'https://auth-craft-prod-customer.acme.workers.dev',\n  edgeGateSecret: env.EDGE_GATE_SECRET,\n});\n\nexport default { fetch: (req: Request) => proxy(req) };\n```\n\n## Security notes\n\n- **Not a CORS replacement — better.** Same-origin requests have no CORS at all. Site\n  isolation comes from first-party `SameSite` cookies + `HttpOnly` + the backend's CSRF\n  Origin check (`trustedOrigins`), not from CORS.\n- **Scope is enforced, not trusted.** With `scope` set, the prepended segment is the\n  lock's value; the client's path is normalized (`..` collapsed) before matching, so it\n  can't smuggle another scope. A customer app physically cannot reach system/tenant —\n  even against one shared gateway worker. Use one locked proxy per app/scope.\n- Keep `EDGE_GATE_SECRET` in a Pages **secret**. With a Service Binding you can configure\n  the gateway to trust the binding and avoid exposing the gate entirely.\n- The proxy preserves method, body, headers and cookies; it adds only `X-Edge-Gate`.\n","readmeFilename":"README.md"}