@lenne.tech/nest-server 11.37.0 → 11.39.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.
Files changed (139) hide show
  1. package/.claude/rules/configurable-features.md +51 -0
  2. package/.claude/rules/module-deprecation.md +25 -1
  3. package/.claude/rules/package-management.md +66 -0
  4. package/.claude/rules/testing.md +81 -5
  5. package/FRAMEWORK-API.md +4 -2
  6. package/dist/config.env.js +5 -3
  7. package/dist/config.env.js.map +1 -1
  8. package/dist/core/common/helpers/config.helper.d.ts +2 -0
  9. package/dist/core/common/helpers/config.helper.js +18 -0
  10. package/dist/core/common/helpers/config.helper.js.map +1 -1
  11. package/dist/core/common/helpers/cookies.helper.d.ts +3 -0
  12. package/dist/core/common/helpers/cookies.helper.js +9 -0
  13. package/dist/core/common/helpers/cookies.helper.js.map +1 -1
  14. package/dist/core/common/helpers/input.helper.d.ts +1 -0
  15. package/dist/core/common/helpers/input.helper.js +4 -0
  16. package/dist/core/common/helpers/input.helper.js.map +1 -1
  17. package/dist/core/common/helpers/service.helper.js +6 -1
  18. package/dist/core/common/helpers/service.helper.js.map +1 -1
  19. package/dist/core/common/interceptors/check-security.interceptor.js +1 -0
  20. package/dist/core/common/interceptors/check-security.interceptor.js.map +1 -1
  21. package/dist/core/common/interfaces/server-options.interface.d.ts +21 -0
  22. package/dist/core/common/middlewares/security-headers.middleware.d.ts +18 -0
  23. package/dist/core/common/middlewares/security-headers.middleware.js +90 -0
  24. package/dist/core/common/middlewares/security-headers.middleware.js.map +1 -0
  25. package/dist/core/common/services/email.service.d.ts +4 -1
  26. package/dist/core/common/services/email.service.js +25 -2
  27. package/dist/core/common/services/email.service.js.map +1 -1
  28. package/dist/core/common/services/module.service.js +1 -0
  29. package/dist/core/common/services/module.service.js.map +1 -1
  30. package/dist/core/modules/auth/core-auth.controller.js +2 -5
  31. package/dist/core/modules/auth/core-auth.controller.js.map +1 -1
  32. package/dist/core/modules/auth/core-auth.module.js +2 -0
  33. package/dist/core/modules/auth/core-auth.module.js.map +1 -1
  34. package/dist/core/modules/auth/core-auth.resolver.js +2 -5
  35. package/dist/core/modules/auth/core-auth.resolver.js.map +1 -1
  36. package/dist/core/modules/auth/core-legacy-auth-deprecation.initializer.d.ts +12 -0
  37. package/dist/core/modules/auth/core-legacy-auth-deprecation.initializer.js +83 -0
  38. package/dist/core/modules/auth/core-legacy-auth-deprecation.initializer.js.map +1 -0
  39. package/dist/core/modules/auth/helpers/legacy-endpoints.helper.d.ts +3 -0
  40. package/dist/core/modules/auth/helpers/legacy-endpoints.helper.js +14 -0
  41. package/dist/core/modules/auth/helpers/legacy-endpoints.helper.js.map +1 -0
  42. package/dist/core/modules/better-auth/better-auth.config.d.ts +12 -0
  43. package/dist/core/modules/better-auth/better-auth.config.js +40 -1
  44. package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
  45. package/dist/core/modules/better-auth/core-better-auth-api.middleware.d.ts +9 -1
  46. package/dist/core/modules/better-auth/core-better-auth-api.middleware.js +53 -4
  47. package/dist/core/modules/better-auth/core-better-auth-api.middleware.js.map +1 -1
  48. package/dist/core/modules/better-auth/core-better-auth-email-verification.service.d.ts +5 -2
  49. package/dist/core/modules/better-auth/core-better-auth-email-verification.service.js +57 -4
  50. package/dist/core/modules/better-auth/core-better-auth-email-verification.service.js.map +1 -1
  51. package/dist/core/modules/better-auth/core-better-auth-error-codes.helper.d.ts +2 -0
  52. package/dist/core/modules/better-auth/core-better-auth-error-codes.helper.js +54 -0
  53. package/dist/core/modules/better-auth/core-better-auth-error-codes.helper.js.map +1 -0
  54. package/dist/core/modules/better-auth/core-better-auth-password-reset.registry.d.ts +2 -0
  55. package/dist/core/modules/better-auth/core-better-auth-password-reset.registry.js +19 -0
  56. package/dist/core/modules/better-auth/core-better-auth-password-reset.registry.js.map +1 -0
  57. package/dist/core/modules/better-auth/core-better-auth-user.mapper.d.ts +4 -1
  58. package/dist/core/modules/better-auth/core-better-auth-user.mapper.js +13 -10
  59. package/dist/core/modules/better-auth/core-better-auth-user.mapper.js.map +1 -1
  60. package/dist/core/modules/better-auth/core-better-auth.module.d.ts +8 -2
  61. package/dist/core/modules/better-auth/core-better-auth.module.js +34 -4
  62. package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -1
  63. package/dist/core/modules/better-auth/index.d.ts +1 -0
  64. package/dist/core/modules/better-auth/index.js +1 -0
  65. package/dist/core/modules/better-auth/index.js.map +1 -1
  66. package/dist/core/modules/error-code/error-codes.d.ts +27 -0
  67. package/dist/core/modules/error-code/error-codes.js +24 -0
  68. package/dist/core/modules/error-code/error-codes.js.map +1 -1
  69. package/dist/core/modules/hub/core-hub.service.js +1 -0
  70. package/dist/core/modules/hub/core-hub.service.js.map +1 -1
  71. package/dist/core/modules/user/core-user.model.d.ts +1 -0
  72. package/dist/core/modules/user/core-user.model.js +11 -1
  73. package/dist/core/modules/user/core-user.model.js.map +1 -1
  74. package/dist/core/modules/user/core-user.service.d.ts +10 -1
  75. package/dist/core/modules/user/core-user.service.js +124 -15
  76. package/dist/core/modules/user/core-user.service.js.map +1 -1
  77. package/dist/core/modules/user/inputs/core-user.input.js +1 -1
  78. package/dist/core/modules/user/inputs/core-user.input.js.map +1 -1
  79. package/dist/core.module.js +2 -0
  80. package/dist/core.module.js.map +1 -1
  81. package/dist/index.d.ts +3 -0
  82. package/dist/index.js +3 -0
  83. package/dist/index.js.map +1 -1
  84. package/dist/server/modules/error-code/error-codes.d.ts +3 -0
  85. package/dist/server/modules/user/user.controller.js +2 -1
  86. package/dist/server/modules/user/user.controller.js.map +1 -1
  87. package/dist/server/modules/user/user.model.d.ts +5 -0
  88. package/dist/server/modules/user/user.resolver.js +2 -1
  89. package/dist/server/modules/user/user.resolver.js.map +1 -1
  90. package/dist/server/modules/user/user.service.d.ts +1 -1
  91. package/dist/server/modules/user/user.service.js +18 -5
  92. package/dist/server/modules/user/user.service.js.map +1 -1
  93. package/dist/templates/password-reset-de.ejs +12 -0
  94. package/dist/templates/password-reset-en.ejs +12 -0
  95. package/dist/templates/password-reset.ejs +1 -0
  96. package/dist/tsconfig.build.tsbuildinfo +1 -1
  97. package/docs/REQUEST-LIFECYCLE.md +49 -1
  98. package/migration-guides/11.37.x-to-11.38.x.md +528 -0
  99. package/migration-guides/11.38.x-to-11.39.x.md +456 -0
  100. package/package.json +1 -1
  101. package/src/config.env.ts +28 -4
  102. package/src/core/common/helpers/config.helper.ts +79 -0
  103. package/src/core/common/helpers/cookies.helper.ts +38 -0
  104. package/src/core/common/helpers/input.helper.ts +37 -0
  105. package/src/core/common/helpers/service.helper.ts +9 -1
  106. package/src/core/common/interceptors/check-security.interceptor.ts +1 -0
  107. package/src/core/common/interfaces/server-options.interface.ts +326 -10
  108. package/src/core/common/middlewares/security-headers.middleware.ts +155 -0
  109. package/src/core/common/services/email.service.ts +46 -1
  110. package/src/core/common/services/module.service.ts +1 -0
  111. package/src/core/modules/auth/README.md +104 -0
  112. package/src/core/modules/auth/core-auth.controller.ts +4 -11
  113. package/src/core/modules/auth/core-auth.module.ts +5 -0
  114. package/src/core/modules/auth/core-auth.resolver.ts +4 -11
  115. package/src/core/modules/auth/core-legacy-auth-deprecation.initializer.ts +128 -0
  116. package/src/core/modules/auth/helpers/legacy-endpoints.helper.ts +69 -0
  117. package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +22 -0
  118. package/src/core/modules/better-auth/README.md +32 -59
  119. package/src/core/modules/better-auth/better-auth.config.ts +131 -4
  120. package/src/core/modules/better-auth/core-better-auth-api.middleware.ts +126 -3
  121. package/src/core/modules/better-auth/core-better-auth-email-verification.service.ts +166 -7
  122. package/src/core/modules/better-auth/core-better-auth-error-codes.helper.ts +146 -0
  123. package/src/core/modules/better-auth/core-better-auth-password-reset.registry.ts +92 -0
  124. package/src/core/modules/better-auth/core-better-auth-user.mapper.ts +42 -10
  125. package/src/core/modules/better-auth/core-better-auth.module.ts +67 -4
  126. package/src/core/modules/better-auth/index.ts +1 -0
  127. package/src/core/modules/error-code/error-codes.ts +55 -0
  128. package/src/core/modules/hub/core-hub.service.ts +1 -0
  129. package/src/core/modules/user/core-user.model.ts +28 -1
  130. package/src/core/modules/user/core-user.service.ts +389 -22
  131. package/src/core/modules/user/inputs/core-user.input.ts +16 -1
  132. package/src/core.module.ts +8 -0
  133. package/src/index.ts +3 -0
  134. package/src/server/modules/user/user.controller.ts +7 -1
  135. package/src/server/modules/user/user.resolver.ts +7 -1
  136. package/src/server/modules/user/user.service.ts +58 -13
  137. package/src/templates/password-reset-de.ejs +12 -0
  138. package/src/templates/password-reset-en.ejs +12 -0
  139. package/src/templates/password-reset.ejs +1 -0
