@fonderie/auth 5.3.0 → 6.0.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/brain/outcomes.md +2 -2
- package/brain/signatures.md +1 -1
- package/dist/index.cjs +22 -24
- 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 +22 -24
- package/dist/index.js.map +1 -1
- package/dist/middlewares/index.d.cts +1 -1
- package/dist/middlewares/index.d.ts +1 -1
- package/dist/{session-jITwnc3m.d.cts → session-Db6KZca3.d.cts} +1 -1
- package/dist/{session-jITwnc3m.d.ts → session-Db6KZca3.d.ts} +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -2,8 +2,8 @@ export { IMfaChallenge, ISession, IUser } from './types.cjs';
|
|
|
2
2
|
import { IFonderieModule, IReadinessProblem, IFonderieApp } from '@fonderie/core';
|
|
3
3
|
import { IStoreAdapter } from '@fonderie/store';
|
|
4
4
|
import { EventBus } from '@fonderie/events';
|
|
5
|
-
import { I as IAuthConfig } from './session-
|
|
6
|
-
export { A as AUTH_CONFIG_KEYS, a as AuthLimitedRoute, b as AuthMessageKey, c as IAuthRateLimitConfig, d as IAuthRuntimeConfig, e as IAuthSecrets, f as IDataExportContributor, M as MESSAGE_KEYS, g as buildAuthAccountLimiter, h as buildAuthIpLimiter, w as withSession } from './session-
|
|
5
|
+
import { I as IAuthConfig } from './session-Db6KZca3.cjs';
|
|
6
|
+
export { A as AUTH_CONFIG_KEYS, a as AuthLimitedRoute, b as AuthMessageKey, c as IAuthRateLimitConfig, d as IAuthRuntimeConfig, e as IAuthSecrets, f as IDataExportContributor, M as MESSAGE_KEYS, g as buildAuthAccountLimiter, h as buildAuthIpLimiter, w as withSession } from './session-Db6KZca3.cjs';
|
|
7
7
|
export { IUserDTO, toUserDTO } from './dtos/user.cjs';
|
|
8
8
|
export { requireAuth, validate } from '@fonderie/core/middlewares';
|
|
9
9
|
import { z } from 'zod';
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export { IMfaChallenge, ISession, IUser } from './types.js';
|
|
|
2
2
|
import { IFonderieModule, IReadinessProblem, IFonderieApp } from '@fonderie/core';
|
|
3
3
|
import { IStoreAdapter } from '@fonderie/store';
|
|
4
4
|
import { EventBus } from '@fonderie/events';
|
|
5
|
-
import { I as IAuthConfig } from './session-
|
|
6
|
-
export { A as AUTH_CONFIG_KEYS, a as AuthLimitedRoute, b as AuthMessageKey, c as IAuthRateLimitConfig, d as IAuthRuntimeConfig, e as IAuthSecrets, f as IDataExportContributor, M as MESSAGE_KEYS, g as buildAuthAccountLimiter, h as buildAuthIpLimiter, w as withSession } from './session-
|
|
5
|
+
import { I as IAuthConfig } from './session-Db6KZca3.js';
|
|
6
|
+
export { A as AUTH_CONFIG_KEYS, a as AuthLimitedRoute, b as AuthMessageKey, c as IAuthRateLimitConfig, d as IAuthRuntimeConfig, e as IAuthSecrets, f as IDataExportContributor, M as MESSAGE_KEYS, g as buildAuthAccountLimiter, h as buildAuthIpLimiter, w as withSession } from './session-Db6KZca3.js';
|
|
7
7
|
export { IUserDTO, toUserDTO } from './dtos/user.js';
|
|
8
8
|
export { requireAuth, validate } from '@fonderie/core/middlewares';
|
|
9
9
|
import { z } from 'zod';
|
package/dist/index.js
CHANGED
|
@@ -92,6 +92,9 @@ var DEFAULTS = {
|
|
|
92
92
|
// so this IP bucket is the guard. (Moving reset to a high-entropy opaque
|
|
93
93
|
// token is tracked as a follow-up hardening.)
|
|
94
94
|
reset: { ip: { capacity: 10, refillPerSec: 10 / min(15) } },
|
|
95
|
+
// verify: 10/15min per IP — /auth/verify checks a 6-digit email/phone OTP,
|
|
96
|
+
// and in the phone flow that OTP is the login credential itself.
|
|
97
|
+
verify: { ip: { capacity: 10, refillPerSec: 10 / min(15) } },
|
|
95
98
|
// mfaVerify: 10/15min per IP — TOTP brute-force.
|
|
96
99
|
mfaVerify: { ip: { capacity: 10, refillPerSec: 10 / min(15) } }
|
|
97
100
|
};
|
|
@@ -1424,23 +1427,17 @@ function authController(store, config, bus) {
|
|
|
1424
1427
|
reqOpts2
|
|
1425
1428
|
).catch(() => {
|
|
1426
1429
|
});
|
|
1427
|
-
const
|
|
1428
|
-
loginMethod: "phone"
|
|
1429
|
-
});
|
|
1430
|
-
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken), sid, requestMeta(ctx));
|
|
1430
|
+
const otpToken = issueMfaPendingToken(user.id, config, "phone");
|
|
1431
1431
|
return Response.json(
|
|
1432
1432
|
{
|
|
1433
1433
|
reason: "USER_PHONE_REGISTERED",
|
|
1434
1434
|
explanation: "Account created. A verification code has been sent to your phone.",
|
|
1435
1435
|
result: {
|
|
1436
|
-
|
|
1436
|
+
otpToken,
|
|
1437
1437
|
user: toUserDTO(user)
|
|
1438
1438
|
}
|
|
1439
1439
|
},
|
|
1440
|
-
{
|
|
1441
|
-
status: 202,
|
|
1442
|
-
headers: cookieHeaders(tokenPairCookies(accessToken, refreshToken, config))
|
|
1443
|
-
}
|
|
1440
|
+
{ status: 202 }
|
|
1444
1441
|
);
|
|
1445
1442
|
}
|
|
1446
1443
|
return setApiResponse3(
|
|
@@ -1554,23 +1551,14 @@ function authController(store, config, bus) {
|
|
|
1554
1551
|
recipient: { email: null, phone: normalizePhone(phone2), deviceToken: null }
|
|
1555
1552
|
}).catch(() => {
|
|
1556
1553
|
});
|
|
1557
|
-
const
|
|
1558
|
-
loginMethod: "phone"
|
|
1559
|
-
});
|
|
1560
|
-
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken), sid, meta);
|
|
1554
|
+
const otpToken = issueMfaPendingToken(user.id, config, "phone");
|
|
1561
1555
|
return Response.json(
|
|
1562
1556
|
{
|
|
1563
1557
|
reason: "USER_PHONE_OTP_SENT",
|
|
1564
1558
|
explanation: "A verification code has been sent to your phone.",
|
|
1565
|
-
result: {
|
|
1566
|
-
tokens: { access: accessToken, refresh: refreshToken },
|
|
1567
|
-
user: toUserDTO(user, false)
|
|
1568
|
-
}
|
|
1559
|
+
result: { otpToken }
|
|
1569
1560
|
},
|
|
1570
|
-
{
|
|
1571
|
-
status: 202,
|
|
1572
|
-
headers: cookieHeaders(tokenPairCookies(accessToken, refreshToken, config))
|
|
1573
|
-
}
|
|
1561
|
+
{ status: 202 }
|
|
1574
1562
|
);
|
|
1575
1563
|
}
|
|
1576
1564
|
return setApiResponse3(
|
|
@@ -1722,6 +1710,9 @@ function authController(store, config, bus) {
|
|
|
1722
1710
|
return setApiResponse3(HTTP3.OK, "PASSWORD_RESET_SUCCESSFUL", "Password reset successfully.");
|
|
1723
1711
|
},
|
|
1724
1712
|
verify: async (ctx) => {
|
|
1713
|
+
if (ctx.user.mfaPending && ctx.user.loginMethod !== "phone") {
|
|
1714
|
+
return setApiResponse3(HTTP3.FORBIDDEN, "MFA_REQUIRED", "Complete MFA verification to continue");
|
|
1715
|
+
}
|
|
1725
1716
|
if (ctx.user.loginMethod !== "phone" && ctx.user.emailVerifiedAt) {
|
|
1726
1717
|
return setApiResponse3(HTTP3.OK, "VERIFIED", "Email verified successfully.", {
|
|
1727
1718
|
verified: true,
|
|
@@ -1808,6 +1799,9 @@ function authController(store, config, bus) {
|
|
|
1808
1799
|
});
|
|
1809
1800
|
},
|
|
1810
1801
|
sendVerification: async (ctx) => {
|
|
1802
|
+
if (ctx.user.mfaPending && ctx.user.loginMethod !== "phone") {
|
|
1803
|
+
return setApiResponse3(HTTP3.FORBIDDEN, "MFA_REQUIRED", "Complete MFA verification to continue");
|
|
1804
|
+
}
|
|
1811
1805
|
const resolved = { ...config, ...config.resolve?.(ctx) };
|
|
1812
1806
|
const cooldown = resolved.verificationCooldown ?? DEFAULT_VERIFICATION_COOLDOWN;
|
|
1813
1807
|
if (ctx.user.loginMethod === "phone") {
|
|
@@ -2374,9 +2368,13 @@ function buildAuthRoutes(store, config, bus) {
|
|
|
2374
2368
|
// Email — Password Recovery (Public)
|
|
2375
2369
|
R("forgotPassword", "POST", "/auth/email/forgot", ipLimit("forgot"), validate(forgotPasswordSchema), acctLimit("forgot"), auth.forgotPassword),
|
|
2376
2370
|
R("resetPassword", "POST", "/auth/email/reset", ipLimit("reset"), validate(resetPasswordSchema), auth.resetPassword),
|
|
2377
|
-
// Verification (
|
|
2378
|
-
|
|
2379
|
-
|
|
2371
|
+
// Verification (email or phone, determined by loginMethod). requireAnyAuth,
|
|
2372
|
+
// not requireAuth: the phone-OTP flow reaches these with a short-lived
|
|
2373
|
+
// pending token (login/register no longer issue real tokens before the OTP
|
|
2374
|
+
// round-trip); the controllers reject pending tokens for any other flow.
|
|
2375
|
+
// ipLimit guards the 6-digit OTP against brute force (like mfaVerify).
|
|
2376
|
+
R("verifyEmail", "POST", "/auth/verify", ipLimit("verify"), requireAnyAuth, validate(verifySchema), auth.verify),
|
|
2377
|
+
R("sendVerification", "GET", "/auth/send-verification", requireAnyAuth, auth.sendVerification),
|
|
2380
2378
|
// Account Management (Protected)
|
|
2381
2379
|
R("logout", "POST", "/auth/logout", requireAuth, validate(refreshSchema), auth.logout),
|
|
2382
2380
|
// User Profile (Protected; writes also gate on requireVerification)
|