dbsc-toolkit 2.5.0 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +61 -41
  2. package/dist/core/crypto/jwk.js +3 -1
  3. package/dist/core/crypto/jwk.js.map +1 -1
  4. package/dist/core/index.d.ts +4 -1
  5. package/dist/core/index.d.ts.map +1 -1
  6. package/dist/core/index.js +2 -0
  7. package/dist/core/index.js.map +1 -1
  8. package/dist/core/protect-policy.d.ts +27 -0
  9. package/dist/core/protect-policy.d.ts.map +1 -0
  10. package/dist/core/protect-policy.js +8 -0
  11. package/dist/core/protect-policy.js.map +1 -0
  12. package/dist/core/protocol/cookies.d.ts +6 -0
  13. package/dist/core/protocol/cookies.d.ts.map +1 -0
  14. package/dist/core/protocol/cookies.js +33 -0
  15. package/dist/core/protocol/cookies.js.map +1 -0
  16. package/dist/core/types.d.ts +15 -0
  17. package/dist/core/types.d.ts.map +1 -1
  18. package/dist/express/create-dbsc.d.ts +41 -0
  19. package/dist/express/create-dbsc.d.ts.map +1 -0
  20. package/dist/express/create-dbsc.js +88 -0
  21. package/dist/express/create-dbsc.js.map +1 -0
  22. package/dist/express/index.d.ts +9 -0
  23. package/dist/express/index.d.ts.map +1 -1
  24. package/dist/express/index.js +11 -1
  25. package/dist/express/index.js.map +1 -1
  26. package/dist/express/require-proof.d.ts +20 -0
  27. package/dist/express/require-proof.d.ts.map +1 -0
  28. package/dist/express/require-proof.js +56 -0
  29. package/dist/express/require-proof.js.map +1 -0
  30. package/dist/fastify/create-dbsc.d.ts +32 -0
  31. package/dist/fastify/create-dbsc.d.ts.map +1 -0
  32. package/dist/fastify/create-dbsc.js +70 -0
  33. package/dist/fastify/create-dbsc.js.map +1 -0
  34. package/dist/fastify/index.d.ts +9 -0
  35. package/dist/fastify/index.d.ts.map +1 -1
  36. package/dist/fastify/index.js +7 -0
  37. package/dist/fastify/index.js.map +1 -1
  38. package/dist/fastify/require-proof.d.ts +14 -0
  39. package/dist/fastify/require-proof.d.ts.map +1 -0
  40. package/dist/fastify/require-proof.js +50 -0
  41. package/dist/fastify/require-proof.js.map +1 -0
  42. package/dist/hono/create-dbsc.d.ts +34 -0
  43. package/dist/hono/create-dbsc.d.ts.map +1 -0
  44. package/dist/hono/create-dbsc.js +69 -0
  45. package/dist/hono/create-dbsc.js.map +1 -0
  46. package/dist/hono/index.d.ts +9 -0
  47. package/dist/hono/index.d.ts.map +1 -1
  48. package/dist/hono/index.js +7 -0
  49. package/dist/hono/index.js.map +1 -1
  50. package/dist/hono/require-proof.d.ts +13 -0
  51. package/dist/hono/require-proof.d.ts.map +1 -0
  52. package/dist/hono/require-proof.js +47 -0
  53. package/dist/hono/require-proof.js.map +1 -0
  54. package/dist/nextjs/create-dbsc.d.ts +44 -0
  55. package/dist/nextjs/create-dbsc.d.ts.map +1 -0
  56. package/dist/nextjs/create-dbsc.js +68 -0
  57. package/dist/nextjs/create-dbsc.js.map +1 -0
  58. package/dist/nextjs/index.d.ts +4 -0
  59. package/dist/nextjs/index.d.ts.map +1 -1
  60. package/dist/nextjs/index.js +2 -0
  61. package/dist/nextjs/index.js.map +1 -1
  62. package/dist/nextjs/require-proof.d.ts +28 -0
  63. package/dist/nextjs/require-proof.d.ts.map +1 -0
  64. package/dist/nextjs/require-proof.js +44 -0
  65. package/dist/nextjs/require-proof.js.map +1 -0
  66. package/package.json +1 -1
package/README.md CHANGED
@@ -42,36 +42,28 @@ npm install dbsc-toolkit
42
42
  Pick the framework adapter and storage you actually use (each is an optional peer dependency):
43
43
 
44
44
  ```sh
45
- npm install express cookie-parser ioredis # Express + Redis
46
- npm install express cookie-parser pg # Express + Postgres
45
+ npm install express ioredis # Express + Redis
46
+ npm install express pg # Express + Postgres
47
47
  ```
48
48
 
49
49
  ## Quick start
50
50
 
51
- Copy-paste runnable. The whole picture is ~25 lines `trust proxy`, `cookieParser`, `express.json` and the static-file mount for the polyfill all matter, and missing any of them is the #1 reason "tier is always `none`" tickets get opened.
51
+ Copy-paste runnable. `createDbsc()` takes your config once; `install()` mounts everything the protocol routes, the bound-route JSON parser, the `/dbsc-client` SDK, and `trust proxy`. No `cookie-parser`, no manual static mount.
52
52
 
