@holeauth/core 0.0.1-alpha.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 +21 -0
- package/README.md +5 -0
- package/cjs-error.cjs +8 -0
- package/dist/adapters/index.d.ts +1 -0
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/cookies/index.d.ts +3 -0
- package/dist/cookies/index.js +74 -0
- package/dist/cookies/index.js.map +1 -0
- package/dist/errors/index.d.ts +40 -0
- package/dist/errors/index.js +70 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/events/index.d.ts +3 -0
- package/dist/events/index.js +52 -0
- package/dist/events/index.js.map +1 -0
- package/dist/flows/index.d.ts +4 -0
- package/dist/flows/index.js +835 -0
- package/dist/flows/index.js.map +1 -0
- package/dist/index-BIXESLma.d.ts +58 -0
- package/dist/index-BYtkmk9_.d.ts +18 -0
- package/dist/index-BbEXbI_k.d.ts +116 -0
- package/dist/index-BmYQquGs.d.ts +563 -0
- package/dist/index-BwEvEa8-.d.ts +20 -0
- package/dist/index-CHS-socJ.d.ts +97 -0
- package/dist/index-CNtnPdzk.d.ts +136 -0
- package/dist/index-CjEXpqaW.d.ts +22 -0
- package/dist/index-CotvcK_b.d.ts +42 -0
- package/dist/index-D57PvFMN.d.ts +105 -0
- package/dist/index-DRN-5E_H.d.ts +26 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +1757 -0
- package/dist/index.js.map +1 -0
- package/dist/jwt/index.d.ts +2 -0
- package/dist/jwt/index.js +53 -0
- package/dist/jwt/index.js.map +1 -0
- package/dist/otp/index.d.ts +1 -0
- package/dist/otp/index.js +16 -0
- package/dist/otp/index.js.map +1 -0
- package/dist/password/index.d.ts +1 -0
- package/dist/password/index.js +75 -0
- package/dist/password/index.js.map +1 -0
- package/dist/plugins/index.d.ts +4 -0
- package/dist/plugins/index.js +480 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/registry-CZhM1tEB.d.ts +101 -0
- package/dist/session/index.d.ts +3 -0
- package/dist/session/index.js +346 -0
- package/dist/session/index.js.map +1 -0
- package/dist/sso/index.d.ts +3 -0
- package/dist/sso/index.js +475 -0
- package/dist/sso/index.js.map +1 -0
- package/package.json +121 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import { SignJWT } from 'jose';
|
|
2
|
+
|
|
3
|
+
// src/plugins/define.ts
|
|
4
|
+
function definePlugin(p) {
|
|
5
|
+
return p;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// src/events/emitter.ts
|
|
9
|
+
var busByConfig = /* @__PURE__ */ new WeakMap();
|
|
10
|
+
function getBus(cfg) {
|
|
11
|
+
let bus = busByConfig.get(cfg);
|
|
12
|
+
if (!bus) {
|
|
13
|
+
bus = { byType: /* @__PURE__ */ new Map(), wildcard: /* @__PURE__ */ new Set() };
|
|
14
|
+
busByConfig.set(cfg, bus);
|
|
15
|
+
}
|
|
16
|
+
return bus;
|
|
17
|
+
}
|
|
18
|
+
function subscribe(cfg, type, handler) {
|
|
19
|
+
const bus = getBus(cfg);
|
|
20
|
+
if (type === "*") {
|
|
21
|
+
bus.wildcard.add(handler);
|
|
22
|
+
return () => bus.wildcard.delete(handler);
|
|
23
|
+
}
|
|
24
|
+
let set = bus.byType.get(type);
|
|
25
|
+
if (!set) {
|
|
26
|
+
set = /* @__PURE__ */ new Set();
|
|
27
|
+
bus.byType.set(type, set);
|
|
28
|
+
}
|
|
29
|
+
set.add(handler);
|
|
30
|
+
return () => set.delete(handler);
|
|
31
|
+
}
|
|
32
|
+
function unsubscribe(cfg, type, handler) {
|
|
33
|
+
const bus = getBus(cfg);
|
|
34
|
+
if (type === "*") {
|
|
35
|
+
bus.wildcard.delete(handler);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
bus.byType.get(type)?.delete(handler);
|
|
39
|
+
}
|
|
40
|
+
async function emit(cfg, event) {
|
|
41
|
+
const withTimestamp = { at: /* @__PURE__ */ new Date(), ...event };
|
|
42
|
+
await cfg.adapters.auditLog.record(withTimestamp);
|
|
43
|
+
const bus = getBus(cfg);
|
|
44
|
+
const typed = bus.byType.get(withTimestamp.type);
|
|
45
|
+
const fire = (h) => {
|
|
46
|
+
Promise.resolve().then(() => h(withTimestamp)).catch(() => {
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
if (typed) for (const h of typed) fire(h);
|
|
50
|
+
for (const h of bus.wildcard) fire(h);
|
|
51
|
+
if (cfg.onEvent) {
|
|
52
|
+
Promise.resolve().then(() => cfg.onEvent?.(withTimestamp)).catch(() => {
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function toKey(secret) {
|
|
57
|
+
return typeof secret === "string" ? new TextEncoder().encode(secret) : secret;
|
|
58
|
+
}
|
|
59
|
+
async function sign(payload, secret, opts = {}) {
|
|
60
|
+
const jwt = new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt();
|
|
61
|
+
if (opts.issuer) jwt.setIssuer(opts.issuer);
|
|
62
|
+
if (opts.audience) jwt.setAudience(opts.audience);
|
|
63
|
+
if (opts.subject) jwt.setSubject(opts.subject);
|
|
64
|
+
if (opts.jti) jwt.setJti(opts.jti);
|
|
65
|
+
if (opts.expiresIn !== void 0) jwt.setExpirationTime(opts.expiresIn);
|
|
66
|
+
return jwt.sign(toKey(secret));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/cookies/csrf.ts
|
|
70
|
+
var b64urlChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
71
|
+
function generateCsrfToken() {
|
|
72
|
+
const bytes = crypto.getRandomValues(new Uint8Array(32));
|
|
73
|
+
let out = "";
|
|
74
|
+
for (const b of bytes) out += b64urlChars[b % 64];
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/plugins/runner-ref.ts
|
|
79
|
+
var runners = /* @__PURE__ */ new WeakMap();
|
|
80
|
+
var NOOP = {
|
|
81
|
+
async runRegisterBefore() {
|
|
82
|
+
},
|
|
83
|
+
async runRegisterAfter() {
|
|
84
|
+
},
|
|
85
|
+
async runSignInBefore() {
|
|
86
|
+
},
|
|
87
|
+
async runSignInChallenge() {
|
|
88
|
+
return null;
|
|
89
|
+
},
|
|
90
|
+
async runSignInAfter() {
|
|
91
|
+
},
|
|
92
|
+
async runSignOutAfter() {
|
|
93
|
+
},
|
|
94
|
+
async runRefreshBefore() {
|
|
95
|
+
},
|
|
96
|
+
async runRefreshAfter() {
|
|
97
|
+
},
|
|
98
|
+
async runPasswordChangeBefore() {
|
|
99
|
+
},
|
|
100
|
+
async runPasswordChangeAfter() {
|
|
101
|
+
},
|
|
102
|
+
async runPasswordResetBefore() {
|
|
103
|
+
},
|
|
104
|
+
async runPasswordResetAfter() {
|
|
105
|
+
},
|
|
106
|
+
async runUserUpdateAfter() {
|
|
107
|
+
},
|
|
108
|
+
async runUserDeleteAfter() {
|
|
109
|
+
},
|
|
110
|
+
async runSessionIssue() {
|
|
111
|
+
},
|
|
112
|
+
async runSessionRotate() {
|
|
113
|
+
},
|
|
114
|
+
async runSessionRevoke() {
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
function getHookRunner(cfg) {
|
|
118
|
+
return runners.get(cfg) ?? NOOP;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/session/hash.ts
|
|
122
|
+
async function sha256b64url(input) {
|
|
123
|
+
const buf = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input));
|
|
124
|
+
const bytes = new Uint8Array(buf);
|
|
125
|
+
let s = "";
|
|
126
|
+
for (const b of bytes) s += String.fromCharCode(b);
|
|
127
|
+
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/session/issue.ts
|
|
131
|
+
var ACCESS_DEFAULT = 900;
|
|
132
|
+
var REFRESH_DEFAULT = 2592e3;
|
|
133
|
+
async function issueSession(cfg, input) {
|
|
134
|
+
const accessTtl = cfg.tokens?.accessTtl ?? ACCESS_DEFAULT;
|
|
135
|
+
const refreshTtl = cfg.tokens?.refreshTtl ?? REFRESH_DEFAULT;
|
|
136
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
137
|
+
const familyId = input.familyId ?? crypto.randomUUID();
|
|
138
|
+
const sessionId = crypto.randomUUID();
|
|
139
|
+
const nonce = crypto.randomUUID();
|
|
140
|
+
const [accessToken, refreshToken] = await Promise.all([
|
|
141
|
+
sign(
|
|
142
|
+
{ sid: sessionId, sub: input.userId, fam: familyId, nce: nonce },
|
|
143
|
+
cfg.secrets.jwtSecret,
|
|
144
|
+
{ expiresIn: `${accessTtl}s` }
|
|
145
|
+
),
|
|
146
|
+
sign(
|
|
147
|
+
{ sid: sessionId, sub: input.userId, fam: familyId, typ: "refresh", nce: nonce },
|
|
148
|
+
cfg.secrets.jwtSecret,
|
|
149
|
+
{ expiresIn: `${refreshTtl}s`, jti: nonce }
|
|
150
|
+
)
|
|
151
|
+
]);
|
|
152
|
+
const refreshTokenHash = await sha256b64url(refreshToken);
|
|
153
|
+
await cfg.adapters.session.createSession({
|
|
154
|
+
id: sessionId,
|
|
155
|
+
userId: input.userId,
|
|
156
|
+
familyId,
|
|
157
|
+
refreshTokenHash,
|
|
158
|
+
expiresAt: new Date((now + refreshTtl) * 1e3),
|
|
159
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
160
|
+
revokedAt: null,
|
|
161
|
+
ip: input.ip ?? null,
|
|
162
|
+
userAgent: input.userAgent ?? null
|
|
163
|
+
});
|
|
164
|
+
const csrfToken = generateCsrfToken();
|
|
165
|
+
await emit(cfg, {
|
|
166
|
+
type: "session.created",
|
|
167
|
+
userId: input.userId,
|
|
168
|
+
sessionId,
|
|
169
|
+
ip: input.ip ?? null,
|
|
170
|
+
userAgent: input.userAgent ?? null,
|
|
171
|
+
data: { familyId }
|
|
172
|
+
});
|
|
173
|
+
await getHookRunner(cfg).runSessionIssue({ userId: input.userId, sessionId, familyId });
|
|
174
|
+
return {
|
|
175
|
+
accessToken,
|
|
176
|
+
refreshToken,
|
|
177
|
+
csrfToken,
|
|
178
|
+
sessionId,
|
|
179
|
+
familyId,
|
|
180
|
+
accessExpiresAt: (now + accessTtl) * 1e3,
|
|
181
|
+
refreshExpiresAt: (now + refreshTtl) * 1e3
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/session/revoke.ts
|
|
186
|
+
async function revokeSession(cfg, sessionId, userId) {
|
|
187
|
+
await cfg.adapters.session.deleteSession(sessionId);
|
|
188
|
+
await emit(cfg, {
|
|
189
|
+
type: "session.revoked",
|
|
190
|
+
userId: userId ?? null,
|
|
191
|
+
sessionId
|
|
192
|
+
});
|
|
193
|
+
await getHookRunner(cfg).runSessionRevoke({ userId: userId ?? null, sessionId });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// src/plugins/registry.ts
|
|
197
|
+
var CORE_ROUTE_PATHS = /* @__PURE__ */ new Set([
|
|
198
|
+
"GET /session",
|
|
199
|
+
"GET /csrf",
|
|
200
|
+
"GET /authorize/:provider",
|
|
201
|
+
"GET /callback/:provider",
|
|
202
|
+
"POST /register",
|
|
203
|
+
"POST /signin",
|
|
204
|
+
"POST /signout",
|
|
205
|
+
"POST /refresh",
|
|
206
|
+
"POST /password/change",
|
|
207
|
+
"POST /password/reset/request",
|
|
208
|
+
"POST /password/reset/consume",
|
|
209
|
+
"GET /invite/info",
|
|
210
|
+
"GET /invite/list",
|
|
211
|
+
"POST /invite/create",
|
|
212
|
+
"POST /invite/consume",
|
|
213
|
+
"POST /invite/revoke"
|
|
214
|
+
]);
|
|
215
|
+
function defaultLogger(cfg) {
|
|
216
|
+
const prefix = "[holeauth]";
|
|
217
|
+
const silent = cfg.logger?.silent === true;
|
|
218
|
+
return {
|
|
219
|
+
debug: silent ? () => {
|
|
220
|
+
} : (m, d) => console.debug(prefix, m, d ?? ""),
|
|
221
|
+
info: silent ? () => {
|
|
222
|
+
} : (m, d) => console.info(prefix, m, d ?? ""),
|
|
223
|
+
warn: (m, d) => console.warn(prefix, m, d ?? ""),
|
|
224
|
+
error: (m, e) => console.error(prefix, m, e ?? "")
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function topoSort(plugins) {
|
|
228
|
+
const byId = /* @__PURE__ */ new Map();
|
|
229
|
+
for (const p of plugins) {
|
|
230
|
+
if (byId.has(p.id)) throw new Error(`holeauth: duplicate plugin id "${p.id}"`);
|
|
231
|
+
byId.set(p.id, p);
|
|
232
|
+
}
|
|
233
|
+
const inDeg = /* @__PURE__ */ new Map();
|
|
234
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
235
|
+
for (const p of plugins) {
|
|
236
|
+
inDeg.set(p.id, 0);
|
|
237
|
+
dependents.set(p.id, []);
|
|
238
|
+
}
|
|
239
|
+
for (const p of plugins) {
|
|
240
|
+
for (const dep of p.dependsOn ?? []) {
|
|
241
|
+
if (!byId.has(dep)) {
|
|
242
|
+
throw new Error(`holeauth: plugin "${p.id}" depends on missing plugin "${dep}"`);
|
|
243
|
+
}
|
|
244
|
+
inDeg.set(p.id, (inDeg.get(p.id) ?? 0) + 1);
|
|
245
|
+
dependents.get(dep).push(p.id);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const queue = [];
|
|
249
|
+
for (const [id, d] of inDeg) if (d === 0) queue.push(id);
|
|
250
|
+
const sorted = [];
|
|
251
|
+
while (queue.length) {
|
|
252
|
+
const id = queue.shift();
|
|
253
|
+
sorted.push(byId.get(id));
|
|
254
|
+
for (const dep of dependents.get(id) ?? []) {
|
|
255
|
+
const next = (inDeg.get(dep) ?? 0) - 1;
|
|
256
|
+
inDeg.set(dep, next);
|
|
257
|
+
if (next === 0) queue.push(dep);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (sorted.length !== plugins.length) {
|
|
261
|
+
throw new Error("holeauth: plugin dependsOn graph has a cycle");
|
|
262
|
+
}
|
|
263
|
+
return sorted;
|
|
264
|
+
}
|
|
265
|
+
function buildPluginEvents(cfg) {
|
|
266
|
+
return {
|
|
267
|
+
on(type, handler) {
|
|
268
|
+
return subscribe(cfg, type, handler);
|
|
269
|
+
},
|
|
270
|
+
off(type, handler) {
|
|
271
|
+
unsubscribe(cfg, type, handler);
|
|
272
|
+
},
|
|
273
|
+
async emit(e) {
|
|
274
|
+
await emit(cfg, e);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function routeKey(r) {
|
|
279
|
+
return `${r.method} ${r.path}`;
|
|
280
|
+
}
|
|
281
|
+
function validatePluginRoutes(plugins) {
|
|
282
|
+
const seen = /* @__PURE__ */ new Map();
|
|
283
|
+
const out = [];
|
|
284
|
+
for (const p of plugins) {
|
|
285
|
+
for (const r of p.routes ?? []) {
|
|
286
|
+
if (!r.path.startsWith("/")) {
|
|
287
|
+
throw new Error(
|
|
288
|
+
`holeauth: plugin "${p.id}" declared route with invalid path "${r.path}" (must start with '/')`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
const key = routeKey(r);
|
|
292
|
+
if (CORE_ROUTE_PATHS.has(key)) {
|
|
293
|
+
throw new Error(
|
|
294
|
+
`holeauth: plugin "${p.id}" route ${key} conflicts with a core route`
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
const prev = seen.get(key);
|
|
298
|
+
if (prev) {
|
|
299
|
+
throw new Error(
|
|
300
|
+
`holeauth: plugin "${p.id}" route ${key} conflicts with plugin "${prev}"`
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
seen.set(key, p.id);
|
|
304
|
+
out.push(r);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return out;
|
|
308
|
+
}
|
|
309
|
+
function makeHookRunner(plugins, ctx) {
|
|
310
|
+
const logger = ctx.logger;
|
|
311
|
+
async function runAfter(label, fns, data) {
|
|
312
|
+
for (const fn of fns) {
|
|
313
|
+
try {
|
|
314
|
+
await fn(data, ctx);
|
|
315
|
+
} catch (err) {
|
|
316
|
+
logger.error(`hook ${label} threw`, err);
|
|
317
|
+
void ctx.events.emit({
|
|
318
|
+
type: "plugin.error",
|
|
319
|
+
data: { hook: label, error: String(err?.message ?? err) }
|
|
320
|
+
}).catch(() => {
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async function runBefore(fns, input) {
|
|
326
|
+
for (const fn of fns) await fn(input, ctx);
|
|
327
|
+
}
|
|
328
|
+
const pick = (group, slot) => plugins.map((p) => {
|
|
329
|
+
const g = p.hooks?.[group];
|
|
330
|
+
return g?.[slot];
|
|
331
|
+
}).filter((x) => !!x);
|
|
332
|
+
const regBefore = pick("register", "before");
|
|
333
|
+
const regAfter = pick("register", "after");
|
|
334
|
+
const siBefore = pick("signIn", "before");
|
|
335
|
+
const siChallenge = plugins.map((p) => ({ id: p.id, fn: p.hooks?.signIn?.challenge })).filter((x) => !!x.fn);
|
|
336
|
+
const siAfter = pick("signIn", "after");
|
|
337
|
+
const soAfter = pick("signOut", "after");
|
|
338
|
+
const rfBefore = pick("refresh", "before");
|
|
339
|
+
const rfAfter = pick("refresh", "after");
|
|
340
|
+
const pcBefore = pick("passwordChange", "before");
|
|
341
|
+
const pcAfter = pick("passwordChange", "after");
|
|
342
|
+
const prBefore = pick("passwordReset", "before");
|
|
343
|
+
const prAfter = pick("passwordReset", "after");
|
|
344
|
+
const uuAfter = pick("userUpdate", "after");
|
|
345
|
+
const udAfter = pick("userDelete", "after");
|
|
346
|
+
const seIssue = pick("session", "onIssue");
|
|
347
|
+
const seRotate = pick("session", "onRotate");
|
|
348
|
+
const seRevoke = pick("session", "onRevoke");
|
|
349
|
+
return {
|
|
350
|
+
runRegisterBefore: (i) => runBefore(regBefore, i),
|
|
351
|
+
runRegisterAfter: (u) => runAfter("register.after", regAfter, u),
|
|
352
|
+
runSignInBefore: (i) => runBefore(siBefore, i),
|
|
353
|
+
async runSignInChallenge(user, input) {
|
|
354
|
+
let winner = null;
|
|
355
|
+
for (const { id, fn } of siChallenge) {
|
|
356
|
+
let result;
|
|
357
|
+
try {
|
|
358
|
+
result = await fn(user, input, ctx) ?? null;
|
|
359
|
+
} catch (err) {
|
|
360
|
+
logger.error(`signIn.challenge[${id}] threw`, err);
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
if (!result) continue;
|
|
364
|
+
if (result.pluginId !== id) {
|
|
365
|
+
logger.warn(
|
|
366
|
+
`signIn.challenge[${id}] returned pluginId="${result.pluginId}"; forcing "${id}"`
|
|
367
|
+
);
|
|
368
|
+
result.pluginId = id;
|
|
369
|
+
}
|
|
370
|
+
if (winner) {
|
|
371
|
+
logger.warn(
|
|
372
|
+
`multiple signIn.challenge winners \u2014 using first ("${winner.pluginId}"), ignoring "${result.pluginId}"`
|
|
373
|
+
);
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
winner = result;
|
|
377
|
+
}
|
|
378
|
+
return winner;
|
|
379
|
+
},
|
|
380
|
+
runSignInAfter: (d) => runAfter("signIn.after", siAfter, d),
|
|
381
|
+
runSignOutAfter: (d) => runAfter("signOut.after", soAfter, d),
|
|
382
|
+
runRefreshBefore: (i) => runBefore(rfBefore, i),
|
|
383
|
+
runRefreshAfter: (d) => runAfter("refresh.after", rfAfter, d),
|
|
384
|
+
runPasswordChangeBefore: (i) => runBefore(pcBefore, i),
|
|
385
|
+
runPasswordChangeAfter: (d) => runAfter("passwordChange.after", pcAfter, d),
|
|
386
|
+
runPasswordResetBefore: (i) => runBefore(prBefore, i),
|
|
387
|
+
runPasswordResetAfter: (d) => runAfter("passwordReset.after", prAfter, d),
|
|
388
|
+
runUserUpdateAfter: (d) => runAfter("userUpdate.after", uuAfter, d),
|
|
389
|
+
runUserDeleteAfter: (d) => runAfter("userDelete.after", udAfter, d),
|
|
390
|
+
runSessionIssue: (d) => runAfter("session.onIssue", seIssue, d),
|
|
391
|
+
runSessionRotate: (d) => runAfter("session.onRotate", seRotate, d),
|
|
392
|
+
runSessionRevoke: (d) => runAfter("session.onRevoke", seRevoke, d)
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
function makeCoreSurface(cfg, getHooks) {
|
|
396
|
+
return {
|
|
397
|
+
getUserById: (id) => cfg.adapters.user.getUserById(id),
|
|
398
|
+
getUserByEmail: (email) => cfg.adapters.user.getUserByEmail(email),
|
|
399
|
+
async issueSession(input) {
|
|
400
|
+
return issueSession(cfg, {
|
|
401
|
+
userId: input.userId,
|
|
402
|
+
ip: input.ip ?? null,
|
|
403
|
+
userAgent: input.userAgent ?? null
|
|
404
|
+
});
|
|
405
|
+
},
|
|
406
|
+
async revokeSession(sessionId, userId) {
|
|
407
|
+
return revokeSession(cfg, sessionId, userId);
|
|
408
|
+
},
|
|
409
|
+
async completeSignIn(userId, input) {
|
|
410
|
+
const user = await cfg.adapters.user.getUserById(userId);
|
|
411
|
+
if (!user) throw new Error("completeSignIn: user not found");
|
|
412
|
+
const tokens = await issueSession(cfg, {
|
|
413
|
+
userId: user.id,
|
|
414
|
+
ip: input.ip ?? null,
|
|
415
|
+
userAgent: input.userAgent ?? null
|
|
416
|
+
});
|
|
417
|
+
await emit(cfg, {
|
|
418
|
+
type: "user.signed_in",
|
|
419
|
+
userId: user.id,
|
|
420
|
+
sessionId: tokens.sessionId,
|
|
421
|
+
ip: input.ip ?? null,
|
|
422
|
+
userAgent: input.userAgent ?? null,
|
|
423
|
+
data: { method: input.method }
|
|
424
|
+
});
|
|
425
|
+
await getHooks().runSignInAfter({ user, tokens, method: input.method });
|
|
426
|
+
return { user, tokens };
|
|
427
|
+
},
|
|
428
|
+
async issueSignInResult(user, input) {
|
|
429
|
+
const tokens = await issueSession(cfg, {
|
|
430
|
+
userId: user.id,
|
|
431
|
+
ip: input.ip ?? null,
|
|
432
|
+
userAgent: input.userAgent ?? null
|
|
433
|
+
});
|
|
434
|
+
return { kind: "ok", user, tokens };
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
function buildRegistry(cfg, rawPlugins = []) {
|
|
439
|
+
const plugins = topoSort(rawPlugins);
|
|
440
|
+
const routes = validatePluginRoutes(plugins);
|
|
441
|
+
const apiMap = {};
|
|
442
|
+
let hookRunner;
|
|
443
|
+
const ctx = {
|
|
444
|
+
config: cfg,
|
|
445
|
+
events: buildPluginEvents(cfg),
|
|
446
|
+
logger: defaultLogger(cfg),
|
|
447
|
+
core: makeCoreSurface(cfg, () => hookRunner),
|
|
448
|
+
getPlugin(id) {
|
|
449
|
+
const v = apiMap[id];
|
|
450
|
+
if (v === void 0) throw new Error(`holeauth: plugin "${id}" not registered`);
|
|
451
|
+
return v;
|
|
452
|
+
},
|
|
453
|
+
getPluginAdapter(id) {
|
|
454
|
+
return cfg.pluginAdapters?.[id];
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
for (const p of plugins) {
|
|
458
|
+
apiMap[p.id] = p.api(ctx);
|
|
459
|
+
}
|
|
460
|
+
hookRunner = makeHookRunner(plugins, ctx);
|
|
461
|
+
return {
|
|
462
|
+
plugins,
|
|
463
|
+
api: apiMap,
|
|
464
|
+
routes,
|
|
465
|
+
hooks: hookRunner,
|
|
466
|
+
ctx
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
function emptyRegistry(cfg) {
|
|
470
|
+
return buildRegistry(cfg, []);
|
|
471
|
+
}
|
|
472
|
+
async function runOnInit(registry) {
|
|
473
|
+
for (const p of registry.plugins) {
|
|
474
|
+
if (p.onInit) await p.onInit(registry.ctx);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
export { buildRegistry, definePlugin, emptyRegistry, runOnInit };
|
|
479
|
+
//# sourceMappingURL=index.js.map
|
|
480
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/plugins/define.ts","../../src/events/emitter.ts","../../src/jwt/index.ts","../../src/cookies/csrf.ts","../../src/plugins/runner-ref.ts","../../src/session/hash.ts","../../src/session/issue.ts","../../src/session/revoke.ts","../../src/plugins/registry.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { HoleauthPlugin, PluginContext } from './types.js';\n\n/**\n * Identity helper that preserves the literal `id` on the plugin type so\n * `PluginsApi<Plugins>` can index by it with full type safety.\n *\n * Usage:\n * export const twofa = () => definePlugin({\n * id: 'twofa' as const,\n * api: (ctx) => ({ setup(userId) { ... } }),\n * });\n */\nexport function definePlugin<const P extends HoleauthPlugin<string, unknown>>(p: P): P {\n return p;\n}\n\nexport type { HoleauthPlugin, PluginContext } from './types.js';\n","import type { HoleauthConfig } from '../types/index.js';\nimport type { HoleauthEvent } from './types.js';\n\ntype Handler = (e: HoleauthEvent) => void | Promise<void>;\n\ninterface EventBus {\n byType: Map<string, Set<Handler>>;\n wildcard: Set<Handler>;\n}\n\nconst busByConfig = new WeakMap<HoleauthConfig, EventBus>();\n\nfunction getBus(cfg: HoleauthConfig): EventBus {\n let bus = busByConfig.get(cfg);\n if (!bus) {\n bus = { byType: new Map(), wildcard: new Set() };\n busByConfig.set(cfg, bus);\n }\n return bus;\n}\n\n/** Subscribe to an event type. Use '*' to match all events. Returns an unsubscribe fn. */\nexport function subscribe(cfg: HoleauthConfig, type: string, handler: Handler): () => void {\n const bus = getBus(cfg);\n if (type === '*') {\n bus.wildcard.add(handler);\n return () => bus.wildcard.delete(handler);\n }\n let set = bus.byType.get(type);\n if (!set) {\n set = new Set();\n bus.byType.set(type, set);\n }\n set.add(handler);\n return () => set!.delete(handler);\n}\n\nexport function unsubscribe(cfg: HoleauthConfig, type: string, handler: Handler): void {\n const bus = getBus(cfg);\n if (type === '*') {\n bus.wildcard.delete(handler);\n return;\n }\n bus.byType.get(type)?.delete(handler);\n}\n\n/**\n * emit() persists the event via the mandatory AuditLogAdapter and\n * additionally fans out to all subscribers (typed + wildcard) plus the\n * legacy `cfg.onEvent` hook — all fire-and-forget so business flows are\n * never blocked by observer failures.\n *\n * Callers MUST await emit(): audit persistence is a hard requirement.\n */\nexport async function emit(cfg: HoleauthConfig, event: HoleauthEvent): Promise<void> {\n const withTimestamp: HoleauthEvent = { at: new Date(), ...event };\n await cfg.adapters.auditLog.record(withTimestamp);\n\n const bus = getBus(cfg);\n const typed = bus.byType.get(withTimestamp.type);\n const fire = (h: Handler) => {\n Promise.resolve()\n .then(() => h(withTimestamp))\n .catch(() => { /* observer errors do not propagate */ });\n };\n if (typed) for (const h of typed) fire(h);\n for (const h of bus.wildcard) fire(h);\n\n if (cfg.onEvent) {\n Promise.resolve()\n .then(() => cfg.onEvent?.(withTimestamp))\n .catch(() => { /* observer errors do not propagate */ });\n }\n}\n","import { SignJWT, jwtVerify, decodeJwt, type JWTPayload } from 'jose';\nimport { InvalidTokenError } from '../errors/index.js';\n\nfunction toKey(secret: string | Uint8Array): Uint8Array {\n return typeof secret === 'string' ? new TextEncoder().encode(secret) : secret;\n}\n\nexport interface SignOptions {\n issuer?: string;\n audience?: string;\n subject?: string;\n expiresIn?: string | number; // e.g. '15m' or seconds\n jti?: string;\n}\n\nexport async function sign(\n payload: JWTPayload,\n secret: string | Uint8Array,\n opts: SignOptions = {},\n): Promise<string> {\n const jwt = new SignJWT(payload).setProtectedHeader({ alg: 'HS256' }).setIssuedAt();\n if (opts.issuer) jwt.setIssuer(opts.issuer);\n if (opts.audience) jwt.setAudience(opts.audience);\n if (opts.subject) jwt.setSubject(opts.subject);\n if (opts.jti) jwt.setJti(opts.jti);\n if (opts.expiresIn !== undefined) jwt.setExpirationTime(opts.expiresIn);\n return jwt.sign(toKey(secret));\n}\n\nexport async function verify<T extends JWTPayload = JWTPayload>(\n token: string,\n secret: string | Uint8Array,\n): Promise<T> {\n try {\n const { payload } = await jwtVerify(token, toKey(secret));\n return payload as T;\n } catch (e) {\n throw new InvalidTokenError((e as Error).message);\n }\n}\n\nexport function decode<T extends JWTPayload = JWTPayload>(token: string): T {\n try {\n return decodeJwt(token) as T;\n } catch (e) {\n throw new InvalidTokenError((e as Error).message);\n }\n}\n","/**\n * Double-submit CSRF protection.\n * The cookie holeauth.csrf is readable by JS (httpOnly:false). The client\n * echoes its value in header `x-csrf-token`; the server compares the two.\n * Because cross-origin JS cannot read the cookie, an attacker cannot mint\n * a matching header, defeating the cross-site POST scenario.\n */\n\nconst b64urlChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';\n\nexport function generateCsrfToken(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n let out = '';\n for (const b of bytes) out += b64urlChars[b % 64];\n return out;\n}\n\n/** Constant-time compare. */\nexport function verifyCsrf(cookieValue: string | undefined, headerValue: string | undefined): boolean {\n if (!cookieValue || !headerValue) return false;\n if (cookieValue.length !== headerValue.length) return false;\n let diff = 0;\n for (let i = 0; i < cookieValue.length; i++) {\n diff |= cookieValue.charCodeAt(i) ^ headerValue.charCodeAt(i);\n }\n return diff === 0;\n}\n\nexport const CSRF_HEADER = 'x-csrf-token';\n","/**\n * Per-config hook runner attachment. Stored via WeakMap so the runner is\n * reachable from low-level helpers (session/issue, session/rotate, …)\n * without threading it through every signature.\n *\n * `defineHoleauth()` attaches the runner once at instance creation.\n */\nimport type { HoleauthConfig } from '../types/index.js';\nimport type { HookRunner } from './registry.js';\n\nconst runners = new WeakMap<HoleauthConfig, HookRunner>();\n\nexport function attachHookRunner(cfg: HoleauthConfig, runner: HookRunner): void {\n runners.set(cfg, runner);\n}\n\nconst NOOP: HookRunner = {\n async runRegisterBefore() {},\n async runRegisterAfter() {},\n async runSignInBefore() {},\n async runSignInChallenge() { return null; },\n async runSignInAfter() {},\n async runSignOutAfter() {},\n async runRefreshBefore() {},\n async runRefreshAfter() {},\n async runPasswordChangeBefore() {},\n async runPasswordChangeAfter() {},\n async runPasswordResetBefore() {},\n async runPasswordResetAfter() {},\n async runUserUpdateAfter() {},\n async runUserDeleteAfter() {},\n async runSessionIssue() {},\n async runSessionRotate() {},\n async runSessionRevoke() {},\n};\n\nexport function getHookRunner(cfg: HoleauthConfig): HookRunner {\n return runners.get(cfg) ?? NOOP;\n}\n","/** SHA-256 → base64url. Works on Node 20+ and all Edge runtimes. */\nexport async function sha256b64url(input: string): Promise<string> {\n const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(input));\n const bytes = new Uint8Array(buf);\n let s = '';\n for (const b of bytes) s += String.fromCharCode(b);\n return btoa(s).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n","import type { HoleauthConfig, IssuedTokens } from '../types/index.js';\nimport { sign } from '../jwt/index.js';\nimport { generateCsrfToken } from '../cookies/csrf.js';\nimport { emit } from '../events/emitter.js';\nimport { getHookRunner } from '../plugins/runner-ref.js';\nimport { sha256b64url } from './hash.js';\n\nconst ACCESS_DEFAULT = 900; // 15m\nconst REFRESH_DEFAULT = 2592000; // 30d\n\nexport interface IssueInput {\n userId: string;\n /** Omit to start a fresh family (e.g. on a real login). */\n familyId?: string;\n ip?: string | null;\n userAgent?: string | null;\n}\n\n/**\n * Mint a brand new session row + JWT pair + CSRF token.\n * Used by: fresh login, passkey login, SSO callback, 2FA verify.\n */\nexport async function issueSession(cfg: HoleauthConfig, input: IssueInput): Promise<IssuedTokens> {\n const accessTtl = cfg.tokens?.accessTtl ?? ACCESS_DEFAULT;\n const refreshTtl = cfg.tokens?.refreshTtl ?? REFRESH_DEFAULT;\n const now = Math.floor(Date.now() / 1000);\n\n const familyId = input.familyId ?? crypto.randomUUID();\n const sessionId = crypto.randomUUID();\n const nonce = crypto.randomUUID();\n\n const [accessToken, refreshToken] = await Promise.all([\n sign(\n { sid: sessionId, sub: input.userId, fam: familyId, nce: nonce },\n cfg.secrets.jwtSecret,\n { expiresIn: `${accessTtl}s` },\n ),\n sign(\n { sid: sessionId, sub: input.userId, fam: familyId, typ: 'refresh', nce: nonce },\n cfg.secrets.jwtSecret,\n { expiresIn: `${refreshTtl}s`, jti: nonce },\n ),\n ]);\n const refreshTokenHash = await sha256b64url(refreshToken);\n\n await cfg.adapters.session.createSession({\n id: sessionId,\n userId: input.userId,\n familyId,\n refreshTokenHash,\n expiresAt: new Date((now + refreshTtl) * 1000),\n createdAt: new Date(),\n revokedAt: null,\n ip: input.ip ?? null,\n userAgent: input.userAgent ?? null,\n });\n\n const csrfToken = generateCsrfToken();\n\n await emit(cfg, {\n type: 'session.created',\n userId: input.userId,\n sessionId,\n ip: input.ip ?? null,\n userAgent: input.userAgent ?? null,\n data: { familyId },\n });\n await getHookRunner(cfg).runSessionIssue({ userId: input.userId, sessionId, familyId });\n\n return {\n accessToken,\n refreshToken,\n csrfToken,\n sessionId,\n familyId,\n accessExpiresAt: (now + accessTtl) * 1000,\n refreshExpiresAt: (now + refreshTtl) * 1000,\n };\n}\n","import type { HoleauthConfig } from '../types/index.js';\nimport { verify } from '../jwt/index.js';\nimport { emit } from '../events/emitter.js';\nimport { getHookRunner } from '../plugins/runner-ref.js';\n\n/** Revoke a single session by id (signout). */\nexport async function revokeSession(cfg: HoleauthConfig, sessionId: string, userId?: string): Promise<void> {\n await cfg.adapters.session.deleteSession(sessionId);\n await emit(cfg, {\n type: 'session.revoked',\n userId: userId ?? null,\n sessionId,\n });\n await getHookRunner(cfg).runSessionRevoke({ userId: userId ?? null, sessionId });\n}\n\n/** Revoke by presented refresh token (best-effort). */\nexport async function revokeByRefresh(cfg: HoleauthConfig, refreshToken: string): Promise<void> {\n try {\n const p = await verify<{ sid?: string; sub?: string }>(refreshToken, cfg.secrets.jwtSecret);\n if (p.sid) {\n await cfg.adapters.session.deleteSession(p.sid);\n await emit(cfg, { type: 'session.revoked', userId: p.sub ?? null, sessionId: p.sid });\n await getHookRunner(cfg).runSessionRevoke({ userId: p.sub ?? null, sessionId: p.sid });\n }\n } catch { /* ignore */ }\n}\n\n/** Global signout — all sessions for a user. */\nexport async function revokeAllForUser(cfg: HoleauthConfig, userId: string): Promise<void> {\n if (cfg.adapters.session.revokeUser) {\n await cfg.adapters.session.revokeUser(userId);\n await emit(cfg, { type: 'session.revoked', userId, data: { scope: 'all' } });\n await getHookRunner(cfg).runSessionRevoke({ userId, sessionId: null, scope: 'all' });\n }\n}\n","import type { HoleauthConfig, IssuedTokens, SignInResult } from '../types/index.js';\nimport type { AdapterUser } from '../adapters/index.js';\nimport type { HoleauthEvent } from '../events/types.js';\nimport type {\n HoleauthPlugin,\n PluginContext,\n PluginEvents,\n PluginLogger,\n PluginRoute,\n HoleauthHooks,\n ChallengeResult,\n} from './types.js';\nimport { emit, subscribe, unsubscribe } from '../events/emitter.js';\nimport { issueSession } from '../session/issue.js';\nimport { revokeSession as coreRevokeSession } from '../session/revoke.js';\n\n/**\n * Canonical list of paths handled directly by core in @holeauth/nextjs'\n * dispatcher. Exported so framework bindings and plugin route validation\n * share a single source of truth.\n */\nexport const CORE_ROUTE_PATHS: ReadonlySet<string> = new Set<string>([\n 'GET /session',\n 'GET /csrf',\n 'GET /authorize/:provider',\n 'GET /callback/:provider',\n 'POST /register',\n 'POST /signin',\n 'POST /signout',\n 'POST /refresh',\n 'POST /password/change',\n 'POST /password/reset/request',\n 'POST /password/reset/consume',\n 'GET /invite/info',\n 'GET /invite/list',\n 'POST /invite/create',\n 'POST /invite/consume',\n 'POST /invite/revoke',\n]);\n\nexport interface PluginRegistry {\n /** All registered plugins, topo-sorted by dependsOn. */\n readonly plugins: readonly HoleauthPlugin[];\n /** Map of plugin id → ReturnType<plugin.api>. */\n readonly api: Record<string, unknown>;\n /** All collected routes (in plugin load order). */\n readonly routes: readonly PluginRoute[];\n /** Hook runner helpers, closed over the sorted plugin list. */\n readonly hooks: HookRunner;\n readonly ctx: PluginContext;\n}\n\nexport interface HookRunner {\n runRegisterBefore(input: { email: string; password: string; name?: string | null }): Promise<void>;\n runRegisterAfter(user: AdapterUser): Promise<void>;\n runSignInBefore(input: { email: string; ip?: string; userAgent?: string }): Promise<void>;\n runSignInChallenge(\n user: AdapterUser,\n input: { ip?: string; userAgent?: string },\n ): Promise<ChallengeResult | null>;\n runSignInAfter(data: {\n user: AdapterUser;\n tokens: IssuedTokens;\n method: string;\n }): Promise<void>;\n runSignOutAfter(data: { userId: string | null; sessionId: string | null }): Promise<void>;\n runRefreshBefore(input: { ip?: string; userAgent?: string }): Promise<void>;\n runRefreshAfter(data: { userId: string; sessionId: string; tokens: IssuedTokens }): Promise<void>;\n runPasswordChangeBefore(input: { userId: string; currentPassword: string; newPassword: string }): Promise<void>;\n runPasswordChangeAfter(data: { userId: string }): Promise<void>;\n runPasswordResetBefore(input: { email: string; token?: string; newPassword?: string }): Promise<void>;\n runPasswordResetAfter(data: { userId: string; stage: 'request' | 'consume' }): Promise<void>;\n runUserUpdateAfter(data: { user: AdapterUser; patch: Partial<AdapterUser> }): Promise<void>;\n runUserDeleteAfter(data: { userId: string }): Promise<void>;\n runSessionIssue(data: { userId: string; sessionId: string; familyId: string }): Promise<void>;\n runSessionRotate(data: { userId: string; sessionId: string; familyId: string }): Promise<void>;\n runSessionRevoke(data: { userId: string | null; sessionId: string | null; scope?: 'all' }): Promise<void>;\n}\n\nfunction defaultLogger(cfg: HoleauthConfig): PluginLogger {\n const prefix = '[holeauth]';\n const silent = cfg.logger?.silent === true;\n return {\n debug: silent ? () => {} : (m, d) => console.debug(prefix, m, d ?? ''),\n info: silent ? () => {} : (m, d) => console.info(prefix, m, d ?? ''),\n warn: (m, d) => console.warn(prefix, m, d ?? ''),\n error: (m, e) => console.error(prefix, m, e ?? ''),\n };\n}\n\n/** Kahn's algorithm — throws on cycles / missing deps. */\nfunction topoSort(plugins: readonly HoleauthPlugin[]): HoleauthPlugin[] {\n const byId = new Map<string, HoleauthPlugin>();\n for (const p of plugins) {\n if (byId.has(p.id)) throw new Error(`holeauth: duplicate plugin id \"${p.id}\"`);\n byId.set(p.id, p);\n }\n const inDeg = new Map<string, number>();\n const dependents = new Map<string, string[]>();\n for (const p of plugins) {\n inDeg.set(p.id, 0);\n dependents.set(p.id, []);\n }\n for (const p of plugins) {\n for (const dep of p.dependsOn ?? []) {\n if (!byId.has(dep)) {\n throw new Error(`holeauth: plugin \"${p.id}\" depends on missing plugin \"${dep}\"`);\n }\n inDeg.set(p.id, (inDeg.get(p.id) ?? 0) + 1);\n dependents.get(dep)!.push(p.id);\n }\n }\n const queue: string[] = [];\n for (const [id, d] of inDeg) if (d === 0) queue.push(id);\n const sorted: HoleauthPlugin[] = [];\n while (queue.length) {\n const id = queue.shift()!;\n sorted.push(byId.get(id)!);\n for (const dep of dependents.get(id) ?? []) {\n const next = (inDeg.get(dep) ?? 0) - 1;\n inDeg.set(dep, next);\n if (next === 0) queue.push(dep);\n }\n }\n if (sorted.length !== plugins.length) {\n throw new Error('holeauth: plugin dependsOn graph has a cycle');\n }\n return sorted;\n}\n\nfunction buildPluginEvents(cfg: HoleauthConfig): PluginEvents {\n return {\n on(type, handler) {\n return subscribe(cfg, type, handler);\n },\n off(type, handler) {\n unsubscribe(cfg, type, handler);\n },\n async emit(e) {\n await emit(cfg, e);\n },\n };\n}\n\nfunction routeKey(r: PluginRoute): string {\n return `${r.method} ${r.path}`;\n}\n\nfunction validatePluginRoutes(plugins: readonly HoleauthPlugin[]): PluginRoute[] {\n const seen = new Map<string, string>();\n const out: PluginRoute[] = [];\n for (const p of plugins) {\n for (const r of p.routes ?? []) {\n if (!r.path.startsWith('/')) {\n throw new Error(\n `holeauth: plugin \"${p.id}\" declared route with invalid path \"${r.path}\" (must start with '/')`,\n );\n }\n const key = routeKey(r);\n if (CORE_ROUTE_PATHS.has(key)) {\n throw new Error(\n `holeauth: plugin \"${p.id}\" route ${key} conflicts with a core route`,\n );\n }\n const prev = seen.get(key);\n if (prev) {\n throw new Error(\n `holeauth: plugin \"${p.id}\" route ${key} conflicts with plugin \"${prev}\"`,\n );\n }\n seen.set(key, p.id);\n out.push(r);\n }\n }\n return out;\n}\n\n/* ─────────────────────── Hook runner factory ─────────────────────── */\n\nfunction makeHookRunner(\n plugins: readonly HoleauthPlugin[],\n ctx: PluginContext,\n): HookRunner {\n const logger = ctx.logger;\n\n async function runAfter<T>(\n label: string,\n fns: Array<(data: unknown, ctx: PluginContext) => Promise<void> | void>,\n data: T,\n ): Promise<void> {\n for (const fn of fns) {\n try {\n await fn(data, ctx);\n } catch (err) {\n logger.error(`hook ${label} threw`, err);\n // Fire-and-forget event so observers can surface it.\n void ctx.events\n .emit({\n type: 'plugin.error',\n data: { hook: label, error: String((err as Error)?.message ?? err) },\n })\n .catch(() => {});\n }\n }\n }\n\n async function runBefore<T>(\n fns: Array<(input: unknown, ctx: PluginContext) => Promise<void> | void>,\n input: T,\n ): Promise<void> {\n // `before` hooks may throw to abort — propagate.\n for (const fn of fns) await fn(input, ctx);\n }\n\n // Collect per-hook function lists up-front for fast dispatch.\n const pick = <K extends keyof HoleauthHooks, S extends string>(\n group: K,\n slot: S,\n ): Array<(arg: unknown, ctx: PluginContext) => Promise<void> | void> =>\n plugins\n .map((p) => {\n const g = p.hooks?.[group] as Record<string, unknown> | undefined;\n return g?.[slot] as ((arg: unknown, ctx: PluginContext) => Promise<void> | void) | undefined;\n })\n .filter((x): x is (arg: unknown, ctx: PluginContext) => Promise<void> | void => !!x);\n\n const regBefore = pick('register', 'before');\n const regAfter = pick('register', 'after');\n const siBefore = pick('signIn', 'before');\n const siChallenge = plugins\n .map((p) => ({ id: p.id, fn: p.hooks?.signIn?.challenge }))\n .filter((x): x is { id: string; fn: NonNullable<NonNullable<HoleauthHooks['signIn']>['challenge']> } => !!x.fn);\n const siAfter = pick('signIn', 'after');\n const soAfter = pick('signOut', 'after');\n const rfBefore = pick('refresh', 'before');\n const rfAfter = pick('refresh', 'after');\n const pcBefore = pick('passwordChange', 'before');\n const pcAfter = pick('passwordChange', 'after');\n const prBefore = pick('passwordReset', 'before');\n const prAfter = pick('passwordReset', 'after');\n const uuAfter = pick('userUpdate', 'after');\n const udAfter = pick('userDelete', 'after');\n const seIssue = pick('session', 'onIssue');\n const seRotate = pick('session', 'onRotate');\n const seRevoke = pick('session', 'onRevoke');\n\n return {\n runRegisterBefore: (i) => runBefore(regBefore, i),\n runRegisterAfter: (u) => runAfter('register.after', regAfter, u),\n runSignInBefore: (i) => runBefore(siBefore, i),\n async runSignInChallenge(user, input) {\n let winner: ChallengeResult | null = null;\n for (const { id, fn } of siChallenge) {\n let result: ChallengeResult | null;\n try {\n result = (await fn(user, input, ctx)) ?? null;\n } catch (err) {\n logger.error(`signIn.challenge[${id}] threw`, err);\n continue;\n }\n if (!result) continue;\n if (result.pluginId !== id) {\n logger.warn(\n `signIn.challenge[${id}] returned pluginId=\"${result.pluginId}\"; forcing \"${id}\"`,\n );\n result.pluginId = id;\n }\n if (winner) {\n logger.warn(\n `multiple signIn.challenge winners — using first (\"${winner.pluginId}\"), ignoring \"${result.pluginId}\"`,\n );\n continue;\n }\n winner = result;\n }\n return winner;\n },\n runSignInAfter: (d) => runAfter('signIn.after', siAfter, d),\n runSignOutAfter: (d) => runAfter('signOut.after', soAfter, d),\n runRefreshBefore: (i) => runBefore(rfBefore, i),\n runRefreshAfter: (d) => runAfter('refresh.after', rfAfter, d),\n runPasswordChangeBefore: (i) => runBefore(pcBefore, i),\n runPasswordChangeAfter: (d) => runAfter('passwordChange.after', pcAfter, d),\n runPasswordResetBefore: (i) => runBefore(prBefore, i),\n runPasswordResetAfter: (d) => runAfter('passwordReset.after', prAfter, d),\n runUserUpdateAfter: (d) => runAfter('userUpdate.after', uuAfter, d),\n runUserDeleteAfter: (d) => runAfter('userDelete.after', udAfter, d),\n runSessionIssue: (d) => runAfter('session.onIssue', seIssue, d),\n runSessionRotate: (d) => runAfter('session.onRotate', seRotate, d),\n runSessionRevoke: (d) => runAfter('session.onRevoke', seRevoke, d),\n };\n}\n\n/* ──────────────────────── Core surface factory ──────────────────────── */\n\nfunction makeCoreSurface(\n cfg: HoleauthConfig,\n getHooks: () => HookRunner,\n): PluginContext['core'] {\n return {\n getUserById: (id) => cfg.adapters.user.getUserById(id),\n getUserByEmail: (email) => cfg.adapters.user.getUserByEmail(email),\n async issueSession(input) {\n return issueSession(cfg, {\n userId: input.userId,\n ip: input.ip ?? null,\n userAgent: input.userAgent ?? null,\n });\n },\n async revokeSession(sessionId, userId) {\n return coreRevokeSession(cfg, sessionId, userId);\n },\n async completeSignIn(userId, input) {\n const user = await cfg.adapters.user.getUserById(userId);\n if (!user) throw new Error('completeSignIn: user not found');\n const tokens = await issueSession(cfg, {\n userId: user.id,\n ip: input.ip ?? null,\n userAgent: input.userAgent ?? null,\n });\n await emit(cfg, {\n type: 'user.signed_in',\n userId: user.id,\n sessionId: tokens.sessionId,\n ip: input.ip ?? null,\n userAgent: input.userAgent ?? null,\n data: { method: input.method },\n });\n await getHooks().runSignInAfter({ user, tokens, method: input.method });\n return { user, tokens };\n },\n async issueSignInResult(user, input): Promise<SignInResult> {\n const tokens = await issueSession(cfg, {\n userId: user.id,\n ip: input.ip ?? null,\n userAgent: input.userAgent ?? null,\n });\n return { kind: 'ok', user, tokens };\n },\n };\n}\n\n/* ─────────────────────────── Build registry ─────────────────────────── */\n\n/**\n * Build a PluginRegistry from the given plugin list. Throws on:\n * - duplicate ids\n * - missing `dependsOn` targets\n * - dependency cycles\n * - route collisions with core or other plugins\n */\nexport function buildRegistry(\n cfg: HoleauthConfig,\n rawPlugins: readonly HoleauthPlugin[] = [],\n): PluginRegistry {\n const plugins = topoSort(rawPlugins);\n const routes = validatePluginRoutes(plugins);\n\n const apiMap: Record<string, unknown> = {};\n // Build a late-binding context so plugins can call getPlugin() on each\n // other once all apis are wired. We seed `hooks` with a lazy getter too\n // so the core surface can invoke hooks from within completeSignIn.\n let hookRunner!: HookRunner;\n\n const ctx: PluginContext = {\n config: cfg,\n events: buildPluginEvents(cfg),\n logger: defaultLogger(cfg),\n core: makeCoreSurface(cfg, () => hookRunner),\n getPlugin<T = unknown>(id: string): T {\n const v = apiMap[id];\n if (v === undefined) throw new Error(`holeauth: plugin \"${id}\" not registered`);\n return v as T;\n },\n getPluginAdapter<T = unknown>(id: string): T | undefined {\n return cfg.pluginAdapters?.[id] as T | undefined;\n },\n };\n\n // Wire each plugin's api (hooks run later via runner). Ordered by topo sort.\n for (const p of plugins) {\n apiMap[p.id] = p.api(ctx);\n }\n hookRunner = makeHookRunner(plugins, ctx);\n\n return {\n plugins,\n api: apiMap,\n routes,\n hooks: hookRunner,\n ctx,\n };\n}\n\n/** No-op registry for when no plugins are supplied. */\nexport function emptyRegistry(cfg: HoleauthConfig): PluginRegistry {\n return buildRegistry(cfg, []);\n}\n\nexport async function runOnInit(registry: PluginRegistry): Promise<void> {\n for (const p of registry.plugins) {\n if (p.onInit) await p.onInit(registry.ctx);\n }\n}\n\nexport type { HoleauthEvent };\n"]}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { C as ChallengeResult, k as IssuedTokens, H as HoleauthPlugin, q as PluginRoute, m as PluginContext, a as HoleauthConfig } from './index-BmYQquGs.js';
|
|
2
|
+
import { A as AdapterUser } from './index-CNtnPdzk.js';
|
|
3
|
+
|
|
4
|
+
interface PluginRegistry {
|
|
5
|
+
/** All registered plugins, topo-sorted by dependsOn. */
|
|
6
|
+
readonly plugins: readonly HoleauthPlugin[];
|
|
7
|
+
/** Map of plugin id → ReturnType<plugin.api>. */
|
|
8
|
+
readonly api: Record<string, unknown>;
|
|
9
|
+
/** All collected routes (in plugin load order). */
|
|
10
|
+
readonly routes: readonly PluginRoute[];
|
|
11
|
+
/** Hook runner helpers, closed over the sorted plugin list. */
|
|
12
|
+
readonly hooks: HookRunner;
|
|
13
|
+
readonly ctx: PluginContext;
|
|
14
|
+
}
|
|
15
|
+
interface HookRunner {
|
|
16
|
+
runRegisterBefore(input: {
|
|
17
|
+
email: string;
|
|
18
|
+
password: string;
|
|
19
|
+
name?: string | null;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
runRegisterAfter(user: AdapterUser): Promise<void>;
|
|
22
|
+
runSignInBefore(input: {
|
|
23
|
+
email: string;
|
|
24
|
+
ip?: string;
|
|
25
|
+
userAgent?: string;
|
|
26
|
+
}): Promise<void>;
|
|
27
|
+
runSignInChallenge(user: AdapterUser, input: {
|
|
28
|
+
ip?: string;
|
|
29
|
+
userAgent?: string;
|
|
30
|
+
}): Promise<ChallengeResult | null>;
|
|
31
|
+
runSignInAfter(data: {
|
|
32
|
+
user: AdapterUser;
|
|
33
|
+
tokens: IssuedTokens;
|
|
34
|
+
method: string;
|
|
35
|
+
}): Promise<void>;
|
|
36
|
+
runSignOutAfter(data: {
|
|
37
|
+
userId: string | null;
|
|
38
|
+
sessionId: string | null;
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
runRefreshBefore(input: {
|
|
41
|
+
ip?: string;
|
|
42
|
+
userAgent?: string;
|
|
43
|
+
}): Promise<void>;
|
|
44
|
+
runRefreshAfter(data: {
|
|
45
|
+
userId: string;
|
|
46
|
+
sessionId: string;
|
|
47
|
+
tokens: IssuedTokens;
|
|
48
|
+
}): Promise<void>;
|
|
49
|
+
runPasswordChangeBefore(input: {
|
|
50
|
+
userId: string;
|
|
51
|
+
currentPassword: string;
|
|
52
|
+
newPassword: string;
|
|
53
|
+
}): Promise<void>;
|
|
54
|
+
runPasswordChangeAfter(data: {
|
|
55
|
+
userId: string;
|
|
56
|
+
}): Promise<void>;
|
|
57
|
+
runPasswordResetBefore(input: {
|
|
58
|
+
email: string;
|
|
59
|
+
token?: string;
|
|
60
|
+
newPassword?: string;
|
|
61
|
+
}): Promise<void>;
|
|
62
|
+
runPasswordResetAfter(data: {
|
|
63
|
+
userId: string;
|
|
64
|
+
stage: 'request' | 'consume';
|
|
65
|
+
}): Promise<void>;
|
|
66
|
+
runUserUpdateAfter(data: {
|
|
67
|
+
user: AdapterUser;
|
|
68
|
+
patch: Partial<AdapterUser>;
|
|
69
|
+
}): Promise<void>;
|
|
70
|
+
runUserDeleteAfter(data: {
|
|
71
|
+
userId: string;
|
|
72
|
+
}): Promise<void>;
|
|
73
|
+
runSessionIssue(data: {
|
|
74
|
+
userId: string;
|
|
75
|
+
sessionId: string;
|
|
76
|
+
familyId: string;
|
|
77
|
+
}): Promise<void>;
|
|
78
|
+
runSessionRotate(data: {
|
|
79
|
+
userId: string;
|
|
80
|
+
sessionId: string;
|
|
81
|
+
familyId: string;
|
|
82
|
+
}): Promise<void>;
|
|
83
|
+
runSessionRevoke(data: {
|
|
84
|
+
userId: string | null;
|
|
85
|
+
sessionId: string | null;
|
|
86
|
+
scope?: 'all';
|
|
87
|
+
}): Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Build a PluginRegistry from the given plugin list. Throws on:
|
|
91
|
+
* - duplicate ids
|
|
92
|
+
* - missing `dependsOn` targets
|
|
93
|
+
* - dependency cycles
|
|
94
|
+
* - route collisions with core or other plugins
|
|
95
|
+
*/
|
|
96
|
+
declare function buildRegistry(cfg: HoleauthConfig, rawPlugins?: readonly HoleauthPlugin[]): PluginRegistry;
|
|
97
|
+
/** No-op registry for when no plugins are supplied. */
|
|
98
|
+
declare function emptyRegistry(cfg: HoleauthConfig): PluginRegistry;
|
|
99
|
+
declare function runOnInit(registry: PluginRegistry): Promise<void>;
|
|
100
|
+
|
|
101
|
+
export { type HookRunner as H, type PluginRegistry as P, buildRegistry as b, emptyRegistry as e, runOnInit as r };
|