@dbsc-toolkit/better-auth 0.1.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/LICENSE +202 -0
- package/README.md +168 -0
- package/dist/adapter.d.ts +71 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +213 -0
- package/dist/adapter.js.map +1 -0
- package/dist/client.d.ts +43 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +53 -0
- package/dist/client.js.map +1 -0
- package/dist/express.d.ts +65 -0
- package/dist/express.d.ts.map +1 -0
- package/dist/express.js +124 -0
- package/dist/express.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +138 -0
- package/dist/index.js.map +1 -0
- package/dist/init-script.d.ts +18 -0
- package/dist/init-script.d.ts.map +1 -0
- package/dist/init-script.js +35 -0
- package/dist/init-script.js.map +1 -0
- package/dist/internal.d.ts +8 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +7 -0
- package/dist/internal.js.map +1 -0
- package/dist/schema.d.ts +69 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +67 -0
- package/dist/schema.js.map +1 -0
- package/package.json +73 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client plugin for @dbsc-toolkit/better-auth.
|
|
3
|
+
*
|
|
4
|
+
* Provides TypeScript inference for the DBSC endpoints so your
|
|
5
|
+
* Better Auth client knows about /dbsc/* and /dbsc-bound/* paths.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { createAuthClient } from "better-auth/client"
|
|
9
|
+
* import { dbscClient } from "@dbsc-toolkit/better-auth/client"
|
|
10
|
+
*
|
|
11
|
+
* export const authClient = createAuthClient({
|
|
12
|
+
* plugins: [dbscClient()]
|
|
13
|
+
* })
|
|
14
|
+
*
|
|
15
|
+
* Then initialize the polyfill in your browser entry point:
|
|
16
|
+
* import { initBoundDbsc } from "dbsc-toolkit/client"
|
|
17
|
+
* initBoundDbsc({ baseUrl: "" })
|
|
18
|
+
*/
|
|
19
|
+
export declare function dbscClient(): {
|
|
20
|
+
id: string;
|
|
21
|
+
pathMethods: {
|
|
22
|
+
readonly "/dbsc/registration": "POST";
|
|
23
|
+
readonly "/dbsc/refresh": "POST";
|
|
24
|
+
readonly "/dbsc-bound/state": "GET";
|
|
25
|
+
readonly "/dbsc-bound/challenge": "GET";
|
|
26
|
+
readonly "/dbsc-bound/registration": "POST";
|
|
27
|
+
readonly "/dbsc-bound/refresh": "POST";
|
|
28
|
+
};
|
|
29
|
+
getActions(fetch: (path: string, options?: RequestInit) => Promise<Response>): {
|
|
30
|
+
dbsc: {
|
|
31
|
+
/** Check bound state for the current session. */
|
|
32
|
+
getBoundState: () => Promise<Response>;
|
|
33
|
+
/** Manually trigger polyfill registration (normally automatic). */
|
|
34
|
+
registerBound: (body: {
|
|
35
|
+
sessionId: string;
|
|
36
|
+
jwk: JsonWebKey;
|
|
37
|
+
challenge: string;
|
|
38
|
+
}) => Promise<Response>;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
getAtoms(): {};
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,wBAAgB,UAAU;;;;;;;;;;sBAcJ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC;;YAGtE,iDAAiD;;YAEjD,mEAAmE;kCAC7C;gBAAE,SAAS,EAAE,MAAM,CAAC;gBAAC,GAAG,EAAE,UAAU,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE;;;;EAiBxF"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client plugin for @dbsc-toolkit/better-auth.
|
|
3
|
+
*
|
|
4
|
+
* Provides TypeScript inference for the DBSC endpoints so your
|
|
5
|
+
* Better Auth client knows about /dbsc/* and /dbsc-bound/* paths.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { createAuthClient } from "better-auth/client"
|
|
9
|
+
* import { dbscClient } from "@dbsc-toolkit/better-auth/client"
|
|
10
|
+
*
|
|
11
|
+
* export const authClient = createAuthClient({
|
|
12
|
+
* plugins: [dbscClient()]
|
|
13
|
+
* })
|
|
14
|
+
*
|
|
15
|
+
* Then initialize the polyfill in your browser entry point:
|
|
16
|
+
* import { initBoundDbsc } from "dbsc-toolkit/client"
|
|
17
|
+
* initBoundDbsc({ baseUrl: "" })
|
|
18
|
+
*/
|
|
19
|
+
export function dbscClient() {
|
|
20
|
+
return {
|
|
21
|
+
id: "dbsc-toolkit",
|
|
22
|
+
// Path → HTTP method map so Better Auth's client can infer the right fetch
|
|
23
|
+
pathMethods: {
|
|
24
|
+
"/dbsc/registration": "POST",
|
|
25
|
+
"/dbsc/refresh": "POST",
|
|
26
|
+
"/dbsc-bound/state": "GET",
|
|
27
|
+
"/dbsc-bound/challenge": "GET",
|
|
28
|
+
"/dbsc-bound/registration": "POST",
|
|
29
|
+
"/dbsc-bound/refresh": "POST",
|
|
30
|
+
},
|
|
31
|
+
getActions(fetch) {
|
|
32
|
+
return {
|
|
33
|
+
dbsc: {
|
|
34
|
+
/** Check bound state for the current session. */
|
|
35
|
+
getBoundState: () => fetch("/dbsc-bound/state"),
|
|
36
|
+
/** Manually trigger polyfill registration (normally automatic). */
|
|
37
|
+
registerBound: (body) => fetch("/dbsc-bound/registration", {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: { "content-type": "application/json" },
|
|
40
|
+
body: JSON.stringify(body),
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
getAtoms() {
|
|
46
|
+
// No reactive state atoms needed for DBSC — the binding happens
|
|
47
|
+
// transparently in the background. Add nanostores here if you need
|
|
48
|
+
// UI that reflects binding status.
|
|
49
|
+
return {};
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,UAAU,UAAU;IACxB,OAAO;QACL,EAAE,EAAE,cAAc;QAElB,2EAA2E;QAC3E,WAAW,EAAE;YACX,oBAAoB,EAAE,MAAM;YAC5B,eAAe,EAAE,MAAM;YACvB,mBAAmB,EAAE,KAAK;YAC1B,uBAAuB,EAAE,KAAK;YAC9B,0BAA0B,EAAE,MAAM;YAClC,qBAAqB,EAAE,MAAM;SACrB;QAEV,UAAU,CAAC,KAAiE;YAC1E,OAAO;gBACL,IAAI,EAAE;oBACJ,iDAAiD;oBACjD,aAAa,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,mBAAmB,CAAC;oBAC/C,mEAAmE;oBACnE,aAAa,EAAE,CAAC,IAA+D,EAAE,EAAE,CACjF,KAAK,CAAC,0BAA0B,EAAE;wBAChC,MAAM,EAAE,MAAM;wBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;wBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;qBAC3B,CAAC;iBACL;aACF,CAAC;QACJ,CAAC;QAED,QAAQ;YACN,gEAAgE;YAChE,mEAAmE;YACnE,mCAAmC;YACnC,OAAO,EAAE,CAAC;QACZ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Express adapter for @dbsc-toolkit/better-auth.
|
|
3
|
+
*
|
|
4
|
+
* One factory call gives you the same `install / requireProof` shape the
|
|
5
|
+
* Express adapter in `dbsc-toolkit` exposes, but backed by Better Auth's DB.
|
|
6
|
+
*
|
|
7
|
+
* import { dbscExpress } from "@dbsc-toolkit/better-auth/express"
|
|
8
|
+
* const dbsc = dbscExpress(auth)
|
|
9
|
+
* dbsc.install(app)
|
|
10
|
+
* app.get("/profile", dbsc.requireProof(), handler)
|
|
11
|
+
*
|
|
12
|
+
* The storage adapter is resolved lazily from `auth.$context` on the first
|
|
13
|
+
* DBSC request — that's why `dbscExpress()` is sync and never touches the DB
|
|
14
|
+
* at module-evaluation time.
|
|
15
|
+
*/
|
|
16
|
+
import type { Express, RequestHandler } from "express";
|
|
17
|
+
import { type AnyTelemetryEvent, type ProofReplayCache, type RateLimiter } from "dbsc-toolkit";
|
|
18
|
+
export interface AuthLike {
|
|
19
|
+
$context: Promise<{
|
|
20
|
+
adapter: any;
|
|
21
|
+
internalAdapter: any;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
export interface DbscExpressOptions {
|
|
25
|
+
/** Base path Better Auth is mounted at. Default "/api/auth". Must match `dbsc({ basePath })` in your auth.ts. */
|
|
26
|
+
basePath?: string;
|
|
27
|
+
/** Mount path for the polyfill SDK + init shim. Default "/dbsc-client". Set false to skip serving. */
|
|
28
|
+
clientPath?: string | false;
|
|
29
|
+
/** Cookie scope. Default "host". */
|
|
30
|
+
cookieScope?: "host" | "site";
|
|
31
|
+
/** Required when cookieScope is "site". */
|
|
32
|
+
cookieDomain?: string;
|
|
33
|
+
/** Use `Secure` cookies + `__Host-`/`__Secure-` prefixes. Default true. Set false on bare-http localhost. */
|
|
34
|
+
secure?: boolean;
|
|
35
|
+
/** Bound cookie TTL (ms). Default 600_000 (10 min). */
|
|
36
|
+
sessionTtl?: number;
|
|
37
|
+
/** Replay cache for per-request proofs. Default no-op. */
|
|
38
|
+
replayCache?: ProofReplayCache;
|
|
39
|
+
/** Rate limiter for /dbsc/* routes. Default no-op. */
|
|
40
|
+
rateLimiter?: RateLimiter;
|
|
41
|
+
/** Telemetry hook fired on registration, refresh, failures. */
|
|
42
|
+
onEvent?: (event: AnyTelemetryEvent) => void | Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
export interface DbscExpressKit {
|
|
45
|
+
/** Mount the full DBSC surface: protocol routes + polyfill routes + /dbsc-client/*. */
|
|
46
|
+
install(app: Express): Express;
|
|
47
|
+
/** Raw middleware for manual mounting (skips install()'s static SDK + init shim). */
|
|
48
|
+
middleware(): RequestHandler;
|
|
49
|
+
/** Route guard that verifies the X-Dbsc-Bound-Proof header. 403 on missing/invalid. */
|
|
50
|
+
requireProof(): RequestHandler;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build a DBSC kit for an Express + Better Auth app.
|
|
54
|
+
*
|
|
55
|
+
* The Better Auth plugin (`dbsc()` in `auth.ts`) issues the
|
|
56
|
+
* `Secure-Session-Registration` header after every sign-in. This kit handles
|
|
57
|
+
* everything that follows: the protocol routes Chrome posts to, the polyfill
|
|
58
|
+
* endpoints, and the per-request proof verifier.
|
|
59
|
+
*
|
|
60
|
+
* const dbsc = dbscExpress(auth)
|
|
61
|
+
* dbsc.install(app)
|
|
62
|
+
* app.get("/profile", dbsc.requireProof(), profileHandler)
|
|
63
|
+
*/
|
|
64
|
+
export declare function dbscExpress(auth: AuthLike, opts?: DbscExpressOptions): DbscExpressKit;
|
|
65
|
+
//# sourceMappingURL=express.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAmC,MAAM,SAAS,CAAC;AAExF,OAAO,EAML,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AAKtB,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,OAAO,CAAC;QAChB,OAAO,EAAE,GAAG,CAAC;QACb,eAAe,EAAE,GAAG,CAAC;KACtB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,kBAAkB;IACjC,iHAAiH;IACjH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sGAAsG;IACtG,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5B,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6GAA6G;IAC7G,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,sDAAsD;IACtD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9D;AAuDD,MAAM,WAAW,cAAc;IAC7B,uFAAuF;IACvF,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;IAC/B,qFAAqF;IACrF,UAAU,IAAI,cAAc,CAAC;IAC7B,uFAAuF;IACvF,YAAY,IAAI,cAAc,CAAC;CAChC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,GAAE,kBAAuB,GAAG,cAAc,CA6DzF"}
|
package/dist/express.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { createDbsc } from "dbsc-toolkit/express";
|
|
2
|
+
import { createBetterAuthStorageAdapter } from "./adapter.js";
|
|
3
|
+
import { buildInitScript } from "./init-script.js";
|
|
4
|
+
const DEFAULT_BASE_PATH = "/api/auth";
|
|
5
|
+
const DEFAULT_CLIENT_PATH = "/dbsc-client";
|
|
6
|
+
/**
|
|
7
|
+
* Wraps a `Promise<StorageAdapter>` as a synchronous `StorageAdapter` whose
|
|
8
|
+
* methods await the underlying adapter on first call. Lets us hand a
|
|
9
|
+
* StorageAdapter to `createDbsc()` synchronously, without ever resolving
|
|
10
|
+
* `auth.$context` at module load.
|
|
11
|
+
*/
|
|
12
|
+
function lazyStorage(resolve) {
|
|
13
|
+
let cached;
|
|
14
|
+
const get = async () => {
|
|
15
|
+
if (!cached)
|
|
16
|
+
cached = await resolve();
|
|
17
|
+
return cached;
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
async getSession(id) {
|
|
21
|
+
return (await get()).getSession(id);
|
|
22
|
+
},
|
|
23
|
+
async setSession(session) {
|
|
24
|
+
return (await get()).setSession(session);
|
|
25
|
+
},
|
|
26
|
+
async deleteSession(id) {
|
|
27
|
+
return (await get()).deleteSession(id);
|
|
28
|
+
},
|
|
29
|
+
async getBoundKey(sessionId, kind) {
|
|
30
|
+
return (await get()).getBoundKey(sessionId, kind);
|
|
31
|
+
},
|
|
32
|
+
async setBoundKey(key) {
|
|
33
|
+
return (await get()).setBoundKey(key);
|
|
34
|
+
},
|
|
35
|
+
async deleteBoundKey(sessionId, kind) {
|
|
36
|
+
return (await get()).deleteBoundKey(sessionId, kind);
|
|
37
|
+
},
|
|
38
|
+
async getChallenge(jti) {
|
|
39
|
+
return (await get()).getChallenge(jti);
|
|
40
|
+
},
|
|
41
|
+
async setChallenge(challenge) {
|
|
42
|
+
return (await get()).setChallenge(challenge);
|
|
43
|
+
},
|
|
44
|
+
async consumeChallenge(jti) {
|
|
45
|
+
return (await get()).consumeChallenge(jti);
|
|
46
|
+
},
|
|
47
|
+
async revokeSession(sessionId) {
|
|
48
|
+
return (await get()).revokeSession(sessionId);
|
|
49
|
+
},
|
|
50
|
+
async revokeAllForUser(userId) {
|
|
51
|
+
return (await get()).revokeAllForUser(userId);
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Build a DBSC kit for an Express + Better Auth app.
|
|
57
|
+
*
|
|
58
|
+
* The Better Auth plugin (`dbsc()` in `auth.ts`) issues the
|
|
59
|
+
* `Secure-Session-Registration` header after every sign-in. This kit handles
|
|
60
|
+
* everything that follows: the protocol routes Chrome posts to, the polyfill
|
|
61
|
+
* endpoints, and the per-request proof verifier.
|
|
62
|
+
*
|
|
63
|
+
* const dbsc = dbscExpress(auth)
|
|
64
|
+
* dbsc.install(app)
|
|
65
|
+
* app.get("/profile", dbsc.requireProof(), profileHandler)
|
|
66
|
+
*/
|
|
67
|
+
export function dbscExpress(auth, opts = {}) {
|
|
68
|
+
const basePath = opts.basePath ?? DEFAULT_BASE_PATH;
|
|
69
|
+
const clientPath = opts.clientPath ?? DEFAULT_CLIENT_PATH;
|
|
70
|
+
const secure = opts.secure ?? true;
|
|
71
|
+
const storage = lazyStorage(async () => {
|
|
72
|
+
const ctx = await auth.$context;
|
|
73
|
+
return createBetterAuthStorageAdapter(ctx.adapter, ctx.internalAdapter);
|
|
74
|
+
});
|
|
75
|
+
// Mount paths reflect basePath so Chrome's auto-POST lands correctly.
|
|
76
|
+
const registrationPath = `${basePath}/dbsc/registration`;
|
|
77
|
+
const refreshPath = `${basePath}/dbsc/refresh`;
|
|
78
|
+
const boundStatePath = `${basePath}/dbsc-bound/state`;
|
|
79
|
+
const boundChallengePath = `${basePath}/dbsc-bound/challenge`;
|
|
80
|
+
const boundRegistrationPath = `${basePath}/dbsc-bound/registration`;
|
|
81
|
+
const boundRefreshPath = `${basePath}/dbsc-bound/refresh`;
|
|
82
|
+
const kit = createDbsc({
|
|
83
|
+
storage,
|
|
84
|
+
secure,
|
|
85
|
+
registrationPath,
|
|
86
|
+
refreshPath,
|
|
87
|
+
boundStatePath,
|
|
88
|
+
boundChallengePath,
|
|
89
|
+
boundRegistrationPath,
|
|
90
|
+
boundRefreshPath,
|
|
91
|
+
clientPath: clientPath === false ? false : clientPath,
|
|
92
|
+
...(opts.cookieScope !== undefined && { cookieScope: opts.cookieScope }),
|
|
93
|
+
...(opts.cookieDomain !== undefined && { cookieDomain: opts.cookieDomain }),
|
|
94
|
+
...(opts.sessionTtl !== undefined && { sessionTtl: opts.sessionTtl }),
|
|
95
|
+
...(opts.replayCache !== undefined && { replayCache: opts.replayCache }),
|
|
96
|
+
...(opts.rateLimiter !== undefined && { rateLimiter: opts.rateLimiter }),
|
|
97
|
+
...(opts.onEvent !== undefined && { onEvent: opts.onEvent }),
|
|
98
|
+
});
|
|
99
|
+
function install(app) {
|
|
100
|
+
// Serve /dbsc-client/init.js BEFORE the static directory below so it wins
|
|
101
|
+
// the match. The shim re-points the polyfill SDK at the basePath the
|
|
102
|
+
// user actually configured.
|
|
103
|
+
if (clientPath !== false) {
|
|
104
|
+
const initJs = buildInitScript({ basePath, clientPath });
|
|
105
|
+
const initRoute = `${clientPath}/init.js`;
|
|
106
|
+
app.get(initRoute, (_req, res) => {
|
|
107
|
+
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
|
|
108
|
+
res.setHeader("Cache-Control", "public, max-age=300");
|
|
109
|
+
res.send(initJs);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// kit.install mounts the dbsc-toolkit middleware (all protocol + bound
|
|
113
|
+
// routes) and serves the polyfill SDK static files at clientPath.
|
|
114
|
+
return kit.install(app);
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
install,
|
|
118
|
+
middleware: kit.middleware,
|
|
119
|
+
requireProof() {
|
|
120
|
+
return kit.requireProof();
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=express.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.js","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @dbsc-toolkit/better-auth
|
|
3
|
+
*
|
|
4
|
+
* DBSC (Device Bound Session Credentials) plugin for Better Auth.
|
|
5
|
+
* Powered by dbsc-toolkit — hardware-bound sessions on Chromium 145+,
|
|
6
|
+
* Web Crypto polyfill for Firefox/Safari/older Chromium.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* import { betterAuth } from "better-auth"
|
|
10
|
+
* import { dbsc } from "@dbsc-toolkit/better-auth"
|
|
11
|
+
*
|
|
12
|
+
* export const auth = betterAuth({
|
|
13
|
+
* plugins: [dbsc()]
|
|
14
|
+
* })
|
|
15
|
+
*/
|
|
16
|
+
import { type AnyTelemetryEvent } from "dbsc-toolkit";
|
|
17
|
+
export interface DbscPluginOptions {
|
|
18
|
+
/**
|
|
19
|
+
* The base path where Better Auth is mounted. Default: "/api/auth".
|
|
20
|
+
* Used to build the full registration path in the Secure-Session-Registration header.
|
|
21
|
+
*/
|
|
22
|
+
basePath?: string;
|
|
23
|
+
/** Cookie scope: "host" (default, __Host-) or "site" (__Secure- + Domain). */
|
|
24
|
+
cookieScope?: "host" | "site";
|
|
25
|
+
/** Required when cookieScope is "site". E.g. "example.com" */
|
|
26
|
+
cookieDomain?: string;
|
|
27
|
+
/** Bound cookie TTL in ms. Default: 600_000 (10 min) */
|
|
28
|
+
sessionTtl?: number;
|
|
29
|
+
/** Telemetry hook */
|
|
30
|
+
onEvent?: (event: AnyTelemetryEvent) => void | Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
export declare function dbsc(opts?: DbscPluginOptions): object;
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAOL,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AAKtB,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9D;AAMD,wBAAgB,IAAI,CAAC,IAAI,GAAE,iBAAsB,GAAG,MAAM,CA2IzD"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @dbsc-toolkit/better-auth
|
|
3
|
+
*
|
|
4
|
+
* DBSC (Device Bound Session Credentials) plugin for Better Auth.
|
|
5
|
+
* Powered by dbsc-toolkit — hardware-bound sessions on Chromium 145+,
|
|
6
|
+
* Web Crypto polyfill for Firefox/Safari/older Chromium.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* import { betterAuth } from "better-auth"
|
|
10
|
+
* import { dbsc } from "@dbsc-toolkit/better-auth"
|
|
11
|
+
*
|
|
12
|
+
* export const auth = betterAuth({
|
|
13
|
+
* plugins: [dbsc()]
|
|
14
|
+
* })
|
|
15
|
+
*/
|
|
16
|
+
import { issueChallenge, buildRegistrationHeader, REGISTRATION_HEADER, LEGACY_REGISTRATION_HEADER, resolveCookieNames, } from "dbsc-toolkit";
|
|
17
|
+
import { createBetterAuthStorageAdapter } from "./adapter.js";
|
|
18
|
+
import { dbscSchema } from "./schema.js";
|
|
19
|
+
const REGISTRATION_PATH = "/dbsc/registration";
|
|
20
|
+
const DEFAULT_SESSION_TTL = 600_000;
|
|
21
|
+
const DEFAULT_BASE_PATH = "/api/auth";
|
|
22
|
+
export function dbsc(opts = {}) {
|
|
23
|
+
const { basePath = DEFAULT_BASE_PATH, cookieScope = "host", cookieDomain, sessionTtl = DEFAULT_SESSION_TTL, } = opts;
|
|
24
|
+
const secure = true;
|
|
25
|
+
const scopeOpts = cookieDomain
|
|
26
|
+
? { secure, cookieScope, cookieDomain }
|
|
27
|
+
: { secure, cookieScope };
|
|
28
|
+
const names = resolveCookieNames(scopeOpts);
|
|
29
|
+
function regCookieHeader(sessionId) {
|
|
30
|
+
return [
|
|
31
|
+
`${names.reg}=${sessionId}`,
|
|
32
|
+
"HttpOnly",
|
|
33
|
+
"Path=/",
|
|
34
|
+
"SameSite=Lax",
|
|
35
|
+
`Max-Age=${Math.floor(sessionTtl / 1000)}`,
|
|
36
|
+
...(secure ? ["Secure"] : []),
|
|
37
|
+
...(cookieDomain ? [`Domain=${cookieDomain}`] : []),
|
|
38
|
+
].join("; ");
|
|
39
|
+
}
|
|
40
|
+
function sessionCookieHeader(sessionId) {
|
|
41
|
+
return [
|
|
42
|
+
`${names.bound}=${sessionId}`,
|
|
43
|
+
"HttpOnly",
|
|
44
|
+
"Path=/",
|
|
45
|
+
"SameSite=Lax",
|
|
46
|
+
`Max-Age=${Math.floor(sessionTtl / 1000)}`,
|
|
47
|
+
...(secure ? ["Secure"] : []),
|
|
48
|
+
...(cookieDomain ? [`Domain=${cookieDomain}`] : []),
|
|
49
|
+
].join("; ");
|
|
50
|
+
}
|
|
51
|
+
function challengeCookieHeader(jti) {
|
|
52
|
+
return [
|
|
53
|
+
`${names.challenge}=${jti}`,
|
|
54
|
+
"HttpOnly",
|
|
55
|
+
"Path=/",
|
|
56
|
+
"SameSite=Lax",
|
|
57
|
+
`Max-Age=300`,
|
|
58
|
+
...(secure ? ["Secure"] : []),
|
|
59
|
+
...(cookieDomain ? [`Domain=${cookieDomain}`] : []),
|
|
60
|
+
].join("; ");
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
id: "dbsc-toolkit",
|
|
64
|
+
schema: dbscSchema,
|
|
65
|
+
// The DBSC protocol routes do NOT live inside the plugin — Better Auth's
|
|
66
|
+
// createAuthEndpoint refuses POSTs without a body (responds 415), and
|
|
67
|
+
// Chrome's registration request has the JWS in a header with no body.
|
|
68
|
+
// Use mountDbscRoutes(app, auth) on your Hono/Express app instead.
|
|
69
|
+
hooks: {
|
|
70
|
+
after: [
|
|
71
|
+
{
|
|
72
|
+
// Fires after any endpoint whose response contains a token — sign-in,
|
|
73
|
+
// sign-up, OAuth callback, magic link, passkey. Better Auth returns
|
|
74
|
+
// { token, user } for all these paths.
|
|
75
|
+
matcher: (ctx) => {
|
|
76
|
+
const returned = ctx.context?.returned;
|
|
77
|
+
const token = returned?.["token"];
|
|
78
|
+
return typeof token === "string" && token.length > 0;
|
|
79
|
+
},
|
|
80
|
+
handler: async (ctx) => {
|
|
81
|
+
const empty = { headers: new Headers() };
|
|
82
|
+
const returned = ctx.context?.returned;
|
|
83
|
+
const token = returned?.token;
|
|
84
|
+
if (!token)
|
|
85
|
+
return empty;
|
|
86
|
+
const internalAdapter = ctx.context?.internalAdapter;
|
|
87
|
+
if (!internalAdapter?.findSession)
|
|
88
|
+
return empty;
|
|
89
|
+
let sessionId;
|
|
90
|
+
let userId;
|
|
91
|
+
try {
|
|
92
|
+
const result = await internalAdapter.findSession(token);
|
|
93
|
+
sessionId = result?.session?.id;
|
|
94
|
+
userId = result?.session?.userId ?? result?.user?.id;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return empty;
|
|
98
|
+
}
|
|
99
|
+
if (!sessionId || !userId)
|
|
100
|
+
return empty;
|
|
101
|
+
const store = createBetterAuthStorageAdapter(ctx.context.adapter, ctx.context.internalAdapter);
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
const existing = await store.getSession(sessionId);
|
|
104
|
+
if (!existing) {
|
|
105
|
+
await store.setSession({
|
|
106
|
+
id: sessionId,
|
|
107
|
+
userId,
|
|
108
|
+
tier: "none",
|
|
109
|
+
createdAt: now,
|
|
110
|
+
expiresAt: now + sessionTtl,
|
|
111
|
+
lastRefreshAt: now,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
const { jti } = await issueChallenge(sessionId, store, sessionTtl);
|
|
115
|
+
const regHeader = buildRegistrationHeader({
|
|
116
|
+
registrationPath: `${basePath}${REGISTRATION_PATH}`,
|
|
117
|
+
challenge: jti,
|
|
118
|
+
cookieName: names.bound,
|
|
119
|
+
});
|
|
120
|
+
// Return headers — Better Auth merges these into the response.
|
|
121
|
+
// Three cookies:
|
|
122
|
+
// - __Host-dbsc-session: bound cookie Chrome watches
|
|
123
|
+
// - __Host-dbsc-reg: carries sessionId to /dbsc/registration
|
|
124
|
+
// - __Host-dbsc-challenge: carries jti to /dbsc/registration
|
|
125
|
+
const headers = new Headers();
|
|
126
|
+
headers.set(REGISTRATION_HEADER, regHeader);
|
|
127
|
+
headers.set(LEGACY_REGISTRATION_HEADER, regHeader);
|
|
128
|
+
headers.append("Set-Cookie", sessionCookieHeader(sessionId));
|
|
129
|
+
headers.append("Set-Cookie", regCookieHeader(sessionId));
|
|
130
|
+
headers.append("Set-Cookie", challengeCookieHeader(jti));
|
|
131
|
+
return { headers };
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates the browser-side init shim served at `<clientPath>/init.js`.
|
|
3
|
+
*
|
|
4
|
+
* The user drops one tag in their HTML:
|
|
5
|
+
*
|
|
6
|
+
* <script src="/dbsc-client/init.js"></script>
|
|
7
|
+
*
|
|
8
|
+
* The shim imports the polyfill SDK from `<clientPath>/index.js`, calls
|
|
9
|
+
* `initBoundDbsc()` with paths that match the configured Better Auth
|
|
10
|
+
* basePath, and exposes `window.boundFetch` so app code can sign per-request
|
|
11
|
+
* proofs by calling `boundFetch(...)` instead of `fetch(...)`.
|
|
12
|
+
*/
|
|
13
|
+
export interface InitScriptOptions {
|
|
14
|
+
basePath: string;
|
|
15
|
+
clientPath: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function buildInitScript(opts: InitScriptOptions): string;
|
|
18
|
+
//# sourceMappingURL=init-script.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-script.d.ts","sourceRoot":"","sources":["../src/init-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAiC/D"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export function buildInitScript(opts) {
|
|
2
|
+
const { basePath, clientPath } = opts;
|
|
3
|
+
return `// @dbsc-toolkit/better-auth — browser init shim
|
|
4
|
+
import { initBoundDbsc, wrapFetch, clearBoundKey } from "${clientPath}/index.js";
|
|
5
|
+
|
|
6
|
+
const paths = {
|
|
7
|
+
statePath: "${basePath}/dbsc-bound/state",
|
|
8
|
+
challengePath: "${basePath}/dbsc-bound/challenge",
|
|
9
|
+
registrationPath: "${basePath}/dbsc-bound/registration",
|
|
10
|
+
refreshPath: "${basePath}/dbsc-bound/refresh",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// Polyfill kicks in on Firefox / Safari / older Chromium. On Chromium 145+
|
|
14
|
+
// native DBSC runs first; the polyfill co-registers a key so per-request
|
|
15
|
+
// proofs work everywhere.
|
|
16
|
+
//
|
|
17
|
+
// Expose initDbsc() on window so callers can re-trigger the flow after a
|
|
18
|
+
// fresh sign-in or sign-up — without that, a logged-out page-load probe
|
|
19
|
+
// resolves to "unbound" and the key record never appears.
|
|
20
|
+
window.initDbsc = () => initBoundDbsc({ nativeProbeWindowMs: 8000, ...paths });
|
|
21
|
+
|
|
22
|
+
// boundFetch signs every request body (empty bytes for GET). Replace
|
|
23
|
+
// \`fetch\` with \`boundFetch\` on any call to a requireProof()-guarded route.
|
|
24
|
+
window.boundFetch = wrapFetch({ signBody: true });
|
|
25
|
+
window.clearBoundKey = clearBoundKey;
|
|
26
|
+
|
|
27
|
+
// First probe on page load — finds an existing binding (returning user) or
|
|
28
|
+
// resolves to unbound (logged-out user). Either way, callers must call
|
|
29
|
+
// window.initDbsc() again after sign-up / sign-in so the SDK observes the
|
|
30
|
+
// newly issued session.
|
|
31
|
+
window.__dbscOutcome = window.initDbsc();
|
|
32
|
+
window.__dbscOutcome.then((o) => console.log("[dbsc]", o)).catch((e) => console.error("[dbsc]", e));
|
|
33
|
+
`;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=init-script.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-script.js","sourceRoot":"","sources":["../src/init-script.ts"],"names":[],"mappings":"AAiBA,MAAM,UAAU,eAAe,CAAC,IAAuB;IACrD,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IACtC,OAAO;2DACkD,UAAU;;;gBAGrD,QAAQ;oBACJ,QAAQ;uBACL,QAAQ;kBACb,QAAQ;;;;;;;;;;;;;;;;;;;;;;;CAuBzB,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal helpers — exposed for advanced users who need to construct the
|
|
3
|
+
* storage adapter manually (e.g. for custom requireProof middleware in their
|
|
4
|
+
* own routes). Normal usage does NOT need this import.
|
|
5
|
+
*/
|
|
6
|
+
export { createBetterAuthStorageAdapter } from "./adapter.js";
|
|
7
|
+
export type { BetterAuthDbAdapter, BetterAuthInternalAdapter } from "./adapter.js";
|
|
8
|
+
//# sourceMappingURL=internal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC;AAC9D,YAAY,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/internal.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal helpers — exposed for advanced users who need to construct the
|
|
3
|
+
* storage adapter manually (e.g. for custom requireProof middleware in their
|
|
4
|
+
* own routes). Normal usage does NOT need this import.
|
|
5
|
+
*/
|
|
6
|
+
export { createBetterAuthStorageAdapter } from "./adapter.js";
|
|
7
|
+
//# sourceMappingURL=internal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Better Auth schema extension for dbsc-toolkit.
|
|
3
|
+
*
|
|
4
|
+
* Defines two tables:
|
|
5
|
+
* dbscSession — tracks binding state (tier, lastRefreshAt) per session
|
|
6
|
+
* dbscBoundKey — stores the JWK for native (TPM) and bound (polyfill) keys
|
|
7
|
+
*
|
|
8
|
+
* Challenges are stored in Better Auth's built-in `verification` table
|
|
9
|
+
* so we get atomic consumeVerificationValue for free.
|
|
10
|
+
*/
|
|
11
|
+
export declare const dbscSchema: {
|
|
12
|
+
readonly dbscSession: {
|
|
13
|
+
readonly fields: {
|
|
14
|
+
readonly userId: {
|
|
15
|
+
readonly type: "string";
|
|
16
|
+
readonly required: true;
|
|
17
|
+
readonly references: {
|
|
18
|
+
readonly model: "user";
|
|
19
|
+
readonly field: "id";
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
readonly tier: {
|
|
23
|
+
readonly type: "string";
|
|
24
|
+
readonly required: true;
|
|
25
|
+
};
|
|
26
|
+
readonly createdAt: {
|
|
27
|
+
readonly type: "number";
|
|
28
|
+
readonly required: true;
|
|
29
|
+
};
|
|
30
|
+
readonly expiresAt: {
|
|
31
|
+
readonly type: "number";
|
|
32
|
+
readonly required: true;
|
|
33
|
+
};
|
|
34
|
+
readonly lastRefreshAt: {
|
|
35
|
+
readonly type: "number";
|
|
36
|
+
readonly required: true;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
readonly dbscBoundKey: {
|
|
41
|
+
readonly fields: {
|
|
42
|
+
readonly sessionId: {
|
|
43
|
+
readonly type: "string";
|
|
44
|
+
readonly required: true;
|
|
45
|
+
readonly references: {
|
|
46
|
+
readonly model: "dbscSession";
|
|
47
|
+
readonly field: "id";
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
readonly kind: {
|
|
51
|
+
readonly type: "string";
|
|
52
|
+
readonly required: true;
|
|
53
|
+
};
|
|
54
|
+
readonly jwk: {
|
|
55
|
+
readonly type: "string";
|
|
56
|
+
readonly required: true;
|
|
57
|
+
};
|
|
58
|
+
readonly createdAt: {
|
|
59
|
+
readonly type: "number";
|
|
60
|
+
readonly required: true;
|
|
61
|
+
};
|
|
62
|
+
readonly algorithm: {
|
|
63
|
+
readonly type: "string";
|
|
64
|
+
readonly required: true;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDb,CAAC"}
|