53
53
  ```ts
54
54
  import express from "express";
55
- import cookieParser from "cookie-parser";
56
55
  import { randomUUID } from "node:crypto";
57
- import { dbsc, bindSession } from "dbsc-toolkit/express";
56
+ import { createDbsc } from "dbsc-toolkit/express";
58
57
  import { MemoryStorage } from "dbsc-toolkit/storage/memory";
59
58
 
60
59
  const app = express();
60
+ app.use(express.json()); // for your own routes' JSON bodies
61
61
 
62
- app.set("trust proxy", true); // required behind Render / Fly / Cloudflare / nginx
63
- app.use(cookieParser()); // required: bindSession sets HttpOnly cookies
64
- app.use(express.json()); // required: bound polyfill POSTs JSON
65
- app.use(
66
- "/dbsc-client", // serves the browser SDK for Firefox / Safari
67
- express.static(new URL("../node_modules/dbsc-toolkit/dist/client/", import.meta.url).pathname),
68
- );
69
-
70
- const storage = new MemoryStorage(); // swap for RedisStorage / PostgresStorage in production
71
- app.use(dbsc({ storage })); // mounts /dbsc/registration, /dbsc/refresh, /dbsc-bound/*
62
+ const dbsc = createDbsc({ storage: new MemoryStorage() }); // swap for Redis/Postgres in prod
63
+ dbsc.install(app); // mounts /dbsc/*, /dbsc-bound/*, the SDK, trust proxy
72
64
 
73
65
  app.post("/login", async (req, res) => {
74
- await bindSession(res, randomUUID(), storage, { userId: req.body.username });
66
+ await dbsc.bind(res, randomUUID(), { userId: req.body.username });
75
67
  res.json({ ok: true });
76
68
  });
77
69
 
@@ -92,46 +84,67 @@ Without the script tag those browsers stay on `tier: "none"`. Native Chromium 14
92
84
 
93
85
  ### Common failure modes
94
86
 
95
- - **`tier` always reads `"none"` on Chromium 145+?** Forgot `trust proxy`, on plain HTTP, or middleware order is wrong (cookieParser must run before `dbsc`).
87
+ - **`tier` always reads `"none"` on Chromium 145+?** Running on plain HTTP (DBSC needs HTTPS), or `dbsc.install(app)` was never called. `install()` already sets `trust proxy` and parses cookies, so the old "middleware order" class of bug is gone.
96
88
  - **Chrome loops registration?** Storage was wiped — switch off `MemoryStorage` to Redis or Postgres before deploying anywhere that ever restarts.
97
89
  - **Tier flips back to `"none"` right after login?** The race between `/login` returning and the browser running `POST /dbsc/registration`. Poll `/me` for ~1 s after login or await the bound-SDK outcome promise. The demo wires both — see [examples/express/src/server.js](./examples/express/src/server.js).
98
- - **Firefox / Safari still on `"none"`?** Forgot the `<script type="module">` tag above, or the static-file mount is wrong.
90
+ - **Firefox / Safari still on `"none"`?** Forgot the `<script type="module">` tag above. `install()` serves the SDK at `/dbsc-client` for you; you still load it on the page.
99
91
 
100
92
  Full walk-through: [docs/getting-started.md](./docs/getting-started.md).
101
93
 
102
94
  ## Adding to an existing app
103
95
 
104
- You don't rewrite login, you don't migrate the session store. DBSC sits alongside your existing session cookie and binds to the same session id. For a typical Express app, the new lines you write are:
96
+ You don't rewrite login, you don't migrate the session store. DBSC sits alongside your existing session cookie and binds to the same session id. The whole integration for an Express app:
97
+
98
+ ```ts
99
+ import { createDbsc, requireProof } from "dbsc-toolkit/express";
100
+ import { RedisStorage } from "dbsc-toolkit/storage/redis";
101
+ import Redis from "ioredis";
102
+
103
+ // 1. configure the kit once — storage is the only required option
104
+ const dbsc = createDbsc({ storage: new RedisStorage(new Redis(process.env.REDIS_URL)) });
105
+
106
+ // 2. install once — mounts the protocol routes, trust proxy, the SDK
107
+ dbsc.install(app);
108
+
109
+ // 3. one line in your existing /login, after the password check
110
+ app.post("/login", async (req, res) => {
111
+ const user = await yourPasswordCheck(req.body); // unchanged
112
+ const sid = await issueYourOwnSession(user.id); // unchanged
113
+ await dbsc.bind(res, sid, { userId: user.id }); // <- the new line
114
+ res.json({ ok: true });
115
+ });
116
+
117
+ // 4. guard sensitive routes — one call. POST routes deliver raw bytes.
118
+ app.post("/payment", express.raw({ type: "*/*" }), requireProof(), paymentHandler);
105
119
 
