@lenne.tech/nest-server 11.6.1 → 11.7.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 (120) hide show
  1. package/dist/config.env.js +132 -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/filter.helper.d.ts +9 -9
  8. package/dist/core/common/helpers/filter.helper.js +2 -4
  9. package/dist/core/common/helpers/filter.helper.js.map +1 -1
  10. package/dist/core/common/helpers/gridfs.helper.js +3 -3
  11. package/dist/core/common/helpers/gridfs.helper.js.map +1 -1
  12. package/dist/core/common/helpers/input.helper.d.ts +1 -0
  13. package/dist/core/common/helpers/input.helper.js +1 -1
  14. package/dist/core/common/helpers/input.helper.js.map +1 -1
  15. package/dist/core/common/interfaces/server-options.interface.d.ts +51 -0
  16. package/dist/core/common/services/crud.service.d.ts +16 -16
  17. package/dist/core/common/services/crud.service.js +1 -1
  18. package/dist/core/common/services/crud.service.js.map +1 -1
  19. package/dist/core/modules/auth/auth-guard-strategy.enum.d.ts +1 -0
  20. package/dist/core/modules/auth/auth-guard-strategy.enum.js +1 -0
  21. package/dist/core/modules/auth/auth-guard-strategy.enum.js.map +1 -1
  22. package/dist/core/modules/auth/guards/auth.guard.js +11 -5
  23. package/dist/core/modules/auth/guards/auth.guard.js.map +1 -1
  24. package/dist/core/modules/auth/tokens.decorator.d.ts +1 -1
  25. package/dist/core/modules/better-auth/better-auth-auth.model.d.ts +9 -0
  26. package/dist/core/modules/better-auth/better-auth-auth.model.js +63 -0
  27. package/dist/core/modules/better-auth/better-auth-auth.model.js.map +1 -0
  28. package/dist/core/modules/better-auth/better-auth-models.d.ts +43 -0
  29. package/dist/core/modules/better-auth/better-auth-models.js +181 -0
  30. package/dist/core/modules/better-auth/better-auth-models.js.map +1 -0
  31. package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.d.ts +12 -0
  32. package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.js +70 -0
  33. package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.js.map +1 -0
  34. package/dist/core/modules/better-auth/better-auth-rate-limiter.service.d.ts +32 -0
  35. package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js +173 -0
  36. package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js.map +1 -0
  37. package/dist/core/modules/better-auth/better-auth-user.mapper.d.ts +43 -0
  38. package/dist/core/modules/better-auth/better-auth-user.mapper.js +159 -0
  39. package/dist/core/modules/better-auth/better-auth-user.mapper.js.map +1 -0
  40. package/dist/core/modules/better-auth/better-auth.config.d.ts +9 -0
  41. package/dist/core/modules/better-auth/better-auth.config.js +254 -0
  42. package/dist/core/modules/better-auth/better-auth.config.js.map +1 -0
  43. package/dist/core/modules/better-auth/better-auth.middleware.d.ts +20 -0
  44. package/dist/core/modules/better-auth/better-auth.middleware.js +79 -0
  45. package/dist/core/modules/better-auth/better-auth.middleware.js.map +1 -0
  46. package/dist/core/modules/better-auth/better-auth.module.d.ts +38 -0
  47. package/dist/core/modules/better-auth/better-auth.module.js +253 -0
  48. package/dist/core/modules/better-auth/better-auth.module.js.map +1 -0
  49. package/dist/core/modules/better-auth/better-auth.resolver.d.ts +45 -0
  50. package/dist/core/modules/better-auth/better-auth.resolver.js +221 -0
  51. package/dist/core/modules/better-auth/better-auth.resolver.js.map +1 -0
  52. package/dist/core/modules/better-auth/better-auth.service.d.ts +37 -0
  53. package/dist/core/modules/better-auth/better-auth.service.js +148 -0
  54. package/dist/core/modules/better-auth/better-auth.service.js.map +1 -0
  55. package/dist/core/modules/better-auth/better-auth.types.d.ts +39 -0
  56. package/dist/core/modules/better-auth/better-auth.types.js +26 -0
  57. package/dist/core/modules/better-auth/better-auth.types.js.map +1 -0
  58. package/dist/core/modules/better-auth/core-better-auth.controller.d.ts +66 -0
  59. package/dist/core/modules/better-auth/core-better-auth.controller.js +491 -0
  60. package/dist/core/modules/better-auth/core-better-auth.controller.js.map +1 -0
  61. package/dist/core/modules/better-auth/core-better-auth.resolver.d.ts +59 -0
  62. package/dist/core/modules/better-auth/core-better-auth.resolver.js +538 -0
  63. package/dist/core/modules/better-auth/core-better-auth.resolver.js.map +1 -0
  64. package/dist/core/modules/better-auth/index.d.ts +13 -0
  65. package/dist/core/modules/better-auth/index.js +30 -0
  66. package/dist/core/modules/better-auth/index.js.map +1 -0
  67. package/dist/core/modules/user/core-user.model.d.ts +2 -0
  68. package/dist/core/modules/user/core-user.model.js +21 -0
  69. package/dist/core/modules/user/core-user.model.js.map +1 -1
  70. package/dist/core.module.js +7 -0
  71. package/dist/core.module.js.map +1 -1
  72. package/dist/index.d.ts +1 -0
  73. package/dist/index.js +1 -0
  74. package/dist/index.js.map +1 -1
  75. package/dist/server/modules/better-auth/better-auth.controller.d.ts +10 -0
  76. package/dist/server/modules/better-auth/better-auth.controller.js +36 -0
  77. package/dist/server/modules/better-auth/better-auth.controller.js.map +1 -0
  78. package/dist/server/modules/better-auth/better-auth.module.d.ts +9 -0
  79. package/dist/server/modules/better-auth/better-auth.module.js +44 -0
  80. package/dist/server/modules/better-auth/better-auth.module.js.map +1 -0
  81. package/dist/server/modules/better-auth/better-auth.resolver.d.ts +45 -0
  82. package/dist/server/modules/better-auth/better-auth.resolver.js +221 -0
  83. package/dist/server/modules/better-auth/better-auth.resolver.js.map +1 -0
  84. package/dist/server/modules/file/file-info.model.d.ts +71 -3
  85. package/dist/server/modules/user/user.model.d.ts +169 -3
  86. package/dist/server/server.module.js +6 -1
  87. package/dist/server/server.module.js.map +1 -1
  88. package/dist/tsconfig.build.tsbuildinfo +1 -1
  89. package/package.json +21 -22
  90. package/src/config.env.ts +139 -1
  91. package/src/core/common/decorators/restricted.decorator.ts +2 -2
  92. package/src/core/common/helpers/filter.helper.ts +15 -17
  93. package/src/core/common/helpers/gridfs.helper.ts +5 -5
  94. package/src/core/common/helpers/input.helper.ts +2 -2
  95. package/src/core/common/interfaces/server-options.interface.ts +377 -20
  96. package/src/core/common/services/crud.service.ts +22 -22
  97. package/src/core/modules/auth/auth-guard-strategy.enum.ts +1 -0
  98. package/src/core/modules/auth/guards/auth.guard.ts +20 -6
  99. package/src/core/modules/better-auth/README.md +1422 -0
  100. package/src/core/modules/better-auth/better-auth-auth.model.ts +69 -0
  101. package/src/core/modules/better-auth/better-auth-models.ts +140 -0
  102. package/src/core/modules/better-auth/better-auth-rate-limit.middleware.ts +113 -0
  103. package/src/core/modules/better-auth/better-auth-rate-limiter.service.ts +326 -0
  104. package/src/core/modules/better-auth/better-auth-user.mapper.ts +269 -0
  105. package/src/core/modules/better-auth/better-auth.config.ts +488 -0
  106. package/src/core/modules/better-auth/better-auth.middleware.ts +111 -0
  107. package/src/core/modules/better-auth/better-auth.module.ts +474 -0
  108. package/src/core/modules/better-auth/better-auth.resolver.ts +213 -0
  109. package/src/core/modules/better-auth/better-auth.service.ts +314 -0
  110. package/src/core/modules/better-auth/better-auth.types.ts +90 -0
  111. package/src/core/modules/better-auth/core-better-auth.controller.ts +605 -0
  112. package/src/core/modules/better-auth/core-better-auth.resolver.ts +705 -0
  113. package/src/core/modules/better-auth/index.ts +32 -0
  114. package/src/core/modules/user/core-user.model.ts +29 -0
  115. package/src/core.module.ts +13 -0
  116. package/src/index.ts +6 -0
  117. package/src/server/modules/better-auth/better-auth.controller.ts +41 -0
  118. package/src/server/modules/better-auth/better-auth.module.ts +88 -0
  119. package/src/server/modules/better-auth/better-auth.resolver.ts +201 -0
  120. package/src/server/server.module.ts +10 -1
