@lenne.tech/nest-server 11.6.1 → 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 (84) 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/interfaces/server-options.interface.d.ts +50 -0
  11. package/dist/core/modules/auth/auth-guard-strategy.enum.d.ts +1 -0
  12. package/dist/core/modules/auth/auth-guard-strategy.enum.js +1 -0
  13. package/dist/core/modules/auth/auth-guard-strategy.enum.js.map +1 -1
  14. package/dist/core/modules/auth/guards/auth.guard.js +11 -5
  15. package/dist/core/modules/auth/guards/auth.guard.js.map +1 -1
  16. package/dist/core/modules/auth/tokens.decorator.d.ts +1 -1
  17. package/dist/core/modules/better-auth/better-auth-auth.model.d.ts +9 -0
  18. package/dist/core/modules/better-auth/better-auth-auth.model.js +63 -0
  19. package/dist/core/modules/better-auth/better-auth-auth.model.js.map +1 -0
  20. package/dist/core/modules/better-auth/better-auth-models.d.ts +44 -0
  21. package/dist/core/modules/better-auth/better-auth-models.js +185 -0
  22. package/dist/core/modules/better-auth/better-auth-models.js.map +1 -0
  23. package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.d.ts +12 -0
  24. package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.js +70 -0
  25. package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.js.map +1 -0
  26. package/dist/core/modules/better-auth/better-auth-rate-limiter.service.d.ts +32 -0
  27. package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js +173 -0
  28. package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js.map +1 -0
  29. package/dist/core/modules/better-auth/better-auth-user.mapper.d.ts +43 -0
  30. package/dist/core/modules/better-auth/better-auth-user.mapper.js +159 -0
  31. package/dist/core/modules/better-auth/better-auth-user.mapper.js.map +1 -0
  32. package/dist/core/modules/better-auth/better-auth.config.d.ts +9 -0
  33. package/dist/core/modules/better-auth/better-auth.config.js +251 -0
  34. package/dist/core/modules/better-auth/better-auth.config.js.map +1 -0
  35. package/dist/core/modules/better-auth/better-auth.middleware.d.ts +20 -0
  36. package/dist/core/modules/better-auth/better-auth.middleware.js +79 -0
  37. package/dist/core/modules/better-auth/better-auth.middleware.js.map +1 -0
  38. package/dist/core/modules/better-auth/better-auth.module.d.ts +30 -0
  39. package/dist/core/modules/better-auth/better-auth.module.js +265 -0
  40. package/dist/core/modules/better-auth/better-auth.module.js.map +1 -0
  41. package/dist/core/modules/better-auth/better-auth.resolver.d.ts +49 -0
  42. package/dist/core/modules/better-auth/better-auth.resolver.js +539 -0
  43. package/dist/core/modules/better-auth/better-auth.resolver.js.map +1 -0
  44. package/dist/core/modules/better-auth/better-auth.service.d.ts +38 -0
  45. package/dist/core/modules/better-auth/better-auth.service.js +151 -0
  46. package/dist/core/modules/better-auth/better-auth.service.js.map +1 -0
  47. package/dist/core/modules/better-auth/better-auth.types.d.ts +38 -0
  48. package/dist/core/modules/better-auth/better-auth.types.js +15 -0
  49. package/dist/core/modules/better-auth/better-auth.types.js.map +1 -0
  50. package/dist/core/modules/better-auth/index.d.ts +11 -0
  51. package/dist/core/modules/better-auth/index.js +28 -0
  52. package/dist/core/modules/better-auth/index.js.map +1 -0
  53. package/dist/core/modules/user/core-user.model.d.ts +2 -0
  54. package/dist/core/modules/user/core-user.model.js +21 -0
  55. package/dist/core/modules/user/core-user.model.js.map +1 -1
  56. package/dist/core.module.js +7 -0
  57. package/dist/core.module.js.map +1 -1
  58. package/dist/index.d.ts +1 -0
  59. package/dist/index.js +1 -0
  60. package/dist/index.js.map +1 -1
  61. package/dist/tsconfig.build.tsbuildinfo +1 -1
  62. package/package.json +9 -1
  63. package/src/config.env.ts +148 -1
  64. package/src/core/common/decorators/restricted.decorator.ts +2 -2
  65. package/src/core/common/helpers/input.helper.ts +2 -2
  66. package/src/core/common/interfaces/server-options.interface.ts +344 -20
  67. package/src/core/modules/auth/auth-guard-strategy.enum.ts +1 -0
  68. package/src/core/modules/auth/guards/auth.guard.ts +20 -6
  69. package/src/core/modules/better-auth/README.md +1096 -0
  70. package/src/core/modules/better-auth/better-auth-auth.model.ts +69 -0
  71. package/src/core/modules/better-auth/better-auth-models.ts +143 -0
  72. package/src/core/modules/better-auth/better-auth-rate-limit.middleware.ts +113 -0
  73. package/src/core/modules/better-auth/better-auth-rate-limiter.service.ts +326 -0
  74. package/src/core/modules/better-auth/better-auth-user.mapper.ts +269 -0
  75. package/src/core/modules/better-auth/better-auth.config.ts +483 -0
  76. package/src/core/modules/better-auth/better-auth.middleware.ts +111 -0
  77. package/src/core/modules/better-auth/better-auth.module.ts +433 -0
  78. package/src/core/modules/better-auth/better-auth.resolver.ts +678 -0
  79. package/src/core/modules/better-auth/better-auth.service.ts +323 -0
  80. package/src/core/modules/better-auth/better-auth.types.ts +75 -0
  81. package/src/core/modules/better-auth/index.ts +25 -0
  82. package/src/core/modules/user/core-user.model.ts +29 -0
  83. package/src/core.module.ts +12 -0
  84. package/src/index.ts +6 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "11.6.1",