106
- 1. `import { dbsc, bindSession, requireBoundProof } from "dbsc-toolkit/express";`
107
- 2. `import { RedisStorage } from "dbsc-toolkit/storage/redis";`
108
- 3. `const dbscStorage = new RedisStorage(new Redis(process.env.REDIS_URL));`
109
- 4. `app.use(dbsc({ storage: dbscStorage }));`
110
- 5. End of `/login`: `await bindSession(res, sessionId, dbscStorage, { userId: user.id });`
111
- 6. Start of `/logout`: `await res.locals.dbsc.revoke();`
120
+ // 5. one line in /logout
121
+ app.post("/logout", async (req, res) => {
122
+ await res.locals.dbsc.revoke(); // <- the new line
123
+ await yourSessionStore.delete(req.cookies.sid); // unchanged
124
+ res.json({ ok: true });
125
+ });
126
+ ```
112
127
 
113
- If your app isn't already set up for HTTPS-terminating proxies, `cookieParser`, or JSON bodies, you also need the four lines in the quick-start block above (`trust proxy`, `cookieParser()`, `express.json()`, and the `/dbsc-client/*` static mount for the polyfill). Most existing apps already have the first three.
128
+ `install()` handles `trust proxy`, cookie parsing, the bound-route JSON parser, and the `/dbsc-client` static mount you don't wire those. You still need `express.json()` for your *own* routes' bodies. `sessionId` is whatever id your session store already issues; DBSC binds to it — no second id-space.
114
129
 
115
- `sessionId` on line 5 is whatever id your existing session store issues. DBSC binds to that same id; you don't manage a second id-space.
130
+ **No server-side session id** (NextAuth JWT mode, iron-session, Lucia stateless)? Call `dbsc.bind(res, { userId })` with no id the kit derives a stable one and manages a per-device cookie so each browser of the same user binds independently. Per-system recipes: [docs/integration-recipes.md](./docs/integration-recipes.md).
116
131
 
117
- Using **NextAuth (JWT mode), iron-session, Lucia, or a hand-rolled JWT cookie**? Those have no server-side session id — call `deriveSessionId({ userId })` to get a stable one to pass to `bindSession()`. Copy-paste recipes for every common session system: [docs/integration-recipes.md](./docs/integration-recipes.md).
132
+ **The full step-by-step** every option and its default, the `autoBind` transparent-rollout variant, the per-route policy table, the migration timeline is in [docs/integrating-existing-auth.md](./docs/integrating-existing-auth.md).
118
133
 
119
- ## Choose your protection level per route
134
+ ## Protect your routes
120
135
 
121
- > **First time here?** [docs/usage.md](./docs/usage.md) walks the 6-line setup and the table below in order, with concrete code for each lever. Five-minute read.
136
+ > **First time here?** [docs/usage.md](./docs/usage.md) walks the setup and the guard in order, with concrete code. Five-minute read.
122
137
 
123
- After the 6-line setup, every request through the middleware has a `tier` field on the request context. The library does not auto-protect anything — you pick the level per route. The library exposes three levers; this table tells you which one to reach for.
138
+ After `createDbsc().install()`, every request through the middleware has a `tier` field on the request context. The library does not auto-protect anything — you add **one guard, `requireProof()`**, to each route that matters.
124
139
 
125
- | Your route does… | Use this guard | What it stops | Detailed in |
126
- |---|---|---|---|
127
- | Public / read-only (feed, search, public profile) | Nothing | n/a — no auth gate at all | n/a |
128
- | Authenticated action with no money / takeover risk (post, comment, upvote, edit own bio) | `if (req.dbsc.tier === "none") return 403` | Stolen cookie loses access after one refresh cycle (~60s–10min depending on `boundCookieTtl`) | [docs/integrating-existing-auth.md](./docs/integrating-existing-auth.md) (per-route policy table) |
129
- | Account takeover risk (password change, email change, admin actions) | `requireBoundProof({ storage })` | Stolen cookie cannot ride along even while the victim is online — Firefox / Safari now have the same guarantee Chrome gets from native DBSC | [docs/per-request-signing.md](./docs/per-request-signing.md) |
130
- | Moves money or numeric input that matters (payment, transfer, refund, withdraw) | `requireBoundProof({ storage, signBody: true })` on the server + `wrapFetch({ signBody: true })` on the client | All of the above PLUS an active MITM cannot substitute the request body (amount, recipient, etc.) within the timestamp window | [docs/per-request-signing.md#body-signing-setup-v230](./docs/per-request-signing.md) |
140
+ | Your route does… | Use this guard | What it stops |
141
+ |---|---|---|
142
+ | Public / read-only (feed, search, public profile) | Nothing | n/a — no auth gate at all |
143
+ | Anything authenticated (post, comment, upvote, settings, payment, admin) | `requireProof()` (server) + `wrapFetch({ signBody: true })` (client, for Firefox/Safari) | A stolen cookie cannot be replayed from another device, cannot ride along during the freshness window, and an MITM cannot substitute a POST body |
131
144
 
132
- **Each row is opt-in and additive.** A route can sit at row 2 today and graduate to row 3 next quarter when you ship paymentsno migration, no wire-format change, no version bump. The defaults (no guard) give DBSC's binding semantics without enforcement; pick the row your threat model needs.
145
+ There is deliberately **no tier-level argument**. A `dbsc`-only gate would lock out every Firefox/Safari user; a `bound`-only check (tier without a per-request proof) is not actually secure. `requireProof()` is the one honest answer it requires a bound device + a per-request proof and works on every browser (Chromium passes through natively, Firefox/Safari supply the signed proof). It signs the request body, so a POST guarded route mounts `express.raw()` in front.
133
146
 
134
- The full threat boundary for each level, the per-framework wiring (Fastify / Hono / Next.js), and the migration timeline for an existing app are in [docs/integrating-existing-auth.md](./docs/integrating-existing-auth.md) and [docs/per-request-signing.md](./docs/per-request-signing.md).
147
+ The full threat boundary, the per-framework wiring (Fastify / Hono / Next.js), and the migration timeline for an existing app are in [docs/integrating-existing-auth.md](./docs/integrating-existing-auth.md) and [docs/per-request-signing.md](./docs/per-request-signing.md).
135
148
 
136
149
  ## Subpath imports
137
150
 
@@ -149,13 +162,20 @@ Tree-shaking eliminates anything you don't import. Using Koa, Hapi, raw `http`,
149
162
 
150
163
  ## Protection tiers
151
164
 
165
+ `tier` is the **state** of a session — what kind of binding the browser achieved. It is *not* a knob you gate on directly:
166
+
152
167
  | Tier | Mechanism | Protects against |
153
168
  |------|-----------|------------------|
154
169
  | `dbsc` | Native W3C DBSC, key in TPM / Secure Enclave / Android Keystore | Cookie theft (XSS, network, logs, paste-to-other-browser) **and** infostealer malware reading the browser profile |
155
170
  | `bound` | Web Crypto polyfill, non-extractable ECDSA P-256 key in IndexedDB | Cookie theft. Does not defeat infostealer malware on the user's machine. |
156
- | `none` | Plain cookie | Nothing the cookie itself doesn't already do |
171
+ | `none` | No active / fresh binding | Nothing the cookie itself doesn't already do |
172
+
173
+ **How to gate routes — the whole decision tree:**
174
+
175
+ - **Public / read-only route?** → no guard.
176
+ - **Anything authenticated?** → `requireProof()`. That's it.
157
177
 
158
- The library exposes the tier; **enforcing it is your responsibility**. Gate most routes on `tier !== "none"`; gate genuinely sensitive routes (payments, password change, admin) on `tier === "dbsc"`. Full guidance in [docs/security/best-practices.md](./docs/security/best-practices.md).
178
+ There is no third option, and **never gate a route on `tier === "dbsc"`** that locks out every Firefox and Safari user (they can only reach `tier: "bound"`), and `requireProof()` already gives those browsers the same per-request guarantee via a signed proof. `requireProof()` is the one guard; it works on every browser. Read `res.locals.dbsc.tier` only if you want to *display* binding state in the UI — not to build a gate. Full guidance in [docs/security/best-practices.md](./docs/security/best-practices.md).
159
179
 
160
180
  ## Going deeper
161
181
 
@@ -30,7 +30,9 @@ export function detectAlgorithm(jwk) {
30
30
  return "RS256";
31
31
  throw new DbscVerificationError(ErrorCodes.UNKNOWN_ALGORITHM, `cannot determine algorithm for kty=${jwk.kty} crv=${jwk.crv}`);
32
32
  }
33
+ /** Bit length of the bytes a base64url string decodes to. JWK `n` is unpadded. */
33
34
  function base64urlBits(b64) {
34
- return (b64.length * 6) / 8 * 8;
35
+ const len = b64.replace(/=+$/, "").length;
36
+ return Math.floor((len * 3) / 4) * 8;
35
37
  }
