@fonderie/auth 7.14.4 → 7.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/brain/signatures.md +1 -0
- package/dist/index.cjs +31 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +36 -13
- package/dist/index.js.map +1 -1
- package/dist/middlewares/index.cjs +15 -4
- package/dist/middlewares/index.cjs.map +1 -1
- package/dist/middlewares/index.d.cts +1 -1
- package/dist/middlewares/index.d.ts +1 -1
- package/dist/middlewares/index.js +16 -5
- package/dist/middlewares/index.js.map +1 -1
- package/dist/{session-BBF5t-3Y.d.cts → session-auNb2L8m.d.cts} +1 -0
- package/dist/{session-BBF5t-3Y.d.ts → session-auNb2L8m.d.ts} +1 -0
- package/env.json +10 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -3,8 +3,8 @@ export { IMfaChallenge, ISession } from './types.cjs';
|
|
|
3
3
|
import { IFonderieModule, IAdminDescription, IReadinessProblem, IFonderieApp, IDefaultTemplate, IAdminRoute } from '@fonderie/core';
|
|
4
4
|
import { IStoreAdapter } from '@fonderie/store';
|
|
5
5
|
import { EventBus } from '@fonderie/events';
|
|
6
|
-
import { I as IAuthConfig, a as IRequestLocation } from './session-
|
|
7
|
-
export { A as AUTH_CONFIG_KEYS, b as AuthLimitedRoute, c as AuthMessageKey, d as IAuthRateLimitConfig, e as IAuthRuntimeConfig, f as IAuthSecrets, g as IDataExportContributor, h as ILocationRequest, L as LocationResolver, M as MESSAGE_KEYS, i as buildAuthAccountLimiter, j as buildAuthIpLimiter, r as resolveLocation, s as sanitizeLocation, w as withSession } from './session-
|
|
6
|
+
import { I as IAuthConfig, a as IRequestLocation } from './session-auNb2L8m.cjs';
|
|
7
|
+
export { A as AUTH_CONFIG_KEYS, b as AuthLimitedRoute, c as AuthMessageKey, d as IAuthRateLimitConfig, e as IAuthRuntimeConfig, f as IAuthSecrets, g as IDataExportContributor, h as ILocationRequest, L as LocationResolver, M as MESSAGE_KEYS, i as buildAuthAccountLimiter, j as buildAuthIpLimiter, r as resolveLocation, s as sanitizeLocation, w as withSession } from './session-auNb2L8m.cjs';
|
|
8
8
|
import { IUserDTO } from './dtos/user.cjs';
|
|
9
9
|
export { toUserDTO } from './dtos/user.cjs';
|
|
10
10
|
export { requireAuth, validate } from '@fonderie/core/middlewares';
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ export { IMfaChallenge, ISession } from './types.js';
|
|
|
3
3
|
import { IFonderieModule, IAdminDescription, IReadinessProblem, IFonderieApp, IDefaultTemplate, IAdminRoute } from '@fonderie/core';
|
|
4
4
|
import { IStoreAdapter } from '@fonderie/store';
|
|
5
5
|
import { EventBus } from '@fonderie/events';
|
|
6
|
-
import { I as IAuthConfig, a as IRequestLocation } from './session-
|
|
7
|
-
export { A as AUTH_CONFIG_KEYS, b as AuthLimitedRoute, c as AuthMessageKey, d as IAuthRateLimitConfig, e as IAuthRuntimeConfig, f as IAuthSecrets, g as IDataExportContributor, h as ILocationRequest, L as LocationResolver, M as MESSAGE_KEYS, i as buildAuthAccountLimiter, j as buildAuthIpLimiter, r as resolveLocation, s as sanitizeLocation, w as withSession } from './session-
|
|
6
|
+
import { I as IAuthConfig, a as IRequestLocation } from './session-auNb2L8m.js';
|
|
7
|
+
export { A as AUTH_CONFIG_KEYS, b as AuthLimitedRoute, c as AuthMessageKey, d as IAuthRateLimitConfig, e as IAuthRuntimeConfig, f as IAuthSecrets, g as IDataExportContributor, h as ILocationRequest, L as LocationResolver, M as MESSAGE_KEYS, i as buildAuthAccountLimiter, j as buildAuthIpLimiter, r as resolveLocation, s as sanitizeLocation, w as withSession } from './session-auNb2L8m.js';
|
|
8
8
|
import { IUserDTO } from './dtos/user.js';
|
|
9
9
|
export { toUserDTO } from './dtos/user.js';
|
|
10
10
|
export { requireAuth, validate } from '@fonderie/core/middlewares';
|
package/dist/index.js
CHANGED
|
@@ -284,8 +284,14 @@ var EVENT_KEYS = {
|
|
|
284
284
|
};
|
|
285
285
|
|
|
286
286
|
// src/services/jwt.ts
|
|
287
|
-
import { randomUUID } from "crypto";
|
|
287
|
+
import { createHash, randomUUID } from "crypto";
|
|
288
288
|
import jwt from "jsonwebtoken";
|
|
289
|
+
function keyIdOf(secret) {
|
|
290
|
+
return createHash("sha256").update(secret).digest("hex").slice(0, 16);
|
|
291
|
+
}
|
|
292
|
+
function keyRing(config) {
|
|
293
|
+
return [config.jwtSecret, ...config.jwtPreviousSecrets ?? []].filter((s) => typeof s === "string" && s.length > 0);
|
|
294
|
+
}
|
|
289
295
|
function issueMfaPendingToken(userId, config, loginMethod) {
|
|
290
296
|
return jwt.sign(
|
|
291
297
|
{
|
|
@@ -296,7 +302,7 @@ function issueMfaPendingToken(userId, config, loginMethod) {
|
|
|
296
302
|
mfaPending: true
|
|
297
303
|
},
|
|
298
304
|
config.jwtSecret,
|
|
299
|
-
{ expiresIn: "5m" }
|
|
305
|
+
{ expiresIn: "5m", keyid: keyIdOf(config.jwtSecret) }
|
|
300
306
|
);
|
|
301
307
|
}
|
|
302
308
|
function issueTokenPair(userId, config, options) {
|
|
@@ -308,12 +314,12 @@ function issueTokenPair(userId, config, options) {
|
|
|
308
314
|
const accessToken = jwt.sign(
|
|
309
315
|
{ sub: userId, type: "access", loginMethod, phoneVerified, sid },
|
|
310
316
|
config.jwtSecret,
|
|
311
|
-
{ expiresIn: accessDuration }
|
|
317
|
+
{ expiresIn: accessDuration, keyid: keyIdOf(config.jwtSecret) }
|
|
312
318
|
);
|
|
313
319
|
const refreshToken = jwt.sign(
|
|
314
320
|
{ sub: userId, type: "refresh", loginMethod, phoneVerified, sid },
|
|
315
321
|
config.jwtSecret,
|
|
316
|
-
{ expiresIn: duration }
|
|
322
|
+
{ expiresIn: duration, keyid: keyIdOf(config.jwtSecret) }
|
|
317
323
|
);
|
|
318
324
|
return { accessToken, refreshToken, sid };
|
|
319
325
|
}
|
|
@@ -322,11 +328,16 @@ function refreshTokenExpiry(token) {
|
|
|
322
328
|
return decoded?.exp ? new Date(decoded.exp * 1e3) : new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3);
|
|
323
329
|
}
|
|
324
330
|
function verifyToken(token, config) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
331
|
+
const ring = keyRing(config);
|
|
332
|
+
const kid = jwt.decode(token, { complete: true })?.header?.kid;
|
|
333
|
+
const candidates = typeof kid === "string" ? ring.filter((s) => keyIdOf(s) === kid) : ring;
|
|
334
|
+
for (const secret of candidates) {
|
|
335
|
+
try {
|
|
336
|
+
return jwt.verify(token, secret);
|
|
337
|
+
} catch {
|
|
338
|
+
}
|
|
329
339
|
}
|
|
340
|
+
return null;
|
|
330
341
|
}
|
|
331
342
|
|
|
332
343
|
// src/services/mfa.ts
|
|
@@ -1421,8 +1432,8 @@ function normalizeEmailSafe(email2) {
|
|
|
1421
1432
|
}
|
|
1422
1433
|
|
|
1423
1434
|
// src/models/password-reset.model.ts
|
|
1424
|
-
import { createHash } from "crypto";
|
|
1425
|
-
var hashSecret = (value) =>
|
|
1435
|
+
import { createHash as createHash2 } from "crypto";
|
|
1436
|
+
var hashSecret = (value) => createHash2("sha256").update(value).digest("hex");
|
|
1426
1437
|
var PasswordResetModel = class {
|
|
1427
1438
|
constructor(store) {
|
|
1428
1439
|
this.store = store;
|
|
@@ -2589,7 +2600,7 @@ function userController(store, config, bus) {
|
|
|
2589
2600
|
}
|
|
2590
2601
|
|
|
2591
2602
|
// src/controllers/oauth.controller.ts
|
|
2592
|
-
import { randomBytes as randomBytes4, createPublicKey, createHash as
|
|
2603
|
+
import { randomBytes as randomBytes4, createPublicKey, createHash as createHash3 } from "crypto";
|
|
2593
2604
|
import jwt2 from "jsonwebtoken";
|
|
2594
2605
|
import { setApiResponse as setApiResponse5, HTTP as HTTP5, constantTimeEqual as constantTimeEqual2, background as background5 } from "@fonderie/core";
|
|
2595
2606
|
import { NOTIFICATION_EVENT as NOTIFICATION_EVENT4 } from "@fonderie/events";
|
|
@@ -3066,7 +3077,7 @@ function oauthController(store, config, bus) {
|
|
|
3066
3077
|
});
|
|
3067
3078
|
return setApiResponse5(HTTP5.UNAUTHORIZED, "APPLE_AUTH_FAILED", "Invalid Apple identity token");
|
|
3068
3079
|
}
|
|
3069
|
-
const tokenHash =
|
|
3080
|
+
const tokenHash = createHash3("sha256").update(identityToken).digest("hex");
|
|
3070
3081
|
const expMs = typeof claims.exp === "number" ? claims.exp * 1e3 : Date.now() + 5 * 60 * 1e3;
|
|
3071
3082
|
const firstUse = await consumedTokens.consumeOnce(tokenHash, new Date(expMs));
|
|
3072
3083
|
if (!firstUse) {
|
|
@@ -3226,6 +3237,18 @@ function collectAuthConfigProblems(config) {
|
|
|
3226
3237
|
reason: "JWT_SECRET_PLACEHOLDER"
|
|
3227
3238
|
});
|
|
3228
3239
|
}
|
|
3240
|
+
(config.jwtPreviousSecrets ?? []).forEach((previous, index) => {
|
|
3241
|
+
if (secretStrengthProblem(previous ?? "")) {
|
|
3242
|
+
problems.push({
|
|
3243
|
+
module: MODULE,
|
|
3244
|
+
severity: "error",
|
|
3245
|
+
message: `jwtPreviousSecrets[${index}] is too short or a placeholder \u2014 it still verifies tokens`,
|
|
3246
|
+
domain: "auth",
|
|
3247
|
+
reason: "JWT_PREVIOUS_SECRET_WEAK",
|
|
3248
|
+
metadata: { index }
|
|
3249
|
+
});
|
|
3250
|
+
}
|
|
3251
|
+
});
|
|
3229
3252
|
if (config.secureCookies === false) {
|
|
3230
3253
|
problems.push({
|
|
3231
3254
|
module: MODULE,
|
|
@@ -3511,7 +3534,7 @@ var AuthModule = class {
|
|
|
3511
3534
|
name = "@fonderie/auth";
|
|
3512
3535
|
// Baked in at build time by tsup.base, so the operator's Modules page can
|
|
3513
3536
|
// say what is actually deployed rather than 'not reported'.
|
|
3514
|
-
version = "7.
|
|
3537
|
+
version = "7.15.0";
|
|
3515
3538
|
// Users, sessions, login history, suspend — only through @fonderie/admin.
|
|
3516
3539
|
describeAdmin() {
|
|
3517
3540
|
return { routes: describeAuthAdminRoutes(this.store) };
|