3
+ "version": "11.6.2",
4
4
  "description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
5
5
  "keywords": [
6
6
  "node",
@@ -77,6 +77,8 @@
77
77
  "node": ">= 20"
78
78
  },
79
79
  "dependencies": {
80
+ "@apollo/server": "4.12.2",
81
+ "@better-auth/passkey": "1.4.7",
80
82
  "@getbrevo/brevo": "3.0.1",
81
83
  "@nestjs/apollo": "13.1.0",
82
84
  "@nestjs/common": "11.1.9",
@@ -89,8 +91,11 @@
89
91
  "@nestjs/schedule": "6.1.0",
90
92
  "@nestjs/swagger": "11.2.3",
91
93
  "@nestjs/terminus": "11.0.0",
94
+ "@nestjs/websockets": "11.1.9",
95
+ "@thallesp/nestjs-better-auth": "2.2.0",
92
96
  "apollo-server-core": "3.13.0",
93
97
  "bcrypt": "6.0.0",
98
+ "better-auth": "1.4.7",
94
99
  "class-transformer": "0.5.1",
95
100
  "class-validator": "0.14.3",
96
101
  "compression": "1.8.1",
@@ -174,6 +179,9 @@
174
179
  "flat": "5.0.2",
175
180
  "mime": "2.6.0"
176
181
  },
182
+ "better-auth": {
183
+ "mongodb": "7.0.0"
184
+ },
177
185
  "ts-morph": "27.0.2"
178
186
  },