36
38
  //# sourceMappingURL=jwk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"jwk.js","sourceRoot":"","sources":["../../../src/core/crypto/jwk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEjE,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AAC5C,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B,MAAM,UAAU,WAAW,CAAC,GAAe;IACzC,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,qBAAqB,CAC7B,UAAU,CAAC,WAAW,EACtB,sBAAsB,GAAG,CAAC,GAAG,EAAE,CAChC,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,qBAAqB,CAC7B,UAAU,CAAC,WAAW,EACtB,kCAAkC,CACnC,CAAC;QACJ,CAAC;QACD,OAAO;IACT,CAAC;IAED,IAAI,GAAG,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,qBAAqB,CAC7B,UAAU,CAAC,WAAW,EACtB,yBAAyB,CAC1B,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,IAAI,GAAG,YAAY,EAAE,CAAC;YACxB,MAAM,IAAI,qBAAqB,CAC7B,UAAU,CAAC,WAAW,EACtB,sBAAsB,IAAI,kBAAkB,YAAY,EAAE,CAC3D,CAAC;QACJ,CAAC;QACD,OAAO;IACT,CAAC;IAED,MAAM,IAAI,qBAAqB,CAC7B,UAAU,CAAC,WAAW,EACtB,yBAAyB,GAAG,CAAC,GAAG,EAAE,CACnC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAe;IAC7C,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,GAAG,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5D,IAAI,GAAG,CAAC,GAAG,KAAK,KAAK;QAAE,OAAO,OAAO,CAAC;IACtC,MAAM,IAAI,qBAAqB,CAC7B,UAAU,CAAC,iBAAiB,EAC5B,sCAAsC,GAAG,CAAC,GAAG,QAAQ,GAAG,CAAC,GAAG,EAAE,CAC/D,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"jwk.js","sourceRoot":"","sources":["../../../src/core/crypto/jwk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEjE,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AAC5C,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B,MAAM,UAAU,WAAW,CAAC,GAAe;IACzC,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,qBAAqB,CAC7B,UAAU,CAAC,WAAW,EACtB,sBAAsB,GAAG,CAAC,GAAG,EAAE,CAChC,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,qBAAqB,CAC7B,UAAU,CAAC,WAAW,EACtB,kCAAkC,CACnC,CAAC;QACJ,CAAC;QACD,OAAO;IACT,CAAC;IAED,IAAI,GAAG,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,qBAAqB,CAC7B,UAAU,CAAC,WAAW,EACtB,yBAAyB,CAC1B,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,IAAI,GAAG,YAAY,EAAE,CAAC;YACxB,MAAM,IAAI,qBAAqB,CAC7B,UAAU,CAAC,WAAW,EACtB,sBAAsB,IAAI,kBAAkB,YAAY,EAAE,CAC3D,CAAC;QACJ,CAAC;QACD,OAAO;IACT,CAAC;IAED,MAAM,IAAI,qBAAqB,CAC7B,UAAU,CAAC,WAAW,EACtB,yBAAyB,GAAG,CAAC,GAAG,EAAE,CACnC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAe;IAC7C,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,GAAG,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5D,IAAI,GAAG,CAAC,GAAG,KAAK,KAAK;QAAE,OAAO,OAAO,CAAC;IACtC,MAAM,IAAI,qBAAqB,CAC7B,UAAU,CAAC,iBAAiB,EAC5B,sCAAsC,GAAG,CAAC,GAAG,QAAQ,GAAG,CAAC,GAAG,EAAE,CAC/D,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,SAAS,aAAa,CAAC,GAAW;IAChC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC;IAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AACvC,CAAC"}
