@vsaas/loopback 12.1.1 → 12.2.1

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.
Files changed (117) hide show
  1. package/README.md +6 -0
  2. package/common/models/rate-limit-state.json +121 -0
  3. package/common/models/two-factor-challenge.json +112 -0
  4. package/common/models/two-factor-credential.json +117 -0
  5. package/common/models/two-factor-recovery-code.json +63 -0
  6. package/common/models/user.json +30 -0
  7. package/dist/_virtual/_rolldown/runtime.cjs +9 -2
  8. package/dist/common/models/access-token.d.cts +3 -0
  9. package/dist/common/models/acl.cjs +1 -3
  10. package/dist/common/models/acl.d.cts +1 -0
  11. package/dist/common/models/application.d.cts +3 -0
  12. package/dist/common/models/change.cjs +21 -18
  13. package/dist/common/models/change.d.cts +1 -0
  14. package/dist/common/models/checkpoint.d.cts +3 -0
  15. package/dist/common/models/email.d.cts +3 -0
  16. package/dist/common/models/key-value-model.d.cts +3 -0
  17. package/dist/common/models/rate-limit-state.cjs +7 -0
  18. package/dist/common/models/rate-limit-state.d.cts +3 -0
  19. package/dist/common/models/rate-limit-state2.cjs +109 -0
  20. package/dist/common/models/role-mapping.d.cts +3 -0
  21. package/dist/common/models/role.cjs +12 -8
  22. package/dist/common/models/role.d.cts +1 -0
  23. package/dist/common/models/scope.d.cts +3 -0
  24. package/dist/common/models/two-factor-challenge.cjs +7 -0
  25. package/dist/common/models/two-factor-challenge.d.cts +3 -0
  26. package/dist/common/models/two-factor-challenge2.cjs +108 -0
  27. package/dist/common/models/two-factor-credential.cjs +7 -0
  28. package/dist/common/models/two-factor-credential.d.cts +3 -0
  29. package/dist/common/models/two-factor-credential2.cjs +103 -0
  30. package/dist/common/models/two-factor-recovery-code.cjs +7 -0
  31. package/dist/common/models/two-factor-recovery-code.d.cts +3 -0
  32. package/dist/common/models/two-factor-recovery-code2.cjs +57 -0
  33. package/dist/common/models/user.cjs +261 -21
  34. package/dist/common/models/user.d.cts +3 -0
  35. package/dist/common/models/user2.cjs +30 -0
  36. package/dist/index.cjs +2 -0
  37. package/dist/index.d.cts +3 -0
  38. package/dist/lib/access-context.d.cts +33 -0
  39. package/dist/lib/application.d.cts +1 -0
  40. package/dist/lib/authentication-security-initializer.cjs +351 -0
  41. package/dist/lib/authentication-security-initializer.d.cts +4 -0
  42. package/dist/lib/builtin-models.cjs +12 -0
  43. package/dist/lib/builtin-models.d.cts +3 -0
  44. package/dist/lib/configure-shared-methods.cjs +7 -4
  45. package/dist/lib/configure-shared-methods.d.cts +3 -0
  46. package/dist/lib/connectors/base-connector.d.cts +6 -0
  47. package/dist/lib/connectors/mail-direct-transport.cjs +26 -23
  48. package/dist/lib/connectors/mail-direct-transport.d.cts +22 -0
  49. package/dist/lib/connectors/mail-stub-transport.d.cts +14 -0
  50. package/dist/lib/connectors/mail.d.cts +7 -0
  51. package/dist/lib/connectors/memory.d.cts +6 -0
  52. package/dist/lib/current-context.d.cts +3 -0
  53. package/dist/lib/globalize.d.cts +10 -0
  54. package/dist/lib/loopback.cjs +6 -0
  55. package/dist/lib/loopback.d.cts +1 -0
  56. package/dist/lib/model.d.cts +1 -0
  57. package/dist/lib/persisted-model.cjs +7 -12
  58. package/dist/lib/persisted-model.d.cts +1 -0
  59. package/dist/lib/rate-limit/algorithms/common.cjs +37 -0
  60. package/dist/lib/rate-limit/algorithms/failure-lockout.cjs +70 -0
  61. package/dist/lib/rate-limit/algorithms/fixed-window.cjs +61 -0
  62. package/dist/lib/rate-limit/algorithms/index.cjs +55 -0
  63. package/dist/lib/rate-limit/config.cjs +107 -0
  64. package/dist/lib/rate-limit/errors.cjs +58 -0
  65. package/dist/lib/rate-limit/index.cjs +15 -0
  66. package/dist/lib/rate-limit/index.d.cts +1 -0
  67. package/dist/lib/rate-limit/initializer.cjs +92 -0
  68. package/dist/lib/rate-limit/initializer.d.cts +4 -0
  69. package/dist/lib/rate-limit/limiter.cjs +368 -0
  70. package/dist/lib/rate-limit/registry.cjs +92 -0
  71. package/dist/lib/rate-limit/remote.cjs +180 -0
  72. package/dist/lib/rate-limit/remote.d.cts +6 -0
  73. package/dist/lib/rate-limit/stores/persisted-model.cjs +353 -0
  74. package/dist/lib/rate-limit/subject.cjs +127 -0
  75. package/dist/lib/rate-limit/types.cjs +1 -0
  76. package/dist/lib/rate-limit/types.d.cts +167 -0
  77. package/dist/lib/registry.d.cts +1 -0
  78. package/dist/lib/runtime.d.cts +10 -0
  79. package/dist/lib/server-app.d.cts +3 -0
  80. package/dist/lib/two-factor/config.cjs +199 -0
  81. package/dist/lib/two-factor/config.d.cts +55 -0
  82. package/dist/lib/two-factor/crypto.cjs +68 -0
  83. package/dist/lib/two-factor/crypto.d.cts +16 -0
  84. package/dist/lib/two-factor/errors.cjs +58 -0
  85. package/dist/lib/two-factor/errors.d.cts +18 -0
  86. package/dist/lib/two-factor/rate-limit.cjs +153 -0
  87. package/dist/lib/two-factor/rate-limit.d.cts +22 -0
  88. package/dist/lib/two-factor/recovery-codes.cjs +49 -0
  89. package/dist/lib/two-factor/recovery-codes.d.cts +16 -0
  90. package/dist/lib/two-factor/service.cjs +1108 -0
  91. package/dist/lib/two-factor/service.d.cts +78 -0
  92. package/dist/lib/two-factor/totp.cjs +50 -0
  93. package/dist/lib/two-factor/totp.d.cts +14 -0
  94. package/dist/lib/utils.d.cts +36 -0
  95. package/dist/package.cjs +46 -10
  96. package/dist/server/middleware/context.d.cts +3 -0
  97. package/dist/server/middleware/error-handler.d.cts +3 -0
  98. package/dist/server/middleware/favicon.d.cts +7 -0
  99. package/dist/server/middleware/rest.d.cts +6 -0
  100. package/dist/server/middleware/static.d.cts +3 -0
  101. package/dist/server/middleware/status.d.cts +19 -0
  102. package/dist/server/middleware/token.d.cts +3 -0
  103. package/dist/server/middleware/url-not-found.d.cts +13 -0
  104. package/docs/api-explorer-details.md +110 -0
  105. package/docs/application-guide.md +1110 -0
  106. package/docs/assets/explorer-api.png +0 -0
  107. package/docs/assets/explorer-endpoint.png +0 -0
  108. package/docs/assets/explorer-listing.png +0 -0
  109. package/docs/assets/explorer-req-res.png +0 -0
  110. package/docs/assets/lb-modules.png +0 -0
  111. package/docs/assets/loopback-architecture.png +0 -0
  112. package/docs/assets/loopback-concepts.png +0 -0
  113. package/docs/assets/loopback_ov.png +0 -0
  114. package/docs/rate-limiting.md +879 -0
  115. package/docs/two-factor-authentication.md +611 -0
  116. package/docs/two-factor-authenticator-loss-runbook.md +117 -0
  117. package/package.json +56 -20
