@fonderie/auth 1.1.1 → 1.2.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/dist/index.d.cts CHANGED
@@ -2,11 +2,12 @@ export { IMfaChallenge, ISession, IUser } from './types.cjs';
2
2
  import { IFonderieModule, 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-BGjjwz5_.cjs';
6
- export { A as AUTH_CONFIG_KEYS, a as AuthMessageKey, b as IAuthRuntimeConfig, c as IAuthSecrets, M as MESSAGE_KEYS, w as withSession } from './session-BGjjwz5_.cjs';
5
+ import { I as IAuthConfig } from './session-C54ksYE8.cjs';
6
+ export { A as AUTH_CONFIG_KEYS, a as AuthLimitedRoute, b as AuthMessageKey, c as IAuthRateLimitConfig, d as IAuthRuntimeConfig, e as IAuthSecrets, M as MESSAGE_KEYS, f as buildAuthAccountLimiter, g as buildAuthIpLimiter, w as withSession } from './session-C54ksYE8.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';
10
+ import '@fonderie/rate-limit';
10
11
 
11
12
  declare class AuthModule implements IFonderieModule {
12
13
  private store;
package/dist/index.d.ts CHANGED
@@ -2,11 +2,12 @@ export { IMfaChallenge, ISession, IUser } from './types.js';
2
2
  import { IFonderieModule, 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-BGjjwz5_.js';
6
- export { A as AUTH_CONFIG_KEYS, a as AuthMessageKey, b as IAuthRuntimeConfig, c as IAuthSecrets, M as MESSAGE_KEYS, w as withSession } from './session-BGjjwz5_.js';
5
+ import { I as IAuthConfig } from './session-C54ksYE8.js';
6
+ export { A as AUTH_CONFIG_KEYS, a as AuthLimitedRoute, b as AuthMessageKey, c as IAuthRateLimitConfig, d as IAuthRuntimeConfig, e as IAuthSecrets, M as MESSAGE_KEYS, f as buildAuthAccountLimiter, g as buildAuthIpLimiter, w as withSession } from './session-C54ksYE8.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';
10
+ import '@fonderie/rate-limit';
10
11
 
11
12
  declare class AuthModule implements IFonderieModule {
12
13
  private store;
package/dist/index.js CHANGED
@@ -48,6 +48,60 @@ var requireEmailLogin = async (ctx, next) => {
48
48
  // src/middlewares/validate.ts
49
49
  import { validate } from "@fonderie/core/middlewares";
50
50
 
51
+ // src/services/rate-limit.ts
52
+ import {
53
+ byBodyField,
54
+ byIp,
55
+ rateLimit,
56
+ StoreAdapterStore
57
+ } from "@fonderie/rate-limit";
58
+ var min = (n) => n * 60;
59
+ var DEFAULTS = {
60
+ // login: 10/15min per IP AND 5/15min per account (credential stuffing
61
+ // rotates IPs, so the per-account bucket is the one that bites).
62
+ login: {
63
+ ip: { capacity: 10, refillPerSec: 10 / min(15) },
64
+ account: { capacity: 5, refillPerSec: 5 / min(15) },
65
+ accountField: "email"
66
+ },
67
+ // register: 5/hour per IP — signup abuse is IP-shaped.
68
+ register: { ip: { capacity: 5, refillPerSec: 5 / min(60) } },
69
+ // forgot: 5/hour per IP AND 3/hour per account (email-bombing protection).
70
+ forgot: {
71
+ ip: { capacity: 5, refillPerSec: 5 / min(60) },
72
+ account: { capacity: 3, refillPerSec: 3 / min(60) },
73
+ accountField: "email"
74
+ },
75
+ // mfaVerify: 10/15min per IP — TOTP brute-force.
76
+ mfaVerify: { ip: { capacity: 10, refillPerSec: 10 / min(15) } }
77
+ };
78
+ function resolve(route, store, config) {
79
+ if (config === false) return null;
80
+ const override = config?.rules?.[route];
81
+ if (override === false) return null;
82
+ const backing = config?.store ?? new StoreAdapterStore(store);
83
+ const def = DEFAULTS[route];
84
+ return {
85
+ store: backing,
86
+ ipRule: override ?? def.ip,
87
+ ...def.account && def.accountField ? { account: { rule: def.account, field: def.accountField } } : {}
88
+ };
89
+ }
90
+ function buildAuthIpLimiter(route, store, config) {
91
+ const r = resolve(route, store, config);
92
+ if (!r) return null;
93
+ return rateLimit({ store: r.store, rule: r.ipRule, key: byIp(`auth:${route}`) });
94
+ }
95
+ function buildAuthAccountLimiter(route, store, config) {
96
+ const r = resolve(route, store, config);
97
+ if (!r || !r.account) return null;
98
+ return rateLimit({
99
+ store: r.store,
100
+ rule: r.account.rule,
101
+ key: byBodyField(`auth:${route}`, r.account.field)
102
+ });
103
+ }
104
+
51
105
  // src/schemas.ts
52
106
  var schemas_exports = {};
53
107
  __export(schemas_exports, {
@@ -1851,14 +1905,17 @@ function buildAuthRoutes(store, config, bus) {
1851
1905
  const oauth = oauthController(store, config);
1852
1906
  const mfa = mfaController(store, config, config.appName ?? "Fonderie", bus);
1853
1907
  const verifyGate = config.requireVerification ? requireVerified : (_ctx, next) => next();
1908
+ const passthrough = (_ctx, next) => next();
1909
+ const ipLimit = (route) => buildAuthIpLimiter(route, store, config.rateLimit) ?? passthrough;
1910
+ const acctLimit = (route) => buildAuthAccountLimiter(route, store, config.rateLimit) ?? passthrough;
1854
1911
  const routes = [
1855
1912
  // Registration & Login (Public)
1856
- ["POST", "/auth/register", validate(registerSchema), auth.register],
1857
- ["POST", "/auth/login", validate(loginSchema), auth.login],
1913
+ ["POST", "/auth/register", ipLimit("register"), validate(registerSchema), auth.register],
1914
+ ["POST", "/auth/login", ipLimit("login"), validate(loginSchema), acctLimit("login"), auth.login],
1858
1915
  // Token Management (Public)
1859
1916
  ["POST", "/auth/refresh", validate(refreshSchema), auth.refresh],
1860
1917
  // Email — Password Recovery (Public)
1861
- ["POST", "/auth/email/forgot", validate(forgotPasswordSchema), auth.forgotPassword],
1918
+ ["POST", "/auth/email/forgot", ipLimit("forgot"), validate(forgotPasswordSchema), acctLimit("forgot"), auth.forgotPassword],
1862
1919
  ["POST", "/auth/email/reset", validate(resetPasswordSchema), auth.resetPassword],
1863
1920
  // Verification (Protected — email or phone, determined by loginMethod)
1864
1921
  ["POST", "/auth/verify", requireAuth, validate(verifySchema), auth.verify],
@@ -1878,7 +1935,7 @@ function buildAuthRoutes(store, config, bus) {
1878
1935
  ["POST", "/auth/mfa/setup", requireAuth, requireEmailLogin, requireVerified, mfa.setup],
1879
1936
  // /auth/mfa/verify accepts both mfaPending tokens (TOTP/backup-code login)
1880
1937
  // and full tokens (setup confirmation), so requireAnyAuth is used here.
1881
- ["POST", "/auth/mfa/verify", requireAnyAuth, requireEmailLogin, requireVerified, validate(mfaTokenSchema), mfa.verify],
1938
+ ["POST", "/auth/mfa/verify", ipLimit("mfaVerify"), requireAnyAuth, requireEmailLogin, requireVerified, validate(mfaTokenSchema), mfa.verify],
1882
1939
  ["POST", "/auth/mfa/disable", requireAuth, requireEmailLogin, requireVerified, validate(mfaTokenSchema), mfa.disable],
1883
1940
  [
1884
1941
  "POST",
@@ -1962,6 +2019,8 @@ export {
1962
2019
  AUTH_CONFIG_KEYS,
1963
2020
  AuthModule,
1964
2021
  MESSAGE_KEYS,
2022
+ buildAuthAccountLimiter,
2023
+ buildAuthIpLimiter,
1965
2024
  normalizeEmail,
1966
2025
  normalizeEmailSafe,
1967
2026
  requireAuth2 as requireAuth,