@@ -1,4 +1,4 @@
1
- export type { ProtectionTier, BoundKey, Session, Challenge, RegistrationProof, RefreshProof, StorageAdapter, RateLimiter, DbscOptions, AutoBindResult, AnyTelemetryEvent, TelemetryEvent, RegistrationEvent, RefreshEvent, VerificationFailureEvent, SessionStolenEvent, TierChangeEvent, } from "./types.js";
1
+ export type { ProtectionTier, BoundKey, Session, Challenge, RegistrationProof, RefreshProof, StorageAdapter, RateLimiter, DbscOptions, DbscKitExtras, AutoBindResult, AnyTelemetryEvent, TelemetryEvent, RegistrationEvent, RefreshEvent, VerificationFailureEvent, SessionStolenEvent, TierChangeEvent, } from "./types.js";
2
2
  export { DbscProtocolError, DbscVerificationError, DbscStorageError, ErrorCodes } from "./errors.js";
3
3
  export { validateJwk, detectAlgorithm } from "./crypto/jwk.js";
4
4
  export { verifyDbscJws, parseRegistrationJws } from "./crypto/jws.js";
@@ -16,4 +16,7 @@ export { NoopRateLimiter } from "./ratelimit/interface.js";
16
16
  export { emit } from "./telemetry/hooks.js";
17
17
  export { deriveSessionId } from "./derive-session-id.js";
18
18
  export type { DeriveSessionIdInput } from "./derive-session-id.js";
19
+ export { noBindingReason } from "./protect-policy.js";
20
+ export type { RequireProofOptions } from "./protect-policy.js";
21
+ export { parseCookieHeader } from "./protocol/cookies.js";
19
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,QAAQ,EACR,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,WAAW,EACX,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,wBAAwB,EACxB,kBAAkB,EAClB,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErG,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,yBAAyB,EACzB,oBAAoB,EACpB,yBAAyB,EACzB,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,0BAA0B,EAC1B,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC1F,YAAY,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAEhE,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,QAAQ,EACR,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,WAAW,EACX,WAAW,EACX,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,wBAAwB,EACxB,kBAAkB,EAClB,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErG,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,yBAAyB,EACzB,oBAAoB,EACpB,yBAAyB,EACzB,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,0BAA0B,EAC1B,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC1F,YAAY,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAEhE,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAEnE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -12,4 +12,6 @@ export { verifyBoundProof, parseProofHeader, BOUND_PROOF_HEADER } from "./bound/
12
12
  export { NoopRateLimiter } from "./ratelimit/interface.js";
13
13
  export { emit } from "./telemetry/hooks.js";
14
14
  export { deriveSessionId } from "./derive-session-id.js";