179
187
  "jest": {
package/src/config.env.ts CHANGED
@@ -15,6 +15,54 @@ const config: { [env: string]: IServerOptions } = {
15
15
  // ===========================================================================
16
16
  development: {
17
17
  automaticObjectIdFiltering: true,
18
+ betterAuth: {
19
+ basePath: '/iam',
20
+ baseUrl: 'http://localhost:3000',
21
+ // enabled: true by default - set false to explicitly disable
22
+ jwt: {
23
+ enabled: true,
24
+ expiresIn: '15m',
25
+ },
26
+ legacyPassword: {
27
+ enabled: true,
28
+ },
29
+ passkey: {
30
+ enabled: false,
31
+ origin: 'http://localhost:3000',
32
+ rpId: 'localhost',
33
+ rpName: 'Nest Server Development',
34
+ },
35
+ rateLimit: {
36
+ enabled: true,
37
+ max: 20,
38
+ message: 'Too many requests, please try again later.',
39
+ skipEndpoints: ['/session', '/callback'],
40
+ strictEndpoints: ['/sign-in', '/sign-up', '/forgot-password', '/reset-password'],
41
+ windowSeconds: 60,
42
+ },
43
+ secret: 'BETTER_AUTH_SECRET_DEV_32_CHARS_MIN',
44
+ socialProviders: {
45
+ apple: {
46
+ clientId: process.env.SOCIAL_APPLE_CLIENT_ID || '',
47
+ clientSecret: process.env.SOCIAL_APPLE_CLIENT_SECRET || '',
48
+ enabled: false,
49
+ },
50
+ github: {
51
+ clientId: process.env.SOCIAL_GITHUB_CLIENT_ID || '',
52
+ clientSecret: process.env.SOCIAL_GITHUB_CLIENT_SECRET || '',
53
+ enabled: false,
54
+ },
55
+ google: {
56
+ clientId: process.env.SOCIAL_GOOGLE_CLIENT_ID || '',
57
+ clientSecret: process.env.SOCIAL_GOOGLE_CLIENT_SECRET || '',
58
+ enabled: false,
59
+ },
60
+ },
61
+ twoFactor: {
62
+ appName: 'Nest Server Development',
63
+ enabled: false,
64
+ },
65
+ },
18
66
  compression: true,
19
67
  cookies: false,
20
68
  email: {
@@ -114,10 +162,58 @@ const config: { [env: string]: IServerOptions } = {
114
162
  },
115
163
 
116
164
  // ===========================================================================
117
- // Development environment
165
+ // Local environment
118
166
  // ===========================================================================
119
167
  local: {
120
168
  automaticObjectIdFiltering: true,
169
+ betterAuth: {
170
+ basePath: '/iam',
171
+ baseUrl: 'http://localhost:3000',
172
+ // enabled: true by default - set false to explicitly disable
173
+ jwt: {
174
+ enabled: true,
175
+ expiresIn: '15m',
176
+ },
177
+ legacyPassword: {
178
+ enabled: true,
179
+ },
180
+ passkey: {
181
+ enabled: false,
182
+ origin: 'http://localhost:3000',
183
+ rpId: 'localhost',
184
+ rpName: 'Nest Server Local',
185
+ },
186
+ rateLimit: {
187
+ enabled: true,
188
+ max: 20,
189
+ message: 'Too many requests, please try again later.',
190
+ skipEndpoints: ['/session', '/callback'],
191
+ strictEndpoints: ['/sign-in', '/sign-up', '/forgot-password', '/reset-password'],
192
+ windowSeconds: 60,
193
+ },
194
+ secret: 'BETTER_AUTH_SECRET_LOCAL_32_CHARS_M',
195
+ socialProviders: {
196
+ apple: {
197
+ clientId: process.env.SOCIAL_APPLE_CLIENT_ID || '',
198
+ clientSecret: process.env.SOCIAL_APPLE_CLIENT_SECRET || '',
199
+ enabled: false,
200
+ },
201
+ github: {
202
+ clientId: process.env.SOCIAL_GITHUB_CLIENT_ID || '',
203
+ clientSecret: process.env.SOCIAL_GITHUB_CLIENT_SECRET || '',
204
+ enabled: false,
205
+ },
206
+ google: {
207
+ clientId: process.env.SOCIAL_GOOGLE_CLIENT_ID || '',
208
+ clientSecret: process.env.SOCIAL_GOOGLE_CLIENT_SECRET || '',
209
+ enabled: false,
210
+ },
211
+ },
212
+ twoFactor: {
213
+ appName: 'Nest Server Local',
214
+ enabled: false,
215
+ },
216
+ },
121
217
  compression: true,
122
218
  cookies: false,
123
219
  cronJobs: {
@@ -232,6 +328,57 @@ const config: { [env: string]: IServerOptions } = {
232
328
  // ===========================================================================
233
329
  production: {
234
330
  automaticObjectIdFiltering: true,
331
+ betterAuth: {
332
+ basePath: '/iam',
333
+ baseUrl: process.env.BETTER_AUTH_URL || 'https://example.com',
334
+ // enabled: true by default - set false to explicitly disable
335
+ jwt: {
336
+ enabled: true,
337
+ expiresIn: '15m',
338
+ },
339
+ legacyPassword: {
340
+ enabled: true,
341
+ },
342
+ passkey: {
343
+ enabled: false,
344
+ origin: process.env.BETTER_AUTH_URL || 'https://example.com',
345
+ rpId: process.env.PASSKEY_RP_ID || 'example.com',
346
+ rpName: process.env.PASSKEY_RP_NAME || 'Nest Server Production',
347
+ },
348
+ rateLimit: {
349
+ enabled: process.env.RATE_LIMIT_ENABLED !== 'false',
350
+ max: parseInt(process.env.RATE_LIMIT_MAX || '10', 10),
351
+ message: process.env.RATE_LIMIT_MESSAGE || 'Too many requests, please try again later.',
352
+ skipEndpoints: ['/session', '/callback'],
353
+ strictEndpoints: ['/sign-in', '/sign-up', '/forgot-password', '/reset-password'],
354
+ windowSeconds: parseInt(process.env.RATE_LIMIT_WINDOW_SECONDS || '60', 10),
355
+ },
356
+ // IMPORTANT: Set BETTER_AUTH_SECRET in production!
357
+ // Without it, an insecure default is used which allows session forgery.
358
+ // Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
359
+ secret: process.env.BETTER_AUTH_SECRET,
360
+ socialProviders: {
361
+ apple: {
362
+ clientId: process.env.SOCIAL_APPLE_CLIENT_ID || '',
363
+ clientSecret: process.env.SOCIAL_APPLE_CLIENT_SECRET || '',
364
+ enabled: !!process.env.SOCIAL_APPLE_CLIENT_ID,
365
+ },
366
+ github: {
367
+ clientId: process.env.SOCIAL_GITHUB_CLIENT_ID || '',
368
+ clientSecret: process.env.SOCIAL_GITHUB_CLIENT_SECRET || '',
369
+ enabled: !!process.env.SOCIAL_GITHUB_CLIENT_ID,
370
+ },
371
+ google: {
372
+ clientId: process.env.SOCIAL_GOOGLE_CLIENT_ID || '',
373
+ clientSecret: process.env.SOCIAL_GOOGLE_CLIENT_SECRET || '',
374
+ enabled: !!process.env.SOCIAL_GOOGLE_CLIENT_ID,
375
+ },
376
+ },
377
+ twoFactor: {
378
+ appName: process.env.TWO_FACTOR_APP_NAME || 'Nest Server',
379
+ enabled: process.env.TWO_FACTOR_ENABLED === 'true',
380
+ },
381
+ },
235
382
  compression: true,
236
383
  cookies: false,
237
384
  email: {
@@ -61,7 +61,7 @@ export const getRestricted = (object: unknown, propertyKey?: string): Restricted
61
61
  */
62
62
  export const checkRestricted = (
63
63
  data: any,
64
- user: { hasRole: (roles: string[]) => boolean; id: any; verified?: any; verifiedAt?: any },
64
+ user: { emailVerified?: any; hasRole: (roles: string[]) => boolean; id: any; verified?: any; verifiedAt?: any },
65
65
  options: {
66
66
  allowCreatorOfParent?: boolean;
67
67
  checkObjectItself?: boolean;
@@ -163,7 +163,7 @@ export const checkRestricted = (
163
163
  (roles.includes(RoleEnum.S_CREATOR) &&
164
164
  (('createdBy' in data && equalIds(data.createdBy, user)) ||
165
165
  (config.allowCreatorOfParent && !('createdBy' in data) && config.isCreatorOfParent))) ||
166
- (roles.includes(RoleEnum.S_VERIFIED) && (user?.verified || user?.verifiedAt))
166
+ (roles.includes(RoleEnum.S_VERIFIED) && (user?.verified || user?.verifiedAt || user?.emailVerified))
167
167
  ) {
168
168
  valid = true;
169
169
  }
@@ -209,7 +209,7 @@ export function assignPlain(target: Record<any, any>, ...args: Record<any, any>[
209
209
  */
210
210
  export async function check(
211
211
  value: any,
212
- user: { hasRole: (roles: string[]) => boolean; id: any; verified?: any; verifiedAt?: any },
212
+ user: { emailVerified?: any; hasRole: (roles: string[]) => boolean; id: any; verified?: any; verifiedAt?: any },
213
213
  options?: {
214
214
  allowCreatorOfParent?: boolean;
215
215
  dbObject?: any;
@@ -264,7 +264,7 @@ export async function check(
264
264
  !('createdBy' in config.dbObject) &&
265
265
  config.isCreatorOfParent))) ||
266
266
  // check if the is verified
267
- (roles.includes(RoleEnum.S_VERIFIED) && (user?.verified || user?.verifiedAt))
267
+ (roles.includes(RoleEnum.S_VERIFIED) && (user?.verified || user?.verifiedAt || user?.emailVerified))
268
268
  ) {
269
269
  valid = true;
270
270
  }
@@ -16,6 +16,324 @@ import { CronJobConfigWithTimeZone } from './cron-job-config-with-time-zone.inte
16
16
  import { CronJobConfigWithUtcOffset } from './cron-job-config-with-utc-offset.interface';
17
17
  import { MailjetOptions } from './mailjet-options.interface';
18
18
 
19
+ /**
20
+ * Better-Auth field type definition
21
+ * Matches the DBFieldType from better-auth
22
+ */
23
+ export type BetterAuthFieldType = 'boolean' | 'date' | 'json' | 'number' | 'number[]' | 'string' | 'string[]';
24
+
25
+ /**
26
+ * Interface for better-auth configuration
27
+ */
28
+ export interface IBetterAuth {
29
+ /**
30
+ * Additional user fields beyond the core fields (firstName, lastName, etc.)
31
+ * These fields will be merged with the default user fields.
32
+ * @see https://www.better-auth.com/docs/concepts/users-accounts#additional-fields
33
+ * @example
34
+ * ```typescript
35
+ * additionalUserFields: {
36
+ * phoneNumber: { type: 'string', defaultValue: null },
37
+ * department: { type: 'string', required: true },
38
+ * preferences: { type: 'string', defaultValue: '{}' },
39
+ * }
40
+ * ```
41
+ */
42
+ additionalUserFields?: Record<string, IBetterAuthUserField>;
43
+
44
+ /**
45
+ * Base path for better-auth endpoints
46
+ * default: '/iam'
47
+ */
48
+ basePath?: string;
49
+
50
+ /**
51
+ * Base URL of the application
52
+ * e.g. 'http://localhost:3000'
53
+ */
54
+ baseUrl?: string;
55
+
56
+ /**
57
+ * Whether better-auth is enabled.
58
+ * BetterAuth is enabled by default (zero-config philosophy).
59
+ * Set to false to explicitly disable it.
60
+ * @default true
61
+ */
62
+ enabled?: boolean;
63
+
64
+ /**
65
+ * JWT plugin configuration for API clients.
66
+ * Enabled by default when this config block is present.
67
+ * Set `enabled: false` to explicitly disable.
68
+ */
69
+ jwt?: {
70
+ /**
71
+ * Whether JWT plugin is enabled.
72
+ * @default true (when jwt config block is present)
73
+ */
74
+ enabled?: boolean;
75
+
76
+ /**
77
+ * JWT expiration time
78
+ * @default '15m'
79
+ */
80
+ expiresIn?: string;
81
+ };
82
+
83
+ /**
84
+ * Legacy password handling configuration.
85
+ * Used during migration from old auth system.
86
+ * Enabled by default when this config block is present.
87
+ * Set `enabled: false` to explicitly disable.
88
+ */
89
+ legacyPassword?: {
90
+ /**
91
+ * Whether legacy password handling is enabled.
92
+ * @default true (when legacyPassword config block is present)
93
+ */
94
+ enabled?: boolean;
95
+ };
96
+
97
+ /**
98
+ * Advanced Better-Auth options passthrough.
99
+ * These options are passed directly to Better-Auth, allowing full customization.
100
+ * Use this for any Better-Auth options not explicitly defined in this interface.
101
+ * @see https://www.better-auth.com/docs/reference/options
102
+ * @example
103
+ * ```typescript
104
+ * options: {
105
+ * emailAndPassword: {
106
+ * enabled: true,
107
+ * requireEmailVerification: true,
108
+ * sendResetPassword: async ({ user, url }) => { ... },
109
+ * },
110
+ * account: {
111
+ * accountLinking: { enabled: true },
112
+ * },
113
+ * session: {
114
+ * expiresIn: 60 * 60 * 24 * 7, // 7 days
115
+ * updateAge: 60 * 60 * 24, // 1 day
116
+ * },
117
+ * advanced: {
118
+ * cookiePrefix: 'my-app',
119
+ * useSecureCookies: true,
120
+ * },
121
+ * }
122
+ * ```
123
+ */
124
+ options?: Record<string, unknown>;
125
+
126
+ /**
127
+ * Passkey/WebAuthn configuration.
128
+ * Enabled by default when this config block is present.
129
+ * Set `enabled: false` to explicitly disable.
130
+ */
131
+ passkey?: {
132
+ /**
133
+ * Whether passkey authentication is enabled.
134
+ * @default true (when passkey config block is present)
135
+ */
136
+ enabled?: boolean;
137
+
138
+ /**
139
+ * Origin URL for WebAuthn
140
+ * e.g. 'http://localhost:3000'
141
+ */
142
+ origin?: string;
143
+
144
+ /**
145
+ * Relying Party ID (usually the domain)
146
+ * e.g. 'localhost' or 'example.com'
147
+ */
148
+ rpId?: string;
149
+
150
+ /**
151
+ * Relying Party Name (displayed to users)
152
+ * e.g. 'My Application'
153
+ */
154
+ rpName?: string;
155
+ };
156
+
157
+ /**
158
+ * Additional Better-Auth plugins to include.
159
+ * These will be merged with the built-in plugins (jwt, twoFactor, passkey).
160
+ * @see https://www.better-auth.com/docs/plugins
161
+ * @example
162
+ * ```typescript
163
+ * import { organization } from 'better-auth/plugins';
164
+ * import { magicLink } from 'better-auth/plugins';
165
+ *
166
+ * plugins: [
167
+ * organization({ ... }),
168
+ * magicLink({ ... }),
169
+ * ]
170
+ * ```
171
+ */
172
+ plugins?: unknown[];
173
+
174
+ /**
175
+ * Rate limiting configuration for Better-Auth endpoints
176
+ * Protects against brute-force attacks
177
+ */
178
+ rateLimit?: IBetterAuthRateLimit;
179
+
180
+ /**
181
+ * Secret for better-auth (min 32 characters)
182
+ * Used for session encryption
183
+ */
184
+ secret?: string;
185
+
186
+ /**
187
+ * Social login providers configuration
188
+ * Supports all Better-Auth providers dynamically (google, github, apple, discord, etc.)
189
+ *
190
+ * **Enabled by default:** Providers are automatically enabled when credentials
191
+ * are configured. Set `enabled: false` to explicitly disable a provider.
192
+ *
193
+ * @see https://www.better-auth.com/docs/authentication/social-sign-in
194
+ * @example
195
+ * ```typescript
196
+ * socialProviders: {
197
+ * // These providers are enabled (no need for enabled: true)
198
+ * google: { clientId: '...', clientSecret: '...' },
199
+ * github: { clientId: '...', clientSecret: '...' },
200
+ * // This provider is explicitly disabled
201
+ * discord: { clientId: '...', clientSecret: '...', enabled: false },
202
+ * }
203
+ * ```
204
+ */
205
+ socialProviders?: Record<string, IBetterAuthSocialProvider>;
206
+
207
+ /**
208
+ * Trusted origins for CORS and OAuth callbacks
209
+ * If not specified, defaults to [baseUrl]
210
+ * e.g. ['https://example.com', 'https://app.example.com']
211
+ */
212
+ trustedOrigins?: string[];
213
+
214
+ /**
215
+ * Two-factor authentication configuration.
216
+ * Enabled by default when this config block is present.
217
+ * Set `enabled: false` to explicitly disable.
218
+ */
219
+ twoFactor?: {
220
+ /**
221
+ * App name shown in authenticator apps
222
+ * e.g. 'My Application'
223
+ */
224
+ appName?: string;
225
+
226
+ /**
227
+ * Whether 2FA is enabled.
228
+ * @default true (when twoFactor config block is present)
229
+ */
230
+ enabled?: boolean;
231
+ };
232
+ }
233
+
234
+ /**
235
+ * Interface for Better-Auth rate limiting configuration
236
+ */
237
+ export interface IBetterAuthRateLimit {
238
+ /**
239
+ * Whether rate limiting is enabled
240
+ * default: false
241
+ */
242
+ enabled?: boolean;
243
+
244
+ /**
245
+ * Maximum number of requests within the time window
246
+ * default: 10
247
+ */
248
+ max?: number;
249
+
250
+ /**
251
+ * Custom message when rate limit is exceeded
252
+ * default: 'Too many requests, please try again later.'
253
+ */
254
+ message?: string;
255
+
256
+ /**
257
+ * Endpoints to skip rate limiting entirely
258
+ * e.g., ['/iam/session'] for session checks
259
+ */
260
+ skipEndpoints?: string[];
261
+
262
+ /**
263
+ * Endpoints to apply stricter rate limiting (e.g., sign-in, sign-up)
264
+ * These endpoints will have half the max requests
265
+ */
266
+ strictEndpoints?: string[];
267
+
268
+ /**
269
+ * Time window in seconds
270
+ * default: 60 (1 minute)
271
+ */
272
+ windowSeconds?: number;
273
+ }
274
+
275
+ /**
276
+ * Interface for better-auth social provider configuration
277
+ *
278
+ * **Enabled by default:** A social provider is automatically enabled when
279
+ * both `clientId` and `clientSecret` are provided. You only need to set
280
+ * `enabled: false` to explicitly disable a configured provider.
281
+ *
282
+ * @example
283
+ * ```typescript
284
+ * // Provider is enabled (has credentials, no explicit enabled flag needed)
285
+ * google: { clientId: '...', clientSecret: '...' }
286
+ *
287
+ * // Provider is explicitly disabled despite having credentials
288
+ * github: { clientId: '...', clientSecret: '...', enabled: false }
289
+ * ```
290
+ */
291
+ export interface IBetterAuthSocialProvider {
292
+ /**
293
+ * OAuth client ID
294
+ */
295
+ clientId: string;
296
+
297
+ /**
298
+ * OAuth client secret
299
+ */
300
+ clientSecret: string;
301
+
302
+ /**
303
+ * Whether this provider is enabled.
304
+ * Defaults to true when clientId and clientSecret are provided.
305
+ * Set to false to explicitly disable this provider.
306
+ * @default true (when credentials are configured)
307
+ */
308
+ enabled?: boolean;
309
+ }
310
+
311
+ /**
312
+ * Interface for additional user fields in Better-Auth
313
+ * @see https://www.better-auth.com/docs/concepts/users-accounts#additional-fields
314
+ */
315
+ export interface IBetterAuthUserField {
316
+ /**
317
+ * Default value for the field
318
+ */
319
+ defaultValue?: unknown;
320
+
321
+ /**
322
+ * Database field name (if different from key)
323
+ */
324
+ fieldName?: string;
325
+
326
+ /**
327
+ * Whether this field is required
328
+ */
329
+ required?: boolean;
330
+
331
+ /**
332
+ * Field type
333
+ */
334
+ type: BetterAuthFieldType;
335
+ }
336
+
19
337
  /**
20
338
  * Interface for JWT configuration (main and refresh)
21
339
  */
@@ -71,6 +389,12 @@ export interface IServerOptions {
71
389
  */
72
390
  automaticObjectIdFiltering?: boolean;
73
391
 
392
+ /**
393
+ * Configuration for better-auth authentication framework
394
+ * See: https://better-auth.com
395
+ */
396
+ betterAuth?: IBetterAuth;
397
+
74
398
  /**
75
399
  * Configuration for Brevo
76
400
  * See: https://developers.brevo.com/
@@ -324,29 +648,29 @@ export interface IServerOptions {
324
648
  * Hint: The secrets of the different environments should be different, otherwise a JWT can be used in different
325
649
  * environments, which can lead to security vulnerabilities.
326
650
  */
327
- jwt?: IJwt & JwtModuleOptions &
328
- {
329
- /**
330
- * Configuration for refresh Token (JWT)
331
- * Hint: The secret of the JWT and the Refresh Token should be different, otherwise a new RefreshToken can also be
332
- * requested with the JWT, which can lead to a security vulnerability.
333
- */
334
- refresh?: IJwt & {
651
+ jwt?: IJwt &
652
+ JwtModuleOptions & {
335
653
  /**
336
- * Whether renewal of the refresh token is permitted
337
- * If falsy (default): during refresh only a new token, the refresh token retains its original term
338
- * If true: during refresh not only a new token but also a new refresh token is created
654
+ * Configuration for refresh Token (JWT)
655
+ * Hint: The secret of the JWT and the Refresh Token should be different, otherwise a new RefreshToken can also be
656
+ * requested with the JWT, which can lead to a security vulnerability.
339
657
  */
340
- renewal?: boolean;
341
- };
658
+ refresh?: IJwt & {
659
+ /**
660
+ * Whether renewal of the refresh token is permitted
661
+ * If falsy (default): during refresh only a new token, the refresh token retains its original term
662
+ * If true: during refresh not only a new token but also a new refresh token is created
663
+ */
664
+ renewal?: boolean;
665
+ };
342
666
 
343
- /**
344
- * Time period in milliseconds
345
- * in which the same token ID is used so that all parallel token refresh requests of a device can be generated.
346
- * default: 0 (every token includes a new token ID, all parallel token refresh requests must be prevented by the client or processed accordingly)
347
- */
348
- sameTokenIdPeriod?: number;
349
- };
667
+ /**
668
+ * Time period in milliseconds
669
+ * in which the same token ID is used so that all parallel token refresh requests of a device can be generated.
670
+ * default: 0 (every token includes a new token ID, all parallel token refresh requests must be prevented by the client or processed accordingly)
671
+ */
672
+ sameTokenIdPeriod?: number;
673
+ };
350
674
 
351
675
  /**
352
676
  * Load local configuration
@@ -1,4 +1,5 @@
1
1
  export enum AuthGuardStrategy {
2
+ BETTER_AUTH = 'better-auth',
2
3
  JWT = 'jwt',
3
4
  JWT_REFRESH = 'jwt-refresh',
4
5
  }
@@ -13,9 +13,9 @@ import { InvalidTokenException } from '../exceptions/invalid-token.exception';
13
13
  /**
14
14
  * Missing strategy error
15
15
  */
16
- const NO_STRATEGY_ERROR
17
- = 'In order to use "defaultStrategy", please, ensure to import PassportModule in each '
18
- + 'place where AuthGuard() is being used. Otherwise, passport won\'t work correctly.';
16
+ const NO_STRATEGY_ERROR =
17
+ 'In order to use "defaultStrategy", please, ensure to import PassportModule in each ' +
18
+ "place where AuthGuard() is being used. Otherwise, passport won't work correctly.";
19
19
 
20
20
  /**
21
21
  * Interface for auth guard
@@ -38,7 +38,7 @@ const createPassportContext = (request, response) => (type, options, callback: (
38
38
  } catch (err) {
39
39
  reject(err);
40
40
  }
41
- })(request, response, err => (err ? reject(err) : resolve)),
41
+ })(request, response, (err) => (err ? reject(err) : resolve)),
42
42
  );
43
43
 
44
44
  /**
@@ -70,8 +70,22 @@ function createAuthGuard(type?: AuthGuardStrategy | string | string[]): Type<IAu
70
70
  }
71
71
 
72
72
  const options = { ...defaultOptions, ...this.options };
73
- const response = context?.switchToHttp()?.getResponse();
74
73
  const request = this.getRequest(context);
74
+
75
+ // Check if user is already authenticated via Better-Auth middleware
76
+ // Only skip Passport for Better-Auth users (marked with _authenticatedViaBetterAuth)
77
+ // This ensures JWT_REFRESH guard still validates refresh tokens properly
78
+ const existingUser = request?.[options.property || defaultOptions.property];
79
+ if (existingUser && existingUser._authenticatedViaBetterAuth === true) {
80
+ // User is authenticated via Better-Auth - skip Passport authentication
81
+ // Validate through handleRequest to ensure role checks work
82
+ const validatedUser = this.handleRequest(null, existingUser, null, context);
83
+ request[options.property || defaultOptions.property] = validatedUser;
84
+ return true;
85
+ }
86
+
87
+ // Proceed with Passport authentication
88
+ const response = context?.switchToHttp()?.getResponse();
75
89
  const passportFn = createPassportContext(request, response);
76
90
  const user = await passportFn(type || this.options.defaultStrategy, options, (err, currentUser, info) =>
77
91
  this.handleRequest(err, currentUser, info, context),
@@ -101,7 +115,7 @@ function createAuthGuard(type?: AuthGuardStrategy | string | string[]): Type<IAu
101
115
  */
102
116
  async logIn<TRequest extends { logIn: (...params) => any } = any>(request: TRequest) {
103
117
  const user = request[this.options.property || defaultOptions.property];
104
- await new Promise<void>((resolve, reject) => request.logIn(user, err => (err ? reject(err) : resolve())));
118
+ await new Promise<void>((resolve, reject) => request.logIn(user, (err) => (err ? reject(err) : resolve())));
105
119
  }
106
120
 
107
121
  /**