dbsc-toolkit 2.10.0 → 2.11.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 +21 -7
- package/dist/koa/create-dbsc.d.ts +25 -0
- package/dist/koa/create-dbsc.d.ts.map +1 -0
- package/dist/koa/create-dbsc.js +32 -0
- package/dist/koa/create-dbsc.js.map +1 -0
- package/dist/koa/index.d.ts +19 -0
- package/dist/koa/index.d.ts.map +1 -0
- package/dist/koa/index.js +26 -0
- package/dist/koa/index.js.map +1 -0
- package/dist/koa/require-proof.d.ts +9 -0
- package/dist/koa/require-proof.d.ts.map +1 -0
- package/dist/koa/require-proof.js +69 -0
- package/dist/koa/require-proof.js.map +1 -0
- package/dist/nestjs/index.d.ts +45 -0
- package/dist/nestjs/index.d.ts.map +1 -0
- package/dist/nestjs/index.js +147 -0
- package/dist/nestjs/index.js.map +1 -0
- package/dist/node/create-dbsc.d.ts +29 -0
- package/dist/node/create-dbsc.d.ts.map +1 -0
- package/dist/node/create-dbsc.js +33 -0
- package/dist/node/create-dbsc.js.map +1 -0
- package/dist/node/index.d.ts +53 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +412 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/require-proof.d.ts +17 -0
- package/dist/node/require-proof.d.ts.map +1 -0
- package/dist/node/require-proof.js +74 -0
- package/dist/node/require-proof.js.map +1 -0
- package/dist/sveltekit/index.d.ts +48 -0
- package/dist/sveltekit/index.d.ts.map +1 -0
- package/dist/sveltekit/index.js +318 -0
- package/dist/sveltekit/index.js.map +1 -0
- package/dist/sveltekit/require-proof.d.ts +15 -0
- package/dist/sveltekit/require-proof.d.ts.map +1 -0
- package/dist/sveltekit/require-proof.js +52 -0
- package/dist/sveltekit/require-proof.js.map +1 -0
- package/package.json +41 -2
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<strong>Stop stolen session cookies from being replayed on another device.</strong><br>
|
|
9
|
-
|
|
9
|
+
Device Bound Session Credentials for Node.js — one framework-agnostic core with adapters for Express, Fastify, Hono, Next.js, NestJS, Koa, SvelteKit and raw <code>node:http</code>, plus a Web Crypto polyfill so every browser is covered, not just Chrome. A language-neutral protocol spec with a Node reference implementation.
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
@@ -122,9 +122,23 @@ Load the polyfill in your HTML so non-Chromium browsers reach `tier: "bound"`:
|
|
|
122
122
|
|
|
123
123
|
Full walk-through, failure modes, and migration timeline: **[docs/getting-started.md](./docs/getting-started.md)** and **[docs/integrating-existing-auth.md](./docs/integrating-existing-auth.md)**.
|
|
124
124
|
|
|
125
|
-
###
|
|
125
|
+
### Any framework
|
|
126
126
|
|
|
127
|
-
The protocol lives in a framework-agnostic core — plain functions plus a `StorageAdapter`, no HTTP-layer assumptions. The
|
|
127
|
+
The protocol lives in a framework-agnostic core — plain functions plus a `StorageAdapter`, no HTTP-layer assumptions. The adapters are thin wrappers; pick yours:
|
|
128
|
+
|
|
129
|
+
| Framework | Import |
|
|
130
|
+
|---|---|
|
|
131
|
+
| Express | `dbsc-toolkit/express` |
|
|
132
|
+
| Fastify | `dbsc-toolkit/fastify` |
|
|
133
|
+
| Hono (Node, Bun, Deno, Workers) | `dbsc-toolkit/hono` |
|
|
134
|
+
| Next.js (App Router) | `dbsc-toolkit/nextjs` |
|
|
135
|
+
| NestJS | `dbsc-toolkit/nestjs` |
|
|
136
|
+
| Koa | `dbsc-toolkit/koa` |
|
|
137
|
+
| SvelteKit | `dbsc-toolkit/sveltekit` |
|
|
138
|
+
| Any other server (raw `node:http`) | `dbsc-toolkit/node` |
|
|
139
|
+
| [Better Auth](https://better-auth.com) | `@dbsc-toolkit/better-auth` |
|
|
140
|
+
|
|
141
|
+
**No adapter for your framework? You can still use it.** Every adapter is a thin shell over the same core, so any HTTP framework on Node works — wire the core in directly:
|
|
128
142
|
|
|
129
143
|
```ts
|
|
130
144
|
import {
|
|
@@ -136,7 +150,7 @@ import {
|
|
|
136
150
|
} from "dbsc-toolkit";
|
|
137
151
|
```
|
|
138
152
|
|
|
139
|
-
|
|
153
|
+
The generic `dbsc-toolkit/node` adapter is exactly this over raw `node:http`. Complete example: [docs/adapters.md](./docs/adapters.md#writing-your-own-adapter).
|
|
140
154
|
|
|
141
155
|
## How it compares
|
|
142
156
|
|
|
@@ -186,7 +200,7 @@ The polyfill is on by default. To run native DBSC only (Chromium 145+, no `bound
|
|
|
186
200
|
|
|
187
201
|
| Package | What it is |
|
|
188
202
|
|---|---|
|
|
189
|
-
| `dbsc-toolkit` | Core + Express
|
|
203
|
+
| `dbsc-toolkit` | Core + adapters for Express, Fastify, Hono, Next.js, NestJS, Koa, SvelteKit & raw `node:http`; memory/Redis/Postgres storage |
|
|
190
204
|
| `dbsc-toolkit/client` | Browser SDK + Web Crypto polyfill |
|
|
191
205
|
| [`@dbsc-toolkit/better-auth`](./packages/better-auth/) | First-class [Better Auth](https://better-auth.com) plugin — binds every sign-in method automatically |
|
|
192
206
|
|
|
@@ -211,7 +225,7 @@ The wire protocol is documented as a language-neutral spec in [`spec/`](./spec/)
|
|
|
211
225
|
- [x] Per-request proof + body signing + replay cache
|
|
212
226
|
- [x] Multi-subdomain binding (`cookieScope: "site"`)
|
|
213
227
|
- [x] Better Auth plugin
|
|
214
|
-
- [
|
|
228
|
+
- [x] NestJS, Koa, SvelteKit & generic `node:http` adapters
|
|
215
229
|
- [ ] Bun / Deno native paths
|
|
216
230
|
- [ ] Third-party security audit
|
|
217
231
|
|
|
@@ -220,7 +234,7 @@ The wire protocol is documented as a language-neutral spec in [`spec/`](./spec/)
|
|
|
220
234
|
| Import | What it is |
|
|
221
235
|
|---|---|
|
|
222
236
|
| `dbsc-toolkit` | Core: types, crypto, protocol handlers, framework-agnostic |
|
|
223
|
-
| `dbsc-toolkit/express` · `/fastify` · `/hono` · `/nextjs` | Framework adapters |
|
|
237
|
+
| `dbsc-toolkit/express` · `/fastify` · `/hono` · `/nextjs` · `/nestjs` · `/koa` · `/sveltekit` · `/node` | Framework adapters |
|
|
224
238
|
| `dbsc-toolkit/client` | Browser SDK + polyfill |
|
|
225
239
|
| `dbsc-toolkit/storage/{memory,redis,postgres}` | Storage adapters |
|
|
226
240
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Context, Middleware } from "koa";
|
|
2
|
+
import type Application from "koa";
|
|
3
|
+
import { type RequireProofOptions } from "../core/index.js";
|
|
4
|
+
import { type DbscKoaOptions } from "./index.js";
|
|
5
|
+
export interface CreateDbscOptions extends DbscKoaOptions {
|
|
6
|
+
/** Default session TTL (ms) for `bind()`. */
|
|
7
|
+
sessionTtl?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface BindOptions {
|
|
10
|
+
userId: string;
|
|
11
|
+
namespace?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface DbscKit {
|
|
14
|
+
/** Mount the dbsc middleware on the Koa app. */
|
|
15
|
+
install(app: Application): Application;
|
|
16
|
+
/** The raw dbsc middleware, for manual mounting. */
|
|
17
|
+
middleware(): Middleware;
|
|
18
|
+
/** Start a binding for an explicit session id. */
|
|
19
|
+
bind(ctx: Context, sessionId: string, opts: BindOptions): Promise<string>;
|
|
20
|
+
/** The route guard — requires a bound device + a per-request proof. */
|
|
21
|
+
requireProof(opts?: RequireProofOptions): Middleware;
|
|
22
|
+
}
|
|
23
|
+
/** Builds a configured DBSC kit for Koa. */
|
|
24
|
+
export declare function createDbsc(opts: CreateDbscOptions): DbscKit;
|
|
25
|
+
//# sourceMappingURL=create-dbsc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-dbsc.d.ts","sourceRoot":"","sources":["../../src/koa/create-dbsc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAC/C,OAAO,KAAK,WAAW,MAAM,KAAK,CAAC;AACnC,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAGpE,MAAM,WAAW,iBAAkB,SAAQ,cAAc;IACvD,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,gDAAgD;IAChD,OAAO,CAAC,GAAG,EAAE,WAAW,GAAG,WAAW,CAAC;IACvC,oDAAoD;IACpD,UAAU,IAAI,UAAU,CAAC;IACzB,kDAAkD;IAClD,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1E,uEAAuE;IACvE,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,UAAU,CAAC;CACtD;AAED,4CAA4C;AAC5C,wBAAgB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CA6B3D"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { dbsc, bindSession } from "./index.js";
|
|
2
|
+
import { requireProof } from "./require-proof.js";
|
|
3
|
+
/** Builds a configured DBSC kit for Koa. */
|
|
4
|
+
export function createDbsc(opts) {
|
|
5
|
+
const secure = opts.secure ?? true;
|
|
6
|
+
const cookieScope = opts.cookieScope;
|
|
7
|
+
const cookieDomain = opts.cookieDomain;
|
|
8
|
+
const registrationPath = opts.registrationPath ?? "/dbsc/registration";
|
|
9
|
+
const middleware = dbsc(opts);
|
|
10
|
+
async function bind(ctx, sessionId, bindOpts) {
|
|
11
|
+
await bindSession(ctx, sessionId, opts.storage, {
|
|
12
|
+
userId: bindOpts.userId,
|
|
13
|
+
secure,
|
|
14
|
+
...(cookieScope !== undefined && { cookieScope }),
|
|
15
|
+
...(cookieDomain !== undefined && { cookieDomain }),
|
|
16
|
+
registrationPath,
|
|
17
|
+
...(opts.registrationCookieTtl !== undefined && { registrationCookieTtl: opts.registrationCookieTtl }),
|
|
18
|
+
...(opts.sessionTtl !== undefined && { sessionTtl: opts.sessionTtl }),
|
|
19
|
+
});
|
|
20
|
+
return sessionId;
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
middleware: () => middleware,
|
|
24
|
+
install(app) {
|
|
25
|
+
app.use(middleware);
|
|
26
|
+
return app;
|
|
27
|
+
},
|
|
28
|
+
bind,
|
|
29
|
+
requireProof,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=create-dbsc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-dbsc.js","sourceRoot":"","sources":["../../src/koa/create-dbsc.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,WAAW,EAAuB,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAuBlD,4CAA4C;AAC5C,MAAM,UAAU,UAAU,CAAC,IAAuB;IAChD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC;IACnC,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IACrC,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;IACvC,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,IAAI,oBAAoB,CAAC;IACvE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAE9B,KAAK,UAAU,IAAI,CAAC,GAAY,EAAE,SAAiB,EAAE,QAAqB;QACxE,MAAM,WAAW,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,EAAE;YAC9C,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,MAAM;YACN,GAAG,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,CAAC;YACjD,GAAG,CAAC,YAAY,KAAK,SAAS,IAAI,EAAE,YAAY,EAAE,CAAC;YACnD,gBAAgB;YAChB,GAAG,CAAC,IAAI,CAAC,qBAAqB,KAAK,SAAS,IAAI,EAAE,qBAAqB,EAAE,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACtG,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;SACtE,CAAC,CAAC;QACH,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,UAAU,EAAE,GAAG,EAAE,CAAC,UAAU;QAC5B,OAAO,CAAC,GAAgB;YACtB,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACpB,OAAO,GAAG,CAAC;QACb,CAAC;QACD,IAAI;QACJ,YAAY;KACb,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Context, Middleware } from "koa";
|
|
2
|
+
import type { StorageAdapter } from "../core/index.js";
|
|
3
|
+
import { type DbscNodeOptions, type BindSessionOptions } from "../node/index.js";
|
|
4
|
+
export { requireProof } from "./require-proof.js";
|
|
5
|
+
export { createDbsc } from "./create-dbsc.js";
|
|
6
|
+
export type { CreateDbscOptions, DbscKit, BindOptions } from "./create-dbsc.js";
|
|
7
|
+
export type { DbscNodeSession as DbscKoaSession, BindSessionOptions } from "../node/index.js";
|
|
8
|
+
export interface DbscKoaOptions extends DbscNodeOptions {
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Koa middleware. Koa's `ctx.req` / `ctx.res` are the raw `node:http` objects,
|
|
12
|
+
* so this delegates to the generic node handler: when it answers a DBSC
|
|
13
|
+
* protocol route it sets `ctx.respond = false` (the node handler already wrote
|
|
14
|
+
* the response). Otherwise the resolved session lands on `ctx.state.dbsc`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function dbsc(opts: DbscKoaOptions): Middleware;
|
|
17
|
+
/** Start a binding from a Koa login handler, then set your own `ctx.body`. */
|
|
18
|
+
export declare function bindSession(ctx: Context, sessionId: string, storage: StorageAdapter, opts: BindSessionOptions): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/koa/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAIL,KAAK,eAAe,EAEpB,KAAK,kBAAkB,EACxB,MAAM,kBAAkB,CAAC;AAE1B,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;AAChF,YAAY,EAAE,eAAe,IAAI,cAAc,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE9F,MAAM,WAAW,cAAe,SAAQ,eAAe;CAAG;AAE1D;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,CAWrD;AAED,8EAA8E;AAC9E,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAEf"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { dbsc as nodeDbsc, bindSession as nodeBindSession, getDbscSession, } from "../node/index.js";
|
|
2
|
+
export { requireProof } from "./require-proof.js";
|
|
3
|
+
export { createDbsc } from "./create-dbsc.js";
|
|
4
|
+
/**
|
|
5
|
+
* Koa middleware. Koa's `ctx.req` / `ctx.res` are the raw `node:http` objects,
|
|
6
|
+
* so this delegates to the generic node handler: when it answers a DBSC
|
|
7
|
+
* protocol route it sets `ctx.respond = false` (the node handler already wrote
|
|
8
|
+
* the response). Otherwise the resolved session lands on `ctx.state.dbsc`.
|
|
9
|
+
*/
|
|
10
|
+
export function dbsc(opts) {
|
|
11
|
+
const handler = nodeDbsc(opts);
|
|
12
|
+
return async (ctx, next) => {
|
|
13
|
+
const handled = await handler(ctx.req, ctx.res);
|
|
14
|
+
if (handled) {
|
|
15
|
+
ctx.respond = false;
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
ctx.state.dbsc = getDbscSession(ctx.req);
|
|
19
|
+
await next();
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** Start a binding from a Koa login handler, then set your own `ctx.body`. */
|
|
23
|
+
export async function bindSession(ctx, sessionId, storage, opts) {
|
|
24
|
+
await nodeBindSession(ctx.res, sessionId, storage, opts);
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/koa/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,IAAI,IAAI,QAAQ,EAChB,WAAW,IAAI,eAAe,EAC9B,cAAc,GAIf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAM9C;;;;;GAKG;AACH,MAAM,UAAU,IAAI,CAAC,IAAoB;IACvC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAChD,IAAI,OAAO,EAAE,CAAC;YACZ,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC;YACpB,OAAO;QACT,CAAC;QACA,GAAG,CAAC,KAAgD,CAAC,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrF,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAY,EACZ,SAAiB,EACjB,OAAuB,EACvB,IAAwB;IAExB,MAAM,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Middleware } from "koa";
|
|
2
|
+
import { type RequireProofOptions } from "../core/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* The route guard for Koa. Mount `dbsc()` first so the session is on
|
|
5
|
+
* `ctx.state.dbsc`. Reads the raw body from `ctx.request.rawBody` when a body
|
|
6
|
+
* parser populated it, else from the socket.
|
|
7
|
+
*/
|
|
8
|
+
export declare function requireProof(opts?: RequireProofOptions): Middleware;
|
|
9
|
+
//# sourceMappingURL=require-proof.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-proof.d.ts","sourceRoot":"","sources":["../../src/koa/require-proof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,UAAU,EAAE,MAAM,KAAK,CAAC;AAE/C,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,kBAAkB,CAAC;AAgB1B;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,mBAAwB,GAAG,UAAU,CAmDvE"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { verifyBoundProof, noBindingReason, DbscVerificationError, } from "../core/index.js";
|
|
2
|
+
import { DBSC_INTERNAL } from "../node/index.js";
|
|
3
|
+
async function readRawBody(ctx) {
|
|
4
|
+
const rawBody = ctx.request.rawBody;
|
|
5
|
+
if (typeof rawBody === "string")
|
|
6
|
+
return new TextEncoder().encode(rawBody);
|
|
7
|
+
const req = ctx.req;
|
|
8
|
+
const cached = req["__dbscRawBody"];
|
|
9
|
+
if (Buffer.isBuffer(cached))
|
|
10
|
+
return new Uint8Array(cached);
|
|
11
|
+
const chunks = [];
|
|
12
|
+
for await (const chunk of req)
|
|
13
|
+
chunks.push(chunk);
|
|
14
|
+
const raw = Buffer.concat(chunks);
|
|
15
|
+
req["__dbscRawBody"] = raw;
|
|
16
|
+
return new Uint8Array(raw);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The route guard for Koa. Mount `dbsc()` first so the session is on
|
|
20
|
+
* `ctx.state.dbsc`. Reads the raw body from `ctx.request.rawBody` when a body
|
|
21
|
+
* parser populated it, else from the socket.
|
|
22
|
+
*/
|
|
23
|
+
export function requireProof(opts = {}) {
|
|
24
|
+
return async (ctx, next) => {
|
|
25
|
+
const session = ctx.state.dbsc;
|
|
26
|
+
const tier = session?.tier ?? "none";
|
|
27
|
+
const skipped = session?.skipped ?? [];
|
|
28
|
+
if (!session?.sessionId || tier === "none") {
|
|
29
|
+
ctx.status = 403;
|
|
30
|
+
ctx.body = { error: "device-bound session required", currentTier: "none", reason: noBindingReason(skipped), skipped };
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const internal = session[DBSC_INTERNAL];
|
|
34
|
+
const storage = opts.storage ?? internal?.storage;
|
|
35
|
+
if (!storage) {
|
|
36
|
+
ctx.status = 500;
|
|
37
|
+
ctx.body = { error: "requireProof: storage unavailable — mount dbsc() before this route, or pass { storage }" };
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const allowDbscWithoutProof = opts.allowDbscWithoutProof ?? (internal?.boundEnabled === false ? true : undefined);
|
|
41
|
+
if (tier === "dbsc" && allowDbscWithoutProof) {
|
|
42
|
+
await next();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const proofHeader = ctx.get("x-dbsc-bound-proof") || undefined;
|
|
46
|
+
try {
|
|
47
|
+
await verifyBoundProof({
|
|
48
|
+
sessionId: session.sessionId,
|
|
49
|
+
proofHeader,
|
|
50
|
+
method: ctx.method,
|
|
51
|
+
path: ctx.path,
|
|
52
|
+
signBody: true,
|
|
53
|
+
bodyBytes: await readRawBody(ctx),
|
|
54
|
+
...(opts.timestampWindowMs !== undefined && { timestampWindowMs: opts.timestampWindowMs }),
|
|
55
|
+
...(internal?.replayCache !== undefined && { replayCache: internal.replayCache }),
|
|
56
|
+
}, storage);
|
|
57
|
+
await next();
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
if (err instanceof DbscVerificationError) {
|
|
61
|
+
ctx.status = 403;
|
|
62
|
+
ctx.body = { error: err.message, code: err.code };
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=require-proof.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-proof.js","sourceRoot":"","sources":["../../src/koa/require-proof.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type DynamicModule, type NestModule, type MiddlewareConsumer, type CanActivate, type ExecutionContext } from "@nestjs/common";
|
|
2
|
+
import type { Response } from "express";
|
|
3
|
+
import { type RequireProofOptions } from "../core/index.js";
|
|
4
|
+
import { type DbscExpressOptions, type BindOptions } from "../express/index.js";
|
|
5
|
+
export { bindSession } from "../express/index.js";
|
|
6
|
+
export type { DbscExpressOptions as DbscNestOptions, BindOptions } from "../express/index.js";
|
|
7
|
+
/** DI token for the DBSC options passed to `DbscModule.forRoot()`. */
|
|
8
|
+
export declare const DBSC_OPTIONS: unique symbol;
|
|
9
|
+
/** Injectable wrapper around the Express kit's `bind()` — call it in your login controller. */
|
|
10
|
+
export declare class DbscService {
|
|
11
|
+
private readonly kit;
|
|
12
|
+
constructor(opts: DbscExpressOptions);
|
|
13
|
+
/** Start a binding for an explicit session id. */
|
|
14
|
+
bind(res: Response, sessionId: string, opts: BindOptions): Promise<string>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Registers DBSC on a NestJS app (Express platform). It mounts the protocol
|
|
18
|
+
* middleware for every route and exposes `DbscService`:
|
|
19
|
+
*
|
|
20
|
+
* @Module({ imports: [DbscModule.forRoot({ storage })] })
|
|
21
|
+
* export class AppModule {}
|
|
22
|
+
*/
|
|
23
|
+
export declare class DbscModule implements NestModule {
|
|
24
|
+
private readonly opts;
|
|
25
|
+
constructor(opts: DbscExpressOptions);
|
|
26
|
+
static forRoot(opts: DbscExpressOptions): DynamicModule;
|
|
27
|
+
configure(consumer: MiddlewareConsumer): void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Zero-config route guard. Requires a bound device + a per-request proof:
|
|
31
|
+
*
|
|
32
|
+
* @UseGuards(DbscGuard)
|
|
33
|
+
* @Post("payment")
|
|
34
|
+
* pay() { ... }
|
|
35
|
+
*
|
|
36
|
+
* POST bodies are body-hashed, so a guarded POST must deliver raw bytes (set
|
|
37
|
+
* `rawBody: true` in `NestFactory.create` and disable the JSON parser on that
|
|
38
|
+
* route, or use a raw-body middleware). GET routes need no parser.
|
|
39
|
+
*/
|
|
40
|
+
export declare class DbscGuard implements CanActivate {
|
|
41
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
42
|
+
}
|
|
43
|
+
/** Builds a guard with options baked in (e.g. a storage override or timestamp window). */
|
|
44
|
+
export declare function createDbscGuard(opts: RequireProofOptions): new () => CanActivate;
|
|
45
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nestjs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAW,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAIL,KAAK,kBAAkB,EAGvB,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,YAAY,EAAE,kBAAkB,IAAI,eAAe,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAE9F,sEAAsE;AACtE,eAAO,MAAM,YAAY,eAAwC,CAAC;AAElE,+FAA+F;AAC/F,qBACa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAU;gBACI,IAAI,EAAE,kBAAkB;IAG1D,kDAAkD;IAClD,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;CAG3E;AAED;;;;;;GAMG;AACH,qBACa,UAAW,YAAW,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,kBAAkB;IAE3E,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,kBAAkB,GAAG,aAAa;IASvD,SAAS,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI;CAG9C;AAiDD;;;;;;;;;;GAUG;AACH,qBACa,SAAU,YAAW,WAAW;IACrC,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CAK/D;AAED,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,UAAU,WAAW,CAUhF"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
var DbscModule_1;
|
|
14
|
+
import { Injectable, Module, Inject, ForbiddenException, InternalServerErrorException, } from "@nestjs/common";
|
|
15
|
+
import { verifyBoundProof, noBindingReason, DbscVerificationError, } from "../core/index.js";
|
|
16
|
+
import { dbsc, createDbsc, DBSC_INTERNAL, } from "../express/index.js";
|
|
17
|
+
export { bindSession } from "../express/index.js";
|
|
18
|
+
/** DI token for the DBSC options passed to `DbscModule.forRoot()`. */
|
|
19
|
+
export const DBSC_OPTIONS = Symbol("dbsc-toolkit.nestjs.options");
|
|
20
|
+
/** Injectable wrapper around the Express kit's `bind()` — call it in your login controller. */
|
|
21
|
+
let DbscService = class DbscService {
|
|
22
|
+
kit;
|
|
23
|
+
constructor(opts) {
|
|
24
|
+
this.kit = createDbsc(opts);
|
|
25
|
+
}
|
|
26
|
+
/** Start a binding for an explicit session id. */
|
|
27
|
+
bind(res, sessionId, opts) {
|
|
28
|
+
return this.kit.bind(res, sessionId, opts);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
DbscService = __decorate([
|
|
32
|
+
Injectable(),
|
|
33
|
+
__param(0, Inject(DBSC_OPTIONS)),
|
|
34
|
+
__metadata("design:paramtypes", [Object])
|
|
35
|
+
], DbscService);
|
|
36
|
+
export { DbscService };
|
|
37
|
+
/**
|
|
38
|
+
* Registers DBSC on a NestJS app (Express platform). It mounts the protocol
|
|
39
|
+
* middleware for every route and exposes `DbscService`:
|
|
40
|
+
*
|
|
41
|
+
* @Module({ imports: [DbscModule.forRoot({ storage })] })
|
|
42
|
+
* export class AppModule {}
|
|
43
|
+
*/
|
|
44
|
+
let DbscModule = DbscModule_1 = class DbscModule {
|
|
45
|
+
opts;
|
|
46
|
+
constructor(opts) {
|
|
47
|
+
this.opts = opts;
|
|
48
|
+
}
|
|
49
|
+
static forRoot(opts) {
|
|
50
|
+
return {
|
|
51
|
+
module: DbscModule_1,
|
|
52
|
+
global: true,
|
|
53
|
+
providers: [{ provide: DBSC_OPTIONS, useValue: opts }, DbscService],
|
|
54
|
+
exports: [DBSC_OPTIONS, DbscService],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
configure(consumer) {
|
|
58
|
+
consumer.apply(dbsc(this.opts)).forRoutes("*");
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
DbscModule = DbscModule_1 = __decorate([
|
|
62
|
+
Module({}),
|
|
63
|
+
__param(0, Inject(DBSC_OPTIONS)),
|
|
64
|
+
__metadata("design:paramtypes", [Object])
|
|
65
|
+
], DbscModule);
|
|
66
|
+
export { DbscModule };
|
|
67
|
+
async function runProof(req, res, opts) {
|
|
68
|
+
const dbscLocals = res.locals.dbsc;
|
|
69
|
+
const tier = dbscLocals?.tier ?? "none";
|
|
70
|
+
const skipped = dbscLocals?.skipped ?? [];
|
|
71
|
+
if (!dbscLocals?.sessionId || tier === "none") {
|
|
72
|
+
throw new ForbiddenException({ error: "device-bound session required", currentTier: "none", reason: noBindingReason(skipped), skipped });
|
|
73
|
+
}
|
|
74
|
+
const internal = res.locals[DBSC_INTERNAL];
|
|
75
|
+
const storage = opts.storage ?? internal?.storage;
|
|
76
|
+
if (!storage) {
|
|
77
|
+
throw new InternalServerErrorException("requireProof: storage unavailable — import DbscModule.forRoot(...)");
|
|
78
|
+
}
|
|
79
|
+
const allowDbscWithoutProof = opts.allowDbscWithoutProof ?? (internal?.boundEnabled === false ? true : undefined);
|
|
80
|
+
if (tier === "dbsc" && allowDbscWithoutProof)
|
|
81
|
+
return;
|
|
82
|
+
let bodyBytes;
|
|
83
|
+
const raw = req.body;
|
|
84
|
+
if (raw instanceof Buffer)
|
|
85
|
+
bodyBytes = new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);
|
|
86
|
+
else if (raw instanceof Uint8Array)
|
|
87
|
+
bodyBytes = raw;
|
|
88
|
+
else if (typeof raw === "string")
|
|
89
|
+
bodyBytes = new TextEncoder().encode(raw);
|
|
90
|
+
else
|
|
91
|
+
bodyBytes = new Uint8Array(0);
|
|
92
|
+
try {
|
|
93
|
+
await verifyBoundProof({
|
|
94
|
+
sessionId: dbscLocals.sessionId,
|
|
95
|
+
proofHeader: req.headers["x-dbsc-bound-proof"],
|
|
96
|
+
method: req.method,
|
|
97
|
+
path: req.path,
|
|
98
|
+
signBody: true,
|
|
99
|
+
bodyBytes,
|
|
100
|
+
...(opts.timestampWindowMs !== undefined && { timestampWindowMs: opts.timestampWindowMs }),
|
|
101
|
+
...(internal?.replayCache !== undefined && { replayCache: internal.replayCache }),
|
|
102
|
+
}, storage);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
if (err instanceof DbscVerificationError) {
|
|
106
|
+
throw new ForbiddenException({ error: err.message, code: err.code });
|
|
107
|
+
}
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Zero-config route guard. Requires a bound device + a per-request proof:
|
|
113
|
+
*
|
|
114
|
+
* @UseGuards(DbscGuard)
|
|
115
|
+
* @Post("payment")
|
|
116
|
+
* pay() { ... }
|
|
117
|
+
*
|
|
118
|
+
* POST bodies are body-hashed, so a guarded POST must deliver raw bytes (set
|
|
119
|
+
* `rawBody: true` in `NestFactory.create` and disable the JSON parser on that
|
|
120
|
+
* route, or use a raw-body middleware). GET routes need no parser.
|
|
121
|
+
*/
|
|
122
|
+
let DbscGuard = class DbscGuard {
|
|
123
|
+
async canActivate(context) {
|
|
124
|
+
const http = context.switchToHttp();
|
|
125
|
+
await runProof(http.getRequest(), http.getResponse(), {});
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
DbscGuard = __decorate([
|
|
130
|
+
Injectable()
|
|
131
|
+
], DbscGuard);
|
|
132
|
+
export { DbscGuard };
|
|
133
|
+
/** Builds a guard with options baked in (e.g. a storage override or timestamp window). */
|
|
134
|
+
export function createDbscGuard(opts) {
|
|
135
|
+
let ConfiguredDbscGuard = class ConfiguredDbscGuard {
|
|
136
|
+
async canActivate(context) {
|
|
137
|
+
const http = context.switchToHttp();
|
|
138
|
+
await runProof(http.getRequest(), http.getResponse(), opts);
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
ConfiguredDbscGuard = __decorate([
|
|
143
|
+
Injectable()
|
|
144
|
+
], ConfiguredDbscGuard);
|
|
145
|
+
return ConfiguredDbscGuard;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/nestjs/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,OAAO,EACL,UAAU,EACV,MAAM,EACN,MAAM,EACN,kBAAkB,EAClB,4BAA4B,GAM7B,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,qBAAqB,GAEtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,IAAI,EACJ,UAAU,EACV,aAAa,GAKd,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGlD,sEAAsE;AACtE,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,6BAA6B,CAAC,CAAC;AAElE,+FAA+F;AAExF,IAAM,WAAW,GAAjB,MAAM,WAAW;IACL,GAAG,CAAU;IAC9B,YAAkC,IAAwB;QACxD,IAAI,CAAC,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IACD,kDAAkD;IAClD,IAAI,CAAC,GAAa,EAAE,SAAiB,EAAE,IAAiB;QACtD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;CACF,CAAA;AATY,WAAW;IADvB,UAAU,EAAE;IAGE,WAAA,MAAM,CAAC,YAAY,CAAC,CAAA;;GAFtB,WAAW,CASvB;;AAED;;;;;;GAMG;AAEI,IAAM,UAAU,kBAAhB,MAAM,UAAU;IAC8B;IAAnD,YAAmD,IAAwB;QAAxB,SAAI,GAAJ,IAAI,CAAoB;IAAG,CAAC;IAE/E,MAAM,CAAC,OAAO,CAAC,IAAwB;QACrC,OAAO;YACL,MAAM,EAAE,YAAU;YAClB,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,WAAW,CAAC;YACnE,OAAO,EAAE,CAAC,YAAY,EAAE,WAAW,CAAC;SACrC,CAAC;IACJ,CAAC;IAED,SAAS,CAAC,QAA4B;QACpC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACjD,CAAC;CACF,CAAA;AAfY,UAAU;IADtB,MAAM,CAAC,EAAE,CAAC;IAEI,WAAA,MAAM,CAAC,YAAY,CAAC,CAAA;;GADtB,UAAU,CAetB;;AAED,KAAK,UAAU,QAAQ,CAAC,GAAY,EAAE,GAAa,EAAE,IAAyB;IAC5E,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;IACnC,MAAM,IAAI,GAAG,UAAU,EAAE,IAAI,IAAI,MAAM,CAAC;IACxC,MAAM,OAAO,GAAG,UAAU,EAAE,OAAO,IAAI,EAAE,CAAC;IAC1C,IAAI,CAAC,UAAU,EAAE,SAAS,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QAC9C,MAAM,IAAI,kBAAkB,CAAC,EAAE,KAAK,EAAE,+BAA+B,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3I,CAAC;IAED,MAAM,QAAQ,GAAI,GAAG,CAAC,MAAuC,CAAC,aAAa,CAA6B,CAAC;IACzG,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,QAAQ,EAAE,OAAO,CAAC;IAClD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,4BAA4B,CAAC,oEAAoE,CAAC,CAAC;IAC/G,CAAC;IAED,MAAM,qBAAqB,GACzB,IAAI,CAAC,qBAAqB,IAAI,CAAC,QAAQ,EAAE,YAAY,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACtF,IAAI,IAAI,KAAK,MAAM,IAAI,qBAAqB;QAAE,OAAO;IAErD,IAAI,SAAqB,CAAC;IAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,IAAe,CAAC;IAChC,IAAI,GAAG,YAAY,MAAM;QAAE,SAAS,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;SAC7F,IAAI,GAAG,YAAY,UAAU;QAAE,SAAS,GAAG,GAAG,CAAC;SAC/C,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;;QACvE,SAAS,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IAEnC,IAAI,CAAC;QACH,MAAM,gBAAgB,CACpB;YACE,SAAS,EAAE,UAAU,CAAC,SAAS;YAC/B,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAuB;YACpE,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,QAAQ,EAAE,IAAI;YACd,SAAS;YACT,GAAG,CAAC,IAAI,CAAC,iBAAiB,KAAK,SAAS,IAAI,EAAE,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC1F,GAAG,CAAC,QAAQ,EAAE,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC;SAClF,EACD,OAAO,CACR,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,qBAAqB,EAAE,CAAC;YACzC,MAAM,IAAI,kBAAkB,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QACvE,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AAEI,IAAM,SAAS,GAAf,MAAM,SAAS;IACpB,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;QACpC,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAW,EAAE,IAAI,CAAC,WAAW,EAAY,EAAE,EAAE,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AANY,SAAS;IADrB,UAAU,EAAE;GACA,SAAS,CAMrB;;AAED,0FAA0F;AAC1F,MAAM,UAAU,eAAe,CAAC,IAAyB;IAEvD,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;QACvB,KAAK,CAAC,WAAW,CAAC,OAAyB;YACzC,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;YACpC,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAW,EAAE,IAAI,CAAC,WAAW,EAAY,EAAE,IAAI,CAAC,CAAC;YAC/E,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAA;IANK,mBAAmB;QADxB,UAAU,EAAE;OACP,mBAAmB,CAMxB;IACD,OAAO,mBAAmB,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { type RequireProofOptions } from "../core/index.js";
|
|
3
|
+
import { type DbscNodeOptions, type DbscNodeHandler, type DbscNodeSession } from "./index.js";
|
|
4
|
+
import { type NodeProofGuard } from "./require-proof.js";
|
|
5
|
+
export interface CreateDbscOptions extends DbscNodeOptions {
|
|
6
|
+
/** Default session TTL (ms) for `bind()`. */
|
|
7
|
+
sessionTtl?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface BindOptions {
|
|
10
|
+
userId: string;
|
|
11
|
+
namespace?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface DbscKit {
|
|
14
|
+
/** The dbsc handler — run it first in your request pipeline. Returns true when it answered a protocol route. */
|
|
15
|
+
handler(): DbscNodeHandler;
|
|
16
|
+
/** Read the resolved session off a request the handler has processed. */
|
|
17
|
+
getSession(req: IncomingMessage): DbscNodeSession | undefined;
|
|
18
|
+
/** Start a binding for an explicit session id (e.g. your server-session id). */
|
|
19
|
+
bind(res: ServerResponse, sessionId: string, opts: BindOptions): Promise<string>;
|
|
20
|
+
/** The route guard — requires a bound device + a per-request proof. */
|
|
21
|
+
requireProof(opts?: RequireProofOptions): NodeProofGuard;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Builds a configured DBSC kit for raw `node:http`. There is no `install()`
|
|
25
|
+
* because raw http has no router — wire `handler()` at the top of your request
|
|
26
|
+
* listener and branch on its boolean return.
|
|
27
|
+
*/
|
|
28
|
+
export declare function createDbsc(opts: CreateDbscOptions): DbscKit;
|
|
29
|
+
//# sourceMappingURL=create-dbsc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-dbsc.d.ts","sourceRoot":"","sources":["../../src/node/create-dbsc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEvE,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,gHAAgH;IAChH,OAAO,IAAI,eAAe,CAAC;IAC3B,yEAAyE;IACzE,UAAU,CAAC,GAAG,EAAE,eAAe,GAAG,eAAe,GAAG,SAAS,CAAC;IAC9D,gFAAgF;IAChF,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjF,uEAAuE;IACvE,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,cAAc,CAAC;CAC1D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CA0B3D"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { dbsc, bindSession, getDbscSession, } from "./index.js";
|
|
2
|
+
import { requireProof } from "./require-proof.js";
|
|
3
|
+
/**
|
|
4
|
+
* Builds a configured DBSC kit for raw `node:http`. There is no `install()`
|
|
5
|
+
* because raw http has no router — wire `handler()` at the top of your request
|
|
6
|
+
* listener and branch on its boolean return.
|
|
7
|
+
*/
|
|
8
|
+
export function createDbsc(opts) {
|
|
9
|
+
const secure = opts.secure ?? true;
|
|
10
|
+
const cookieScope = opts.cookieScope;
|
|
11
|
+
const cookieDomain = opts.cookieDomain;
|
|
12
|
+
const registrationPath = opts.registrationPath ?? "/dbsc/registration";
|
|
13
|
+
const handler = dbsc(opts);
|
|
14
|
+
async function bind(res, sessionId, bindOpts) {
|
|
15
|
+
await bindSession(res, sessionId, opts.storage, {
|
|
16
|
+
userId: bindOpts.userId,
|
|
17
|
+
secure,
|
|
18
|
+
...(cookieScope !== undefined && { cookieScope }),
|
|
19
|
+
...(cookieDomain !== undefined && { cookieDomain }),
|
|
20
|
+
registrationPath,
|
|
21
|
+
...(opts.registrationCookieTtl !== undefined && { registrationCookieTtl: opts.registrationCookieTtl }),
|
|
22
|
+
...(opts.sessionTtl !== undefined && { sessionTtl: opts.sessionTtl }),
|
|
23
|
+
});
|
|
24
|
+
return sessionId;
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
handler: () => handler,
|
|
28
|
+
getSession: getDbscSession,
|
|
29
|
+
bind,
|
|
30
|
+
requireProof,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=create-dbsc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-dbsc.js","sourceRoot":"","sources":["../../src/node/create-dbsc.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,IAAI,EACJ,WAAW,EACX,cAAc,GAIf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAuB,MAAM,oBAAoB,CAAC;AAuBvE;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,IAAuB;IAChD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC;IACnC,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IACrC,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;IACvC,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,IAAI,oBAAoB,CAAC;IACvE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAE3B,KAAK,UAAU,IAAI,CAAC,GAAmB,EAAE,SAAiB,EAAE,QAAqB;QAC/E,MAAM,WAAW,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,EAAE;YAC9C,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,MAAM;YACN,GAAG,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,CAAC;YACjD,GAAG,CAAC,YAAY,KAAK,SAAS,IAAI,EAAE,YAAY,EAAE,CAAC;YACnD,gBAAgB;YAChB,GAAG,CAAC,IAAI,CAAC,qBAAqB,KAAK,SAAS,IAAI,EAAE,qBAAqB,EAAE,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACtG,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;SACtE,CAAC,CAAC;QACH,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO;QACtB,UAAU,EAAE,cAAc;QAC1B,IAAI;QACJ,YAAY;KACb,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { type DbscOptions, type StorageAdapter, type ProofReplayCache, type ProtectionTier, type SkippedEntry, type CookieScope } from "../core/index.js";
|
|
3
|
+
export { requireProof } from "./require-proof.js";
|
|
4
|
+
export { createDbsc } from "./create-dbsc.js";
|
|
5
|
+
export type { CreateDbscOptions, DbscKit, BindOptions } from "./create-dbsc.js";
|
|
6
|
+
/** Internal carrier so `requireProof()` can reach storage without re-passing it. */
|
|
7
|
+
export interface DbscInternal {
|
|
8
|
+
storage: StorageAdapter;
|
|
9
|
+
secure: boolean;
|
|
10
|
+
/** When false, `requireProof()` auto-relaxes the native dbsc tier. */
|
|
11
|
+
boundEnabled: boolean;
|
|
12
|
+
replayCache?: ProofReplayCache;
|
|
13
|
+
}
|
|
14
|
+
export declare const DBSC_INTERNAL: unique symbol;
|
|
15
|
+
export interface DbscNodeOptions extends DbscOptions {
|
|
16
|
+
secure?: boolean;
|
|
17
|
+
boundStatePath?: string;
|
|
18
|
+
boundChallengePath?: string;
|
|
19
|
+
boundRegistrationPath?: string;
|
|
20
|
+
boundRefreshPath?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface DbscNodeSession {
|
|
23
|
+
sessionId: string | null;
|
|
24
|
+
tier: ProtectionTier;
|
|
25
|
+
skipped: SkippedEntry[];
|
|
26
|
+
revoke: () => Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The dbsc handler for raw `node:http`. Call it first in your request
|
|
30
|
+
* pipeline: it returns `true` when it has fully answered a DBSC protocol route
|
|
31
|
+
* (the response is ended — stop), and `false` otherwise, having attached the
|
|
32
|
+
* resolved session to the request. Read it back with `getDbscSession(req)`.
|
|
33
|
+
*/
|
|
34
|
+
export type DbscNodeHandler = (req: IncomingMessage, res: ServerResponse) => Promise<boolean>;
|
|
35
|
+
/** Reads the session the `dbsc()` handler attached to this request. */
|
|
36
|
+
export declare function getDbscSession(req: IncomingMessage): DbscNodeSession | undefined;
|
|
37
|
+
/** Reads and JSON-parses the raw request body. Returns `{}` on empty/invalid. */
|
|
38
|
+
export declare function readJsonBody(req: IncomingMessage): Promise<Record<string, unknown>>;
|
|
39
|
+
export interface BindSessionOptions {
|
|
40
|
+
userId: string;
|
|
41
|
+
/** Match the value passed to dbsc({ secure }). Defaults true. */
|
|
42
|
+
secure?: boolean;
|
|
43
|
+
/** Match the value passed to dbsc({ cookieScope }). Defaults "host". */
|
|
44
|
+
cookieScope?: CookieScope;
|
|
45
|
+
/** Match the value passed to dbsc({ cookieDomain }). Required for cookieScope: "site". */
|
|
46
|
+
cookieDomain?: string;
|
|
47
|
+
registrationPath?: string;
|
|
48
|
+
registrationCookieTtl?: number;
|
|
49
|
+
sessionTtl?: number;
|
|
50
|
+
}
|
|
51
|
+
export declare function bindSession(res: ServerResponse, sessionId: string, storage: StorageAdapter, opts: BindSessionOptions): Promise<void>;
|
|
52
|
+
export declare function dbsc(opts: DbscNodeOptions): DbscNodeHandler;
|
|
53
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAwBL,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAC;AAE1B,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;IAChB,sEAAsE;IACtE,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAChC;AACD,eAAO,MAAM,aAAa,EAAE,OAAO,MAA6C,CAAC;AAejF,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,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,eAAe;IAC9B,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;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE9F,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG,eAAe,GAAG,SAAS,CAEhF;AAmED,iFAAiF;AACjF,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAiBzF;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,0FAA0F;IAC1F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,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,cAAc,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAsCf;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,eAAe,GAAG,eAAe,CAwT3D"}
|