15
+ export { noBindingReason } from "./protect-policy.js";
16
+ export { parseCookieHeader } from "./protocol/cookies.js";
15
17
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErG,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,yBAAyB,EACzB,oBAAoB,EACpB,yBAAyB,EACzB,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,0BAA0B,EAC1B,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG1F,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErG,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,yBAAyB,EACzB,oBAAoB,EACpB,yBAAyB,EACzB,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,0BAA0B,EAC1B,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG1F,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { StorageAdapter } from "./types.js";
2
+ import type { SkippedEntry } from "./protocol/headers.js";
3
+ /**
4
+ * Options for `requireProof()`. All optional — `requireProof()` with no
5
+ * arguments is the normal call. `requireProof()` always means: the request
6
+ * must come from a bound device and prove it per-request. It works on every
7
+ * browser — Chromium's hardware-backed `dbsc` tier passes through, the
8
+ * software `bound` tier (Firefox / Safari / older Chromium) supplies a signed,
9
+ * body-hashed proof.
10
+ */
11
+ export interface RequireProofOptions {
12
+ /**
13
+ * Let the hardware-backed `dbsc` tier through without a proof header.
14
+ * Default true — Chromium enforces the cookie↔key binding browser-side, and
15
+ * the native protocol does not sign request bodies. Set false to demand a
16
+ * signed proof from Chromium too (the client must then call
17
+ * `wrapFetch({ signBody: true })`).
18
+ */
19
+ allowDbscWithoutProof?: boolean;
20
+ /** Accepted proof timestamp window, ms. */
21
+ timestampWindowMs?: number;
22
+ /** Storage override. Defaults to the storage the adapter middleware was given. */
23
+ storage?: StorageAdapter;
24
+ }
25
+ /** Human-readable reason for a `tier: "none"` rejection — quota-aware. */
26
+ export declare function noBindingReason(skipped?: SkippedEntry[]): string;
27
+ //# sourceMappingURL=protect-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protect-policy.d.ts","sourceRoot":"","sources":["../../src/core/protect-policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,2CAA2C;IAC3C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kFAAkF;IAClF,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,OAAO,GAAE,YAAY,EAAO,GAAG,MAAM,CAKpE"}
@@ -0,0 +1,8 @@
1
+ /** Human-readable reason for a `tier: "none"` rejection — quota-aware. */
2
+ export function noBindingReason(skipped = []) {
3
+ if (skipped.some((s) => s.reason === "quota_exceeded")) {
4
+ return "Chrome declined native DBSC registration (quota_exceeded). Clear this origin's site data in chrome://settings, or open a fresh profile.";
5
+ }
6
+ return "no active device binding — the session is not bound, or the binding has gone stale";
7
+ }
8
+ //# sourceMappingURL=protect-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protect-policy.js","sourceRoot":"","sources":["../../src/core/protect-policy.ts"],"names":[],"mappings":"AA0BA,0EAA0E;AAC1E,MAAM,UAAU,eAAe,CAAC,UAA0B,EAAE;IAC1D,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,gBAAgB,CAAC,EAAE,CAAC;QACvD,OAAO,yIAAyI,CAAC;IACnJ,CAAC;IACD,OAAO,oFAAoF,CAAC;AAC9F,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Parses a `Cookie` request header into a name→value map. Lets the adapter
3
+ * middlewares read DBSC cookies without depending on `cookie-parser`.
4
+ */
5
+ export declare function parseCookieHeader(header?: string | null): Record<string, string>;
6
+ //# sourceMappingURL=cookies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../../src/core/protocol/cookies.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAsBhF"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Parses a `Cookie` request header into a name→value map. Lets the adapter
3
+ * middlewares read DBSC cookies without depending on `cookie-parser`.
4
+ */
5
+ export function parseCookieHeader(header) {
6
+ // Null-prototype map: a `__proto__` / `constructor` cookie name is then a
7
+ // plain own property, never a prototype-pollution vector.
8
+ const out = Object.create(null);
9
+ if (!header)
10
+ return out;
11
+ for (const part of header.split(";")) {
12
+ const eq = part.indexOf("=");
13
+ if (eq < 0)
14
+ continue;
15
+ const name = part.slice(0, eq).trim();
16
+ if (!name)
17
+ continue;
18
+ let value = part.slice(eq + 1).trim();
19
+ if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
20
+ value = value.slice(1, -1);
21
+ }
22
+ try {
23
+ value = decodeURIComponent(value);
24
+ }
25
+ catch {
26
+ // keep the raw value if it is not valid percent-encoding
27
+ }
28
+ if (!(name in out))
29
+ out[name] = value;
30
+ }
31
+ return out;
32
+ }
33
+ //# sourceMappingURL=cookies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../../src/core/protocol/cookies.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAsB;IACtD,0EAA0E;IAC1E,0DAA0D;IAC1D,MAAM,GAAG,GAA2B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,CAAC,MAAM;QAAE,OAAO,GAAG,CAAC;IACxB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,EAAE,GAAG,CAAC;YAAE,SAAS;QACrB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACtC,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACtE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC;YACH,KAAK,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,yDAAyD;QAC3D,CAAC;QACD,IAAI,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;IACxC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -119,4 +119,19 @@ export interface AutoBindResult {
119
119
  sessionId: string;
120
120
  userId: string;
121
121
  }
