@lenne.tech/nest-server 11.6.0 → 11.6.2

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 (87) hide show
  1. package/dist/config.env.js +141 -0
  2. package/dist/config.env.js.map +1 -1
  3. package/dist/core/common/decorators/graphql-populate.decorator.d.ts +2 -2
  4. package/dist/core/common/decorators/restricted.decorator.d.ts +1 -0
  5. package/dist/core/common/decorators/restricted.decorator.js +1 -1
  6. package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
  7. package/dist/core/common/helpers/input.helper.d.ts +1 -0
  8. package/dist/core/common/helpers/input.helper.js +1 -1
  9. package/dist/core/common/helpers/input.helper.js.map +1 -1
  10. package/dist/core/common/interceptors/check-security.interceptor.js +4 -3
  11. package/dist/core/common/interceptors/check-security.interceptor.js.map +1 -1
  12. package/dist/core/common/interfaces/server-options.interface.d.ts +50 -0
  13. package/dist/core/modules/auth/auth-guard-strategy.enum.d.ts +1 -0
  14. package/dist/core/modules/auth/auth-guard-strategy.enum.js +1 -0
  15. package/dist/core/modules/auth/auth-guard-strategy.enum.js.map +1 -1
  16. package/dist/core/modules/auth/guards/auth.guard.js +11 -5
  17. package/dist/core/modules/auth/guards/auth.guard.js.map +1 -1
  18. package/dist/core/modules/auth/tokens.decorator.d.ts +1 -1
  19. package/dist/core/modules/better-auth/better-auth-auth.model.d.ts +9 -0
  20. package/dist/core/modules/better-auth/better-auth-auth.model.js +63 -0
  21. package/dist/core/modules/better-auth/better-auth-auth.model.js.map +1 -0
  22. package/dist/core/modules/better-auth/better-auth-models.d.ts +44 -0
  23. package/dist/core/modules/better-auth/better-auth-models.js +185 -0
  24. package/dist/core/modules/better-auth/better-auth-models.js.map +1 -0
  25. package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.d.ts +12 -0
  26. package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.js +70 -0
  27. package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.js.map +1 -0
  28. package/dist/core/modules/better-auth/better-auth-rate-limiter.service.d.ts +32 -0
  29. package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js +173 -0
  30. package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js.map +1 -0
  31. package/dist/core/modules/better-auth/better-auth-user.mapper.d.ts +43 -0
  32. package/dist/core/modules/better-auth/better-auth-user.mapper.js +159 -0
  33. package/dist/core/modules/better-auth/better-auth-user.mapper.js.map +1 -0
  34. package/dist/core/modules/better-auth/better-auth.config.d.ts +9 -0
  35. package/dist/core/modules/better-auth/better-auth.config.js +251 -0
  36. package/dist/core/modules/better-auth/better-auth.config.js.map +1 -0
  37. package/dist/core/modules/better-auth/better-auth.middleware.d.ts +20 -0
  38. package/dist/core/modules/better-auth/better-auth.middleware.js +79 -0
  39. package/dist/core/modules/better-auth/better-auth.middleware.js.map +1 -0
  40. package/dist/core/modules/better-auth/better-auth.module.d.ts +30 -0
  41. package/dist/core/modules/better-auth/better-auth.module.js +265 -0
  42. package/dist/core/modules/better-auth/better-auth.module.js.map +1 -0
  43. package/dist/core/modules/better-auth/better-auth.resolver.d.ts +49 -0
  44. package/dist/core/modules/better-auth/better-auth.resolver.js +539 -0
  45. package/dist/core/modules/better-auth/better-auth.resolver.js.map +1 -0
  46. package/dist/core/modules/better-auth/better-auth.service.d.ts +38 -0
  47. package/dist/core/modules/better-auth/better-auth.service.js +151 -0
  48. package/dist/core/modules/better-auth/better-auth.service.js.map +1 -0
  49. package/dist/core/modules/better-auth/better-auth.types.d.ts +38 -0
  50. package/dist/core/modules/better-auth/better-auth.types.js +15 -0
  51. package/dist/core/modules/better-auth/better-auth.types.js.map +1 -0
  52. package/dist/core/modules/better-auth/index.d.ts +11 -0
  53. package/dist/core/modules/better-auth/index.js +28 -0
  54. package/dist/core/modules/better-auth/index.js.map +1 -0
  55. package/dist/core/modules/user/core-user.model.d.ts +2 -0
  56. package/dist/core/modules/user/core-user.model.js +21 -0
  57. package/dist/core/modules/user/core-user.model.js.map +1 -1
  58. package/dist/core.module.js +7 -0
  59. package/dist/core.module.js.map +1 -1
  60. package/dist/index.d.ts +1 -0
  61. package/dist/index.js +1 -0
  62. package/dist/index.js.map +1 -1
  63. package/dist/tsconfig.build.tsbuildinfo +1 -1
  64. package/package.json +9 -1
  65. package/src/config.env.ts +148 -1
  66. package/src/core/common/decorators/restricted.decorator.ts +2 -2
  67. package/src/core/common/helpers/input.helper.ts +2 -2
  68. package/src/core/common/interceptors/check-security.interceptor.ts +6 -5
  69. package/src/core/common/interfaces/server-options.interface.ts +344 -20
  70. package/src/core/modules/auth/auth-guard-strategy.enum.ts +1 -0
  71. package/src/core/modules/auth/guards/auth.guard.ts +20 -6
  72. package/src/core/modules/better-auth/README.md +1096 -0
  73. package/src/core/modules/better-auth/better-auth-auth.model.ts +69 -0
  74. package/src/core/modules/better-auth/better-auth-models.ts +143 -0
  75. package/src/core/modules/better-auth/better-auth-rate-limit.middleware.ts +113 -0
  76. package/src/core/modules/better-auth/better-auth-rate-limiter.service.ts +326 -0
  77. package/src/core/modules/better-auth/better-auth-user.mapper.ts +269 -0
  78. package/src/core/modules/better-auth/better-auth.config.ts +483 -0
  79. package/src/core/modules/better-auth/better-auth.middleware.ts +111 -0
  80. package/src/core/modules/better-auth/better-auth.module.ts +433 -0
  81. package/src/core/modules/better-auth/better-auth.resolver.ts +678 -0
  82. package/src/core/modules/better-auth/better-auth.service.ts +323 -0
  83. package/src/core/modules/better-auth/better-auth.types.ts +75 -0
  84. package/src/core/modules/better-auth/index.ts +25 -0
  85. package/src/core/modules/user/core-user.model.ts +29 -0
  86. package/src/core.module.ts +12 -0
  87. package/src/index.ts +6 -0
