@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/brain/outcomes.md
CHANGED
|
@@ -141,11 +141,11 @@ Raw SQL ships in `node_modules/@fonderie/auth/dist/migrations/sql/` — read it
|
|
|
141
141
|
| POST | `/auth/mfa/verify` | `ipLimit('mfaVerify') → requireAnyAuth → requireEmailLogin → requireVerified → validate(mfaTokenSchema) → mfa.verify` |
|
|
142
142
|
| POST | `/auth/refresh` | `validate(refreshSchema) → auth.refresh` |
|
|
143
143
|
| POST | `/auth/register` | `ipLimit('register') → validate(registerSchema) → auth.register` |
|
|
144
|
-
| GET | `/auth/send-verification` | `
|
|
144
|
+
| GET | `/auth/send-verification` | `requireAnyAuth → auth.sendVerification` |
|
|
145
145
|
| GET | `/auth/sessions` | `requireAuth → user.listSessions` |
|
|
146
146
|
| DELETE | `/auth/sessions/:id` | `requireAuth → user.terminateSession` |
|
|
147
147
|
| DELETE | `/auth/sessions/others` | `requireAuth → user.terminateOtherSessions` |
|
|
148
|
-
| POST | `/auth/verify` | `
|
|
148
|
+
| POST | `/auth/verify` | `ipLimit('verify') → requireAnyAuth → validate(verifySchema) → auth.verify` |
|
|
149
149
|
| DELETE | `/users` | `requireAuth → verifyGate → user.deleteMe` |
|
|
150
150
|
| GET | `/users` | `requireAuth → user.me` |
|
|
151
151
|
| PUT | `/users/email` | `requireAuth → verifyGate → validate(updateEmailSchema) → user.updateEmail` |
|
package/brain/signatures.md
CHANGED
|
@@ -203,5 +203,5 @@ interface IAuthRateLimitConfig {
|
|
|
203
203
|
rules?: Partial<Record<AuthLimitedRoute, IRateLimitRule | false>>;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
type AuthLimitedRoute = 'login' | 'register' | 'forgot' | 'reset' | 'mfaVerify';
|
|
206
|
+
type AuthLimitedRoute = 'login' | 'register' | 'forgot' | 'reset' | 'verify' | 'mfaVerify';
|
|
207
207
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -132,6 +132,9 @@ var DEFAULTS = {
|
|
|
132
132
|
// so this IP bucket is the guard. (Moving reset to a high-entropy opaque
|
|
133
133
|
// token is tracked as a follow-up hardening.)
|
|
134
134
|
reset: { ip: { capacity: 10, refillPerSec: 10 / min(15) } },
|
|
135
|
+
// verify: 10/15min per IP — /auth/verify checks a 6-digit email/phone OTP,
|
|
136
|
+
// and in the phone flow that OTP is the login credential itself.
|
|
137
|
+
verify: { ip: { capacity: 10, refillPerSec: 10 / min(15) } },
|
|
135
138
|
// mfaVerify: 10/15min per IP — TOTP brute-force.
|
|
136
139
|
mfaVerify: { ip: { capacity: 10, refillPerSec: 10 / min(15) } }
|
|
137
140
|
};
|
|
@@ -1464,23 +1467,17 @@ function authController(store, config, bus) {
|
|
|
1464
1467
|
reqOpts2
|
|
1465
1468
|
).catch(() => {
|
|
1466
1469
|
});
|
|
1467
|
-
const
|
|
1468
|
-
loginMethod: "phone"
|
|
1469
|
-
});
|
|
1470
|
-
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken), sid, requestMeta(ctx));
|
|
1470
|
+
const otpToken = issueMfaPendingToken(user.id, config, "phone");
|
|
1471
1471
|
return Response.json(
|
|
1472
1472
|
{
|
|
1473
1473
|
reason: "USER_PHONE_REGISTERED",
|
|
1474
1474
|
explanation: "Account created. A verification code has been sent to your phone.",
|
|
1475
1475
|
result: {
|
|
1476
|
-
|
|
1476
|
+
otpToken,
|
|
1477
1477
|
user: toUserDTO(user)
|
|
1478
1478
|
}
|
|
1479
1479
|
},
|
|
1480
|
-
{
|
|
1481
|
-
status: 202,
|
|
1482
|
-
headers: cookieHeaders(tokenPairCookies(accessToken, refreshToken, config))
|
|
1483
|
-
}
|
|
1480
|
+
{ status: 202 }
|
|
1484
1481
|
);
|
|
1485
1482
|
}
|
|
1486
1483
|
return (0, import_core5.setApiResponse)(
|
|
@@ -1594,23 +1591,14 @@ function authController(store, config, bus) {
|
|
|
1594
1591
|
recipient: { email: null, phone: normalizePhone(phone2), deviceToken: null }
|
|
1595
1592
|
}).catch(() => {
|
|
1596
1593
|
});
|
|
1597
|
-
const
|
|
1598
|
-
loginMethod: "phone"
|
|
1599
|
-
});
|
|
1600
|
-
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken), sid, meta);
|
|
1594
|
+
const otpToken = issueMfaPendingToken(user.id, config, "phone");
|
|
1601
1595
|
return Response.json(
|
|
1602
1596
|
{
|
|
1603
1597
|
reason: "USER_PHONE_OTP_SENT",
|
|
1604
1598
|
explanation: "A verification code has been sent to your phone.",
|
|
1605
|
-
result: {
|
|
1606
|
-
tokens: { access: accessToken, refresh: refreshToken },
|
|
1607
|
-
user: toUserDTO(user, false)
|
|
1608
|
-
}
|
|
1599
|
+
result: { otpToken }
|
|
1609
1600
|
},
|
|
1610
|
-
{
|
|
1611
|
-
status: 202,
|
|
1612
|
-
headers: cookieHeaders(tokenPairCookies(accessToken, refreshToken, config))
|
|
1613
|
-
}
|
|
1601
|
+
{ status: 202 }
|
|
1614
1602
|
);
|
|
1615
1603
|
}
|
|
1616
1604
|
return (0, import_core5.setApiResponse)(
|
|
@@ -1762,6 +1750,9 @@ function authController(store, config, bus) {
|
|
|
1762
1750
|
return (0, import_core5.setApiResponse)(import_core5.HTTP.OK, "PASSWORD_RESET_SUCCESSFUL", "Password reset successfully.");
|
|
1763
1751
|
},
|
|
1764
1752
|
verify: async (ctx) => {
|
|
1753
|
+
if (ctx.user.mfaPending && ctx.user.loginMethod !== "phone") {
|
|
1754
|
+
return (0, import_core5.setApiResponse)(import_core5.HTTP.FORBIDDEN, "MFA_REQUIRED", "Complete MFA verification to continue");
|
|
1755
|
+
}
|
|
1765
1756
|
if (ctx.user.loginMethod !== "phone" && ctx.user.emailVerifiedAt) {
|
|
1766
1757
|
return (0, import_core5.setApiResponse)(import_core5.HTTP.OK, "VERIFIED", "Email verified successfully.", {
|
|
1767
1758
|
verified: true,
|
|
@@ -1848,6 +1839,9 @@ function authController(store, config, bus) {
|
|
|
1848
1839
|
});
|
|
1849
1840
|
},
|
|
1850
1841
|
sendVerification: async (ctx) => {
|
|
1842
|
+
if (ctx.user.mfaPending && ctx.user.loginMethod !== "phone") {
|
|
1843
|
+
return (0, import_core5.setApiResponse)(import_core5.HTTP.FORBIDDEN, "MFA_REQUIRED", "Complete MFA verification to continue");
|
|
1844
|
+
}
|
|
1851
1845
|
const resolved = { ...config, ...config.resolve?.(ctx) };
|
|
1852
1846
|
const cooldown = resolved.verificationCooldown ?? DEFAULT_VERIFICATION_COOLDOWN;
|
|
1853
1847
|
if (ctx.user.loginMethod === "phone") {
|
|
@@ -2414,9 +2408,13 @@ function buildAuthRoutes(store, config, bus) {
|
|
|
2414
2408
|
// Email — Password Recovery (Public)
|
|
2415
2409
|
R("forgotPassword", "POST", "/auth/email/forgot", ipLimit("forgot"), (0, import_middlewares.validate)(forgotPasswordSchema), acctLimit("forgot"), auth.forgotPassword),
|
|
2416
2410
|
R("resetPassword", "POST", "/auth/email/reset", ipLimit("reset"), (0, import_middlewares.validate)(resetPasswordSchema), auth.resetPassword),
|
|
2417
|
-
// Verification (
|
|
2418
|
-
|
|
2419
|
-
|
|
2411
|
+
// Verification (email or phone, determined by loginMethod). requireAnyAuth,
|
|
2412
|
+
// not requireAuth: the phone-OTP flow reaches these with a short-lived
|
|
2413
|
+
// pending token (login/register no longer issue real tokens before the OTP
|
|
2414
|
+
// round-trip); the controllers reject pending tokens for any other flow.
|
|
2415
|
+
// ipLimit guards the 6-digit OTP against brute force (like mfaVerify).
|
|
2416
|
+
R("verifyEmail", "POST", "/auth/verify", ipLimit("verify"), import_middlewares2.requireAnyAuth, (0, import_middlewares.validate)(verifySchema), auth.verify),
|
|
2417
|
+
R("sendVerification", "GET", "/auth/send-verification", import_middlewares2.requireAnyAuth, auth.sendVerification),
|
|
2420
2418
|
// Account Management (Protected)
|
|
2421
2419
|
R("logout", "POST", "/auth/logout", import_middlewares2.requireAuth, (0, import_middlewares.validate)(refreshSchema), auth.logout),
|
|
2422
2420
|
// User Profile (Protected; writes also gate on requireVerification)
|