122
+ /**
123
+ * Extra options accepted by every adapter's `createDbsc()` on top of that
124
+ * adapter's middleware options. The kit returned by `createDbsc()` carries
125
+ * these so `install()`, `bind()`, and `requireProof()` need no re-passing.
126
+ */
127
+ export interface DbscKitExtras {
128
+ /** Use `__Host-` cookies + the Secure flag. Default true. */
129
+ secure?: boolean;
130
+ /** Mount path for the static client SDK. Default "/dbsc-client". `false` skips it. */
131
+ clientPath?: string | false;
132
+ /** Default session TTL (ms) for `bind()`. */
133
+ sessionTtl?: number;
134
+ /** Let `install()` set `trust proxy`. Default true. Set false to leave it alone. */
135
+ trustProxy?: boolean;
136
+ }
122
137
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { type Express, type Response, type RequestHandler } from "express";
2
+ import { type RequireProofOptions } from "../core/index.js";
3
+ import { type DbscExpressOptions } from "./index.js";
4
+ export interface CreateDbscOptions extends DbscExpressOptions {
5
+ /** Mount path for the static client SDK. Default "/dbsc-client". `false` skips it. */
6
+ clientPath?: string | false;
7
+ /** Default session TTL (ms) for `bind()`. */
8
+ sessionTtl?: number;
9
+ /** Let `install()` set `trust proxy`. Default true. */
10
+ trustProxy?: boolean;
11
+ }
12
+ export interface BindOptions {
13
+ userId: string;
14
+ /**
15
+ * Manual per-device value. Optional — when omitted on the no-sessionId
16
+ * (JWT) path, the kit manages a `__Host-dbsc-device` cookie itself so each
17
+ * browser binds independently. Pass this only to control device identity
18
+ * yourself.
19
+ */
20
+ deviceHint?: string;
21
+ /** Namespace to scope derived ids. */
22
+ namespace?: string;
23
+ }
24
+ export interface DbscKit {
25
+ /** Mount the whole DBSC surface on the app: middleware, bound-route JSON, client SDK. */
26
+ install(app: Express): Express;
27
+ /** The raw `dbsc()` middleware, for manual mounting. */
28
+ middleware(): RequestHandler;
29
+ /** Start a binding. Pass a sessionId, or omit it to derive one from `userId` (JWT apps). */
30
+ bind(res: Response, sessionId: string, opts: BindOptions): Promise<string>;
31
+ bind(res: Response, opts: BindOptions): Promise<string>;
32
+ /** The route guard — requires a bound device + a per-request proof. */
33
+ requireProof(opts?: RequireProofOptions): RequestHandler;
34
+ }
35
+ /**
36
+ * Builds a configured DBSC kit. Storage, `secure`, TTLs, rate limiter and
37
+ * telemetry are set once here; `install()`, `bind()` and `requireProof()` all
38
+ * read this config — nothing is re-passed.
39
+ */
40
+ export declare function createDbsc(opts: CreateDbscOptions): DbscKit;
41
+ //# sourceMappingURL=create-dbsc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-dbsc.d.ts","sourceRoot":"","sources":["../../src/express/create-dbsc.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,KAAK,OAAO,EAAgB,KAAK,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAGlG,OAAO,EAAsC,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAChG,OAAO,EAAqB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AA4BxE,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IAC3D,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5B,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,yFAAyF;IACzF,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;IAC/B,wDAAwD;IACxD,UAAU,IAAI,cAAc,CAAC;IAC7B,4FAA4F;IAC5F,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3E,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD,uEAAuE;IACvE,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,cAAc,CAAC;CAC1D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAoD3D"}
@@ -0,0 +1,88 @@
1
+ import express from "express";
2
+ import { fileURLToPath } from "node:url";
3
+ import { randomBytes } from "node:crypto";
4
+ import { deriveSessionId, parseCookieHeader } from "../core/index.js";
5
+ import { dbsc, bindSession } from "./index.js";
6
+ import { requireProof } from "./require-proof.js";
7
+ const DEVICE_COOKIE_TTL_MS = 365 * 24 * 60 * 60 * 1000;
8
+ /**
9
+ * Returns a stable per-device value for the JWT `bind()` path: reads the
10
+ * `__Host-dbsc-device` cookie, or mints + sets one if absent. Two browsers of
11
+ * the same user thus derive distinct sessionIds and bind independently.
12
+ */
13
+ function resolveDeviceHint(res, secure) {
14
+ const name = secure ? "__Host-dbsc-device" : "dbsc-device";
15
+ const req = res.req;
16
+ const existing = req?.cookies?.[name] ??
17
+ parseCookieHeader(req?.headers?.cookie)[name];
18
+ if (existing)
19
+ return existing;
20
+ const value = randomBytes(16).toString("hex");
21
+ res.cookie(name, value, {
22
+ httpOnly: true,
23
+ secure,
24
+ sameSite: "lax",
25
+ path: "/",
26
+ maxAge: DEVICE_COOKIE_TTL_MS,
27
+ });
28
+ return value;
29
+ }
30
+ /**
31
+ * Builds a configured DBSC kit. Storage, `secure`, TTLs, rate limiter and
32
+ * telemetry are set once here; `install()`, `bind()` and `requireProof()` all
33
+ * read this config — nothing is re-passed.
34
+ */
35
+ export function createDbsc(opts) {
36
+ const secure = opts.secure ?? true;
37
+ const clientPath = opts.clientPath ?? "/dbsc-client";
38
+ const registrationPath = opts.registrationPath ?? "/dbsc/registration";
39
+ const boundRegistrationPath = opts.boundRegistrationPath ?? "/dbsc-bound/registration";
40
+ const boundRefreshPath = opts.boundRefreshPath ?? "/dbsc-bound/refresh";
41
+ const middleware = dbsc(opts);
42
+ async function bind(res, a, b) {
43
+ const bindOpts = typeof a === "string" ? b : a;
44
+ let sessionId;
45
+ if (typeof a === "string") {
46
+ sessionId = a;
47
+ }
48
+ else {
49
+ // JWT path: no sessionId. An explicit deviceHint wins; otherwise the kit
50
+ // manages a per-device cookie so each browser binds independently.
51
+ const deviceHint = bindOpts.deviceHint ?? resolveDeviceHint(res, secure);
52
+ sessionId = await deriveSessionId({
53
+ userId: bindOpts.userId,
54
+ deviceHint,
55
+ ...(bindOpts.namespace !== undefined && { namespace: bindOpts.namespace }),
56
+ });
57
+ }
58
+ await bindSession(res, sessionId, opts.storage, {
59
+ userId: bindOpts.userId,
60
+ secure,
61
+ registrationPath,
62
+ ...(opts.registrationCookieTtl !== undefined && {
63
+ registrationCookieTtl: opts.registrationCookieTtl,
64
+ }),
65
+ ...(opts.sessionTtl !== undefined && { sessionTtl: opts.sessionTtl }),
66
+ });
67
+ return sessionId;
68
+ }
69
+ return {
70
+ middleware: () => middleware,
71
+ install(app) {
72
+ if (opts.trustProxy !== false)
73
+ app.set("trust proxy", true);
74
+ const json = express.json();
75
+ app.use(boundRegistrationPath, json);
76
+ app.use(boundRefreshPath, json);
77
+ app.use(middleware);
78
+ if (clientPath !== false) {
79
+ const clientDir = fileURLToPath(new URL("../client/", import.meta.url));
80
+ app.use(clientPath, express.static(clientDir));
81
+ }
82
+ return app;
83
+ },
84
+ bind,
85
+ requireProof,
86
+ };
87
+ }
88
+ //# sourceMappingURL=create-dbsc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-dbsc.js","sourceRoot":"","sources":["../../src/express/create-dbsc.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,15 @@ import type { Response, RequestHandler } from "express";
2
2
  import { type DbscOptions, type StorageAdapter, type ProtectionTier, type SkippedEntry } from "../core/index.js";