@@ -0,0 +1,269 @@
1
+ import { Injectable, Logger, Optional } from '@nestjs/common';
2
+ import { InjectConnection } from '@nestjs/mongoose';
3
+ import { Connection } from 'mongoose';
4
+
5
+ import { RoleEnum } from '../../common/enums/role.enum';
6
+
7
+ /**
8
+ * Interface for Better-Auth session user
9
+ */
10
+ export interface BetterAuthSessionUser {
11
+ createdAt?: Date;
12
+ email: string;
13
+ emailVerified?: boolean;
14
+ id: string;
15
+ image?: string;
16
+ name?: string;
17
+ updatedAt?: Date;
18
+ }
19
+
20
+ /**
21
+ * Interface for mapped user with role capabilities
22
+ */
23
+ export interface MappedUser {
24
+ /**
25
+ * Marker to identify Better-Auth authenticated users
26
+ * Used by AuthGuard to skip Passport authentication for these users
27
+ */
28
+ _authenticatedViaBetterAuth: true;
29
+ email: string;
30
+ emailVerified?: boolean;
31
+ hasRole: (roles: string | string[]) => boolean;
32
+ iamId: string;
33
+ id: string;
34
+ image?: string;
35
+ name?: string;
36
+ roles: string[];
37
+ /**
38
+ * Whether the user is verified (from our database)
39
+ * Used for S_VERIFIED role check
40
+ */
41
+ verified?: boolean;
42
+ }
43
+
44
+ /**
45
+ * Interface for synced user document returned from database
46
+ */
47
+ export interface SyncedUserDocument {
48
+ _id: any;
49
+ avatar?: string;
50
+ createdAt: Date;
51
+ email: string;
52
+ firstName?: string;
53
+ iamId: string;
54
+ lastName?: string;
55
+ password?: string;
56
+ roles: string[];
57
+ updatedAt: Date;
58
+ verified?: boolean;
59
+ }
60
+
61
+ /**
62
+ * Service to map Better-Auth users to the application's User model
63
+ *
64
+ * This service bridges the gap between Better-Auth's session-based users
65
+ * and the application's role-based security system.
66
+ */
67
+ @Injectable()
68
+ export class BetterAuthUserMapper {
69
+ private readonly logger = new Logger(BetterAuthUserMapper.name);
70
+
71
+ constructor(@Optional() @InjectConnection() private readonly connection?: Connection) {}
72
+
73
+ /**
74
+ * Maps a Better-Auth session user to a user with role capabilities
75
+ *
76
+ * This method:
77
+ * 1. Looks up the user in the application's user collection by email
78
+ * 2. If found, returns the full user with roles and hasRole() method
79
+ * 3. If not found, returns a minimal user with default roles
80
+ *
81
+ * @param sessionUser - The Better-Auth session user
82
+ * @returns A mapped user with role capabilities
83
+ */
84
+ async mapSessionUser(sessionUser: BetterAuthSessionUser): Promise<MappedUser | null> {
85
+ if (!sessionUser?.id || !sessionUser?.email) {
86
+ return null;
87
+ }
88
+
89
+ // If no database connection, return user with default roles
90
+ if (!this.connection) {
91
+ this.logger.warn('No database connection available - using default role mapping');
92
+ return this.createMappedUser({
93
+ email: sessionUser.email,
94
+ emailVerified: sessionUser.emailVerified,
95
+ iamId: sessionUser.id,
96
+ id: sessionUser.id,
97
+ image: sessionUser.image,
98
+ name: sessionUser.name,
99
+ roles: [],
100
+ verified: sessionUser.emailVerified, // Use Better-Auth emailVerified status
101
+ });
102
+ }
103
+
104
+ try {
105
+ // Look up the user in our database by email OR iamId
106
+ // This ensures we find the user regardless of which system they signed up with
107
+ const userCollection = this.connection.collection('users');
108
+ const dbUser = await userCollection.findOne({
109
+ $or: [{ email: sessionUser.email }, { iamId: sessionUser.id }],
110
+ });
111
+
112
+ if (dbUser) {
113
+ // User exists in our database - use their roles and verified status
114
+ const roles = Array.isArray(dbUser.roles) ? dbUser.roles : [];
115
+ // Use database verified status, fallback to Better-Auth emailVerified
116
+ const verified = dbUser.verified === true || sessionUser.emailVerified === true;
117
+
118
+ return this.createMappedUser({
119
+ email: sessionUser.email,
120
+ emailVerified: sessionUser.emailVerified,
121
+ iamId: sessionUser.id,
122
+ id: dbUser._id.toString(),
123
+ image: sessionUser.image,
124
+ name: sessionUser.name,
125
+ roles,
126
+ verified,
127
+ });
128
+ }
129
+
130
+ // User doesn't exist in our database yet
131
+ // This can happen if they signed up through Better-Auth but not legacy auth
132
+ // Return a user with default roles (S_USER since they're authenticated)
133
+ this.logger.debug(`Better-Auth user ${sessionUser.email} not found in users collection`);
134
+
135
+ return this.createMappedUser({
136
+ email: sessionUser.email,
137
+ emailVerified: sessionUser.emailVerified,
138
+ iamId: sessionUser.id,
139
+ id: sessionUser.id, // Use Better-Auth ID as fallback
140
+ image: sessionUser.image,
141
+ name: sessionUser.name,
142
+ roles: [], // No default roles - S_ roles are system checks, not actual roles
143
+ verified: sessionUser.emailVerified, // Use Better-Auth emailVerified status
144
+ });
145
+ } catch (error) {
146
+ this.logger.error(`Error mapping Better-Auth user: ${error instanceof Error ? error.message : 'Unknown error'}`);
147
+ return null;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Creates a mapped user object with the hasRole method
153
+ */
154
+ private createMappedUser(userData: Omit<MappedUser, '_authenticatedViaBetterAuth' | 'hasRole'>): MappedUser {
155
+ const roles = userData.roles || [];
156
+
157
+ return {
158
+ ...userData,
159
+ _authenticatedViaBetterAuth: true,
160
+ hasRole: (checkRoles: string | string[]): boolean => {
161
+ const rolesToCheck = Array.isArray(checkRoles) ? checkRoles : [checkRoles];
162
+
163
+ // Check for special roles
164
+ if (rolesToCheck.includes(RoleEnum.S_EVERYONE)) {
165
+ return true;
166
+ }
167
+
168
+ if (rolesToCheck.includes(RoleEnum.S_USER)) {
169
+ return true; // User is authenticated via Better-Auth
170
+ }
171
+
172
+ if (rolesToCheck.includes(RoleEnum.S_NO_ONE)) {
173
+ return false;
174
+ }
175
+
176
+ // S_VERIFIED check - uses verified field (from DB or Better-Auth emailVerified)
177
+ if (rolesToCheck.includes(RoleEnum.S_VERIFIED)) {
178
+ return userData.verified === true;
179
+ }
180
+
181
+ // Check actual roles
182
+ return rolesToCheck.some((role) => roles.includes(role));
183
+ },
184
+ roles,
185
+ };
186
+ }
187
+
188
+ /**
189
+ * Links an existing user or creates a new user from Better-Auth session data
190
+ *
191
+ * This method:
192
+ * 1. Searches by email OR iamId (supports both login paths)
193
+ * 2. Creates new user if not found (with default S_USER role)
194
+ * 3. Links existing user by setting iamId
195
+ *
196
+ * NOTE: No password handling is needed because both Legacy Auth and Better-Auth
197
+ * use bcrypt-compatible password hashing. Users can authenticate with either system.
198
+ *
199
+ * @param sessionUser - The Better-Auth session user
200
+ * @param additionalData - Additional data to set on the user
201
+ * @returns The linked/created user document or null on error
202
+ */
203
+ async linkOrCreateUser(
204
+ sessionUser: BetterAuthSessionUser,
205
+ additionalData?: Record<string, any>,
206
+ ): Promise<null | SyncedUserDocument> {
207
+ if (!sessionUser?.email) {
208
+ return null;
209
+ }
210
+
211
+ // Cannot sync without database connection
212
+ if (!this.connection) {
213
+ this.logger.warn('No database connection available - cannot sync user');
214
+ return null;
215
+ }
216
+
217
+ try {
218
+ const userCollection = this.connection.collection('users');
219
+
220
+ // Check if user already exists
221
+ const existingUser = await userCollection.findOne({
222
+ $or: [{ email: sessionUser.email }, { iamId: sessionUser.id }],
223
+ });
224
+
225
+ const updateData: Record<string, any> = {
226
+ email: sessionUser.email,
227
+ ...(sessionUser.name && { firstName: sessionUser.name.split(' ')[0] }),
228
+ ...(sessionUser.name &&
229
+ sessionUser.name.includes(' ') && {
230
+ lastName: sessionUser.name.split(' ').slice(1).join(' '),
231
+ }),
232
+ ...(sessionUser.emailVerified !== undefined && { verified: sessionUser.emailVerified }),
233
+ ...(sessionUser.image && { avatar: sessionUser.image }),
234
+ iamId: sessionUser.id,
235
+ updatedAt: new Date(),
236
+ ...additionalData,
237
+ };
238
+
239
+ // Build the update query
240
+ const updateQuery: Record<string, any> = {
241
+ $set: updateData,
242
+ };
243
+
244
+ // Only set defaults on insert (new user)
245
+ if (!existingUser) {
246
+ updateQuery.$setOnInsert = {
247
+ createdAt: new Date(),
248
+ roles: [],
249
+ };
250
+ }
251
+
252
+ const result = await userCollection.findOneAndUpdate(
253
+ {
254
+ $or: [{ email: sessionUser.email }, { iamId: sessionUser.id }],
255
+ },
256
+ updateQuery,
257
+ {
258
+ returnDocument: 'after',
259
+ upsert: true,
260
+ },
261
+ );
262
+
263
+ return result as null | SyncedUserDocument;
264
+ } catch (error) {
265
+ this.logger.error(`Error syncing Better-Auth user: ${error instanceof Error ? error.message : 'Unknown error'}`);
266
+ return null;
267
+ }
268
+ }
269
+ }