dbsc-toolkit 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +132 -0
- package/README.md +205 -0
- package/dist/client/detect.d.ts +3 -0
- package/dist/client/detect.d.ts.map +1 -0
- package/dist/client/detect.js +20 -0
- package/dist/client/detect.js.map +1 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +4 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/signals.d.ts +9 -0
- package/dist/client/signals.d.ts.map +1 -0
- package/dist/client/signals.js +13 -0
- package/dist/client/signals.js.map +1 -0
- package/dist/client/webauthn.d.ts +3 -0
- package/dist/client/webauthn.d.ts.map +1 -0
- package/dist/client/webauthn.js +8 -0
- package/dist/client/webauthn.js.map +1 -0
- package/dist/core/crypto/jwk.d.ts +3 -0
- package/dist/core/crypto/jwk.d.ts.map +1 -0
- package/dist/core/crypto/jwk.js +36 -0
- package/dist/core/crypto/jwk.js.map +1 -0
- package/dist/core/crypto/jws.d.ts +15 -0
- package/dist/core/crypto/jws.d.ts.map +1 -0
- package/dist/core/crypto/jws.js +89 -0
- package/dist/core/crypto/jws.js.map +1 -0
- package/dist/core/errors.d.ts +27 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +39 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/fallback/hmac.d.ts +9 -0
- package/dist/core/fallback/hmac.d.ts.map +1 -0
- package/dist/core/fallback/hmac.js +37 -0
- package/dist/core/fallback/hmac.js.map +1 -0
- package/dist/core/fallback/negotiate.d.ts +9 -0
- package/dist/core/fallback/negotiate.d.ts.map +1 -0
- package/dist/core/fallback/negotiate.js +22 -0
- package/dist/core/fallback/negotiate.js.map +1 -0
- package/dist/core/fallback/webauthn.d.ts +10 -0
- package/dist/core/fallback/webauthn.d.ts.map +1 -0
- package/dist/core/fallback/webauthn.js +41 -0
- package/dist/core/fallback/webauthn.js.map +1 -0
- package/dist/core/index.d.ts +14 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/protocol/challenge.d.ts +4 -0
- package/dist/core/protocol/challenge.d.ts.map +1 -0
- package/dist/core/protocol/challenge.js +18 -0
- package/dist/core/protocol/challenge.js.map +1 -0
- package/dist/core/protocol/headers.d.ts +21 -0
- package/dist/core/protocol/headers.d.ts.map +1 -0
- package/dist/core/protocol/headers.js +33 -0
- package/dist/core/protocol/headers.js.map +1 -0
- package/dist/core/protocol/refresh.d.ts +8 -0
- package/dist/core/protocol/refresh.d.ts.map +1 -0
- package/dist/core/protocol/refresh.js +37 -0
- package/dist/core/protocol/refresh.js.map +1 -0
- package/dist/core/protocol/registration.d.ts +11 -0
- package/dist/core/protocol/registration.d.ts.map +1 -0
- package/dist/core/protocol/registration.js +40 -0
- package/dist/core/protocol/registration.js.map +1 -0
- package/dist/core/ratelimit/interface.d.ts +7 -0
- package/dist/core/ratelimit/interface.d.ts.map +1 -0
- package/dist/core/ratelimit/interface.js +11 -0
- package/dist/core/ratelimit/interface.js.map +1 -0
- package/dist/core/telemetry/hooks.d.ts +4 -0
- package/dist/core/telemetry/hooks.d.ts.map +1 -0
- package/dist/core/telemetry/hooks.js +11 -0
- package/dist/core/telemetry/hooks.js.map +1 -0
- package/dist/core/testing/memory-storage-stub.d.ts +18 -0
- package/dist/core/testing/memory-storage-stub.d.ts.map +1 -0
- package/dist/core/testing/memory-storage-stub.js +50 -0
- package/dist/core/testing/memory-storage-stub.js.map +1 -0
- package/dist/core/types.d.ts +92 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/express/index.d.ts +20 -0
- package/dist/express/index.d.ts.map +1 -0
- package/dist/express/index.js +217 -0
- package/dist/express/index.js.map +1 -0
- package/dist/fastify/index.d.ts +17 -0
- package/dist/fastify/index.d.ts.map +1 -0
- package/dist/fastify/index.js +115 -0
- package/dist/fastify/index.js.map +1 -0
- package/dist/hono/index.d.ts +13 -0
- package/dist/hono/index.d.ts.map +1 -0
- package/dist/hono/index.js +107 -0
- package/dist/hono/index.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/nextjs/index.d.ts +13 -0
- package/dist/nextjs/index.d.ts.map +1 -0
- package/dist/nextjs/index.js +126 -0
- package/dist/nextjs/index.js.map +1 -0
- package/dist/storage/memory/index.d.ts +20 -0
- package/dist/storage/memory/index.d.ts.map +1 -0
- package/dist/storage/memory/index.js +79 -0
- package/dist/storage/memory/index.js.map +1 -0
- package/dist/storage/postgres/index.d.ts +19 -0
- package/dist/storage/postgres/index.d.ts.map +1 -0
- package/dist/storage/postgres/index.js +89 -0
- package/dist/storage/postgres/index.js.map +1 -0
- package/dist/storage/redis/index.d.ts +18 -0
- package/dist/storage/redis/index.d.ts.map +1 -0
- package/dist/storage/redis/index.js +88 -0
- package/dist/storage/redis/index.js.map +1 -0
- package/migrations/001_initial.sql +41 -0
- package/package.json +124 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NextRequest } from "next/server.js";
|
|
2
|
+
import { NextResponse } from "next/server.js";
|
|
3
|
+
import { type DbscOptions, type ProtectionTier } from "../core/index.js";
|
|
4
|
+
export interface DbscNextOptions extends DbscOptions {
|
|
5
|
+
secure?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function createDbscMiddleware(opts: DbscNextOptions): (req: NextRequest) => Promise<NextResponse>;
|
|
8
|
+
export interface DbscSessionInfo {
|
|
9
|
+
sessionId: string | null;
|
|
10
|
+
tier: ProtectionTier;
|
|
11
|
+
}
|
|
12
|
+
export declare function getDbscSession(req: NextRequest, storage: DbscOptions["storage"]): Promise<DbscSessionInfo>;
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nextjs/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAYL,KAAK,WAAW,EAChB,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAS1B,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAWD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,eAAe,IAYvB,KAAK,WAAW,KAAG,OAAO,CAAC,YAAY,CAAC,CAqH1E;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,cAAc,CAAC;CACtB;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,WAAW,CAAC,SAAS,CAAC,GAC9B,OAAO,CAAC,eAAe,CAAC,CAQ1B"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { NextResponse } from "next/server.js";
|
|
2
|
+
import { handleRegistration, handleRefresh, issueChallenge, buildChallengeHeader, CHALLENGE_HEADER, LEGACY_CHALLENGE_HEADER, NoopRateLimiter, emit, DbscProtocolError, DbscVerificationError, } from "../core/index.js";
|
|
3
|
+
const BOUND_COOKIE = "__Host-dbsc-session";
|
|
4
|
+
const REGISTRATION_COOKIE = "__Host-dbsc-reg";
|
|
5
|
+
const CHALLENGE_COOKIE = "__Host-dbsc-challenge";
|
|
6
|
+
const DEFAULT_BOUND_TTL = 10 * 60;
|
|
7
|
+
const DEFAULT_REG_TTL = 24 * 60 * 60;
|
|
8
|
+
function cookieBase(secure) {
|
|
9
|
+
return {
|
|
10
|
+
httpOnly: true,
|
|
11
|
+
secure,
|
|
12
|
+
sameSite: "lax",
|
|
13
|
+
path: "/",
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function createDbscMiddleware(opts) {
|
|
17
|
+
const { storage, registrationPath = "/dbsc/registration", refreshPath = "/dbsc/refresh", boundCookieTtl = DEFAULT_BOUND_TTL * 1000, registrationCookieTtl = DEFAULT_REG_TTL * 1000, rateLimiter = new NoopRateLimiter(), onEvent, secure = true, } = opts;
|
|
18
|
+
return async function middleware(req) {
|
|
19
|
+
const url = req.nextUrl.pathname;
|
|
20
|
+
const ip = req.headers.get("x-forwarded-for") ?? "unknown";
|
|
21
|
+
if (req.method === "POST" && url === registrationPath) {
|
|
22
|
+
const sessionId = req.cookies.get(REGISTRATION_COOKIE)?.value;
|
|
23
|
+
const expectedJti = req.cookies.get(CHALLENGE_COOKIE)?.value;
|
|
24
|
+
if (!sessionId || !expectedJti) {
|
|
25
|
+
return NextResponse.json({ error: "missing session or challenge cookie" }, { status: 400 });
|
|
26
|
+
}
|
|
27
|
+
const allowed = await rateLimiter.checkRegistration(ip);
|
|
28
|
+
if (!allowed) {
|
|
29
|
+
return NextResponse.json({ error: "rate limited" }, { status: 429 });
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
await handleRegistration({
|
|
33
|
+
sessionId,
|
|
34
|
+
secSessionResponseHeader: req.headers.get("secure-session-response") ??
|
|
35
|
+
req.headers.get("sec-session-response") ??
|
|
36
|
+
undefined,
|
|
37
|
+
expectedJti,
|
|
38
|
+
}, storage);
|
|
39
|
+
emit(onEvent, {
|
|
40
|
+
type: "registration",
|
|
41
|
+
sessionId,
|
|
42
|
+
tier: "dbsc",
|
|
43
|
+
timestamp: Date.now(),
|
|
44
|
+
algorithm: "ES256",
|
|
45
|
+
ip,
|
|
46
|
+
});
|
|
47
|
+
const res = new NextResponse(null, { status: 204 });
|
|
48
|
+
res.cookies.set(BOUND_COOKIE, sessionId, {
|
|
49
|
+
...cookieBase(secure),
|
|
50
|
+
maxAge: boundCookieTtl / 1000,
|
|
51
|
+
});
|
|
52
|
+
res.cookies.delete(CHALLENGE_COOKIE);
|
|
53
|
+
return res;
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
await rateLimiter.recordFailure(ip, sessionId);
|
|
57
|
+
if (err instanceof DbscVerificationError || err instanceof DbscProtocolError) {
|
|
58
|
+
return NextResponse.json({ error: err.message }, { status: 400 });
|
|
59
|
+
}
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (req.method === "POST" && url === refreshPath) {
|
|
64
|
+
const sessionId = req.cookies.get(BOUND_COOKIE)?.value;
|
|
65
|
+
if (!sessionId) {
|
|
66
|
+
return NextResponse.json({ error: "no session" }, { status: 401 });
|
|
67
|
+
}
|
|
68
|
+
const allowed = await rateLimiter.checkRefresh(ip, sessionId);
|
|
69
|
+
if (!allowed) {
|
|
70
|
+
return NextResponse.json({ error: "rate limited" }, { status: 429 });
|
|
71
|
+
}
|
|
72
|
+
const responseHeader = req.headers.get("secure-session-response") ??
|
|
73
|
+
req.headers.get("sec-session-response");
|
|
74
|
+
if (!responseHeader) {
|
|
75
|
+
const challenge = await issueChallenge(sessionId, storage);
|
|
76
|
+
const res = new NextResponse(null, { status: 403 });
|
|
77
|
+
res.headers.set(CHALLENGE_HEADER, buildChallengeHeader(challenge.jti));
|
|
78
|
+
res.headers.set(LEGACY_CHALLENGE_HEADER, buildChallengeHeader(challenge.jti));
|
|
79
|
+
res.cookies.set(CHALLENGE_COOKIE, challenge.jti, {
|
|
80
|
+
...cookieBase(secure),
|
|
81
|
+
maxAge: 5 * 60,
|
|
82
|
+
});
|
|
83
|
+
return res;
|
|
84
|
+
}
|
|
85
|
+
const expectedJti = req.cookies.get(CHALLENGE_COOKIE)?.value;
|
|
86
|
+
if (!expectedJti) {
|
|
87
|
+
return NextResponse.json({ error: "missing challenge cookie" }, { status: 400 });
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
await handleRefresh({ sessionId, secSessionResponseHeader: responseHeader, expectedJti }, storage);
|
|
91
|
+
emit(onEvent, {
|
|
92
|
+
type: "refresh",
|
|
93
|
+
sessionId,
|
|
94
|
+
tier: "dbsc",
|
|
95
|
+
timestamp: Date.now(),
|
|
96
|
+
ip,
|
|
97
|
+
});
|
|
98
|
+
const res = new NextResponse(null, { status: 204 });
|
|
99
|
+
res.cookies.set(BOUND_COOKIE, sessionId, {
|
|
100
|
+
...cookieBase(secure),
|
|
101
|
+
maxAge: boundCookieTtl / 1000,
|
|
102
|
+
});
|
|
103
|
+
res.cookies.delete(CHALLENGE_COOKIE);
|
|
104
|
+
return res;
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
await rateLimiter.recordFailure(ip, sessionId);
|
|
108
|
+
if (err instanceof DbscVerificationError || err instanceof DbscProtocolError) {
|
|
109
|
+
return NextResponse.json({ error: err.message }, { status: 401 });
|
|
110
|
+
}
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return NextResponse.next();
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export async function getDbscSession(req, storage) {
|
|
118
|
+
const sessionId = req.cookies.get(BOUND_COOKIE)?.value ?? null;
|
|
119
|
+
if (!sessionId)
|
|
120
|
+
return { sessionId: null, tier: "none" };
|
|
121
|
+
const session = await storage.getSession(sessionId);
|
|
122
|
+
if (!session)
|
|
123
|
+
return { sessionId: null, tier: "none" };
|
|
124
|
+
return { sessionId, tier: session.tier };
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/nextjs/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { StorageAdapter, Session, BoundKey, Challenge } from "../../core/index.js";
|
|
2
|
+
export declare class MemoryStorage implements StorageAdapter {
|
|
3
|
+
private sessions;
|
|
4
|
+
private keys;
|
|
5
|
+
private challenges;
|
|
6
|
+
private revoked;
|
|
7
|
+
getSession(id: string): Promise<Session | null>;
|
|
8
|
+
setSession(session: Session): Promise<void>;
|
|
9
|
+
deleteSession(id: string): Promise<void>;
|
|
10
|
+
getBoundKey(sessionId: string): Promise<BoundKey | null>;
|
|
11
|
+
setBoundKey(key: BoundKey): Promise<void>;
|
|
12
|
+
deleteBoundKey(sessionId: string): Promise<void>;
|
|
13
|
+
getChallenge(jti: string): Promise<Challenge | null>;
|
|
14
|
+
setChallenge(challenge: Challenge): Promise<void>;
|
|
15
|
+
consumeChallenge(jti: string): Promise<boolean>;
|
|
16
|
+
revokeSession(sessionId: string): Promise<void>;
|
|
17
|
+
revokeAllForUser(userId: string): Promise<void>;
|
|
18
|
+
gc(): void;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/storage/memory/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAExF,qBAAa,aAAc,YAAW,cAAc;IAClD,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,IAAI,CAA+B;IAC3C,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,OAAO,CAAqB;IAE9B,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAU/C,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAIxD,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAUpD,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjD,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAO/C,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/C,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWrD,EAAE,IAAI,IAAI;CASX"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export class MemoryStorage {
|
|
2
|
+
sessions = new Map();
|
|
3
|
+
keys = new Map();
|
|
4
|
+
challenges = new Map();
|
|
5
|
+
revoked = new Set();
|
|
6
|
+
async getSession(id) {
|
|
7
|
+
const sess = this.sessions.get(id);
|
|
8
|
+
if (!sess)
|
|
9
|
+
return null;
|
|
10
|
+
if (Date.now() > sess.expiresAt) {
|
|
11
|
+
this.sessions.delete(id);
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
return sess;
|
|
15
|
+
}
|
|
16
|
+
async setSession(session) {
|
|
17
|
+
this.sessions.set(session.id, session);
|
|
18
|
+
}
|
|
19
|
+
async deleteSession(id) {
|
|
20
|
+
this.sessions.delete(id);
|
|
21
|
+
this.keys.delete(id);
|
|
22
|
+
}
|
|
23
|
+
async getBoundKey(sessionId) {
|
|
24
|
+
return this.keys.get(sessionId) ?? null;
|
|
25
|
+
}
|
|
26
|
+
async setBoundKey(key) {
|
|
27
|
+
this.keys.set(key.sessionId, key);
|
|
28
|
+
}
|
|
29
|
+
async deleteBoundKey(sessionId) {
|
|
30
|
+
this.keys.delete(sessionId);
|
|
31
|
+
}
|
|
32
|
+
async getChallenge(jti) {
|
|
33
|
+
const challenge = this.challenges.get(jti);
|
|
34
|
+
if (!challenge)
|
|
35
|
+
return null;
|
|
36
|
+
if (Date.now() > challenge.expiresAt) {
|
|
37
|
+
this.challenges.delete(jti);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return challenge;
|
|
41
|
+
}
|
|
42
|
+
async setChallenge(challenge) {
|
|
43
|
+
this.challenges.set(challenge.jti, challenge);
|
|
44
|
+
}
|
|
45
|
+
async consumeChallenge(jti) {
|
|
46
|
+
const challenge = this.challenges.get(jti);
|
|
47
|
+
if (!challenge || challenge.consumed)
|
|
48
|
+
return false;
|
|
49
|
+
challenge.consumed = true;
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
async revokeSession(sessionId) {
|
|
53
|
+
this.revoked.add(sessionId);
|
|
54
|
+
this.sessions.delete(sessionId);
|
|
55
|
+
this.keys.delete(sessionId);
|
|
56
|
+
}
|
|
57
|
+
async revokeAllForUser(userId) {
|
|
58
|
+
for (const [id, sess] of this.sessions.entries()) {
|
|
59
|
+
if (sess.userId === userId) {
|
|
60
|
+
this.revoked.add(id);
|
|
61
|
+
this.sessions.delete(id);
|
|
62
|
+
this.keys.delete(id);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Useful in tests: removes all expired entries from all maps
|
|
67
|
+
gc() {
|
|
68
|
+
const now = Date.now();
|
|
69
|
+
for (const [id, sess] of this.sessions.entries()) {
|
|
70
|
+
if (now > sess.expiresAt)
|
|
71
|
+
this.sessions.delete(id);
|
|
72
|
+
}
|
|
73
|
+
for (const [jti, ch] of this.challenges.entries()) {
|
|
74
|
+
if (now > ch.expiresAt)
|
|
75
|
+
this.challenges.delete(jti);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/storage/memory/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Pool } from "pg";
|
|
2
|
+
import type { StorageAdapter, Session, BoundKey, Challenge } from "../../core/index.js";
|
|
3
|
+
export declare class PostgresStorage implements StorageAdapter {
|
|
4
|
+
private readonly pool;
|
|
5
|
+
constructor(pool: Pool);
|
|
6
|
+
getSession(id: string): Promise<Session | null>;
|
|
7
|
+
setSession(session: Session): Promise<void>;
|
|
8
|
+
deleteSession(id: string): Promise<void>;
|
|
9
|
+
getBoundKey(sessionId: string): Promise<BoundKey | null>;
|
|
10
|
+
setBoundKey(key: BoundKey): Promise<void>;
|
|
11
|
+
deleteBoundKey(sessionId: string): Promise<void>;
|
|
12
|
+
getChallenge(jti: string): Promise<Challenge | null>;
|
|
13
|
+
setChallenge(challenge: Challenge): Promise<void>;
|
|
14
|
+
consumeChallenge(jti: string): Promise<boolean>;
|
|
15
|
+
revokeSession(sessionId: string): Promise<void>;
|
|
16
|
+
revokeAllForUser(userId: string): Promise<void>;
|
|
17
|
+
runMigrations(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/storage/postgres/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,IAAI,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAExF,qBAAa,eAAgB,YAAW,cAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,IAAI;IAEjC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IA6B/C,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAS3C,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAsBxD,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IASzC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAwBpD,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAQjD,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS/C,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;CASrC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export class PostgresStorage {
|
|
2
|
+
pool;
|
|
3
|
+
constructor(pool) {
|
|
4
|
+
this.pool = pool;
|
|
5
|
+
}
|
|
6
|
+
async getSession(id) {
|
|
7
|
+
const { rows } = await this.pool.query("SELECT id, user_id, tier, created_at, expires_at, last_refresh_at FROM dbsc_sessions WHERE id = $1", [id]);
|
|
8
|
+
const row = rows[0];
|
|
9
|
+
if (!row)
|
|
10
|
+
return null;
|
|
11
|
+
const expiresAt = parseInt(row.expires_at, 10);
|
|
12
|
+
if (Date.now() > expiresAt)
|
|
13
|
+
return null;
|
|
14
|
+
return {
|
|
15
|
+
id: row.id,
|
|
16
|
+
userId: row.user_id,
|
|
17
|
+
tier: row.tier,
|
|
18
|
+
createdAt: parseInt(row.created_at, 10),
|
|
19
|
+
expiresAt,
|
|
20
|
+
lastRefreshAt: row.last_refresh_at ? parseInt(row.last_refresh_at, 10) : 0,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
async setSession(session) {
|
|
24
|
+
await this.pool.query(`INSERT INTO dbsc_sessions (id, user_id, tier, created_at, expires_at, last_refresh_at)
|
|
25
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
26
|
+
ON CONFLICT (id) DO UPDATE SET tier = $3, expires_at = $5, last_refresh_at = $6`, [session.id, session.userId, session.tier, session.createdAt, session.expiresAt, session.lastRefreshAt]);
|
|
27
|
+
}
|
|
28
|
+
async deleteSession(id) {
|
|
29
|
+
await this.pool.query("DELETE FROM dbsc_sessions WHERE id = $1", [id]);
|
|
30
|
+
}
|
|
31
|
+
async getBoundKey(sessionId) {
|
|
32
|
+
const { rows } = await this.pool.query("SELECT session_id, jwk, algorithm, created_at FROM dbsc_bound_keys WHERE session_id = $1", [sessionId]);
|
|
33
|
+
const row = rows[0];
|
|
34
|
+
if (!row)
|
|
35
|
+
return null;
|
|
36
|
+
return {
|
|
37
|
+
sessionId: row.session_id,
|
|
38
|
+
jwk: row.jwk,
|
|
39
|
+
algorithm: row.algorithm,
|
|
40
|
+
createdAt: parseInt(row.created_at, 10),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async setBoundKey(key) {
|
|
44
|
+
await this.pool.query(`INSERT INTO dbsc_bound_keys (session_id, jwk, algorithm, created_at)
|
|
45
|
+
VALUES ($1, $2, $3, $4)
|
|
46
|
+
ON CONFLICT (session_id) DO UPDATE SET jwk = $2, algorithm = $3`, [key.sessionId, JSON.stringify(key.jwk), key.algorithm, key.createdAt]);
|
|
47
|
+
}
|
|
48
|
+
async deleteBoundKey(sessionId) {
|
|
49
|
+
await this.pool.query("DELETE FROM dbsc_bound_keys WHERE session_id = $1", [sessionId]);
|
|
50
|
+
}
|
|
51
|
+
async getChallenge(jti) {
|
|
52
|
+
const { rows } = await this.pool.query("SELECT jti, session_id, created_at, expires_at, consumed FROM dbsc_challenges WHERE jti = $1", [jti]);
|
|
53
|
+
const row = rows[0];
|
|
54
|
+
if (!row)
|
|
55
|
+
return null;
|
|
56
|
+
return {
|
|
57
|
+
jti: row.jti,
|
|
58
|
+
sessionId: row.session_id,
|
|
59
|
+
createdAt: parseInt(row.created_at, 10),
|
|
60
|
+
expiresAt: parseInt(row.expires_at, 10),
|
|
61
|
+
consumed: row.consumed,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async setChallenge(challenge) {
|
|
65
|
+
await this.pool.query(`INSERT INTO dbsc_challenges (jti, session_id, created_at, expires_at, consumed)
|
|
66
|
+
VALUES ($1, $2, $3, $4, $5)`, [challenge.jti, challenge.sessionId, challenge.createdAt, challenge.expiresAt, challenge.consumed]);
|
|
67
|
+
}
|
|
68
|
+
async consumeChallenge(jti) {
|
|
69
|
+
const { rowCount } = await this.pool.query(`UPDATE dbsc_challenges SET consumed = TRUE
|
|
70
|
+
WHERE jti = $1 AND consumed = FALSE AND expires_at > $2`, [jti, Date.now()]);
|
|
71
|
+
return (rowCount ?? 0) > 0;
|
|
72
|
+
}
|
|
73
|
+
async revokeSession(sessionId) {
|
|
74
|
+
await this.deleteSession(sessionId);
|
|
75
|
+
}
|
|
76
|
+
async revokeAllForUser(userId) {
|
|
77
|
+
await this.pool.query("DELETE FROM dbsc_sessions WHERE user_id = $1", [userId]);
|
|
78
|
+
}
|
|
79
|
+
async runMigrations() {
|
|
80
|
+
await this.pool.query(`
|
|
81
|
+
CREATE TABLE IF NOT EXISTS dbsc_migrations (
|
|
82
|
+
id SERIAL PRIMARY KEY,
|
|
83
|
+
name TEXT UNIQUE NOT NULL,
|
|
84
|
+
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
85
|
+
)
|
|
86
|
+
`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/storage/postgres/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Redis } from "ioredis";
|
|
2
|
+
import type { StorageAdapter, Session, BoundKey, Challenge } from "../../core/index.js";
|
|
3
|
+
export declare class RedisStorage implements StorageAdapter {
|
|
4
|
+
private readonly client;
|
|
5
|
+
constructor(client: Redis);
|
|
6
|
+
getSession(id: string): Promise<Session | null>;
|
|
7
|
+
setSession(session: Session): Promise<void>;
|
|
8
|
+
deleteSession(id: string): Promise<void>;
|
|
9
|
+
getBoundKey(sessionId: string): Promise<BoundKey | null>;
|
|
10
|
+
setBoundKey(key: BoundKey): Promise<void>;
|
|
11
|
+
deleteBoundKey(sessionId: string): Promise<void>;
|
|
12
|
+
getChallenge(jti: string): Promise<Challenge | null>;
|
|
13
|
+
setChallenge(challenge: Challenge): Promise<void>;
|
|
14
|
+
consumeChallenge(jti: string): Promise<boolean>;
|
|
15
|
+
revokeSession(sessionId: string): Promise<void>;
|
|
16
|
+
revokeAllForUser(userId: string): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/storage/redis/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AA0BxF,qBAAa,YAAa,YAAW,cAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,KAAK;IAEpC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAM/C,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAM3C,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASxC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAMxD,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAMpD,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjD,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK/C,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAYtD"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const KEY = {
|
|
2
|
+
session: (id) => `dbsc:session:${id}`,
|
|
3
|
+
key: (sessionId) => `dbsc:key:${sessionId}`,
|
|
4
|
+
challenge: (jti) => `dbsc:challenge:${jti}`,
|
|
5
|
+
userSessions: (userId) => `dbsc:user:${userId}:sessions`,
|
|
6
|
+
};
|
|
7
|
+
// Lua script for atomic challenge consume: get, check consumed flag, set consumed=true
|
|
8
|
+
const CONSUME_CHALLENGE_SCRIPT = `
|
|
9
|
+
local raw = redis.call('GET', KEYS[1])
|
|
10
|
+
if not raw then return 0 end
|
|
11
|
+
local data = cjson.decode(raw)
|
|
12
|
+
if data.consumed then return 0 end
|
|
13
|
+
data.consumed = true
|
|
14
|
+
local ttl = redis.call('TTL', KEYS[1])
|
|
15
|
+
if ttl > 0 then
|
|
16
|
+
redis.call('SET', KEYS[1], cjson.encode(data), 'EX', ttl)
|
|
17
|
+
else
|
|
18
|
+
redis.call('SET', KEYS[1], cjson.encode(data))
|
|
19
|
+
end
|
|
20
|
+
return 1
|
|
21
|
+
`;
|
|
22
|
+
export class RedisStorage {
|
|
23
|
+
client;
|
|
24
|
+
constructor(client) {
|
|
25
|
+
this.client = client;
|
|
26
|
+
}
|
|
27
|
+
async getSession(id) {
|
|
28
|
+
const raw = await this.client.get(KEY.session(id));
|
|
29
|
+
if (!raw)
|
|
30
|
+
return null;
|
|
31
|
+
return JSON.parse(raw);
|
|
32
|
+
}
|
|
33
|
+
async setSession(session) {
|
|
34
|
+
const ttlSeconds = Math.max(1, Math.ceil((session.expiresAt - Date.now()) / 1000));
|
|
35
|
+
await this.client.set(KEY.session(session.id), JSON.stringify(session), "EX", ttlSeconds);
|
|
36
|
+
await this.client.sadd(KEY.userSessions(session.userId), session.id);
|
|
37
|
+
}
|
|
38
|
+
async deleteSession(id) {
|
|
39
|
+
const raw = await this.client.get(KEY.session(id));
|
|
40
|
+
if (raw) {
|
|
41
|
+
const sess = JSON.parse(raw);
|
|
42
|
+
await this.client.srem(KEY.userSessions(sess.userId), id);
|
|
43
|
+
}
|
|
44
|
+
await this.client.del(KEY.session(id), KEY.key(id));
|
|
45
|
+
}
|
|
46
|
+
async getBoundKey(sessionId) {
|
|
47
|
+
const raw = await this.client.get(KEY.key(sessionId));
|
|
48
|
+
if (!raw)
|
|
49
|
+
return null;
|
|
50
|
+
return JSON.parse(raw);
|
|
51
|
+
}
|
|
52
|
+
async setBoundKey(key) {
|
|
53
|
+
await this.client.set(KEY.key(key.sessionId), JSON.stringify(key));
|
|
54
|
+
}
|
|
55
|
+
async deleteBoundKey(sessionId) {
|
|
56
|
+
await this.client.del(KEY.key(sessionId));
|
|
57
|
+
}
|
|
58
|
+
async getChallenge(jti) {
|
|
59
|
+
const raw = await this.client.get(KEY.challenge(jti));
|
|
60
|
+
if (!raw)
|
|
61
|
+
return null;
|
|
62
|
+
return JSON.parse(raw);
|
|
63
|
+
}
|
|
64
|
+
async setChallenge(challenge) {
|
|
65
|
+
const ttlSeconds = Math.max(1, Math.ceil((challenge.expiresAt - Date.now()) / 1000));
|
|
66
|
+
await this.client.set(KEY.challenge(challenge.jti), JSON.stringify(challenge), "EX", ttlSeconds);
|
|
67
|
+
}
|
|
68
|
+
async consumeChallenge(jti) {
|
|
69
|
+
const result = await this.client.eval(CONSUME_CHALLENGE_SCRIPT, 1, KEY.challenge(jti));
|
|
70
|
+
return result === 1;
|
|
71
|
+
}
|
|
72
|
+
async revokeSession(sessionId) {
|
|
73
|
+
await this.deleteSession(sessionId);
|
|
74
|
+
}
|
|
75
|
+
async revokeAllForUser(userId) {
|
|
76
|
+
const sessionIds = await this.client.smembers(KEY.userSessions(userId));
|
|
77
|
+
if (sessionIds.length === 0)
|
|
78
|
+
return;
|
|
79
|
+
const pipeline = this.client.pipeline();
|
|
80
|
+
for (const id of sessionIds) {
|
|
81
|
+
pipeline.del(KEY.session(id));
|
|
82
|
+
pipeline.del(KEY.key(id));
|
|
83
|
+
}
|
|
84
|
+
pipeline.del(KEY.userSessions(userId));
|
|
85
|
+
await pipeline.exec();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/storage/redis/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS dbsc_sessions (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
user_id TEXT NOT NULL,
|
|
4
|
+
tier TEXT NOT NULL CHECK (tier IN ('dbsc', 'webauthn', 'hmac', 'none')),
|
|
5
|
+
created_at BIGINT NOT NULL,
|
|
6
|
+
expires_at BIGINT NOT NULL,
|
|
7
|
+
last_refresh_at BIGINT NOT NULL DEFAULT 0
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
CREATE INDEX IF NOT EXISTS dbsc_sessions_user_id ON dbsc_sessions (user_id);
|
|
11
|
+
CREATE INDEX IF NOT EXISTS dbsc_sessions_expires_at ON dbsc_sessions (expires_at);
|
|
12
|
+
|
|
13
|
+
CREATE TABLE IF NOT EXISTS dbsc_bound_keys (
|
|
14
|
+
session_id TEXT PRIMARY KEY REFERENCES dbsc_sessions (id) ON DELETE CASCADE,
|
|
15
|
+
jwk JSONB NOT NULL,
|
|
16
|
+
algorithm TEXT NOT NULL CHECK (algorithm IN ('ES256', 'RS256')),
|
|
17
|
+
created_at BIGINT NOT NULL
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
CREATE TABLE IF NOT EXISTS dbsc_challenges (
|
|
21
|
+
jti TEXT PRIMARY KEY,
|
|
22
|
+
session_id TEXT NOT NULL,
|
|
23
|
+
created_at BIGINT NOT NULL,
|
|
24
|
+
expires_at BIGINT NOT NULL,
|
|
25
|
+
consumed BOOLEAN NOT NULL DEFAULT FALSE
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
CREATE INDEX IF NOT EXISTS dbsc_challenges_session_id ON dbsc_challenges (session_id);
|
|
29
|
+
CREATE INDEX IF NOT EXISTS dbsc_challenges_expires_at ON dbsc_challenges (expires_at);
|
|
30
|
+
|
|
31
|
+
CREATE TABLE IF NOT EXISTS dbsc_audit_log (
|
|
32
|
+
id BIGSERIAL PRIMARY KEY,
|
|
33
|
+
session_id TEXT NOT NULL,
|
|
34
|
+
event_type TEXT NOT NULL,
|
|
35
|
+
ip TEXT,
|
|
36
|
+
metadata JSONB,
|
|
37
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
CREATE INDEX IF NOT EXISTS dbsc_audit_log_session_id ON dbsc_audit_log (session_id);
|
|
41
|
+
CREATE INDEX IF NOT EXISTS dbsc_audit_log_created_at ON dbsc_audit_log (created_at);
|