@@ -0,0 +1,117 @@
1
+ # Lost authenticator recovery runbook
2
+
3
+ This runbook covers a user who cannot produce either a valid TOTP code or an
4
+ unused recovery code. It is intentionally an application-owned support
5
+ workflow: LoopBack cannot decide how an organization proves identity or who may
6
+ approve account recovery.
7
+
8
+ Password reset does not disable two-factor authentication. This prevents an
9
+ attacker who compromises email or a password-reset channel from silently
10
+ removing the second factor.
11
+
12
+ Do not enable 2FA in production until the application has implemented and
13
+ tested the workflow below.
14
+
15
+ ## Normal recovery comes first
16
+
17
+ Use the ordinary user-facing operations whenever possible:
18
+
19
+ 1. If the user still has a recovery code, use it to sign in.
20
+ 2. Regenerate recovery codes or disable and re-enroll 2FA using the documented
21
+ management endpoint.
22
+ 3. Treat support recovery as the last path, not as a more convenient login.
23
+
24
+ Support must never ask the user to send a TOTP secret, current TOTP code,
25
+ recovery code, password, access token, or encrypted database value.
26
+
27
+ ## Decisions the application must make
28
+
29
+ Before implementation, document:
30
+
31
+ - which roles may open, approve, and execute a recovery;
32
+ - the out-of-band evidence required to prove account ownership;
33
+ - whether two different operators must approve the action;
34
+ - the mandatory delay or notification window for high-value accounts;
35
+ - accounts that cannot use support recovery, such as break-glass
36
+ administrators;
37
+ - the audit retention period and alerting destination;
38
+ - the session policy after recovery. Revoking all sessions is the recommended
39
+ default.
40
+
41
+ Identity proof must not rely only on the same email channel used for password
42
+ reset. Prefer previously verified organizational records, a known support
43
+ contract, an in-person process, or another independent channel appropriate to
44
+ the application's risk.
45
+
46
+ ## Required application operation
47
+
48
+ Implement a programmatic administrative operation that is not remotely exposed
49
+ by LoopBack by default. The operation receives the concrete User model,
50
+ principal ID, support case ID, executing operator, approving operator, and the
51
+ already-completed identity-proof result.
52
+
53
+ The application authorization layer must reject the operation before any data
54
+ change unless all required approvals are present. Do not accept a client body
55
+ boolean such as `identityVerified: true` as proof.
56
+
57
+ After authorization, perform this sequence:
58
+
59
+ 1. Load the user and the enabled `TwoFactorCredential` using both
60
+ `principalType` and canonical string `principalId`.
61
+ 2. Record a durable recovery operation with a unique case ID and `pending`
62
+ state. A repeated case ID must return the original result.
63
+ 3. Revoke every access token, reset token, remembered device, and application
64
+ session for that principal. Confirm revocation instead of assuming a delete
65
+ succeeded.
66
+ 4. Disable the credential with compare-and-set on its `id`, `status`, and
67
+ `version`. Increment `version`, clear the encrypted secret and active
68
+ recovery-set fields, and set `disabledAt`.
69
+ 5. Expire pending or verifying challenges for the same credential and remove
70
+ its recovery codes.
71
+ 6. Mark the recovery operation completed only after the credential transition
72
+ and session revocation are confirmed.
73
+ 7. Emit a security audit event and notify the account through every previously
74
+ verified channel. The notification must explain how to report an
75
+ unauthorized recovery.
76
+ 8. Require password authentication and a completely new 2FA enrollment on the
77
+ next login. Never restore or disclose the old TOTP secret.
78
+
79
+ Use a datasource transaction when the connector can include the credential,
80
+ sessions, challenges, recovery codes, and recovery-operation record in one
81
+ transaction. Otherwise use durable states and idempotent compare-and-set
82
+ transitions. A partial result must fail closed.
83
+
84
+ Do not implement recovery by directly changing `status` in a database console.
85
+ That skips session revocation, challenge invalidation, versioning, recovery-code
86
+ cleanup, and audit requirements.
87
+
88
+ ## Failure handling
89
+
90
+ | Failure | Required result |
91
+ | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
92
+ | Identity proof or approval fails | Make no security-state change. Record only the rejected case metadata allowed by policy. |
93
+ | Session revocation is unavailable or ambiguous | Keep 2FA enabled, mark the case for reconciliation, and return no success. |
94
+ | Credential CAS loses to another transition | Reload state. Complete only if the same recovery case already produced the required terminal state; otherwise stop for review. |
95
+ | Challenge or recovery-code cleanup fails after credential disable | The old factor remains unusable because credential status/version changed. Emit an operational alert and retry cleanup. |
96
+ | Audit persistence fails | Do not execute recovery. Audit is part of the authorization boundary. |
97
+ | User reports an unauthorized recovery | Lock the account according to incident policy, revoke all sessions again, preserve evidence, and start the security incident process. |
98
+
99
+ ## Verification before rollout
100
+
101
+ Test at least:
102
+
103
+ - password reset leaves an enabled credential enabled;
104
+ - support cannot recover an account without the required approvals;
105
+ - operator and approver cannot be the same person when dual control is enabled;
106
+ - recovery revokes all existing tokens and sessions;
107
+ - an outstanding challenge fails after recovery;
108
+ - old TOTP counters and recovery codes cannot be used;
109
+ - retrying the same case is idempotent;
110
+ - concurrent disable, enrollment, and support recovery cannot overwrite each
111
+ other;
112
+ - failures in every write leave the account fail-closed and generate an alert;
113
+ - two concrete User models cannot recover each other's principals;
114
+ - logs and audit records contain no authentication secrets.
115
+
116
+ Keep an application-specific copy of this runbook with real role names,
117
+ escalation contacts, identity-proof rules, dashboards, and incident links.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vsaas/loopback",
3
- "version": "12.1.1",
3
+ "version": "12.2.1",
4
4
  "description": "Fork of LoopBack 3 framework for building APIs and microservices in Node.js",