3
3
  export { requireBoundProof } from "./proof.js";
4
4
  export type { RequireBoundProofOptions } from "./proof.js";
5
+ export { requireProof } from "./require-proof.js";
6
+ export { createDbsc } from "./create-dbsc.js";
7
+ export type { CreateDbscOptions, DbscKit, BindOptions } from "./create-dbsc.js";
8
+ /** Internal carrier so `requireProof()` can reach storage without re-passing it. */
9
+ export interface DbscInternal {
10
+ storage: StorageAdapter;
11
+ secure: boolean;
12
+ }
13
+ export declare const DBSC_INTERNAL: unique symbol;
5
14
  export interface DbscExpressOptions extends DbscOptions {
6
15
  secure?: boolean;
7
16
  boundStatePath?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/express/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,QAAQ,EAAgB,cAAc,EAAE,MAAM,SAAS,CAAC;AAE/E,OAAO,EAmBL,KAAK,WAAW,EAChB,KAAK,cAAc,EAEnB,KAAK,cAAc,EACnB,KAAK,YAAY,EAElB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAY3D,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO,CAAC;QAChB,UAAU,MAAM;YACd,IAAI,EAAE,UAAU,CAAC;SAClB;KACF;CACF;AAuBD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,gHAAgH;IAChH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,WAAW,CAC/B,GAAG,EAAE,QAAQ,EACb,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAyCf;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,kBAAkB,GAAG,cAAc,CAge7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/express/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,QAAQ,EAAgB,cAAc,EAAE,MAAM,SAAS,CAAC;AAE/E,OAAO,EAoBL,KAAK,WAAW,EAChB,KAAK,cAAc,EAEnB,KAAK,cAAc,EACnB,KAAK,YAAY,EAElB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEhF,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;CACjB;AACD,eAAO,MAAM,aAAa,EAAE,OAAO,MAAgD,CAAC;AAYpF,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO,CAAC;QAChB,UAAU,MAAM;YACd,IAAI,EAAE,UAAU,CAAC;SAClB;KACF;CACF;AAuBD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,gHAAgH;IAChH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,WAAW,CAC/B,GAAG,EAAE,QAAQ,EACb,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAyCf;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,kBAAkB,GAAG,cAAc,CAwe7D"}
@@ -1,5 +1,8 @@
1
- import { handleRegistration, handleRefresh, handleBoundRegistration, handleBoundRefresh, issueChallenge, buildRegistrationHeader, buildChallengeHeader, readSessionResponseHeader, parseSessionSkippedHeader, REGISTRATION_HEADER, CHALLENGE_HEADER, LEGACY_REGISTRATION_HEADER, LEGACY_CHALLENGE_HEADER, NoopRateLimiter, emit, DbscProtocolError, DbscVerificationError, ErrorCodes, } from "../core/index.js";
1
+ import { handleRegistration, handleRefresh, handleBoundRegistration, handleBoundRefresh, issueChallenge, buildRegistrationHeader, buildChallengeHeader, readSessionResponseHeader, parseSessionSkippedHeader, REGISTRATION_HEADER, CHALLENGE_HEADER, LEGACY_REGISTRATION_HEADER, LEGACY_CHALLENGE_HEADER, NoopRateLimiter, emit, parseCookieHeader, DbscProtocolError, DbscVerificationError, ErrorCodes, } from "../core/index.js";
2
2
  export { requireBoundProof } from "./proof.js";
3
+ export { requireProof } from "./require-proof.js";
4
+ export { createDbsc } from "./create-dbsc.js";
5
+ export const DBSC_INTERNAL = Symbol("dbsc-toolkit.express.internal");
3
6
  const cookieNames = (secure) => ({
4
7
  bound: secure ? "__Host-dbsc-session" : "dbsc-session",
5
8
  reg: secure ? "__Host-dbsc-reg" : "dbsc-reg",
@@ -410,6 +413,9 @@ export function dbsc(opts) {
410
413
  }
411
414
  }
412
415
  return async (req, res, next) => {
416
+ if (!req.cookies) {
417
+ req.cookies = parseCookieHeader(req.headers.cookie);
418
+ }
413
419
  if (req.method === "POST" && req.path === registrationPath) {
414
420
  await handleRegistrationRoute(req, res);
415
421
  return;
@@ -448,6 +454,10 @@ export function dbsc(opts) {
448
454
  ]);
449
455
  },
450
456
  };
457
+ res.locals[DBSC_INTERNAL] = {
458
+ storage,
459
+ secure,
460
+ };
451
461
  if (sessionId) {
452
462
  const session = await storage.getSession(sessionId);
453
463
  if (session) {