@@ -74,22 +74,67 @@ export class UserService extends CoreUserService<User, UserInput, UserCreateInpu
74
74
 
75
75
  /**
76
76
  * Request password reset mail
77
+ *
78
+ * REFERENCE IMPLEMENTATION — the two details below are the ones that matter, and a project
79
+ * copying this method needs both. Either one alone leaves the endpoint an account oracle.
80
+ *
81
+ * 1. An unknown address yields `null` (since 11.38.0, unless
82
+ * `auth.passwordReset.preventUserEnumeration` is off) and this method returns quietly, so the
83
+ * caller answers the same way it would for a known one.
84
+ * 2. The mail send is NOT awaited. This is the half that actually closes the channel: the send
85
+ * is a network round trip to SMTP or Brevo, orders of magnitude above everything else in the
86
+ * request. Awaiting it would make the known path visibly slower whatever the status code says
87
+ * — you would have given up the "unknown address" hint in the UI and kept the oracle.
88
+ *
89
+ * A failed send must still be reported rather than crash the process, which is why it carries
90
+ * its own `catch`. The user is told the mail is on its way either way; that is the same trade
91
+ * Better-Auth makes on the IAM path.
77
92
  */
78
- async sendPasswordResetMail(email: string, serviceOptions?: ServiceOptions): Promise<User> {
79
- // Set password reset token
80
- const user = await super.setPasswordResetTokenForEmail(email, serviceOptions);
81
-
82
- // Send email
83
- await this.emailService.sendMail(user.email, 'Password reset', {
84
- htmlTemplate: 'password-reset',
85
- templateData: {
86
- link: `${this.configService.configFastButReadOnly.email.passwordResetLink}/${user.passwordResetToken}`,
87
- name: user.username,
88
- },
89
- });
93
+ async sendPasswordResetMail(email: string, serviceOptions?: ServiceOptions): Promise<null | User> {
94
+ // `createPasswordResetToken`, not `setPasswordResetTokenForEmail`: the latter returns the user
95
+ // through `process()`, where the security interceptor strips `passwordResetToken`. Reading the
96
+ // token off that object is what mailed the word `undefined` to a real recipient.
97
+ const created = await this.createPasswordResetToken(email, serviceOptions);
98
+
99
+ if (!created) {
100
+ // Unknown address, enumeration protection on. Answer exactly as for a known one.
101
+ return null;
102
+ }
103
+
104
+ // `buildPasswordResetLink`, not string concatenation: `email.passwordResetLink` has no default,
105
+ // and this config file does not set one, so concatenating produced `undefined/<token>`.
106
+ const link = this.buildPasswordResetLink(created.token);
107
+ if (!link) {
108
+ // Sending a mail whose link cannot work is worse than sending none. The recipient has no
109
+ // second way in, and a dead link gives them nothing to act on — while no mail at least reads
110
+ // as "try again". The address is still answered exactly as a known one.
111
+ this.userServiceLogger.error(
112
+ 'Password reset mail not sent: no reset link could be built. Set `email.passwordResetLink` or `appUrl`.',
113
+ );
114
+ return created.user;
115
+ }
116
+
117
+ // Deliberately NOT awaited — see the note above.
118
+ void this.emailService
119
+ .sendMail(created.user.email, 'Password reset', {
120
+ htmlTemplate: 'password-reset',
121
+ templateData: {
122
+ link,
123
+ // The mail is the only place the recipient can learn there IS a deadline. Without it an
124
+ // expired link is indistinguishable from a broken one — the same experience this whole
125
+ // area was repaired for.
126
+ linkExpiresInMinutes: this.passwordResetTokenExpiryMinutes(),
127
+ name: created.user.username,
128
+ },
129
+ })
130
+ .catch((error: unknown) => {
131
+ this.userServiceLogger.error(
132
+ `Failed to send the password-reset mail: ${error instanceof Error ? error.message : String(error)}`,
133
+ );
134
+ });
90
135
 
91
136
  // Return user
92
- return user;
137
+ return created.user;
93
138
  }
94
139
 
95
140
  /**
@@ -50,6 +50,18 @@
50
50
  Falls die Schaltfläche nicht funktioniert, kopiere diesen Link in deinen Browser:<br>
51
51
  <a href="<%= link %>" style="color:#1d4ed8; word-break:break-all; overflow-wrap:break-word;"><%= link %></a>
52
52
  </p>
53
+ <%
54
+ /* Optional, like `appName`: a caller that omits it must not turn a password-reset
55
+ mail into a 500. Rendered only for a real, positive value — a deployment that
56
+ switched the expiry off (`0`) has no deadline to announce. */
57
+ const expiryMinutes = (typeof linkExpiresInMinutes !== 'undefined' && Number(linkExpiresInMinutes) > 0)
58
+ ? Number(linkExpiresInMinutes) : 0;
59
+ -%>
60
+ <% if (expiryMinutes) { %>
61
+ <p style="margin:16px 0 0 0; font-size:13px; line-height:20px; color:#737373;">
62
+ Der Link ist <%= expiryMinutes %> Minuten lang gültig. Danach kannst du einfach einen neuen anfordern.
63
+ </p>
64
+ <% } %>
53
65
  </td>
54
66
  </tr>
55
67
  <tr>
@@ -50,6 +50,18 @@
50
50
  If the button doesn't work, copy and paste this link into your browser:<br>
51
51
  <a href="<%= link %>" style="color:#1d4ed8; word-break:break-all; overflow-wrap:break-word;"><%= link %></a>
52
52
  </p>
53
+ <%
54
+ /* Optional, like `appName`: a caller that omits it must not turn a password-reset
55
+ mail into a 500. Rendered only for a real, positive value — a deployment that
56
+ switched the expiry off (`0`) has no deadline to announce. */
57
+ const expiryMinutes = (typeof linkExpiresInMinutes !== 'undefined' && Number(linkExpiresInMinutes) > 0)
58
+ ? Number(linkExpiresInMinutes) : 0;
59
+ -%>
60
+ <% if (expiryMinutes) { %>
61
+ <p style="margin:16px 0 0 0; font-size:13px; line-height:20px; color:#737373;">
62
+ This link is valid for <%= expiryMinutes %> minutes. After that, just request a new one.
63
+ </p>
64
+ <% } %>
53
65
  </td>
54
66
  </tr>
55
67
  <tr>
@@ -1,3 +1,4 @@
1
1
  <h1>Hello <%= name %>,</h1>
2
2
  <p>you requested a link for setting a new password. Here it is.</p><br />
3
3
  <a href="<%= link %>">Reset password</a>
4
+ <% if (typeof linkExpiresInMinutes !== 'undefined' && Number(linkExpiresInMinutes) > 0) { %><br /><p>This link is valid for <%= Number(linkExpiresInMinutes) %> minutes. After that, just request a new one.</p><% } %>