@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,3 @@
|
|
|
1
|
+
export { G as GetSessionOrRefreshInput, a as GetSessionOrRefreshResult, I as IssueInput, g as getSessionOrRefresh, b as issueSession, r as revokeAllForUser, c as revokeByRefresh, d as revokeSession, e as rotateRefresh, s as sha256b64url, v as validateSession } from '../index-D57PvFMN.js';
|
|
2
|
+
import '../index-BmYQquGs.js';
|
|
3
|
+
import '../index-CNtnPdzk.js';
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { SignJWT, jwtVerify } from 'jose';
|
|
2
|
+
|
|
3
|
+
// src/jwt/index.ts
|
|
4
|
+
|
|
5
|
+
// src/errors/index.ts
|
|
6
|
+
var HoleauthError = class extends Error {
|
|
7
|
+
code;
|
|
8
|
+
status;
|
|
9
|
+
constructor(code, message, status = 400) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "HoleauthError";
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.status = status;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var InvalidTokenError = class extends HoleauthError {
|
|
17
|
+
constructor(message = "Invalid token") {
|
|
18
|
+
super("INVALID_TOKEN", message, 401);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var SessionExpiredError = class extends HoleauthError {
|
|
22
|
+
constructor(message = "Session expired") {
|
|
23
|
+
super("SESSION_EXPIRED", message, 401);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var RefreshReuseError = class extends HoleauthError {
|
|
27
|
+
constructor(message = "Refresh token reuse detected") {
|
|
28
|
+
super("REFRESH_REUSE", message, 401);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// src/jwt/index.ts
|
|
33
|
+
function toKey(secret) {
|
|
34
|
+
return typeof secret === "string" ? new TextEncoder().encode(secret) : secret;
|
|
35
|
+
}
|
|
36
|
+
async function sign(payload, secret, opts = {}) {
|
|
37
|
+
const jwt = new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt();
|
|
38
|
+
if (opts.issuer) jwt.setIssuer(opts.issuer);
|
|
39
|
+
if (opts.audience) jwt.setAudience(opts.audience);
|
|
40
|
+
if (opts.subject) jwt.setSubject(opts.subject);
|
|
41
|
+
if (opts.jti) jwt.setJti(opts.jti);
|
|
42
|
+
if (opts.expiresIn !== void 0) jwt.setExpirationTime(opts.expiresIn);
|
|
43
|
+
return jwt.sign(toKey(secret));
|
|
44
|
+
}
|
|
45
|
+
async function verify(token, secret) {
|
|
46
|
+
try {
|
|
47
|
+
const { payload } = await jwtVerify(token, toKey(secret));
|
|
48
|
+
return payload;
|
|
49
|
+
} catch (e) {
|
|
50
|
+
throw new InvalidTokenError(e.message);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/cookies/csrf.ts
|
|
55
|
+
var b64urlChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
56
|
+
function generateCsrfToken() {
|
|
57
|
+
const bytes = crypto.getRandomValues(new Uint8Array(32));
|
|
58
|
+
let out = "";
|
|
59
|
+
for (const b of bytes) out += b64urlChars[b % 64];
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/events/emitter.ts
|
|
64
|
+
var busByConfig = /* @__PURE__ */ new WeakMap();
|
|
65
|
+
function getBus(cfg) {
|
|
66
|
+
let bus = busByConfig.get(cfg);
|
|
67
|
+
if (!bus) {
|
|
68
|
+
bus = { byType: /* @__PURE__ */ new Map(), wildcard: /* @__PURE__ */ new Set() };
|
|
69
|
+
busByConfig.set(cfg, bus);
|
|
70
|
+
}
|
|
71
|
+
return bus;
|
|
72
|
+
}
|
|
73
|
+
async function emit(cfg, event) {
|
|
74
|
+
const withTimestamp = { at: /* @__PURE__ */ new Date(), ...event };
|
|
75
|
+
await cfg.adapters.auditLog.record(withTimestamp);
|
|
76
|
+
const bus = getBus(cfg);
|
|
77
|
+
const typed = bus.byType.get(withTimestamp.type);
|
|
78
|
+
const fire = (h) => {
|
|
79
|
+
Promise.resolve().then(() => h(withTimestamp)).catch(() => {
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
if (typed) for (const h of typed) fire(h);
|
|
83
|
+
for (const h of bus.wildcard) fire(h);
|
|
84
|
+
if (cfg.onEvent) {
|
|
85
|
+
Promise.resolve().then(() => cfg.onEvent?.(withTimestamp)).catch(() => {
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/plugins/runner-ref.ts
|
|
91
|
+
var runners = /* @__PURE__ */ new WeakMap();
|
|
92
|
+
var NOOP = {
|
|
93
|
+
async runRegisterBefore() {
|
|
94
|
+
},
|
|
95
|
+
async runRegisterAfter() {
|
|
96
|
+
},
|
|
97
|
+
async runSignInBefore() {
|
|
98
|
+
},
|
|
99
|
+
async runSignInChallenge() {
|
|
100
|
+
return null;
|
|
101
|
+
},
|
|
102
|
+
async runSignInAfter() {
|
|
103
|
+
},
|
|
104
|
+
async runSignOutAfter() {
|
|
105
|
+
},
|
|
106
|
+
async runRefreshBefore() {
|
|
107
|
+
},
|
|
108
|
+
async runRefreshAfter() {
|
|
109
|
+
},
|
|
110
|
+
async runPasswordChangeBefore() {
|
|
111
|
+
},
|
|
112
|
+
async runPasswordChangeAfter() {
|
|
113
|
+
},
|
|
114
|
+
async runPasswordResetBefore() {
|
|
115
|
+
},
|
|
116
|
+
async runPasswordResetAfter() {
|
|
117
|
+
},
|
|
118
|
+
async runUserUpdateAfter() {
|
|
119
|
+
},
|
|
120
|
+
async runUserDeleteAfter() {
|
|
121
|
+
},
|
|
122
|
+
async runSessionIssue() {
|
|
123
|
+
},
|
|
124
|
+
async runSessionRotate() {
|
|
125
|
+
},
|
|
126
|
+
async runSessionRevoke() {
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
function getHookRunner(cfg) {
|
|
130
|
+
return runners.get(cfg) ?? NOOP;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/session/hash.ts
|
|
134
|
+
async function sha256b64url(input) {
|
|
135
|
+
const buf = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input));
|
|
136
|
+
const bytes = new Uint8Array(buf);
|
|
137
|
+
let s = "";
|
|
138
|
+
for (const b of bytes) s += String.fromCharCode(b);
|
|
139
|
+
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/session/issue.ts
|
|
143
|
+
var ACCESS_DEFAULT = 900;
|
|
144
|
+
var REFRESH_DEFAULT = 2592e3;
|
|
145
|
+
async function issueSession(cfg, input) {
|
|
146
|
+
const accessTtl = cfg.tokens?.accessTtl ?? ACCESS_DEFAULT;
|
|
147
|
+
const refreshTtl = cfg.tokens?.refreshTtl ?? REFRESH_DEFAULT;
|
|
148
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
149
|
+
const familyId = input.familyId ?? crypto.randomUUID();
|
|
150
|
+
const sessionId = crypto.randomUUID();
|
|
151
|
+
const nonce = crypto.randomUUID();
|
|
152
|
+
const [accessToken, refreshToken] = await Promise.all([
|
|
153
|
+
sign(
|
|
154
|
+
{ sid: sessionId, sub: input.userId, fam: familyId, nce: nonce },
|
|
155
|
+
cfg.secrets.jwtSecret,
|
|
156
|
+
{ expiresIn: `${accessTtl}s` }
|
|
157
|
+
),
|
|
158
|
+
sign(
|
|
159
|
+
{ sid: sessionId, sub: input.userId, fam: familyId, typ: "refresh", nce: nonce },
|
|
160
|
+
cfg.secrets.jwtSecret,
|
|
161
|
+
{ expiresIn: `${refreshTtl}s`, jti: nonce }
|
|
162
|
+
)
|
|
163
|
+
]);
|
|
164
|
+
const refreshTokenHash = await sha256b64url(refreshToken);
|
|
165
|
+
await cfg.adapters.session.createSession({
|
|
166
|
+
id: sessionId,
|
|
167
|
+
userId: input.userId,
|
|
168
|
+
familyId,
|
|
169
|
+
refreshTokenHash,
|
|
170
|
+
expiresAt: new Date((now + refreshTtl) * 1e3),
|
|
171
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
172
|
+
revokedAt: null,
|
|
173
|
+
ip: input.ip ?? null,
|
|
174
|
+
userAgent: input.userAgent ?? null
|
|
175
|
+
});
|
|
176
|
+
const csrfToken = generateCsrfToken();
|
|
177
|
+
await emit(cfg, {
|
|
178
|
+
type: "session.created",
|
|
179
|
+
userId: input.userId,
|
|
180
|
+
sessionId,
|
|
181
|
+
ip: input.ip ?? null,
|
|
182
|
+
userAgent: input.userAgent ?? null,
|
|
183
|
+
data: { familyId }
|
|
184
|
+
});
|
|
185
|
+
await getHookRunner(cfg).runSessionIssue({ userId: input.userId, sessionId, familyId });
|
|
186
|
+
return {
|
|
187
|
+
accessToken,
|
|
188
|
+
refreshToken,
|
|
189
|
+
csrfToken,
|
|
190
|
+
sessionId,
|
|
191
|
+
familyId,
|
|
192
|
+
accessExpiresAt: (now + accessTtl) * 1e3,
|
|
193
|
+
refreshExpiresAt: (now + refreshTtl) * 1e3
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/session/rotate.ts
|
|
198
|
+
var ACCESS_DEFAULT2 = 900;
|
|
199
|
+
var REFRESH_DEFAULT2 = 2592e3;
|
|
200
|
+
async function rotateRefresh(cfg, presentedRefresh, meta = {}) {
|
|
201
|
+
let claims;
|
|
202
|
+
try {
|
|
203
|
+
claims = await verify(presentedRefresh, cfg.secrets.jwtSecret);
|
|
204
|
+
} catch {
|
|
205
|
+
throw new InvalidTokenError("refresh token invalid");
|
|
206
|
+
}
|
|
207
|
+
if (claims.typ !== "refresh" || !claims.sid || !claims.sub || !claims.fam) {
|
|
208
|
+
throw new InvalidTokenError("refresh claims malformed");
|
|
209
|
+
}
|
|
210
|
+
const presentedHash = await sha256b64url(presentedRefresh);
|
|
211
|
+
const found = await cfg.adapters.session.getByRefreshHash(presentedHash);
|
|
212
|
+
if (!found || found.revokedAt) {
|
|
213
|
+
await cfg.adapters.session.revokeFamily(claims.fam);
|
|
214
|
+
await emit(cfg, {
|
|
215
|
+
type: "session.reuse_detected",
|
|
216
|
+
userId: claims.sub,
|
|
217
|
+
sessionId: claims.sid,
|
|
218
|
+
ip: meta.ip ?? null,
|
|
219
|
+
userAgent: meta.userAgent ?? null,
|
|
220
|
+
data: { familyId: claims.fam }
|
|
221
|
+
});
|
|
222
|
+
throw new RefreshReuseError();
|
|
223
|
+
}
|
|
224
|
+
if (found.expiresAt.getTime() < Date.now()) {
|
|
225
|
+
await cfg.adapters.session.revokeFamily(claims.fam);
|
|
226
|
+
throw new SessionExpiredError();
|
|
227
|
+
}
|
|
228
|
+
const accessTtl = cfg.tokens?.accessTtl ?? ACCESS_DEFAULT2;
|
|
229
|
+
const refreshTtl = cfg.tokens?.refreshTtl ?? REFRESH_DEFAULT2;
|
|
230
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
231
|
+
const nonce = crypto.randomUUID();
|
|
232
|
+
const [accessToken, refreshToken] = await Promise.all([
|
|
233
|
+
sign(
|
|
234
|
+
{ sid: found.id, sub: found.userId, fam: found.familyId, nce: nonce },
|
|
235
|
+
cfg.secrets.jwtSecret,
|
|
236
|
+
{ expiresIn: `${accessTtl}s` }
|
|
237
|
+
),
|
|
238
|
+
sign(
|
|
239
|
+
{ sid: found.id, sub: found.userId, fam: found.familyId, typ: "refresh", nce: nonce },
|
|
240
|
+
cfg.secrets.jwtSecret,
|
|
241
|
+
{ expiresIn: `${refreshTtl}s`, jti: nonce }
|
|
242
|
+
)
|
|
243
|
+
]);
|
|
244
|
+
const newHash = await sha256b64url(refreshToken);
|
|
245
|
+
await cfg.adapters.session.rotateRefresh(
|
|
246
|
+
found.id,
|
|
247
|
+
newHash,
|
|
248
|
+
new Date((now + refreshTtl) * 1e3)
|
|
249
|
+
);
|
|
250
|
+
await emit(cfg, {
|
|
251
|
+
type: "session.rotated",
|
|
252
|
+
userId: found.userId,
|
|
253
|
+
sessionId: found.id,
|
|
254
|
+
ip: meta.ip ?? null,
|
|
255
|
+
userAgent: meta.userAgent ?? null,
|
|
256
|
+
data: { familyId: found.familyId }
|
|
257
|
+
});
|
|
258
|
+
await getHookRunner(cfg).runSessionRotate({
|
|
259
|
+
userId: found.userId,
|
|
260
|
+
sessionId: found.id,
|
|
261
|
+
familyId: found.familyId
|
|
262
|
+
});
|
|
263
|
+
return {
|
|
264
|
+
accessToken,
|
|
265
|
+
refreshToken,
|
|
266
|
+
csrfToken: generateCsrfToken(),
|
|
267
|
+
sessionId: found.id,
|
|
268
|
+
familyId: found.familyId,
|
|
269
|
+
accessExpiresAt: (now + accessTtl) * 1e3,
|
|
270
|
+
refreshExpiresAt: (now + refreshTtl) * 1e3
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// src/session/validate.ts
|
|
275
|
+
async function validateSession(cfg, token) {
|
|
276
|
+
try {
|
|
277
|
+
const p = await verify(
|
|
278
|
+
token,
|
|
279
|
+
cfg.secrets.jwtSecret
|
|
280
|
+
);
|
|
281
|
+
if (!p.sid || !p.sub) return null;
|
|
282
|
+
return {
|
|
283
|
+
sessionId: p.sid,
|
|
284
|
+
userId: p.sub,
|
|
285
|
+
expiresAt: (p.exp ?? 0) * 1e3,
|
|
286
|
+
familyId: p.fam
|
|
287
|
+
};
|
|
288
|
+
} catch {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/session/revoke.ts
|
|
294
|
+
async function revokeSession(cfg, sessionId, userId) {
|
|
295
|
+
await cfg.adapters.session.deleteSession(sessionId);
|
|
296
|
+
await emit(cfg, {
|
|
297
|
+
type: "session.revoked",
|
|
298
|
+
userId: userId ?? null,
|
|
299
|
+
sessionId
|
|
300
|
+
});
|
|
301
|
+
await getHookRunner(cfg).runSessionRevoke({ userId: userId ?? null, sessionId });
|
|
302
|
+
}
|
|
303
|
+
async function revokeByRefresh(cfg, refreshToken) {
|
|
304
|
+
try {
|
|
305
|
+
const p = await verify(refreshToken, cfg.secrets.jwtSecret);
|
|
306
|
+
if (p.sid) {
|
|
307
|
+
await cfg.adapters.session.deleteSession(p.sid);
|
|
308
|
+
await emit(cfg, { type: "session.revoked", userId: p.sub ?? null, sessionId: p.sid });
|
|
309
|
+
await getHookRunner(cfg).runSessionRevoke({ userId: p.sub ?? null, sessionId: p.sid });
|
|
310
|
+
}
|
|
311
|
+
} catch {
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async function revokeAllForUser(cfg, userId) {
|
|
315
|
+
if (cfg.adapters.session.revokeUser) {
|
|
316
|
+
await cfg.adapters.session.revokeUser(userId);
|
|
317
|
+
await emit(cfg, { type: "session.revoked", userId, data: { scope: "all" } });
|
|
318
|
+
await getHookRunner(cfg).runSessionRevoke({ userId, sessionId: null, scope: "all" });
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/session/get-or-refresh.ts
|
|
323
|
+
async function getSessionOrRefresh(instance, input) {
|
|
324
|
+
const { accessToken, refreshToken, ip, userAgent } = input;
|
|
325
|
+
if (accessToken) {
|
|
326
|
+
const session2 = await validateSession(instance.config, accessToken);
|
|
327
|
+
if (session2 && session2.expiresAt > Date.now()) {
|
|
328
|
+
return { session: session2, tokens: null, refreshed: false };
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (!refreshToken) {
|
|
332
|
+
return { session: null, tokens: null, refreshed: false };
|
|
333
|
+
}
|
|
334
|
+
let tokens;
|
|
335
|
+
try {
|
|
336
|
+
tokens = await instance.refresh({ refreshToken, ip, userAgent });
|
|
337
|
+
} catch {
|
|
338
|
+
return { session: null, tokens: null, refreshed: false };
|
|
339
|
+
}
|
|
340
|
+
const session = await validateSession(instance.config, tokens.accessToken);
|
|
341
|
+
return { session, tokens, refreshed: true };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export { getSessionOrRefresh, issueSession, revokeAllForUser, revokeByRefresh, revokeSession, rotateRefresh, sha256b64url, validateSession };
|
|
345
|
+
//# sourceMappingURL=index.js.map
|
|
346
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/errors/index.ts","../../src/jwt/index.ts","../../src/cookies/csrf.ts","../../src/events/emitter.ts","../../src/plugins/runner-ref.ts","../../src/session/hash.ts","../../src/session/issue.ts","../../src/session/rotate.ts","../../src/session/validate.ts","../../src/session/revoke.ts","../../src/session/get-or-refresh.ts"],"names":["ACCESS_DEFAULT","REFRESH_DEFAULT","session"],"mappings":"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","file":"index.js","sourcesContent":["export class HoleauthError extends Error {\n readonly code: string;\n readonly status: number;\n constructor(code: string, message: string, status = 400) {\n super(message);\n this.name = 'HoleauthError';\n this.code = code;\n this.status = status;\n }\n}\nexport class InvalidTokenError extends HoleauthError {\n constructor(message = 'Invalid token') { super('INVALID_TOKEN', message, 401); }\n}\nexport class SessionExpiredError extends HoleauthError {\n constructor(message = 'Session expired') { super('SESSION_EXPIRED', message, 401); }\n}\nexport class AdapterError extends HoleauthError {\n constructor(message = 'Adapter error') { super('ADAPTER_ERROR', message, 500); }\n}\nexport class ProviderError extends HoleauthError {\n constructor(message = 'Provider error') { super('PROVIDER_ERROR', message, 502); }\n}\nexport class CsrfError extends HoleauthError {\n constructor(message = 'CSRF validation failed') { super('CSRF_FAILED', message, 403); }\n}\nexport class CredentialsError extends HoleauthError {\n constructor(message = 'Invalid credentials') { super('INVALID_CREDENTIALS', message, 401); }\n}\nexport class AccountConflictError extends HoleauthError {\n constructor(message = 'Account conflict') { super('ACCOUNT_CONFLICT', message, 409); }\n}\nexport class RefreshReuseError extends HoleauthError {\n constructor(message = 'Refresh token reuse detected') { super('REFRESH_REUSE', message, 401); }\n}\nexport class PendingChallengeError extends HoleauthError {\n constructor(message = 'Pending challenge required') { super('PENDING_CHALLENGE', message, 401); }\n}\nexport class RegistrationDisabledError extends HoleauthError {\n constructor(message = 'Self-registration is disabled') { super('REGISTRATION_DISABLED', message, 403); }\n}\nexport class NotSupportedError extends HoleauthError {\n constructor(message = 'Operation not supported by adapter') { super('NOT_SUPPORTED', message, 501); }\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","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","/**\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, IssuedTokens } from '../types/index.js';\nimport { sign, verify } 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';\nimport { InvalidTokenError, RefreshReuseError, SessionExpiredError } from '../errors/index.js';\n\nconst ACCESS_DEFAULT = 900;\nconst REFRESH_DEFAULT = 2592000;\n\n/**\n * Rotate-on-use with reuse detection.\n *\n * 1. Decode refresh JWT → recover sid, fam, sub.\n * 2. Hash presented token; look it up.\n * - If not found, the token was already rotated away → reuse! Revoke family.\n * 3. Issue new access + refresh, rotate hash in storage atomically.\n *\n * Returns a fresh IssuedTokens tuple. Session id + family stay stable.\n */\nexport async function rotateRefresh(\n cfg: HoleauthConfig,\n presentedRefresh: string,\n meta: { ip?: string | null; userAgent?: string | null } = {},\n): Promise<IssuedTokens> {\n let claims: { sid?: string; sub?: string; fam?: string; typ?: string; exp?: number };\n try {\n claims = await verify(presentedRefresh, cfg.secrets.jwtSecret);\n } catch {\n throw new InvalidTokenError('refresh token invalid');\n }\n if (claims.typ !== 'refresh' || !claims.sid || !claims.sub || !claims.fam) {\n throw new InvalidTokenError('refresh claims malformed');\n }\n\n const presentedHash = await sha256b64url(presentedRefresh);\n const found = await cfg.adapters.session.getByRefreshHash(presentedHash);\n\n if (!found || found.revokedAt) {\n // Reuse detected — revoke whole family, record an event, throw.\n await cfg.adapters.session.revokeFamily(claims.fam);\n await emit(cfg, {\n type: 'session.reuse_detected',\n userId: claims.sub,\n sessionId: claims.sid,\n ip: meta.ip ?? null,\n userAgent: meta.userAgent ?? null,\n data: { familyId: claims.fam },\n });\n throw new RefreshReuseError();\n }\n\n if (found.expiresAt.getTime() < Date.now()) {\n await cfg.adapters.session.revokeFamily(claims.fam);\n throw new SessionExpiredError();\n }\n\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 const nonce = crypto.randomUUID();\n\n const [accessToken, refreshToken] = await Promise.all([\n sign(\n { sid: found.id, sub: found.userId, fam: found.familyId, nce: nonce },\n cfg.secrets.jwtSecret,\n { expiresIn: `${accessTtl}s` },\n ),\n sign(\n { sid: found.id, sub: found.userId, fam: found.familyId, typ: 'refresh', nce: nonce },\n cfg.secrets.jwtSecret,\n { expiresIn: `${refreshTtl}s`, jti: nonce },\n ),\n ]);\n const newHash = await sha256b64url(refreshToken);\n await cfg.adapters.session.rotateRefresh(\n found.id,\n newHash,\n new Date((now + refreshTtl) * 1000),\n );\n\n await emit(cfg, {\n type: 'session.rotated',\n userId: found.userId,\n sessionId: found.id,\n ip: meta.ip ?? null,\n userAgent: meta.userAgent ?? null,\n data: { familyId: found.familyId },\n });\n await getHookRunner(cfg).runSessionRotate({\n userId: found.userId,\n sessionId: found.id,\n familyId: found.familyId,\n });\n\n return {\n accessToken,\n refreshToken,\n csrfToken: generateCsrfToken(),\n sessionId: found.id,\n familyId: found.familyId,\n accessExpiresAt: (now + accessTtl) * 1000,\n refreshExpiresAt: (now + refreshTtl) * 1000,\n };\n}\n","import type { HoleauthConfig, SessionData } from '../types/index.js';\nimport { verify } from '../jwt/index.js';\n\n/**\n * Edge-compatible: verifies the access JWT only. Does not touch adapters.\n * Use this in middleware / hot paths.\n */\nexport async function validateSession(cfg: HoleauthConfig, token: string): Promise<SessionData | null> {\n try {\n const p = await verify<{ sid: string; sub: string; fam?: string; exp?: number }>(\n token,\n cfg.secrets.jwtSecret,\n );\n if (!p.sid || !p.sub) return null;\n return {\n sessionId: p.sid,\n userId: p.sub,\n expiresAt: (p.exp ?? 0) * 1000,\n familyId: p.fam,\n };\n } catch {\n return null;\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 {\n HoleauthInstance,\n IssuedTokens,\n SessionData,\n} from '../types/index.js';\nimport { validateSession } from './validate.js';\n\nexport interface GetSessionOrRefreshInput {\n /** Current access token (if any). */\n accessToken?: string | null;\n /** Current refresh token (if any). When present, used to rotate on access miss. */\n refreshToken?: string | null;\n /** Request metadata, forwarded to refresh hooks/audit log. */\n ip?: string;\n userAgent?: string;\n}\n\nexport interface GetSessionOrRefreshResult {\n /** Resolved session, or null if both validation and refresh failed. */\n session: SessionData | null;\n /** Freshly-issued token bundle when a refresh actually occurred. */\n tokens: IssuedTokens | null;\n /** True if this call rotated the refresh token. */\n refreshed: boolean;\n}\n\n/**\n * Validate the access token and, if invalid/missing, transparently rotate the\n * refresh token to obtain a new session. Framework-agnostic — used by the\n * Next.js middleware/server helpers and intended for consumption from API\n * server middleware (tRPC, Hono, plain route handlers, …).\n *\n * Cookies are NOT touched here; the caller decides how to surface the new\n * token bundle (Set-Cookie headers, in-memory store, etc.).\n *\n * Returns:\n * - `session` the resolved session (or `null`),\n * - `tokens` the newly-issued token bundle when a refresh occurred,\n * - `refreshed` whether rotation happened.\n *\n * @example\n * ```ts\n * const { session, tokens } = await getSessionOrRefresh(auth, {\n * accessToken: req.cookies.get('holeauth.at')?.value,\n * refreshToken: req.cookies.get('holeauth.rt')?.value,\n * ip, userAgent,\n * });\n * if (tokens) writeAuthCookies(auth.config, res.headers, tokens);\n * ```\n */\nexport async function getSessionOrRefresh(\n instance: HoleauthInstance,\n input: GetSessionOrRefreshInput,\n): Promise<GetSessionOrRefreshResult> {\n const { accessToken, refreshToken, ip, userAgent } = input;\n\n // 1. Fast path: valid access token.\n if (accessToken) {\n const session = await validateSession(instance.config, accessToken);\n if (session && session.expiresAt > Date.now()) {\n return { session, tokens: null, refreshed: false };\n }\n }\n\n // 2. Refresh fallback.\n if (!refreshToken) {\n return { session: null, tokens: null, refreshed: false };\n }\n\n let tokens: IssuedTokens;\n try {\n tokens = await instance.refresh({ refreshToken, ip, userAgent });\n } catch {\n // Reuse, expiry, malformed — caller should clear cookies.\n return { session: null, tokens: null, refreshed: false };\n }\n\n const session = await validateSession(instance.config, tokens.accessToken);\n return { session, tokens, refreshed: true };\n}\n"]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { A as AuthorizeParams, G as GithubProvider, a as GoogleProvider, T as TokenExchangeInput, b as authorize, c as base64url, d as buildAuthorizeUrl, e as callback, f as exchangeCode, g as fetchUserInfo, h as findProvider, j as generateNonce, k as generatePkcePair, l as generateState } from '../index-CHS-socJ.js';
|
|
2
|
+
import '../index-BmYQquGs.js';
|
|
3
|
+
import '../index-CNtnPdzk.js';
|