dbsc-toolkit 2.2.0 → 2.3.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 +1 -1
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +12 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/wrapFetch.d.ts +10 -0
- package/dist/client/wrapFetch.d.ts.map +1 -1
- package/dist/client/wrapFetch.js +47 -3
- package/dist/client/wrapFetch.js.map +1 -1
- package/dist/core/bound/proof.d.ts +10 -0
- package/dist/core/bound/proof.d.ts.map +1 -1
- package/dist/core/bound/proof.js +29 -2
- package/dist/core/bound/proof.js.map +1 -1
- package/dist/express/proof.d.ts +7 -0
- package/dist/express/proof.d.ts.map +1 -1
- package/dist/express/proof.js +19 -0
- package/dist/express/proof.js.map +1 -1
- package/dist/fastify/proof.d.ts +7 -0
- package/dist/fastify/proof.d.ts.map +1 -1
- package/dist/fastify/proof.js +19 -0
- package/dist/fastify/proof.js.map +1 -1
- package/dist/hono/proof.d.ts +6 -0
- package/dist/hono/proof.d.ts.map +1 -1
- package/dist/hono/proof.js +8 -0
- package/dist/hono/proof.js.map +1 -1
- package/dist/nextjs/proof.d.ts +7 -0
- package/dist/nextjs/proof.d.ts.map +1 -1
- package/dist/nextjs/proof.js +8 -0
- package/dist/nextjs/proof.js.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -108,7 +108,7 @@ app.post("/settings/password", requireBoundProof({ storage: dbscStorage }), pass
|
|
|
108
108
|
app.use("/admin", requireBoundProof({ storage: dbscStorage })); // gates everything under /admin
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
The `tier` field on every request is informational. Without a guard, a stolen cookie still reaches your handler — the library cannot infer which routes are sensitive, you mark them. `requireBoundProof` lets native DBSC traffic (`tier: "dbsc"`) through automatically (Chromium enforces session validity browser-side); Firefox / Safari traffic (`tier: "bound"`) must carry a fresh per-request signature, which the client-side [`wrapFetch()`](./docs/per-request-signing.md) adds for you.
|
|
111
|
+
The `tier` field on every request is informational. Without a guard, a stolen cookie still reaches your handler — the library cannot infer which routes are sensitive, you mark them. `requireBoundProof` lets native DBSC traffic (`tier: "dbsc"`) through automatically (Chromium enforces session validity browser-side); Firefox / Safari traffic (`tier: "bound"`) must carry a fresh per-request signature, which the client-side [`wrapFetch()`](./docs/per-request-signing.md) adds for you. For payment routes, pass `signBody: true` to both helpers (v2.3.0+) so the body hash is signed into the proof — closes the MITM body-substitution gap.
|
|
112
112
|
|
|
113
113
|
Fastify / Hono / Next.js variants of these six lines, plus the per-route policy table and a 30-day rollout timeline, are in [docs/integrating-existing-auth.md](./docs/integrating-existing-auth.md).
|
|
114
114
|
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export { wrapFetch } from "./wrapFetch.js";
|
|
2
2
|
export type { WrapFetchOptions } from "./wrapFetch.js";
|
|
3
|
+
/**
|
|
4
|
+
* Clears the bound-key record from IndexedDB. Call this on logout so the next
|
|
5
|
+
* login starts from a clean slate instead of letting the SDK detect a session
|
|
6
|
+
* mismatch and clear it lazily on the next page load.
|
|
7
|
+
*/
|
|
8
|
+
export declare function clearBoundKey(): Promise<void>;
|
|
3
9
|
export interface InitBoundDbscOptions {
|
|
4
10
|
statePath?: string;
|
|
5
11
|
challengePath?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC3E;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,GACpB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAgDtC,wBAAsB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgGjG;AAYD,wBAAgB,aAAa,IAAI,IAAI,CAKpC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD;;;;GAIG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAMnD;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC3E;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,GACpB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAgDtC,wBAAsB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgGjG;AAYD,wBAAgB,aAAa,IAAI,IAAI,CAKpC"}
|
package/dist/client/index.js
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { clearKeyRecord, getKeyRecord, setKeyRecord } from "./keystore.js";
|
|
2
2
|
import { recordServerTime } from "./clockSync.js";
|
|
3
3
|
export { wrapFetch } from "./wrapFetch.js";
|
|
4
|
+
/**
|
|
5
|
+
* Clears the bound-key record from IndexedDB. Call this on logout so the next
|
|
6
|
+
* login starts from a clean slate instead of letting the SDK detect a session
|
|
7
|
+
* mismatch and clear it lazily on the next page load.
|
|
8
|
+
*/
|
|
9
|
+
export async function clearBoundKey() {
|
|
10
|
+
await clearKeyRecord().catch(() => { });
|
|
11
|
+
if (refreshTimer !== null) {
|
|
12
|
+
clearTimeout(refreshTimer);
|
|
13
|
+
refreshTimer = null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
4
16
|
const DEFAULTS = {
|
|
5
17
|
statePath: "/dbsc-bound/state",
|
|
6
18
|
challengePath: "/dbsc-bound/challenge",
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,16 @@
|
|
|
14
14
|
export interface WrapFetchOptions {
|
|
15
15
|
fetch?: typeof fetch;
|
|
16
16
|
headerName?: string;
|
|
17
|
+
/**
|
|
18
|
+
* When true, the wrapper computes sha256(body) and signs it into the proof
|
|
19
|
+
* header. The server must be configured with `requireBoundProof({ signBody: true })`
|
|
20
|
+
* for the matching route. Defaults to false.
|
|
21
|
+
*
|
|
22
|
+
* Cost: one extra SHA-256 hash per request (~0.1 ms for typical JSON
|
|
23
|
+
* payloads). Cannot be used with streaming request bodies — the wrapper
|
|
24
|
+
* reads the body into memory to hash it.
|
|
25
|
+
*/
|
|
26
|
+
signBody?: boolean;
|
|
17
27
|
}
|
|
18
28
|
export declare function wrapFetch(opts?: WrapFetchOptions): typeof fetch;
|
|
19
29
|
//# sourceMappingURL=wrapFetch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapFetch.d.ts","sourceRoot":"","sources":["../../src/client/wrapFetch.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"wrapFetch.d.ts","sourceRoot":"","sources":["../../src/client/wrapFetch.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,SAAS,CAAC,IAAI,GAAE,gBAAqB,GAAG,OAAO,KAAK,CAsDnE"}
|
package/dist/client/wrapFetch.js
CHANGED
|
@@ -2,6 +2,7 @@ import { getKeyRecord } from "./keystore.js";
|
|
|
2
2
|
export function wrapFetch(opts = {}) {
|
|
3
3
|
const base = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
4
4
|
const headerName = opts.headerName ?? "X-Dbsc-Bound-Proof";
|
|
5
|
+
const signBody = opts.signBody ?? false;
|
|
5
6
|
return (async (input, init = {}) => {
|
|
6
7
|
const rec = await getKeyRecord().catch(() => null);
|
|
7
8
|
if (!rec)
|
|
@@ -10,14 +11,57 @@ export function wrapFetch(opts = {}) {
|
|
|
10
11
|
const method = (init.method ?? "GET").toUpperCase();
|
|
11
12
|
const offset = rec.clockOffsetMs ?? 0;
|
|
12
13
|
const ts = Date.now() + offset;
|
|
13
|
-
|
|
14
|
+
let bodyHash = "";
|
|
15
|
+
let finalBody = init.body;
|
|
16
|
+
if (signBody && init.body !== undefined && init.body !== null) {
|
|
17
|
+
const bodyBytes = await readBodyBytes(init.body);
|
|
18
|
+
// Re-use the bytes as the actual request body so server hashes the same.
|
|
19
|
+
// Wrap in a Blob to satisfy BodyInit on every runtime (Node, browser).
|
|
20
|
+
finalBody = new Blob([bodyBytes]);
|
|
21
|
+
bodyHash = await sha256B64Url(bodyBytes);
|
|
22
|
+
}
|
|
23
|
+
const message = signBody && bodyHash
|
|
24
|
+
? `${rec.sessionId}.${method}.${url.pathname}.${ts}.${bodyHash}`
|
|
25
|
+
: `${rec.sessionId}.${method}.${url.pathname}.${ts}`;
|
|
14
26
|
const sigBytes = await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, rec.keyPair.privateKey, new TextEncoder().encode(message));
|
|
15
27
|
const sig = base64url(new Uint8Array(sigBytes));
|
|
16
28
|
const headers = new Headers(init.headers);
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
const headerValue = bodyHash
|
|
30
|
+
? `ts=${ts};sig=${sig};bh=${bodyHash}`
|
|
31
|
+
: `ts=${ts};sig=${sig}`;
|
|
32
|
+
headers.set(headerName, headerValue);
|
|
33
|
+
const nextInit = {
|
|
34
|
+
...init,
|
|
35
|
+
headers,
|
|
36
|
+
credentials: init.credentials ?? "include",
|
|
37
|
+
};
|
|
38
|
+
if (finalBody !== undefined && finalBody !== null) {
|
|
39
|
+
nextInit.body = finalBody;
|
|
40
|
+
}
|
|
41
|
+
return base(input, nextInit);
|
|
19
42
|
});
|
|
20
43
|
}
|
|
44
|
+
async function readBodyBytes(body) {
|
|
45
|
+
if (body instanceof Uint8Array)
|
|
46
|
+
return body;
|
|
47
|
+
if (body instanceof ArrayBuffer)
|
|
48
|
+
return new Uint8Array(body);
|
|
49
|
+
if (typeof body === "string")
|
|
50
|
+
return new TextEncoder().encode(body);
|
|
51
|
+
if (body instanceof Blob)
|
|
52
|
+
return new Uint8Array(await body.arrayBuffer());
|
|
53
|
+
if (body instanceof FormData || body instanceof URLSearchParams) {
|
|
54
|
+
return new TextEncoder().encode(body.toString());
|
|
55
|
+
}
|
|
56
|
+
if (body instanceof ReadableStream) {
|
|
57
|
+
throw new Error("wrapFetch with signBody: ReadableStream body is not supported");
|
|
58
|
+
}
|
|
59
|
+
return new TextEncoder().encode(String(body));
|
|
60
|
+
}
|
|
61
|
+
async function sha256B64Url(bytes) {
|
|
62
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
|
|
63
|
+
return base64url(new Uint8Array(digest));
|
|
64
|
+
}
|
|
21
65
|
function base64url(b) {
|
|
22
66
|
let s = "";
|
|
23
67
|
for (let i = 0; i < b.length; i++)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapFetch.js","sourceRoot":"","sources":["../../src/client/wrapFetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"wrapFetch.js","sourceRoot":"","sources":["../../src/client/wrapFetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,10 +6,20 @@ export interface VerifyBoundProofRequest {
|
|
|
6
6
|
method: string;
|
|
7
7
|
path: string;
|
|
8
8
|
timestampWindowMs?: number | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Raw request body bytes. When present, `signBody` is implied. The server
|
|
11
|
+
* computes sha256(bodyBytes) and demands the proof header carries a
|
|
12
|
+
* matching `bh=` field signed into the message. Pass undefined for GET/HEAD
|
|
13
|
+
* or when body signing is disabled.
|
|
14
|
+
*/
|
|
15
|
+
bodyBytes?: Uint8Array | undefined;
|
|
16
|
+
/** Force body signing even on GET/HEAD requests (rare). */
|
|
17
|
+
signBody?: boolean | undefined;
|
|
9
18
|
}
|
|
10
19
|
export declare function verifyBoundProof(req: VerifyBoundProofRequest, storage: StorageAdapter): Promise<void>;
|
|
11
20
|
export declare function parseProofHeader(s: string): {
|
|
12
21
|
ts: number;
|
|
13
22
|
sig: string;
|
|
23
|
+
bh?: string;
|
|
14
24
|
} | null;
|
|
15
25
|
//# sourceMappingURL=proof.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../../src/core/bound/proof.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAGvD,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../../src/core/bound/proof.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAGvD,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChC;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,uBAAuB,EAC5B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CAqCf;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAW3F"}
|
package/dist/core/bound/proof.js
CHANGED
|
@@ -18,7 +18,21 @@ export async function verifyBoundProof(req, storage) {
|
|
|
18
18
|
if (!key) {
|
|
19
19
|
throw new DbscVerificationError(ErrorCodes.KEY_NOT_FOUND, "no bound key for session");
|
|
20
20
|
}
|
|
21
|
-
const
|
|
21
|
+
const wantBodySig = req.signBody === true || (req.bodyBytes !== undefined && req.bodyBytes.byteLength > 0);
|
|
22
|
+
let expectedBodyHash = "";
|
|
23
|
+
if (wantBodySig) {
|
|
24
|
+
if (!parsed.bh) {
|
|
25
|
+
throw new DbscVerificationError(ErrorCodes.MALFORMED_PROOF, "proof header missing bh (body hash)");
|
|
26
|
+
}
|
|
27
|
+
const actualBodyHash = await sha256Base64Url(req.bodyBytes ?? new Uint8Array(0));
|
|
28
|
+
if (actualBodyHash !== parsed.bh) {
|
|
29
|
+
throw new DbscVerificationError(ErrorCodes.SIGNATURE_INVALID, "body hash mismatch");
|
|
30
|
+
}
|
|
31
|
+
expectedBodyHash = parsed.bh;
|
|
32
|
+
}
|
|
33
|
+
const message = wantBodySig
|
|
34
|
+
? `${req.sessionId}.${req.method.toUpperCase()}.${req.path}.${parsed.ts}.${expectedBodyHash}`
|
|
35
|
+
: `${req.sessionId}.${req.method.toUpperCase()}.${req.path}.${parsed.ts}`;
|
|
22
36
|
const ok = await verifyP256Signature(key.jwk, parsed.sig, message);
|
|
23
37
|
if (!ok) {
|
|
24
38
|
throw new DbscVerificationError(ErrorCodes.SIGNATURE_INVALID, "proof signature did not verify");
|
|
@@ -34,6 +48,19 @@ export function parseProofHeader(s) {
|
|
|
34
48
|
const ts = Number(parts.ts);
|
|
35
49
|
if (!Number.isFinite(ts) || !parts.sig)
|
|
36
50
|
return null;
|
|
37
|
-
|
|
51
|
+
const out = { ts, sig: parts.sig };
|
|
52
|
+
if (parts.bh)
|
|
53
|
+
out.bh = parts.bh;
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
async function sha256Base64Url(bytes) {
|
|
57
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
|
|
58
|
+
return base64UrlBytes(new Uint8Array(digest));
|
|
59
|
+
}
|
|
60
|
+
function base64UrlBytes(b) {
|
|
61
|
+
let s = "";
|
|
62
|
+
for (let i = 0; i < b.length; i++)
|
|
63
|
+
s += String.fromCharCode(b[i]);
|
|
64
|
+
return Buffer.from(s, "binary").toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
38
65
|
}
|
|
39
66
|
//# sourceMappingURL=proof.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../../../src/core/bound/proof.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAC;AACvD,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../../../src/core/bound/proof.ts"],"names":[],"mappings":"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"}
|
package/dist/express/proof.d.ts
CHANGED
|
@@ -6,6 +6,13 @@ export interface RequireBoundProofOptions {
|
|
|
6
6
|
allowDbscWithoutProof?: boolean;
|
|
7
7
|
/** Accepts proofs whose ts is within ±N ms of server time. Defaults to 5 minutes. */
|
|
8
8
|
timestampWindowMs?: number;
|
|
9
|
+
/**
|
|
10
|
+
* When true, the proof must include a `bh=` body-hash field signed into the
|
|
11
|
+
* message. The middleware reads `req.body` as raw bytes — your route MUST
|
|
12
|
+
* use `express.raw({ type: '*\/*' })` for this to work, otherwise the parsed
|
|
13
|
+
* JSON body won't match the client's pre-hash bytes. Defaults to false.
|
|
14
|
+
*/
|
|
15
|
+
signBody?: boolean;
|
|
9
16
|
}
|
|
10
17
|
/**
|
|
11
18
|
* Gates a route on a fresh ECDSA P-256 proof signed by the bound key.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../src/express/proof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmC,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,cAAc,CAAC;IACxB,8HAA8H;IAC9H,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,qFAAqF;IACrF,iBAAiB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../src/express/proof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmC,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,cAAc,CAAC;IACxB,8HAA8H;IAC9H,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,qFAAqF;IACrF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,GAAG,cAAc,CAgDhF"}
|
package/dist/express/proof.js
CHANGED
|
@@ -9,6 +9,7 @@ import { verifyBoundProof, DbscVerificationError, } from "../core/index.js";
|
|
|
9
9
|
*/
|
|
10
10
|
export function requireBoundProof(opts) {
|
|
11
11
|
const allowDbsc = opts.allowDbscWithoutProof ?? true;
|
|
12
|
+
const signBody = opts.signBody ?? false;
|
|
12
13
|
return async (req, res, next) => {
|
|
13
14
|
const dbsc = res.locals.dbsc;
|
|
14
15
|
if (!dbsc?.sessionId || dbsc.tier === "none") {
|
|
@@ -20,12 +21,30 @@ export function requireBoundProof(opts) {
|
|
|
20
21
|
return;
|
|
21
22
|
}
|
|
22
23
|
try {
|
|
24
|
+
let bodyBytes;
|
|
25
|
+
if (signBody) {
|
|
26
|
+
const raw = req.body;
|
|
27
|
+
if (raw instanceof Buffer) {
|
|
28
|
+
bodyBytes = new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);
|
|
29
|
+
}
|
|
30
|
+
else if (raw instanceof Uint8Array) {
|
|
31
|
+
bodyBytes = raw;
|
|
32
|
+
}
|
|
33
|
+
else if (typeof raw === "string") {
|
|
34
|
+
bodyBytes = new TextEncoder().encode(raw);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
bodyBytes = new Uint8Array(0);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
23
40
|
await verifyBoundProof({
|
|
24
41
|
sessionId: dbsc.sessionId,
|
|
25
42
|
proofHeader: req.headers["x-dbsc-bound-proof"],
|
|
26
43
|
method: req.method,
|
|
27
44
|
path: req.path,
|
|
28
45
|
timestampWindowMs: opts.timestampWindowMs,
|
|
46
|
+
signBody,
|
|
47
|
+
bodyBytes,
|
|
29
48
|
}, opts.storage);
|
|
30
49
|
next();
|
|
31
50
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../../src/express/proof.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,qBAAqB,GAEtB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../../src/express/proof.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,qBAAqB,GAEtB,MAAM,kBAAkB,CAAC;AAiB1B;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAA8B;IAC9D,MAAM,SAAS,GAAG,IAAI,CAAC,qBAAqB,IAAI,IAAI,CAAC;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;IACxC,OAAO,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAiB,EAAE;QAC9E,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;QAC7B,IAAI,CAAC,IAAI,EAAE,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC7C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,MAAM,EAAE,CAAC,CAAC;YACjF,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS,EAAE,CAAC;YACtC,IAAI,EAAE,CAAC;YACP,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,IAAI,SAAiC,CAAC;YACtC,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,GAAG,GAAG,GAAG,CAAC,IAAe,CAAC;gBAChC,IAAI,GAAG,YAAY,MAAM,EAAE,CAAC;oBAC1B,SAAS,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;gBACzE,CAAC;qBAAM,IAAI,GAAG,YAAY,UAAU,EAAE,CAAC;oBACrC,SAAS,GAAG,GAAG,CAAC;gBAClB,CAAC;qBAAM,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;oBACnC,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC5C,CAAC;qBAAM,CAAC;oBACN,SAAS,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;gBAChC,CAAC;YACH,CAAC;YACD,MAAM,gBAAgB,CACpB;gBACE,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAuB;gBACpE,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;gBACzC,QAAQ;gBACR,SAAS;aACV,EACD,IAAI,CAAC,OAAO,CACb,CAAC;YACF,IAAI,EAAE,CAAC;QACT,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,qBAAqB,EAAE,CAAC;gBACzC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;gBAC7D,OAAO;YACT,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/fastify/proof.d.ts
CHANGED
|
@@ -4,6 +4,13 @@ export interface RequireBoundProofOptions {
|
|
|
4
4
|
storage: StorageAdapter;
|
|
5
5
|
allowDbscWithoutProof?: boolean;
|
|
6
6
|
timestampWindowMs?: number;
|
|
7
|
+
/**
|
|
8
|
+
* When true, the proof must include a `bh=` body-hash field. Your route
|
|
9
|
+
* MUST register a raw content-type parser via Fastify's
|
|
10
|
+
* `addContentTypeParser('*', { parseAs: 'buffer' }, ...)` for the route to
|
|
11
|
+
* deliver `req.body` as a Buffer matching the client's pre-hash bytes.
|
|
12
|
+
*/
|
|
13
|
+
signBody?: boolean;
|
|
7
14
|
}
|
|
8
15
|
/**
|
|
9
16
|
* Gates a route on a fresh ECDSA P-256 proof signed by the bound key.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../src/fastify/proof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgC,0BAA0B,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,cAAc,CAAC;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../src/fastify/proof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgC,0BAA0B,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,cAAc,CAAC;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,GAAG,0BAA0B,CA8C5F"}
|
package/dist/fastify/proof.js
CHANGED
|
@@ -5,6 +5,7 @@ import { verifyBoundProof, DbscVerificationError, } from "../core/index.js";
|
|
|
5
5
|
*/
|
|
6
6
|
export function requireBoundProof(opts) {
|
|
7
7
|
const allowDbsc = opts.allowDbscWithoutProof ?? true;
|
|
8
|
+
const signBody = opts.signBody ?? false;
|
|
8
9
|
return async function preHandler(req, reply) {
|
|
9
10
|
const dbsc = req.dbsc;
|
|
10
11
|
if (!dbsc?.sessionId || dbsc.tier === "none") {
|
|
@@ -16,12 +17,30 @@ export function requireBoundProof(opts) {
|
|
|
16
17
|
try {
|
|
17
18
|
const proofHeaderRaw = req.headers["x-dbsc-bound-proof"];
|
|
18
19
|
const proofHeader = Array.isArray(proofHeaderRaw) ? proofHeaderRaw[0] : proofHeaderRaw;
|
|
20
|
+
let bodyBytes;
|
|
21
|
+
if (signBody) {
|
|
22
|
+
const raw = req.body;
|
|
23
|
+
if (raw instanceof Buffer) {
|
|
24
|
+
bodyBytes = new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);
|
|
25
|
+
}
|
|
26
|
+
else if (raw instanceof Uint8Array) {
|
|
27
|
+
bodyBytes = raw;
|
|
28
|
+
}
|
|
29
|
+
else if (typeof raw === "string") {
|
|
30
|
+
bodyBytes = new TextEncoder().encode(raw);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
bodyBytes = new Uint8Array(0);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
19
36
|
await verifyBoundProof({
|
|
20
37
|
sessionId: dbsc.sessionId,
|
|
21
38
|
proofHeader,
|
|
22
39
|
method: req.method,
|
|
23
40
|
path: req.url.split("?")[0] ?? req.url,
|
|
24
41
|
timestampWindowMs: opts.timestampWindowMs,
|
|
42
|
+
signBody,
|
|
43
|
+
bodyBytes,
|
|
25
44
|
}, opts.storage);
|
|
26
45
|
}
|
|
27
46
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../../src/fastify/proof.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,qBAAqB,GAEtB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../../src/fastify/proof.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,qBAAqB,GAEtB,MAAM,kBAAkB,CAAC;AAe1B;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAA8B;IAC9D,MAAM,SAAS,GAAG,IAAI,CAAC,qBAAqB,IAAI,IAAI,CAAC;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;IACxC,OAAO,KAAK,UAAU,UAAU,CAAC,GAAmB,EAAE,KAAmB;QACvE,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,IAAI,EAAE,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC7C,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,MAAM,EAAE,CAAC,CAAC;YACnF,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS;YAAE,OAAO;QAC9C,IAAI,CAAC;YACH,MAAM,cAAc,GAAG,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;YACzD,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;YACvF,IAAI,SAAiC,CAAC;YACtC,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,GAAG,GAAG,GAAG,CAAC,IAAe,CAAC;gBAChC,IAAI,GAAG,YAAY,MAAM,EAAE,CAAC;oBAC1B,SAAS,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;gBACzE,CAAC;qBAAM,IAAI,GAAG,YAAY,UAAU,EAAE,CAAC;oBACrC,SAAS,GAAG,GAAG,CAAC;gBAClB,CAAC;qBAAM,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;oBACnC,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC5C,CAAC;qBAAM,CAAC;oBACN,SAAS,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;gBAChC,CAAC;YACH,CAAC;YACD,MAAM,gBAAgB,CACpB;gBACE,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,WAAW;gBACX,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG;gBACtC,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;gBACzC,QAAQ;gBACR,SAAS;aACV,EACD,IAAI,CAAC,OAAO,CACb,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,qBAAqB,EAAE,CAAC;gBACzC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;gBAC/D,OAAO;YACT,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/hono/proof.d.ts
CHANGED
|
@@ -4,6 +4,12 @@ export interface RequireBoundProofOptions {
|
|
|
4
4
|
storage: StorageAdapter;
|
|
5
5
|
allowDbscWithoutProof?: boolean;
|
|
6
6
|
timestampWindowMs?: number;
|
|
7
|
+
/**
|
|
8
|
+
* When true, the middleware reads `c.req.raw.arrayBuffer()` and verifies a
|
|
9
|
+
* matching `bh=` body-hash field. Downstream handlers that need the body
|
|
10
|
+
* after this should use `c.req.arrayBuffer()` themselves (it is cached).
|
|
11
|
+
*/
|
|
12
|
+
signBody?: boolean;
|
|
7
13
|
}
|
|
8
14
|
/**
|
|
9
15
|
* Gates a route on a fresh ECDSA P-256 proof signed by the bound key.
|
package/dist/hono/proof.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../src/hono/proof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,iBAAiB,EAAE,MAAM,MAAM,CAAC;AACvD,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,cAAc,CAAC;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../src/hono/proof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,iBAAiB,EAAE,MAAM,MAAM,CAAC;AACvD,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,cAAc,CAAC;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,GAAG,iBAAiB,CAwCnF"}
|
package/dist/hono/proof.js
CHANGED
|
@@ -5,6 +5,7 @@ import { verifyBoundProof, DbscVerificationError, } from "../core/index.js";
|
|
|
5
5
|
*/
|
|
6
6
|
export function requireBoundProof(opts) {
|
|
7
7
|
const allowDbsc = opts.allowDbscWithoutProof ?? true;
|
|
8
|
+
const signBody = opts.signBody ?? false;
|
|
8
9
|
return async (c, next) => {
|
|
9
10
|
const dbsc = c.get("dbsc");
|
|
10
11
|
if (!dbsc?.sessionId || dbsc.tier === "none") {
|
|
@@ -17,12 +18,19 @@ export function requireBoundProof(opts) {
|
|
|
17
18
|
try {
|
|
18
19
|
const proofHeader = c.req.header("x-dbsc-bound-proof");
|
|
19
20
|
const url = new URL(c.req.url);
|
|
21
|
+
let bodyBytes;
|
|
22
|
+
if (signBody) {
|
|
23
|
+
const ab = await c.req.arrayBuffer();
|
|
24
|
+
bodyBytes = new Uint8Array(ab);
|
|
25
|
+
}
|
|
20
26
|
await verifyBoundProof({
|
|
21
27
|
sessionId: dbsc.sessionId,
|
|
22
28
|
proofHeader,
|
|
23
29
|
method: c.req.method,
|
|
24
30
|
path: url.pathname,
|
|
25
31
|
timestampWindowMs: opts.timestampWindowMs,
|
|
32
|
+
signBody,
|
|
33
|
+
bodyBytes,
|
|
26
34
|
}, opts.storage);
|
|
27
35
|
await next();
|
|
28
36
|
}
|
package/dist/hono/proof.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../../src/hono/proof.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,qBAAqB,GAEtB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../../src/hono/proof.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,qBAAqB,GAEtB,MAAM,kBAAkB,CAAC;AAc1B;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAA8B;IAC9D,MAAM,SAAS,GAAG,IAAI,CAAC,qBAAqB,IAAI,IAAI,CAAC;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;IACxC,OAAO,KAAK,EAAE,CAAU,EAAE,IAAI,EAA4B,EAAE;QAC1D,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC3B,IAAI,CAAC,IAAI,EAAE,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC7C,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC;QACjF,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS,EAAE,CAAC;YACtC,MAAM,IAAI,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;YACvD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,SAAiC,CAAC;YACtC,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrC,SAAS,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;YACjC,CAAC;YACD,MAAM,gBAAgB,CACpB;gBACE,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,WAAW;gBACX,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM;gBACpB,IAAI,EAAE,GAAG,CAAC,QAAQ;gBAClB,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;gBACzC,QAAQ;gBACR,SAAS;aACV,EACD,IAAI,CAAC,OAAO,CACb,CAAC;YACF,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,qBAAqB,EAAE,CAAC;gBACzC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;YAC7D,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/nextjs/proof.d.ts
CHANGED
|
@@ -5,6 +5,13 @@ export interface RequireBoundProofOptions {
|
|
|
5
5
|
storage: StorageAdapter;
|
|
6
6
|
allowDbscWithoutProof?: boolean;
|
|
7
7
|
timestampWindowMs?: number;
|
|
8
|
+
/**
|
|
9
|
+
* When true, the helper calls `req.arrayBuffer()` and verifies a matching
|
|
10
|
+
* `bh=` body-hash field. Note: NextRequest.arrayBuffer() consumes the body
|
|
11
|
+
* stream — the caller must either re-read the body via a clone or call
|
|
12
|
+
* `req.json()` on a clone afterwards.
|
|
13
|
+
*/
|
|
14
|
+
signBody?: boolean;
|
|
8
15
|
}
|
|
9
16
|
export interface RequireBoundProofContext {
|
|
10
17
|
sessionId: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../src/nextjs/proof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,cAAc,CAAC;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../src/nextjs/proof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,cAAc,CAAC;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,cAAc,CAAC;CACtB;AAED,MAAM,MAAM,uBAAuB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAA;CAAE,CAAC;AAE3F;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,wBAAwB,EACjC,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,uBAAuB,CAAC,CA0ClC"}
|
package/dist/nextjs/proof.js
CHANGED
|
@@ -21,12 +21,20 @@ export async function requireBoundProof(req, session, opts) {
|
|
|
21
21
|
if (session.tier === "dbsc" && allowDbsc)
|
|
22
22
|
return { ok: true };
|
|
23
23
|
try {
|
|
24
|
+
const signBody = opts.signBody ?? false;
|
|
25
|
+
let bodyBytes;
|
|
26
|
+
if (signBody) {
|
|
27
|
+
const ab = await req.clone().arrayBuffer();
|
|
28
|
+
bodyBytes = new Uint8Array(ab);
|
|
29
|
+
}
|
|
24
30
|
await verifyBoundProof({
|
|
25
31
|
sessionId: session.sessionId,
|
|
26
32
|
proofHeader: req.headers.get("x-dbsc-bound-proof") ?? undefined,
|
|
27
33
|
method: req.method,
|
|
28
34
|
path: req.nextUrl.pathname,
|
|
29
35
|
timestampWindowMs: opts.timestampWindowMs,
|
|
36
|
+
signBody,
|
|
37
|
+
bodyBytes,
|
|
30
38
|
}, opts.storage);
|
|
31
39
|
return { ok: true };
|
|
32
40
|
}
|
package/dist/nextjs/proof.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../../src/nextjs/proof.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EACL,gBAAgB,EAChB,qBAAqB,GAGtB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../../src/nextjs/proof.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EACL,gBAAgB,EAChB,qBAAqB,GAGtB,MAAM,kBAAkB,CAAC;AAsB1B;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,GAAgB,EAChB,OAAiC,EACjC,IAA8B;IAE9B,MAAM,SAAS,GAAG,IAAI,CAAC,qBAAqB,IAAI,IAAI,CAAC;IACrD,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAClD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,QAAQ,EAAE,YAAY,CAAC,IAAI,CACzB,EAAE,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,EAClD,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB;SACF,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IAE9D,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;QACxC,IAAI,SAAiC,CAAC;QACtC,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,CAAC;YAC3C,SAAS,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;QACjC,CAAC;QACD,MAAM,gBAAgB,CACpB;YACE,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,SAAS;YAC/D,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,QAAQ;YAC1B,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;YACzC,QAAQ;YACR,SAAS;SACV,EACD,IAAI,CAAC,OAAO,CACb,CAAC;QACF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,qBAAqB,EAAE,CAAC;YACzC,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,QAAQ,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;aACrF,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dbsc-toolkit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Server-side Device Bound Session Credentials (DBSC) for Node.js plus a Web Crypto polyfill for Firefox, Safari, and older Chromium. Cookie-theft protection on every modern browser.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -98,9 +98,11 @@
|
|
|
98
98
|
"@types/pg": "^8.0.0",
|
|
99
99
|
"cookie-parser": "^1.4.7",
|
|
100
100
|
"express": "^5.2.1",
|
|
101
|
+
"fake-indexeddb": "^6.2.5",
|
|
101
102
|
"fastify": "^5.8.5",
|
|
102
103
|
"hono": "^4.0.0",
|
|
103
104
|
"ioredis": "^5.3.0",
|
|
105
|
+
"jsdom": "^29.1.1",
|
|
104
106
|
"next": "^16.2.6",
|
|
105
107
|
"pg": "^8.11.0",
|
|
106
108
|
"rimraf": "^6.1.3",
|