@lenne.tech/nest-server 11.9.0 → 11.10.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 (131) hide show
  1. package/dist/config.env.js +2 -0
  2. package/dist/config.env.js.map +1 -1
  3. package/dist/core/common/helpers/logging.helper.d.ts +6 -0
  4. package/dist/core/common/helpers/logging.helper.js +55 -0
  5. package/dist/core/common/helpers/logging.helper.js.map +1 -0
  6. package/dist/core/common/interfaces/server-options.interface.d.ts +37 -19
  7. package/dist/core/modules/auth/guards/roles.guard.js +33 -2
  8. package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
  9. package/dist/core/modules/auth/services/core-auth.service.d.ts +5 -5
  10. package/dist/core/modules/auth/services/core-auth.service.js +4 -4
  11. package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
  12. package/dist/core/modules/auth/tokens.decorator.d.ts +1 -1
  13. package/dist/core/modules/better-auth/better-auth.config.js +32 -10
  14. package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
  15. package/dist/core/modules/better-auth/better-auth.resolver.d.ts +16 -16
  16. package/dist/core/modules/better-auth/better-auth.resolver.js +34 -34
  17. package/dist/core/modules/better-auth/better-auth.resolver.js.map +1 -1
  18. package/dist/core/modules/better-auth/better-auth.types.d.ts +2 -1
  19. package/dist/core/modules/better-auth/better-auth.types.js.map +1 -1
  20. package/dist/core/modules/better-auth/core-better-auth-api.middleware.d.ts +10 -0
  21. package/dist/core/modules/better-auth/core-better-auth-api.middleware.js +91 -0
  22. package/dist/core/modules/better-auth/core-better-auth-api.middleware.js.map +1 -0
  23. package/dist/core/modules/better-auth/core-better-auth-auth.model.d.ts +9 -0
  24. package/dist/core/modules/better-auth/{better-auth-auth.model.js → core-better-auth-auth.model.js} +17 -17
  25. package/dist/core/modules/better-auth/core-better-auth-auth.model.js.map +1 -0
  26. package/dist/core/modules/better-auth/{better-auth-migration-status.model.d.ts → core-better-auth-migration-status.model.d.ts} +1 -1
  27. package/dist/core/modules/better-auth/{better-auth-migration-status.model.js → core-better-auth-migration-status.model.js} +14 -14
  28. package/dist/core/modules/better-auth/core-better-auth-migration-status.model.js.map +1 -0
  29. package/dist/core/modules/better-auth/{better-auth-models.d.ts → core-better-auth-models.d.ts} +8 -8
  30. package/dist/core/modules/better-auth/{better-auth-models.js → core-better-auth-models.js} +61 -61
  31. package/dist/core/modules/better-auth/core-better-auth-models.js.map +1 -0
  32. package/dist/core/modules/better-auth/core-better-auth-rate-limit.middleware.d.ts +12 -0
  33. package/dist/core/modules/better-auth/{better-auth-rate-limit.middleware.js → core-better-auth-rate-limit.middleware.js} +10 -10
  34. package/dist/core/modules/better-auth/core-better-auth-rate-limit.middleware.js.map +1 -0
  35. package/dist/core/modules/better-auth/{better-auth-rate-limiter.service.d.ts → core-better-auth-rate-limiter.service.d.ts} +1 -1
  36. package/dist/core/modules/better-auth/{better-auth-rate-limiter.service.js → core-better-auth-rate-limiter.service.js} +8 -8
  37. package/dist/core/modules/better-auth/core-better-auth-rate-limiter.service.js.map +1 -0
  38. package/dist/core/modules/better-auth/{better-auth-user.mapper.d.ts → core-better-auth-user.mapper.d.ts} +1 -1
  39. package/dist/core/modules/better-auth/{better-auth-user.mapper.js → core-better-auth-user.mapper.js} +10 -9
  40. package/dist/core/modules/better-auth/core-better-auth-user.mapper.js.map +1 -0
  41. package/dist/core/modules/better-auth/core-better-auth-web.helper.d.ts +19 -0
  42. package/dist/core/modules/better-auth/core-better-auth-web.helper.js +152 -0
  43. package/dist/core/modules/better-auth/core-better-auth-web.helper.js.map +1 -0
  44. package/dist/core/modules/better-auth/core-better-auth.controller.d.ts +23 -32
  45. package/dist/core/modules/better-auth/core-better-auth.controller.js +184 -201
  46. package/dist/core/modules/better-auth/core-better-auth.controller.js.map +1 -1
  47. package/dist/core/modules/better-auth/core-better-auth.middleware.d.ts +22 -0
  48. package/dist/core/modules/better-auth/{better-auth.middleware.js → core-better-auth.middleware.js} +45 -18
  49. package/dist/core/modules/better-auth/core-better-auth.middleware.js.map +1 -0
  50. package/dist/core/modules/better-auth/{better-auth.module.d.ts → core-better-auth.module.d.ts} +6 -6
  51. package/dist/core/modules/better-auth/{better-auth.module.js → core-better-auth.module.js} +65 -60
  52. package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -0
  53. package/dist/core/modules/better-auth/core-better-auth.resolver.d.ts +19 -19
  54. package/dist/core/modules/better-auth/core-better-auth.resolver.js +18 -18
  55. package/dist/core/modules/better-auth/core-better-auth.resolver.js.map +1 -1
  56. package/dist/core/modules/better-auth/{better-auth.service.d.ts → core-better-auth.service.d.ts} +3 -2
  57. package/dist/core/modules/better-auth/{better-auth.service.js → core-better-auth.service.js} +75 -35
  58. package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -0
  59. package/dist/core/modules/better-auth/index.d.ts +11 -9
  60. package/dist/core/modules/better-auth/index.js +11 -9
  61. package/dist/core/modules/better-auth/index.js.map +1 -1
  62. package/dist/core/modules/error-code/core-error-code.controller.js.map +1 -1
  63. package/dist/core/modules/user/interfaces/core-user-service-options.interface.d.ts +2 -2
  64. package/dist/core.module.js +6 -6
  65. package/dist/core.module.js.map +1 -1
  66. package/dist/index.d.ts +1 -0
  67. package/dist/index.js +1 -0
  68. package/dist/index.js.map +1 -1
  69. package/dist/server/modules/better-auth/better-auth.controller.d.ts +5 -5
  70. package/dist/server/modules/better-auth/better-auth.controller.js +4 -4
  71. package/dist/server/modules/better-auth/better-auth.controller.js.map +1 -1
  72. package/dist/server/modules/better-auth/better-auth.module.js +3 -3
  73. package/dist/server/modules/better-auth/better-auth.module.js.map +1 -1
  74. package/dist/server/modules/better-auth/better-auth.resolver.d.ts +17 -17
  75. package/dist/server/modules/better-auth/better-auth.resolver.js +18 -18
  76. package/dist/server/modules/better-auth/better-auth.resolver.js.map +1 -1
  77. package/dist/server/modules/user/user.service.d.ts +2 -2
  78. package/dist/server/modules/user/user.service.js +2 -2
  79. package/dist/server/modules/user/user.service.js.map +1 -1
  80. package/dist/test/test.helper.d.ts +1 -0
  81. package/dist/test/test.helper.js +5 -1
  82. package/dist/test/test.helper.js.map +1 -1
  83. package/dist/tsconfig.build.tsbuildinfo +1 -1
  84. package/package.json +5 -3
  85. package/src/config.env.ts +15 -0
  86. package/src/core/common/helpers/logging.helper.ts +134 -0
  87. package/src/core/common/interfaces/server-options.interface.ts +419 -234
  88. package/src/core/modules/auth/guards/roles.guard.ts +44 -3
  89. package/src/core/modules/auth/services/core-auth.service.ts +4 -4
  90. package/src/core/modules/better-auth/ARCHITECTURE.md +102 -0
  91. package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +277 -8
  92. package/src/core/modules/better-auth/README.md +97 -53
  93. package/src/core/modules/better-auth/better-auth.config.ts +66 -18
  94. package/src/core/modules/better-auth/better-auth.resolver.ts +32 -32
  95. package/src/core/modules/better-auth/better-auth.types.ts +3 -2
  96. package/src/core/modules/better-auth/core-better-auth-api.middleware.ts +134 -0
  97. package/src/core/modules/better-auth/{better-auth-auth.model.ts → core-better-auth-auth.model.ts} +6 -6
  98. package/src/core/modules/better-auth/{better-auth-migration-status.model.ts → core-better-auth-migration-status.model.ts} +1 -1
  99. package/src/core/modules/better-auth/{better-auth-models.ts → core-better-auth-models.ts} +9 -9
  100. package/src/core/modules/better-auth/{better-auth-rate-limit.middleware.ts → core-better-auth-rate-limit.middleware.ts} +5 -5
  101. package/src/core/modules/better-auth/{better-auth-rate-limiter.service.ts → core-better-auth-rate-limiter.service.ts} +2 -2
  102. package/src/core/modules/better-auth/{better-auth-user.mapper.ts → core-better-auth-user.mapper.ts} +4 -3
  103. package/src/core/modules/better-auth/core-better-auth-web.helper.ts +272 -0
  104. package/src/core/modules/better-auth/core-better-auth.controller.ts +386 -230
  105. package/src/core/modules/better-auth/{better-auth.middleware.ts → core-better-auth.middleware.ts} +57 -17
  106. package/src/core/modules/better-auth/{better-auth.module.ts → core-better-auth.module.ts} +77 -66
  107. package/src/core/modules/better-auth/core-better-auth.resolver.ts +42 -42
  108. package/src/core/modules/better-auth/{better-auth.service.ts → core-better-auth.service.ts} +86 -40
  109. package/src/core/modules/better-auth/index.ts +18 -11
  110. package/src/core/modules/error-code/INTEGRATION-CHECKLIST.md +4 -1
  111. package/src/core/modules/error-code/core-error-code.controller.ts +3 -2
  112. package/src/core/modules/user/interfaces/core-user-service-options.interface.ts +3 -3
  113. package/src/core.module.ts +12 -12
  114. package/src/index.ts +1 -0
  115. package/src/server/modules/better-auth/better-auth.controller.ts +4 -4
  116. package/src/server/modules/better-auth/better-auth.module.ts +1 -1
  117. package/src/server/modules/better-auth/better-auth.resolver.ts +31 -31
  118. package/src/server/modules/user/user.service.ts +2 -2
  119. package/src/test/test.helper.ts +13 -1
  120. package/dist/core/modules/better-auth/better-auth-auth.model.d.ts +0 -9
  121. package/dist/core/modules/better-auth/better-auth-auth.model.js.map +0 -1
  122. package/dist/core/modules/better-auth/better-auth-migration-status.model.js.map +0 -1
  123. package/dist/core/modules/better-auth/better-auth-models.js.map +0 -1
  124. package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.d.ts +0 -12
  125. package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.js.map +0 -1
  126. package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js.map +0 -1
  127. package/dist/core/modules/better-auth/better-auth-user.mapper.js.map +0 -1
  128. package/dist/core/modules/better-auth/better-auth.middleware.d.ts +0 -21
  129. package/dist/core/modules/better-auth/better-auth.middleware.js.map +0 -1
  130. package/dist/core/modules/better-auth/better-auth.module.js.map +0 -1
  131. package/dist/core/modules/better-auth/better-auth.service.js.map +0 -1