@@ -0,0 +1,314 @@
1
+ import { Inject, Injectable, Logger, Optional } from '@nestjs/common';
2
+ import { Request } from 'express';
3
+
4
+ import { IBetterAuth } from '../../common/interfaces/server-options.interface';
5
+ import { ConfigService } from '../../common/services/config.service';
6
+ import { BetterAuthSessionUser } from './better-auth-user.mapper';
7
+ import { BetterAuthInstance } from './better-auth.config';
8
+ import { BETTER_AUTH_INSTANCE } from './better-auth.module';
9
+
10
+ /**
11
+ * Result of a session validation
12
+ */
13
+ export interface SessionResult {
14
+ session: null | {
15
+ [key: string]: any;
16
+ expiresAt: Date;
17
+ id: string;
18
+ userId: string;
19
+ };
20
+ user: BetterAuthSessionUser | null;
21
+ }
22
+
23
+ /**
24
+ * BetterAuthService provides a NestJS-friendly wrapper around the better-auth instance.
25
+ *
26
+ * This service:
27
+ * - Provides access to the better-auth API
28
+ * - Offers typed methods for common auth operations
29
+ * - Handles the case when better-auth is disabled gracefully
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * @Injectable()
34
+ * export class MyService {
35
+ * constructor(private readonly betterAuthService: BetterAuthService) {}
36
+ *
37
+ * async doSomething() {
38
+ * if (this.betterAuthService.isEnabled()) {
39
+ * const api = this.betterAuthService.getApi();
40
+ * // Use better-auth API
41
+ * }
42
+ * }
43
+ * }
44
+ * ```
45
+ */
46
+ @Injectable()
47
+ export class BetterAuthService {
48
+ private readonly logger = new Logger(BetterAuthService.name);
49
+ private readonly config: IBetterAuth;
50
+
51
+ constructor(
52
+ @Optional() @Inject(BETTER_AUTH_INSTANCE) private readonly authInstance: BetterAuthInstance | null,
53
+ @Optional() private readonly configService?: ConfigService,
54
+ ) {
55
+ // Better-Auth is enabled by default (zero-config) - only disabled if explicitly set to false
56
+ this.config = this.configService?.get<IBetterAuth>('betterAuth') || {};
57
+ }
58
+
59
+ /**
60
+ * Checks if better-auth is enabled and initialized
61
+ * Returns true only if:
62
+ * 1. The better-auth instance was successfully created (not null/undefined)
63
+ * 2. Better-Auth was not explicitly disabled (enabled !== false)
64
+ *
65
+ * Better-Auth is enabled by default unless explicitly set to enabled: false
66
+ */
67
+ isEnabled(): boolean {
68
+ // First check: authInstance must exist (not null or undefined)
69
+ if (!this.authInstance) {
70
+ return false;
71
+ }
72
+ // Second check: must not be explicitly disabled
73
+ return this.config?.enabled !== false;
74
+ }
75
+
76
+ /**
77
+ * Gets the better-auth instance
78
+ * Returns null if better-auth is disabled
79
+ */
80
+ getInstance(): BetterAuthInstance | null {
81
+ return this.authInstance ?? null;
82
+ }
83
+
84
+ /**
85
+ * Gets the better-auth API for direct access to endpoints
86
+ * Returns null if better-auth is disabled
87
+ */
88
+ getApi(): BetterAuthInstance['api'] | null {
89
+ return this.authInstance?.api || null;
90
+ }
91
+
92
+ /**
93
+ * Gets the current better-auth configuration
94
+ */
95
+ getConfig(): IBetterAuth {
96
+ return this.config;
97
+ }
98
+
99
+ /**
100
+ * Checks if JWT plugin is enabled.
101
+ * JWT is enabled by default when the jwt config block is present,
102
+ * unless explicitly disabled with enabled: false.
103
+ */
104
+ isJwtEnabled(): boolean {
105
+ return this.isEnabled() && !!this.config.jwt && this.config.jwt.enabled !== false;
106
+ }
107
+
108
+ /**
109
+ * Checks if 2FA is enabled.
110
+ * 2FA is enabled by default when the twoFactor config block is present,
111
+ * unless explicitly disabled with enabled: false.
112
+ */
113
+ isTwoFactorEnabled(): boolean {
114
+ return this.isEnabled() && !!this.config.twoFactor && this.config.twoFactor.enabled !== false;
115
+ }
116
+
117
+ /**
118
+ * Checks if Passkey/WebAuthn is enabled.
119
+ * Passkey is enabled by default when the passkey config block is present,
120
+ * unless explicitly disabled with enabled: false.
121
+ */
122
+ isPasskeyEnabled(): boolean {
123
+ return this.isEnabled() && !!this.config.passkey && this.config.passkey.enabled !== false;
124
+ }
125
+
126
+ /**
127
+ * Gets the list of enabled social providers
128
+ * Dynamically iterates over all configured providers.
129
+ *
130
+ * A provider is considered enabled if:
131
+ * - It has clientId and clientSecret configured
132
+ * - It is NOT explicitly disabled (enabled !== false)
133
+ *
134
+ * This follows the same "enabled by default" pattern as Better-Auth itself.
135
+ */
136
+ getEnabledSocialProviders(): string[] {
137
+ if (!this.isEnabled()) {
138
+ return [];
139
+ }
140
+
141
+ const providers: string[] = [];
142
+
143
+ // Dynamically iterate over all configured social providers
144
+ if (this.config.socialProviders) {
145
+ for (const [name, provider] of Object.entries(this.config.socialProviders)) {
146
+ // Provider is enabled if: has credentials AND not explicitly disabled
147
+ if (provider?.clientId && provider?.clientSecret && provider?.enabled !== false) {
148
+ providers.push(name);
149
+ }
150
+ }
151
+ }
152
+
153
+ return providers;
154
+ }
155
+
156
+ /**
157
+ * Gets the base path for better-auth endpoints
158
+ */
159
+ getBasePath(): string {
160
+ return this.config.basePath || '/iam';
161
+ }
162
+
163
+ /**
164
+ * Gets the base URL for better-auth
165
+ */
166
+ getBaseUrl(): string {
167
+ return this.config.baseUrl || 'http://localhost:3000';
168
+ }
169
+
170
+ // ===================================================================================================================
171
+ // Session Management Methods
172
+ // ===================================================================================================================
173
+
174
+ /**
175
+ * Gets the current session from request headers
176
+ *
177
+ * @param req - Express request object or headers object
178
+ * @returns Session and user data, or null values if no valid session
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * const { session, user } = await betterAuthService.getSession(req);
183
+ * if (session) {
184
+ * console.log('User:', user.email);
185
+ * console.log('Session expires:', session.expiresAt);
186
+ * }
187
+ * ```
188
+ */
189
+ async getSession(req: Request | { headers: Record<string, string | string[] | undefined> }): Promise<SessionResult> {
190
+ if (!this.isEnabled()) {
191
+ return { session: null, user: null };
192
+ }
193
+
194
+ const api = this.getApi();
195
+ if (!api) {
196
+ return { session: null, user: null };
197
+ }
198
+
199
+ try {
200
+ // Convert headers to the format Better-Auth expects
201
+ const headers = new Headers();
202
+ const reqHeaders = 'headers' in req ? req.headers : {};
203
+
204
+ for (const [key, value] of Object.entries(reqHeaders)) {
205
+ if (typeof value === 'string') {
206
+ headers.set(key, value);
207
+ } else if (Array.isArray(value)) {
208
+ headers.set(key, value.join(', '));
209
+ }
210
+ }
211
+
212
+ const response = await api.getSession({ headers });
213
+
214
+ if (response && typeof response === 'object' && 'user' in response) {
215
+ return response as SessionResult;
216
+ }
217
+
218
+ return { session: null, user: null };
219
+ } catch (error) {
220
+ this.logger.debug(`getSession error: ${error instanceof Error ? error.message : 'Unknown error'}`);
221
+ return { session: null, user: null };
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Revokes a specific session (logout for that session)
227
+ *
228
+ * This should be called when a user wants to log out from a specific device/session.
229
+ * The session token is typically stored in a cookie or sent as a bearer token.
230
+ *
231
+ * @param sessionToken - The session token to revoke
232
+ * @returns true if session was revoked, false otherwise
233
+ *
234
+ * @example
235
+ * ```typescript
236
+ * // Get session token from cookie or header
237
+ * const sessionToken = req.cookies['better-auth.session_token'];
238
+ * const success = await betterAuthService.revokeSession(sessionToken);
239
+ * if (success) {
240
+ * res.clearCookie('better-auth.session_token');
241
+ * }
242
+ * ```
243
+ */
244
+ async revokeSession(sessionToken: string): Promise<boolean> {
245
+ if (!this.isEnabled() || !sessionToken) {
246
+ return false;
247
+ }
248
+
249
+ const api = this.getApi();
250
+ if (!api) {
251
+ return false;
252
+ }
253
+
254
+ try {
255
+ // Create headers with the session token
256
+ const headers = new Headers();
257
+ headers.set('Authorization', `Bearer ${sessionToken}`);
258
+
259
+ // Call Better-Auth's signOut endpoint
260
+ await api.signOut({ headers });
261
+
262
+ this.logger.debug('Session revoked successfully');
263
+ return true;
264
+ } catch (error) {
265
+ this.logger.debug(`revokeSession error: ${error instanceof Error ? error.message : 'Unknown error'}`);
266
+ return false;
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Checks if a session is close to expiring
272
+ *
273
+ * @param session - The session object from getSession()
274
+ * @param thresholdMinutes - Minutes before expiry to consider "close to expiring" (default: 5)
275
+ * @returns true if session expires within the threshold
276
+ *
277
+ * @example
278
+ * ```typescript
279
+ * const { session } = await betterAuthService.getSession(req);
280
+ * if (session && betterAuthService.isSessionExpiringSoon(session)) {
281
+ * // Prompt user to refresh their session
282
+ * }
283
+ * ```
284
+ */
285
+ isSessionExpiringSoon(session: SessionResult['session'], thresholdMinutes: number = 5): boolean {
286
+ if (!session?.expiresAt) {
287
+ return true; // No session or no expiry = treat as expiring
288
+ }
289
+
290
+ const expiresAt = new Date(session.expiresAt);
291
+ const now = new Date();
292
+ const thresholdMs = thresholdMinutes * 60 * 1000;
293
+
294
+ return expiresAt.getTime() - now.getTime() < thresholdMs;
295
+ }
296
+
297
+ /**
298
+ * Gets the remaining session time in seconds
299
+ *
300
+ * @param session - The session object from getSession()
301
+ * @returns Seconds until session expires, or 0 if expired/invalid
302
+ */
303
+ getSessionTimeRemaining(session: SessionResult['session']): number {
304
+ if (!session?.expiresAt) {
305
+ return 0;
306
+ }
307
+
308
+ const expiresAt = new Date(session.expiresAt);
309
+ const now = new Date();
310
+ const remaining = Math.max(0, Math.floor((expiresAt.getTime() - now.getTime()) / 1000));
311
+
312
+ return remaining;
313
+ }
314
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Type definitions for Better-Auth API responses
3
+ *
4
+ * These types provide type safety for interactions with the Better-Auth API
5
+ * and reduce the need for `as any` casts throughout the codebase.
6
+ */
7
+
8
+ import { BetterAuthSessionUser } from './better-auth-user.mapper';
9
+
10
+ /**
11
+ * Better-Auth 2FA verification response
12
+ */
13
+ export interface BetterAuth2FAResponse {
14
+ session?: BetterAuthSessionResponse['session'];
15
+ token?: string;
16
+ user?: BetterAuthSessionUser;
17
+ }
18
+
19
+ /**
20
+ * Better-Auth session response from getSession API
21
+ */
22
+ export interface BetterAuthSessionResponse {
23
+ session: {
24
+ createdAt: Date;
25
+ expiresAt: Date;
26
+ id: string;
27
+ token: string;
28
+ updatedAt: Date;
29
+ userId: string;
30
+ };
31
+ user: BetterAuthSessionUser;
32
+ }
33
+
34
+ /**
35
+ * Better-Auth sign-in response
36
+ */
37
+ export interface BetterAuthSignInResponse {
38
+ session?: BetterAuthSessionResponse['session'];
39
+ token?: string;
40
+ twoFactorRedirect?: boolean;
41
+ user?: BetterAuthSessionUser;
42
+ }
43
+
44
+ /**
45
+ * Better-Auth sign-up response
46
+ */
47
+ export interface BetterAuthSignUpResponse {
48
+ session?: BetterAuthSessionResponse['session'];
49
+ user?: BetterAuthSessionUser;
50
+ }
51
+
52
+ /**
53
+ * Type guard to check if response has session
54
+ * Preserves the original type while asserting session is defined
55
+ */
56
+ export function hasSession<T>(response: T): response is T & { session: { expiresAt: Date; id: string } } {
57
+ return (
58
+ response !== null &&
59
+ typeof response === 'object' &&
60
+ 'session' in response &&
61
+ (response as { session?: unknown }).session !== null &&
62
+ (response as { session?: unknown }).session !== undefined
63
+ );
64
+ }
65
+
66
+ /**
67
+ * Type guard to check if response has user
68
+ * Preserves the original type while asserting user is defined
69
+ */
70
+ export function hasUser<T>(response: T): response is T & { user: BetterAuthSessionUser } {
71
+ return (
72
+ response !== null &&
73
+ typeof response === 'object' &&
74
+ 'user' in response &&
75
+ (response as { user?: unknown }).user !== null &&
76
+ (response as { user?: unknown }).user !== undefined
77
+ );
78
+ }
79
+
80
+ /**
81
+ * Type guard to check if response requires 2FA
82
+ */
83
+ export function requires2FA<T>(response: T): response is T & { twoFactorRedirect: true } {
84
+ return (
85
+ response !== null &&
86
+ typeof response === 'object' &&
87
+ 'twoFactorRedirect' in response &&
88
+ (response as { twoFactorRedirect?: boolean }).twoFactorRedirect === true
89
+ );
90
+ }