5
5
  "keywords": [
6
6
  "StrongLoop",
@@ -36,21 +36,56 @@
36
36
  "files": [
37
37
  "dist",
38
38
  "common/models",
39
+ "docs",
39
40
  "templates",
40
41
  "favicon.ico",
41
42
  "README.md",
42
43
  "LICENSE"
43
44
  ],
44
45
  "main": "dist/index.cjs",
46
+ "types": "dist/index.d.cts",
45
47
  "exports": {
46
- ".": "./dist/index.cjs",
48
+ ".": {
49
+ "types": "./dist/index.d.cts",
50
+ "require": "./dist/index.cjs",
51
+ "default": "./dist/index.cjs"
52
+ },
53
+ "./rate-limit": {
54
+ "types": "./dist/lib/rate-limit/types.d.cts",
55
+ "require": "./dist/lib/rate-limit/types.cjs",
56
+ "default": "./dist/lib/rate-limit/types.cjs"
57
+ },
47
58
  "./common/models/*.json": "./common/models/*.json",
48
- "./common/models/*.js": "./dist/common/models/*.cjs",
49
- "./common/models/*": "./dist/common/models/*.cjs",
50
- "./lib/*.js": "./dist/lib/*.cjs",
51
- "./lib/*": "./dist/lib/*.cjs",
52
- "./server/middleware/*.js": "./dist/server/middleware/*.cjs",
53
- "./server/middleware/*": "./dist/server/middleware/*.cjs",
59
+ "./common/models/*.js": {
60
+ "types": "./dist/common/models/*.d.cts",
61
+ "require": "./dist/common/models/*.cjs",
62
+ "default": "./dist/common/models/*.cjs"
63
+ },
64
+ "./common/models/*": {
65
+ "types": "./dist/common/models/*.d.cts",
66
+ "require": "./dist/common/models/*.cjs",
67
+ "default": "./dist/common/models/*.cjs"
68
+ },
69
+ "./lib/*.js": {
70
+ "types": "./dist/lib/*.d.cts",
71
+ "require": "./dist/lib/*.cjs",
72
+ "default": "./dist/lib/*.cjs"
73
+ },
74
+ "./lib/*": {
75
+ "types": "./dist/lib/*.d.cts",
76
+ "require": "./dist/lib/*.cjs",
77
+ "default": "./dist/lib/*.cjs"
78
+ },
79
+ "./server/middleware/*.js": {
80
+ "types": "./dist/server/middleware/*.d.cts",
81
+ "require": "./dist/server/middleware/*.cjs",
82
+ "default": "./dist/server/middleware/*.cjs"
83
+ },
84
+ "./server/middleware/*": {
85
+ "types": "./dist/server/middleware/*.d.cts",
86
+ "require": "./dist/server/middleware/*.cjs",
87
+ "default": "./dist/server/middleware/*.cjs"
88
+ },
54
89
  "./package.json": "./package.json"
55
90
  },
56
91
  "publishConfig": {
@@ -58,7 +93,7 @@
58
93
  "registry": "https://registry.npmjs.org/"
59
94
  },
60
95
  "dependencies": {
61
- "@aws-sdk/client-sesv2": "3.1034.0",
96
+ "@aws-sdk/client-sesv2": "^3.1115.0",
62
97
  "bcryptjs": "^2.4.3",
63
98
  "debug": "^2.6.9",
64
99
  "depd": "^1.1.2",
@@ -66,11 +101,12 @@
66
101
  "express": "4.22.2",
67
102
  "isemail": "^3.2.0",
68
103
  "loopback-filters": "^1.1.1",
69
- "nodemailer": "^9.0.1",
104
+ "nodemailer": "^9.0.5",
105
+ "otplib": "13.4.1",
70
106
  "serve-favicon": "^2.5.1",
71
- "@vsaas/loopback-datasource-juggler": "^12.1.1",
72
- "@vsaas/loopback-connector-remote": "^12.0.0",
73
- "@vsaas/remoting": "^12.0.1"
107
+ "@vsaas/loopback-connector-remote": "^12.0.1",
108
+ "@vsaas/remoting": "^13.0.0",
109
+ "@vsaas/loopback-datasource-juggler": "^12.1.2"
74
110
  },
75
111
  "devDependencies": {
76
112
  "@types/node": "25.5.0",
@@ -79,17 +115,17 @@
79
115
  "dirty-chai": "^2.0.1",
80
116
  "express-session": "^1.19.0",
81
117
  "loopback-context": "^1.0.0",
82
- "oxfmt": "0.42.0",
83
- "oxlint": "1.57.0",
84
- "sinon": "22.0.0",
118
+ "oxfmt": "0.64.0",
119
+ "oxlint": "1.79.0",
120
+ "sinon": "22.1.0",
85
121
  "sinon-chai": "^3.7.0",
86
122
  "strong-task-emitter": "^0.0.8",
87
123
  "supertest": "7.2.2",
88
- "tsdown": "0.21.7",
124
+ "tsdown": "0.22.14",
89
125
  "typescript": "6.0.2",
90
- "vitest": "4.1.4",
91
- "@vsaas/loopback-boot": "^12.0.1",
92
- "@vsaas/error-handler": "^12.0.0"
126
+ "vitest": "4.1.11",
127
+ "@vsaas/error-handler": "^12.0.0",
128
+ "@vsaas/loopback-boot": "^12.0.2"
93
129
  },
94
130
  "engines": {
95
131
  "node": ">=24"