@@ -0,0 +1,272 @@
1
+ import { Logger } from '@nestjs/common';
2
+ import * as crypto from 'crypto';
3
+ import { Request, Response } from 'express';
4
+
5
+ /**
6
+ * Cookie names used by Better Auth and nest-server
7
+ */
8
+ export const BETTER_AUTH_COOKIE_NAMES = {
9
+ /** Better Auth's default session token cookie */
10
+ BETTER_AUTH_SESSION: 'better-auth.session_token',
11
+ /** Legacy nest-server token cookie */
12
+ TOKEN: 'token',
13
+ } as const;
14
+
15
+ /**
16
+ * Maximum body size for streaming requests (1MB).
17
+ * Prevents DoS attacks via large request bodies.
18
+ */
19
+ export const MAX_BODY_SIZE = 1024 * 1024;
20
+
21
+ /**
22
+ * Options for converting Express Request to Web Standard Request
23
+ */
24
+ export interface ToWebRequestOptions {
25
+ /** Base path for cookie names (e.g., 'iam') */
26
+ basePath?: string;
27
+ /** Base URL for constructing the full URL */
28
+ baseUrl: string;
29
+ /** Logger instance for debug output */
30
+ logger?: Logger;
31
+ /** Secret for signing cookies (if provided, cookies will be signed) */
32
+ secret?: string;
33
+ /** Optional session token to inject into headers */
34
+ sessionToken?: null | string;
35
+ }
36
+
37
+ /**
38
+ * Extracts the session token from Express request cookies or Authorization header.
39
+ *
40
+ * Checks multiple cookie names for compatibility with different configurations:
41
+ * 1. `{basePath}.session_token` - Based on configured basePath (e.g., iam.session_token)
42
+ * 2. `better-auth.session_token` - Better Auth default
43
+ * 3. `token` - Legacy nest-server cookie
44
+ * 4. Authorization: Bearer header
45
+ *
46
+ * @param req - Express request
47
+ * @param basePath - Base path for cookie names (e.g., '/iam' or 'iam')
48
+ * @returns Session token or null if not found
49
+ */
50
+ export function extractSessionToken(req: Request, basePath: string = 'iam'): null | string {
51
+ // Check Authorization header first
52
+ const authHeader = req.headers.authorization;
53
+ if (authHeader?.startsWith('Bearer ')) {
54
+ return authHeader.substring(7);
55
+ }
56
+
57
+ // Normalize basePath (remove leading slash, replace slashes with dots)
58
+ const normalizedBasePath = basePath.replace(/^\//, '').replace(/\//g, '.');
59
+
60
+ // Cookie names to check (in order of priority)
61
+ const cookieNames = [
62
+ `${normalizedBasePath}.session_token`, // Based on configured basePath
63
+ BETTER_AUTH_COOKIE_NAMES.BETTER_AUTH_SESSION, // Better Auth default
64
+ BETTER_AUTH_COOKIE_NAMES.TOKEN, // Legacy nest-server cookie
65
+ ];
66
+
67
+ // Try to get cookies from req.cookies (parsed by cookie-parser) or from header
68
+ const cookies = (req as any).cookies || parseCookieHeader(req.headers.cookie);
69
+
70
+ for (const name of cookieNames) {
71
+ const token = cookies?.[name];
72
+ if (token && typeof token === 'string') {
73
+ return token;
74
+ }
75
+ }
76
+
77
+ return null;
78
+ }
79
+
80
+ /**
81
+ * Parses a Cookie header string into an object.
82
+ *
83
+ * @param cookieHeader - The Cookie header string
84
+ * @returns Object mapping cookie names to values
85
+ */
86
+ export function parseCookieHeader(cookieHeader: string | undefined): Record<string, string> {
87
+ if (!cookieHeader) {
88
+ return {};
89
+ }
90
+
91
+ const cookies: Record<string, string> = {};
92
+ const pairs = cookieHeader.split(';');
93
+
94
+ for (const pair of pairs) {
95
+ const [name, ...valueParts] = pair.trim().split('=');
96
+ if (name && valueParts.length > 0) {
97
+ cookies[name.trim()] = valueParts.join('=').trim();
98
+ }
99
+ }
100
+
101
+ return cookies;
102
+ }
103
+
104
+ /**
105
+ * Sends a Web Standard Response as an Express response.
106
+ *
107
+ * This converts the Fetch API Response object back to Express format,
108
+ * preserving headers, status code, and body.
109
+ *
110
+ * @param res - Express response
111
+ * @param webResponse - Web Standard Response
112
+ */
113
+ export async function sendWebResponse(res: Response, webResponse: globalThis.Response): Promise<void> {
114
+ // Set status code
115
+ res.status(webResponse.status);
116
+
117
+ // Copy headers
118
+ webResponse.headers.forEach((value, key) => {
119
+ // Skip certain headers that Express handles differently
120
+ const lowerKey = key.toLowerCase();
121
+ if (lowerKey === 'content-encoding' || lowerKey === 'transfer-encoding') {
122
+ return;
123
+ }
124
+ res.setHeader(key, value);
125
+ });
126
+
127
+ // Send body
128
+ if (webResponse.body) {
129
+ const reader = webResponse.body.getReader();
130
+ try {
131
+ while (true) {
132
+ const { done, value } = await reader.read();
133
+ if (done) break;
134
+ res.write(value);
135
+ }
136
+ } finally {
137
+ reader.releaseLock();
138
+ }
139
+ }
140
+
141
+ res.end();
142
+ }
143
+
144
+ /**
145
+ * Signs a cookie value using HMAC-SHA256.
146
+ *
147
+ * Better Auth expects signed cookies in the format: `${value}.${signature}`
148
+ * where the signature is a base64-encoded HMAC-SHA256 hash of the value.
149
+ *
150
+ * @param value - The raw cookie value to sign
151
+ * @param secret - The secret to use for signing
152
+ * @returns The signed cookie value (URL-encoded)
153
+ * @throws Error if secret is not provided
154
+ */
155
+ export function signCookieValue(value: string, secret: string): string {
156
+ if (!secret) {
157
+ throw new Error('Cannot sign cookie: Better Auth secret is not configured');
158
+ }
159
+
160
+ const signature = crypto
161
+ .createHmac('sha256', secret)
162
+ .update(value)
163
+ .digest('base64');
164
+ const signedValue = `${value}.${signature}`;
165
+ return encodeURIComponent(signedValue);
166
+ }
167
+
168
+ /**
169
+ * Converts an Express Request to a Web Standard Request.
170
+ *
171
+ * Better Auth uses the Fetch API's Request/Response objects internally.
172
+ * This method converts the Express request (including already-parsed body)
173
+ * to a Web Standard Request that Better Auth can process.
174
+ *
175
+ * @param req - Express request
176
+ * @param options - Conversion options
177
+ * @returns Web Standard Request
178
+ */
179
+ export async function toWebRequest(req: Request, options: ToWebRequestOptions): Promise<globalThis.Request> {
180
+ const { basePath, baseUrl, logger, secret, sessionToken } = options;
181
+ const url = new URL(req.originalUrl || req.url, baseUrl);
182
+
183
+ // Build headers
184
+ const headers = new Headers();
185
+ for (const [key, value] of Object.entries(req.headers)) {
186
+ if (typeof value === 'string') {
187
+ headers.set(key, value);
188
+ } else if (Array.isArray(value)) {
189
+ headers.set(key, value.join(', '));
190
+ }
191
+ }
192
+
193
+ // Inject session token into Authorization header if provided
194
+ // This helps Better Auth find the session via bearer token lookup
195
+ if (sessionToken) {
196
+ headers.set('authorization', `Bearer ${sessionToken}`);
197
+
198
+ // Also ensure the session token is in the cookies with PROPER SIGNING
199
+ // IMPORTANT: We must REPLACE unsigned cookies with signed ones, not just add if missing
200
+ const normalizedBasePath = basePath?.replace(/^\//, '').replace(/\//g, '.') || 'iam';
201
+ const existingCookieString = headers.get('cookie') || '';
202
+
203
+ // Sign the session token for Better Auth (if secret is provided)
204
+ let signedToken: string;
205
+ if (secret) {
206
+ signedToken = signCookieValue(sessionToken, secret);
207
+ } else {
208
+ logger?.warn('No Better Auth secret configured - cookies will not be signed');
209
+ signedToken = sessionToken;
210
+ }
211
+
212
+ // Cookie names that need signed tokens
213
+ const primaryCookieName = `${normalizedBasePath}.session_token`;
214
+ const sessionCookieNames = [
215
+ primaryCookieName,
216
+ BETTER_AUTH_COOKIE_NAMES.BETTER_AUTH_SESSION,
217
+ ];
218
+
219
+ // Parse existing cookies
220
+ const existingCookies = parseCookieHeader(existingCookieString);
221
+
222
+ // Replace session token cookies with signed versions
223
+ for (const cookieName of sessionCookieNames) {
224
+ existingCookies[cookieName] = signedToken;
225
+ }
226
+
227
+ // Keep the unsigned token cookie for nest-server compatibility
228
+ if (!existingCookies[BETTER_AUTH_COOKIE_NAMES.TOKEN]) {
229
+ existingCookies[BETTER_AUTH_COOKIE_NAMES.TOKEN] = sessionToken;
230
+ }
231
+
232
+ // Rebuild the cookie string
233
+ const newCookieString = Object.entries(existingCookies)
234
+ .map(([name, value]) => `${name}=${value}`)
235
+ .join('; ');
236
+
237
+ headers.set('cookie', newCookieString);
238
+ }
239
+
240
+ // Build request options
241
+ const init: RequestInit = {
242
+ headers,
243
+ method: req.method,
244
+ };
245
+
246
+ // Handle body for non-GET/HEAD requests
247
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
248
+ // Check if body was already parsed by NestJS/Express
249
+ if (req.body && typeof req.body === 'object' && Object.keys(req.body).length > 0) {
250
+ // Body was parsed - reconstruct it as JSON
251
+ init.body = JSON.stringify(req.body);
252
+ headers.set('content-type', 'application/json');
253
+ } else if (req.readable) {
254
+ // Body wasn't parsed - stream it directly (ideal case)
255
+ // This happens when the middleware runs before body-parser
256
+ const chunks: Buffer[] = [];
257
+ let totalSize = 0;
258
+ for await (const chunk of req) {
259
+ totalSize += chunk.length;
260
+ if (totalSize > MAX_BODY_SIZE) {
261
+ throw new Error(`Request body too large (max ${MAX_BODY_SIZE} bytes)`);
262
+ }
263
+ chunks.push(chunk);
264
+ }
265
+ if (chunks.length > 0) {
266
+ init.body = Buffer.concat(chunks);
267
+ }
268
+ }
269
+ }
270
+
271
+ return new globalThis.Request(url.toString(), init);
272
+ }