dbsc-toolkit 2.4.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -36
- package/dist/core/derive-session-id.d.ts +21 -0
- package/dist/core/derive-session-id.d.ts.map +1 -0
- package/dist/core/derive-session-id.js +29 -0
- package/dist/core/derive-session-id.js.map +1 -0
- package/dist/core/index.d.ts +6 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/protect-policy.d.ts +27 -0
- package/dist/core/protect-policy.d.ts.map +1 -0
- package/dist/core/protect-policy.js +8 -0
- package/dist/core/protect-policy.js.map +1 -0
- package/dist/core/protocol/cookies.d.ts +6 -0
- package/dist/core/protocol/cookies.d.ts.map +1 -0
- package/dist/core/protocol/cookies.js +31 -0
- package/dist/core/protocol/cookies.js.map +1 -0
- package/dist/core/types.d.ts +32 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/express/create-dbsc.d.ts +36 -0
- package/dist/express/create-dbsc.d.ts.map +1 -0
- package/dist/express/create-dbsc.js +57 -0
- package/dist/express/create-dbsc.js.map +1 -0
- package/dist/express/index.d.ts +9 -0
- package/dist/express/index.d.ts.map +1 -1
- package/dist/express/index.js +13 -3
- package/dist/express/index.js.map +1 -1
- package/dist/express/require-proof.d.ts +20 -0
- package/dist/express/require-proof.d.ts.map +1 -0
- package/dist/express/require-proof.js +56 -0
- package/dist/express/require-proof.js.map +1 -0
- package/dist/fastify/create-dbsc.d.ts +29 -0
- package/dist/fastify/create-dbsc.d.ts.map +1 -0
- package/dist/fastify/create-dbsc.js +44 -0
- package/dist/fastify/create-dbsc.js.map +1 -0
- package/dist/fastify/index.d.ts +9 -0
- package/dist/fastify/index.d.ts.map +1 -1
- package/dist/fastify/index.js +9 -2
- package/dist/fastify/index.js.map +1 -1
- package/dist/fastify/require-proof.d.ts +14 -0
- package/dist/fastify/require-proof.d.ts.map +1 -0
- package/dist/fastify/require-proof.js +50 -0
- package/dist/fastify/require-proof.js.map +1 -0
- package/dist/hono/create-dbsc.d.ts +31 -0
- package/dist/hono/create-dbsc.d.ts.map +1 -0
- package/dist/hono/create-dbsc.js +42 -0
- package/dist/hono/create-dbsc.js.map +1 -0
- package/dist/hono/index.d.ts +9 -0
- package/dist/hono/index.d.ts.map +1 -1
- package/dist/hono/index.js +9 -2
- package/dist/hono/index.js.map +1 -1
- package/dist/hono/require-proof.d.ts +13 -0
- package/dist/hono/require-proof.d.ts.map +1 -0
- package/dist/hono/require-proof.js +47 -0
- package/dist/hono/require-proof.js.map +1 -0
- package/dist/nextjs/create-dbsc.d.ts +33 -0
- package/dist/nextjs/create-dbsc.d.ts.map +1 -0
- package/dist/nextjs/create-dbsc.js +45 -0
- package/dist/nextjs/create-dbsc.js.map +1 -0
- package/dist/nextjs/index.d.ts +5 -0
- package/dist/nextjs/index.d.ts.map +1 -1
- package/dist/nextjs/index.js +4 -1
- package/dist/nextjs/index.js.map +1 -1
- package/dist/nextjs/require-proof.d.ts +28 -0
- package/dist/nextjs/require-proof.d.ts.map +1 -0
- package/dist/nextjs/require-proof.js +44 -0
- package/dist/nextjs/require-proof.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,30 +48,22 @@ npm install express cookie-parser pg # Express + Postgres
|
|
|
48
48
|
|
|
49
49
|
## Quick start
|
|
50
50
|
|
|
51
|
-
Copy-paste runnable.
|
|
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 {
|
|
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
|
-
|
|
63
|
-
|
|
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
|
|
66
|
+
await dbsc.bind(res, randomUUID(), { userId: req.body.username });
|
|
75
67
|
res.json({ ok: true });
|
|
76
68
|
});
|
|
77
69
|
|
|
@@ -92,44 +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+?**
|
|
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
|
|
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.
|
|
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);
|
|
119
|
+
|
|
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
|
+
```
|
|
105
127
|
|
|
106
|
-
|
|
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();`
|
|
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.
|
|
112
129
|
|
|
113
|
-
|
|
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. Per-system recipes: [docs/integration-recipes.md](./docs/integration-recipes.md).
|
|
114
131
|
|
|
115
|
-
|
|
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).
|
|
116
133
|
|
|
117
|
-
##
|
|
134
|
+
## Protect your routes
|
|
118
135
|
|
|
119
|
-
> **First time here?** [docs/usage.md](./docs/usage.md) walks the
|
|
136
|
+
> **First time here?** [docs/usage.md](./docs/usage.md) walks the setup and the guard in order, with concrete code. Five-minute read.
|
|
120
137
|
|
|
121
|
-
After
|
|
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.
|
|
122
139
|
|
|
123
|
-
| Your route does… | Use this guard | What it stops |
|
|
124
|
-
|
|
125
|
-
| Public / read-only (feed, search, public profile) | Nothing | n/a — no auth gate at all |
|
|
126
|
-
|
|
|
127
|
-
| 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) |
|
|
128
|
-
| 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 |
|
|
129
144
|
|
|
130
|
-
|
|
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.
|
|
131
146
|
|
|
132
|
-
The full threat boundary
|
|
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).
|
|
133
148
|
|
|
134
149
|
## Subpath imports
|
|
135
150
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface DeriveSessionIdInput {
|
|
2
|
+
/** Stable user identifier from your JWT (the `sub` claim is the canonical choice). */
|
|
3
|
+
userId: string;
|
|
4
|
+
/** Optional per-device hint. If omitted, one stable id per user — fine for single-device apps. */
|
|
5
|
+
deviceHint?: string;
|
|
6
|
+
/** Optional namespace to scope ids when the same user has multiple binding contexts (e.g. impersonation). Defaults to "default". */
|
|
7
|
+
namespace?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Returns a stable, opaque sessionId suitable for `bindSession()` when the
|
|
11
|
+
* caller has no server-side session row to take the id from (NextAuth in
|
|
12
|
+
* JWT mode, iron-session, Lucia stateless, raw JWT cookies). Output is
|
|
13
|
+
* deterministic for the same input — call it on every request, get the
|
|
14
|
+
* same id, bind against it once at login, look it up on refresh.
|
|
15
|
+
*
|
|
16
|
+
* The id is SHA-256 of `${namespace}.${userId}.${deviceHint ?? ""}`,
|
|
17
|
+
* base64url-encoded. It is not a secret and not reversible; it only needs
|
|
18
|
+
* to be stable and collision-free across your users.
|
|
19
|
+
*/
|
|
20
|
+
export declare function deriveSessionId(input: DeriveSessionIdInput): Promise<string>;
|
|
21
|
+
//# sourceMappingURL=derive-session-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive-session-id.d.ts","sourceRoot":"","sources":["../../src/core/derive-session-id.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;IACf,kGAAkG;IAClG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oIAAoI;IACpI,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAalF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a stable, opaque sessionId suitable for `bindSession()` when the
|
|
3
|
+
* caller has no server-side session row to take the id from (NextAuth in
|
|
4
|
+
* JWT mode, iron-session, Lucia stateless, raw JWT cookies). Output is
|
|
5
|
+
* deterministic for the same input — call it on every request, get the
|
|
6
|
+
* same id, bind against it once at login, look it up on refresh.
|
|
7
|
+
*
|
|
8
|
+
* The id is SHA-256 of `${namespace}.${userId}.${deviceHint ?? ""}`,
|
|
9
|
+
* base64url-encoded. It is not a secret and not reversible; it only needs
|
|
10
|
+
* to be stable and collision-free across your users.
|
|
11
|
+
*/
|
|
12
|
+
export async function deriveSessionId(input) {
|
|
13
|
+
if (!input.userId) {
|
|
14
|
+
throw new Error("deriveSessionId: userId is required");
|
|
15
|
+
}
|
|
16
|
+
const namespace = input.namespace ?? "default";
|
|
17
|
+
const deviceHint = input.deviceHint ?? "";
|
|
18
|
+
const material = `${namespace}.${input.userId}.${deviceHint}`;
|
|
19
|
+
const bytes = new TextEncoder().encode(material);
|
|
20
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
|
|
21
|
+
return base64Url(new Uint8Array(digest));
|
|
22
|
+
}
|
|
23
|
+
function base64Url(b) {
|
|
24
|
+
let s = "";
|
|
25
|
+
for (let i = 0; i < b.length; i++)
|
|
26
|
+
s += String.fromCharCode(b[i]);
|
|
27
|
+
return Buffer.from(s, "binary").toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=derive-session-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive-session-id.js","sourceRoot":"","sources":["../../src/core/derive-session-id.ts"],"names":[],"mappings":"AASA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAA2B;IAC/D,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IACD,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,SAAS,CAAC;IAC/C,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,EAAE,CAAC;IAC1C,MAAM,QAAQ,GAAG,GAAG,SAAS,IAAI,KAAK,CAAC,MAAM,IAAI,UAAU,EAAE,CAAC;IAC9D,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CACvC,SAAS,EACT,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAgB,CACzF,CAAC;IACF,OAAO,SAAS,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,SAAS,CAAC,CAAa;IAC9B,IAAI,CAAC,GAAG,EAAE,CAAC;IACX,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,CAAC,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAW,CAAC,CAAC;IAC5E,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC/G,CAAC"}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -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";
|
|
@@ -14,4 +14,9 @@ export { verifyBoundProof, parseProofHeader, BOUND_PROOF_HEADER } from "./bound/
|
|
|
14
14
|
export type { VerifyBoundProofRequest } from "./bound/proof.js";
|
|
15
15
|
export { NoopRateLimiter } from "./ratelimit/interface.js";
|
|
16
16
|
export { emit } from "./telemetry/hooks.js";
|
|
17
|
+
export { deriveSessionId } from "./derive-session-id.js";
|
|
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";
|
|
17
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/core/index.d.ts.map
CHANGED
|
@@ -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"}
|
|
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"}
|
package/dist/core/index.js
CHANGED
|
@@ -11,4 +11,7 @@ export { verifyP256Signature } from "./bound/verify.js";
|
|
|
11
11
|
export { verifyBoundProof, parseProofHeader, BOUND_PROOF_HEADER } from "./bound/proof.js";
|
|
12
12
|
export { NoopRateLimiter } from "./ratelimit/interface.js";
|
|
13
13
|
export { emit } from "./telemetry/hooks.js";
|
|
14
|
+
export { deriveSessionId } from "./derive-session-id.js";
|
|
15
|
+
export { noBindingReason } from "./protect-policy.js";
|
|
16
|
+
export { parseCookieHeader } from "./protocol/cookies.js";
|
|
14
17
|
//# sourceMappingURL=index.js.map
|
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"
|
|
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,CAoBhF"}
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
const out = {};
|
|
7
|
+
if (!header)
|
|
8
|
+
return out;
|
|
9
|
+
for (const part of header.split(";")) {
|
|
10
|
+
const eq = part.indexOf("=");
|
|
11
|
+
if (eq < 0)
|
|
12
|
+
continue;
|
|
13
|
+
const name = part.slice(0, eq).trim();
|
|
14
|
+
if (!name)
|
|
15
|
+
continue;
|
|
16
|
+
let value = part.slice(eq + 1).trim();
|
|
17
|
+
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
18
|
+
value = value.slice(1, -1);
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
value = decodeURIComponent(value);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// keep the raw value if it is not valid percent-encoding
|
|
25
|
+
}
|
|
26
|
+
if (!(name in out))
|
|
27
|
+
out[name] = value;
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
//# 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,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,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"}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -85,6 +85,23 @@ export interface DbscOptions {
|
|
|
85
85
|
refreshPath?: string;
|
|
86
86
|
boundCookieTtl?: number;
|
|
87
87
|
registrationCookieTtl?: number;
|
|
88
|
+
/**
|
|
89
|
+
* Grace window, in ms, applied after a bound cookie's freshness expires.
|
|
90
|
+
* Between cookie expiry and the browser's next /dbsc/refresh there is a
|
|
91
|
+
* short in-flight gap; without grace, freshness polls during that gap see
|
|
92
|
+
* tier="none" and may false-alarm an auto-logout. The middleware keeps the
|
|
93
|
+
* previous tier until `lastRefreshAt + boundCookieTtl + refreshGraceMs`.
|
|
94
|
+
* Defaults to 30000 (30s). Set 0 to demote the instant freshness expires.
|
|
95
|
+
*/
|
|
96
|
+
refreshGraceMs?: number;
|
|
97
|
+
/**
|
|
98
|
+
* Cookie prefix scope. "host" (default) uses `__Host-` cookies — origin
|
|
99
|
+
* locked, no Domain attribute, strongest. "site" uses `__Secure-` cookies
|
|
100
|
+
* with a Domain attribute so the binding works across subdomains
|
|
101
|
+
* (app.example.com + api.example.com); this drops `__Host-`'s
|
|
102
|
+
* subdomain-takeover protection. See docs/integration-recipes.md.
|
|
103
|
+
*/
|
|
104
|
+
cookieScope?: "host" | "site";
|
|
88
105
|
rateLimiter?: RateLimiter;
|
|
89
106
|
onEvent?: (event: AnyTelemetryEvent) => void;
|
|
90
107
|
/**
|
|
@@ -102,4 +119,19 @@ export interface AutoBindResult {
|
|
|
102
119
|
sessionId: string;
|
|
103
120
|
userId: string;
|
|
104
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
|
+
}
|
|
105
137
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/core/types.d.ts.map
CHANGED
|
@@ -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,36 @@
|
|
|
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
|
+
/** Distinct value per device for separate bindings (the "active sessions" pattern). */
|
|
15
|
+
deviceHint?: string;
|
|
16
|
+
/** Namespace to scope derived ids. */
|
|
17
|
+
namespace?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface DbscKit {
|
|
20
|
+
/** Mount the whole DBSC surface on the app: middleware, bound-route JSON, client SDK. */
|
|
21
|
+
install(app: Express): Express;
|
|
22
|
+
/** The raw `dbsc()` middleware, for manual mounting. */
|
|
23
|
+
middleware(): RequestHandler;
|
|
24
|
+
/** Start a binding. Pass a sessionId, or omit it to derive one from `userId` (JWT apps). */
|
|
25
|
+
bind(res: Response, sessionId: string, opts: BindOptions): Promise<string>;
|
|
26
|
+
bind(res: Response, opts: BindOptions): Promise<string>;
|
|
27
|
+
/** The route guard — requires a bound device + a per-request proof. */
|
|
28
|
+
requireProof(opts?: RequireProofOptions): RequestHandler;
|
|
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 declare function createDbsc(opts: CreateDbscOptions): DbscKit;
|
|
36
|
+
//# 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,EAAE,KAAK,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAEpF,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EAAqB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGxE,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,uFAAuF;IACvF,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,CA+C3D"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { deriveSessionId } from "../core/index.js";
|
|
4
|
+
import { dbsc, bindSession } from "./index.js";
|
|
5
|
+
import { requireProof } from "./require-proof.js";
|
|
6
|
+
/**
|
|
7
|
+
* Builds a configured DBSC kit. Storage, `secure`, TTLs, rate limiter and
|
|
8
|
+
* telemetry are set once here; `install()`, `bind()` and `requireProof()` all
|
|
9
|
+
* read this config — nothing is re-passed.
|
|
10
|
+
*/
|
|
11
|
+
export function createDbsc(opts) {
|
|
12
|
+
const secure = opts.secure ?? true;
|
|
13
|
+
const clientPath = opts.clientPath ?? "/dbsc-client";
|
|
14
|
+
const registrationPath = opts.registrationPath ?? "/dbsc/registration";
|
|
15
|
+
const boundRegistrationPath = opts.boundRegistrationPath ?? "/dbsc-bound/registration";
|
|
16
|
+
const boundRefreshPath = opts.boundRefreshPath ?? "/dbsc-bound/refresh";
|
|
17
|
+
const middleware = dbsc(opts);
|
|
18
|
+
async function bind(res, a, b) {
|
|
19
|
+
const bindOpts = typeof a === "string" ? b : a;
|
|
20
|
+
const sessionId = typeof a === "string"
|
|
21
|
+
? a
|
|
22
|
+
: await deriveSessionId({
|
|
23
|
+
userId: bindOpts.userId,
|
|
24
|
+
...(bindOpts.deviceHint !== undefined && { deviceHint: bindOpts.deviceHint }),
|
|
25
|
+
...(bindOpts.namespace !== undefined && { namespace: bindOpts.namespace }),
|
|
26
|
+
});
|
|
27
|
+
await bindSession(res, sessionId, opts.storage, {
|
|
28
|
+
userId: bindOpts.userId,
|
|
29
|
+
secure,
|
|
30
|
+
registrationPath,
|
|
31
|
+
...(opts.registrationCookieTtl !== undefined && {
|
|
32
|
+
registrationCookieTtl: opts.registrationCookieTtl,
|
|
33
|
+
}),
|
|
34
|
+
...(opts.sessionTtl !== undefined && { sessionTtl: opts.sessionTtl }),
|
|
35
|
+
});
|
|
36
|
+
return sessionId;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
middleware: () => middleware,
|
|
40
|
+
install(app) {
|
|
41
|
+
if (opts.trustProxy !== false)
|
|
42
|
+
app.set("trust proxy", true);
|
|
43
|
+
const json = express.json();
|
|
44
|
+
app.use(boundRegistrationPath, json);
|
|
45
|
+
app.use(boundRefreshPath, json);
|
|
46
|
+
app.use(middleware);
|
|
47
|
+
if (clientPath !== false) {
|
|
48
|
+
const clientDir = fileURLToPath(new URL("../client/", import.meta.url));
|
|
49
|
+
app.use(clientPath, express.static(clientDir));
|
|
50
|
+
}
|
|
51
|
+
return app;
|
|
52
|
+
},
|
|
53
|
+
bind,
|
|
54
|
+
requireProof,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# 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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"}
|
package/dist/express/index.d.ts
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/express/index.js
CHANGED
|
@@ -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",
|
|
@@ -67,7 +70,7 @@ export async function bindSession(res, sessionId, storage, opts) {
|
|
|
67
70
|
]);
|
|
68
71
|
}
|
|
69
72
|
export function dbsc(opts) {
|
|
70
|
-
const { storage, registrationPath = "/dbsc/registration", refreshPath = "/dbsc/refresh", boundStatePath = "/dbsc-bound/state", boundChallengePath = "/dbsc-bound/challenge", boundRegistrationPath = "/dbsc-bound/registration", boundRefreshPath = "/dbsc-bound/refresh", boundCookieTtl = DEFAULT_BOUND_TTL, registrationCookieTtl = DEFAULT_REG_TTL, rateLimiter = new NoopRateLimiter(), onEvent, autoBind, secure = true, } = opts;
|
|
73
|
+
const { storage, registrationPath = "/dbsc/registration", refreshPath = "/dbsc/refresh", boundStatePath = "/dbsc-bound/state", boundChallengePath = "/dbsc-bound/challenge", boundRegistrationPath = "/dbsc-bound/registration", boundRefreshPath = "/dbsc-bound/refresh", boundCookieTtl = DEFAULT_BOUND_TTL, refreshGraceMs = 30_000, registrationCookieTtl = DEFAULT_REG_TTL, rateLimiter = new NoopRateLimiter(), onEvent, autoBind, secure = true, } = opts;
|
|
71
74
|
const COOKIES = cookieNames(secure);
|
|
72
75
|
async function handleRegistrationRoute(req, res) {
|
|
73
76
|
const ip = req.ip ?? "unknown";
|
|
@@ -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,10 +454,14 @@ 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) {
|
|
454
|
-
const staleAfter = session.lastRefreshAt + boundCookieTtl;
|
|
464
|
+
const staleAfter = session.lastRefreshAt + boundCookieTtl + refreshGraceMs;
|
|
455
465
|
const refreshable = session.tier === "dbsc" || session.tier === "bound";
|
|
456
466
|
if (refreshable && Date.now() > staleAfter) {
|
|
457
467
|
res.locals.dbsc.tier = "none";
|