@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,1096 @@
1
+ # Better-Auth Module
2
+
3
+ Integration of the [better-auth](https://better-auth.com) authentication framework with @lenne.tech/nest-server.
4
+
5
+ ## Features
6
+
7
+ ### Built-in Plugins (Explicit Configuration)
8
+
9
+ - **JWT Tokens** - For API clients and stateless authentication
10
+ - **Two-Factor Authentication (2FA)** - TOTP-based second factor
11
+ - **Passkey/WebAuthn** - Passwordless authentication
12
+ - **Legacy Password Handling** - Migration support for existing users
13
+
14
+ ### Core Features
15
+
16
+ - **Email/Password Authentication** - Standard username/password login
17
+ - **Social Login** - Any OAuth provider (Google, GitHub, Apple, Discord, etc.)
18
+ - **Parallel Operation** - Runs alongside Legacy Auth without side effects
19
+
20
+ ### Extensible via Plugins
21
+
22
+ - **Organization** - Multi-tenant, teams, member management
23
+ - **Admin** - User impersonation, banning
24
+ - **SSO** - Single Sign-On (OIDC, SAML)
25
+ - **And many more** - See [Plugins and Extensions](#plugins-and-extensions)
26
+
27
+ ## Quick Start
28
+
29
+ **True Zero-Config: Better-Auth is enabled by default!** No configuration block is required - it works out of the box.
30
+
31
+ ```typescript
32
+ // Works automatically - no betterAuth config needed!
33
+ // Better-Auth will use sensible defaults and fallback secrets
34
+
35
+ // To customize behavior (optional):
36
+ const config = {
37
+ betterAuth: {
38
+ // Only add this block if you need to override defaults
39
+ // baseUrl: 'https://your-domain.com',
40
+ // basePath: '/iam',
41
+ },
42
+ };
43
+ ```
44
+
45
+ **Default values (used when not configured):**
46
+
47
+ - **Secret**: Falls back to `jwt.secret` → `jwt.refresh.secret` → auto-generated
48
+ - **Base URL**: `http://localhost:3000`
49
+ - **Base Path**: `/iam`
50
+ - **Passkey Origin**: `http://localhost:3000`
51
+ - **Passkey rpId**: `localhost`
52
+ - **Passkey rpName**: `Nest Server`
53
+
54
+ To **explicitly disable** Better-Auth (the only way to turn it off):
55
+
56
+ ```typescript
57
+ const config = {
58
+ betterAuth: {
59
+ enabled: false, // Only way to disable Better-Auth
60
+ },
61
+ };
62
+ ```
63
+
64
+ Read the security section below for production deployments.
65
+
66
+ ## Understanding Core Settings
67
+
68
+ ### Base URL (`baseUrl`)
69
+
70
+ **Purpose:** Absolute URL generation for links and redirects
71
+
72
+ **Technically used for:**
73
+
74
+ - **Email Links**: Password reset links, email verification links
75
+ ```text
76
+ https://your-domain.com/iam/reset-password?token=xyz
77
+ ```
78
+ - **OAuth Redirect URIs**: Callback URLs for social login providers
79
+ ```text
80
+ https://your-domain.com/iam/callback/google
81
+ ```
82
+ - **CORS Origin Validation**: If no `trustedOrigins` are set, `baseUrl` is used as the trusted origin
83
+
84
+ **Impact of incorrect value:** Email links point to wrong server, OAuth callbacks fail.
85
+
86
+ ### Base Path (`basePath`)
87
+
88
+ **Purpose:** URL prefix for all Better-Auth REST endpoints
89
+
90
+ **Technically used for:**
91
+
92
+ - **Routing**: All endpoints are registered under this path
93
+ ```text
94
+ /iam/sign-in
95
+ /iam/sign-up
96
+ /iam/session
97
+ /iam/callback/:provider
98
+ ```
99
+ - **Middleware Matching**: `BetterAuthMiddleware` only forwards requests to paths starting with `basePath`
100
+
101
+ **Default `/iam`** avoids collisions with existing `/auth` routes from Legacy Auth.
102
+
103
+ ### Passkey Origin (`passkey.origin`)
104
+
105
+ **Purpose:** WebAuthn/Passkey security validation
106
+
107
+ **Technically used for:**
108
+
109
+ - **Passkey Registration/Authentication**: Browser sends origin in WebAuthn request
110
+ - **Origin Verification**: Server validates that request comes from expected origin
111
+ - **Phishing Protection**: Prevents passkeys from being used on other domains
112
+
113
+ **Impact of incorrect value:** Passkey authentication fails with "origin mismatch" error.
114
+
115
+ ### Configuration Summary
116
+
117
+ | Setting | Technical Purpose | Impact of Wrong Value |
118
+ | ----------------- | ---------------------- | ---------------------------- |
119
+ | `baseUrl` | Email links, OAuth | Links point to wrong server |
120
+ | `basePath` | Endpoint routing | 404 on API calls |
121
+ | `passkey.origin` | WebAuthn security | Passkey auth fails |
122
+
123
+ **For Development:** The defaults (`http://localhost:3000`, `/iam`) are correct.
124
+
125
+ **For Production:** You must set `baseUrl` and `passkey.origin` to your actual domain:
126
+
127
+ ```typescript
128
+ const config = {
129
+ betterAuth: {
130
+ baseUrl: 'https://api.your-domain.com',
131
+ passkey: {
132
+ // enabled by default when config block is present
133
+ origin: 'https://your-domain.com', // Frontend domain
134
+ rpId: 'your-domain.com', // Domain without protocol
135
+ rpName: 'Your Application',
136
+ },
137
+ },
138
+ };
139
+ ```
140
+
141
+ ## IMPORTANT: Secret Configuration
142
+
143
+ ### Secret Resolution (Fallback Chain)
144
+
145
+ Better-Auth resolves the secret in the following order:
146
+
147
+ 1. **`betterAuth.secret`** - If explicitly configured
148
+ 2. **`jwt.secret`** - Fallback for backwards compatibility (if ≥32 chars)
149
+ 3. **`jwt.refresh.secret`** - Second fallback (if ≥32 chars)
150
+ 4. **Auto-generated** - Secure random secret (with warning)
151
+
152
+ ### Backwards Compatibility
153
+
154
+ If you already have `jwt.secret` configured with ≥32 characters, **Better-Auth will automatically use it** as a fallback. This means:
155
+
156
+ - No configuration needed for existing projects with proper JWT secrets
157
+ - Sessions persist across server restarts (uses existing secret)
158
+ - A warning reminds you to set `betterAuth.secret` explicitly
159
+
160
+ ### Development (Auto-Generated Secret)
161
+
162
+ When no valid secret is found (including fallbacks), Better-Auth **automatically generates a secure random secret** at server startup.
163
+
164
+ **Consequences:**
165
+
166
+ - **Secure** - The secret is cryptographically random (32 bytes)
167
+ - **Sessions lost on restart** - All user sessions become invalid when the server restarts
168
+ - **Acceptable for development** - Frequent restarts during development are normal
169
+
170
+ ### Production (Recommended)
171
+
172
+ For production, explicitly set `betterAuth.secret` or ensure `jwt.secret` is ≥32 characters:
173
+
174
+ ```bash
175
+ # Generate a secure secret:
176
+ node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
177
+
178
+ # Set in your environment:
179
+ export BETTER_AUTH_SECRET="your-generated-secret-here"
180
+ ```
181
+
182
+ **Consequences of using auto-generated secret in production:**
183
+
184
+ - Sessions lost on every deployment - All users must re-login after each deploy
185
+ - Sessions lost on server restart - Any restart invalidates all sessions
186
+ - No session sharing in clusters - Multiple server instances can't share sessions
187
+
188
+ ### Secret Requirements
189
+
190
+ | Requirement | Value |
191
+ | ----------------- | --------------------------------------------------------- |
192
+ | Minimum length | 32 characters |
193
+ | Recommended | 44+ characters (32 bytes base64) |
194
+ | Character types | At least 2 of: lowercase, uppercase, numbers, special |
195
+
196
+ ### Configuration Examples
197
+
198
+ **Via Environment Variable (Recommended):**
199
+
200
+ ```bash
201
+ BETTER_AUTH_SECRET=K7xR2mN9pQ4wE6tY8uI0oP3aS5dF7gH9jK1lZ2xC4vB6nM8=
202
+ ```
203
+
204
+ **Via config.env.ts:**
205
+
206
+ ```typescript
207
+ const config = {
208
+ betterAuth: {
209
+ // enabled: true by default - no need to set explicitly
210
+ secret: process.env.BETTER_AUTH_SECRET,
211
+ },
212
+ };
213
+ ```
214
+
215
+ ## Configuration
216
+
217
+ **Optional** - Better-Auth works without any configuration (true zero-config). Only add this block if you need to customize behavior:
218
+
219
+ ```typescript
220
+ // In config.env.ts
221
+ export default {
222
+ // ... other config
223
+
224
+ // OPTIONAL: Better-Auth configuration
225
+ // Omit entirely for default behavior, or customize as needed:
226
+ betterAuth: {
227
+ // enabled: true by default - only set to false to disable
228
+ // secret: auto-generated if not set (see Security section above)
229
+ // baseUrl: 'http://localhost:3000', // Default
230
+ // basePath: '/iam', // Default
231
+
232
+ // JWT Plugin (enabled by default when config block is present)
233
+ // Set enabled: false to explicitly disable
234
+ jwt: {
235
+ expiresIn: '15m',
236
+ },
237
+
238
+ // Two-Factor Authentication (enabled by default when config block is present)
239
+ // Set enabled: false to explicitly disable
240
+ twoFactor: {
241
+ appName: 'My Application',
242
+ },
243
+
244
+ // Passkey/WebAuthn (enabled by default when config block is present)
245
+ // Set enabled: false to explicitly disable
246
+ passkey: {
247
+ rpId: 'localhost',
248
+ rpName: 'My Application',
249
+ origin: 'http://localhost:3000',
250
+ },
251
+
252
+ // Social Providers (enabled by default when credentials are configured)
253
+ // Set enabled: false to explicitly disable a provider
254
+ socialProviders: {
255
+ google: {
256
+ clientId: 'your-google-client-id',
257
+ clientSecret: 'your-google-client-secret',
258
+ },
259
+ github: {
260
+ clientId: 'your-github-client-id',
261
+ clientSecret: 'your-github-client-secret',
262
+ },
263
+ },
264
+
265
+ // Legacy Password Handling (enabled by default when config block is present)
266
+ // Set enabled: false to explicitly disable
267
+ legacyPassword: {},
268
+
269
+ // Trusted Origins for CORS
270
+ trustedOrigins: ['http://localhost:3000', 'https://your-app.com'],
271
+
272
+ // Rate Limiting (optional)
273
+ rateLimit: {
274
+ enabled: true,
275
+ max: 10,
276
+ windowSeconds: 60,
277
+ message: 'Too many requests, please try again later.',
278
+ strictEndpoints: ['/sign-in', '/sign-up', '/forgot-password', '/reset-password'],
279
+ skipEndpoints: ['/session', '/callback'],
280
+ },
281
+ },
282
+ };
283
+ ```
284
+
285
+ ## Plugins and Extensions
286
+
287
+ Better-Auth provides a rich plugin ecosystem. This module uses a **hybrid approach**:
288
+
289
+ - **Built-in plugins** (JWT, 2FA, Passkey): Explicitly configured with typed options
290
+ - **Additional plugins**: Dynamically added via the `plugins` array
291
+
292
+ ### Built-in Plugins (Explicit Configuration)
293
+
294
+ These plugins are enabled by default when their config block is present. **All properties have sensible defaults**, so an empty block `{}` is sufficient!
295
+
296
+ | Plugin | Minimal Config | Default Values |
297
+ | ------------------ | -------------------- | --------------------------------------------------------------------------------- |
298
+ | **JWT** | `jwt: {}` | `expiresIn: '15m'` |
299
+ | **Two-Factor** | `twoFactor: {}` | `appName: 'Nest Server'` |
300
+ | **Passkey** | `passkey: {}` | `origin: 'http://localhost:3000'`, `rpId: 'localhost'`, `rpName: 'Nest Server'` |
301
+ | **Legacy Password**| `legacyPassword: {}` | No config needed |
302
+
303
+ #### Minimal Syntax (Recommended for Development)
304
+
305
+ ```typescript
306
+ const config = {
307
+ betterAuth: {
308
+ // Just add empty blocks - all defaults are applied!
309
+ jwt: {},
310
+ twoFactor: {},
311
+ passkey: {},
312
+ legacyPassword: {},
313
+ },
314
+ };
315
+ ```
316
+
317
+ #### Custom Configuration (Recommended for Production)
318
+
319
+ ```typescript
320
+ const config = {
321
+ betterAuth: {
322
+ jwt: { expiresIn: '30m' },
323
+ twoFactor: { appName: 'My App' },
324
+ passkey: {
325
+ rpId: 'example.com',
326
+ rpName: 'My App',
327
+ origin: 'https://example.com',
328
+ },
329
+ legacyPassword: {},
330
+ },
331
+ };
332
+ ```
333
+
334
+ #### Disabling a Plugin
335
+
336
+ ```typescript
337
+ const config = {
338
+ betterAuth: {
339
+ jwt: { enabled: false }, // Explicitly disable JWT
340
+ twoFactor: {}, // 2FA still enabled with defaults
341
+ },
342
+ };
343
+ ```
344
+
345
+ ### Dynamic Plugins (plugins Array)
346
+
347
+ For all other Better-Auth plugins, use the `plugins` array. This provides maximum flexibility without requiring updates to this package.
348
+
349
+ ```typescript
350
+ import { organization } from 'better-auth/plugins';
351
+ import { admin } from 'better-auth/plugins';
352
+ import { multiSession } from 'better-auth/plugins';
353
+ import { apiKey } from 'better-auth/plugins';
354
+
355
+ const config = {
356
+ betterAuth: {
357
+ // Built-in plugins
358
+ jwt: { expiresIn: '30m' },
359
+
360
+ // Additional plugins via array
361
+ plugins: [
362
+ organization({
363
+ allowUserToCreateOrganization: true,
364
+ creatorRole: 'owner',
365
+ }),
366
+ admin({
367
+ impersonationSessionDuration: 60 * 60,
368
+ }),
369
+ multiSession({
370
+ maximumSessions: 5,
371
+ }),
372
+ apiKey(),
373
+ ],
374
+ },
375
+ };
376
+ ```
377
+
378
+ ### Available Better-Auth Plugins
379
+
380
+ | Plugin | Use Case | Recommendation |
381
+ | ------------------ | --------------------------------------------- | --------------------------- |
382
+ | **organization** | Multi-tenant apps, teams, member management | Common for SaaS/B2B |
383
+ | **admin** | User impersonation, banning, user management | Common for admin panels |
384
+ | **multiSession** | Multiple active sessions per user | Account switching apps |
385
+ | **apiKey** | API key based authentication | Public APIs |
386
+ | **sso** | Single Sign-On (OIDC, SAML 2.0) | Enterprise apps |
387
+ | **oidcProvider** | Build your own identity provider | Identity platforms |
388
+ | **genericOAuth** | Custom OAuth providers | Special OAuth integrations |
389
+ | **polar** | Usage-based billing with Polar | SaaS billing |
390
+
391
+ For the complete list of plugins, see:
392
+
393
+ - [Official Plugins](https://www.better-auth.com/docs/concepts/plugins)
394
+ - [Community Plugins](https://www.better-auth.com/docs/plugins/community-plugins)
395
+
396
+ ### Example: Organization Plugin
397
+
398
+ Multi-tenant support with teams and roles:
399
+
400
+ ```typescript
401
+ import { organization } from 'better-auth/plugins';
402
+
403
+ const config = {
404
+ betterAuth: {
405
+ plugins: [
406
+ organization({
407
+ allowUserToCreateOrganization: true,
408
+ creatorRole: 'owner',
409
+ membershipLimit: 100,
410
+ organizationLimit: 5,
411
+ teams: {
412
+ enabled: true,
413
+ maximumTeams: 10,
414
+ },
415
+ }),
416
+ ],
417
+ },
418
+ };
419
+ ```
420
+
421
+ ### Example: Admin Plugin
422
+
423
+ User management and impersonation:
424
+
425
+ ```typescript
426
+ import { admin } from 'better-auth/plugins';
427
+
428
+ const config = {
429
+ betterAuth: {
430
+ plugins: [
431
+ admin({
432
+ impersonationSessionDuration: 60 * 60,
433
+ defaultRole: 'user',
434
+ adminRole: 'admin',
435
+ }),
436
+ ],
437
+ },
438
+ };
439
+ ```
440
+
441
+ ### Example: SSO Plugin
442
+
443
+ Enterprise Single Sign-On:
444
+
445
+ ```typescript
446
+ import { sso } from 'better-auth/plugins';
447
+
448
+ const config = {
449
+ betterAuth: {
450
+ plugins: [
451
+ sso({
452
+ issuer: 'https://your-identity-provider.com',
453
+ // ... OIDC/SAML configuration
454
+ }),
455
+ ],
456
+ },
457
+ };
458
+ ```
459
+
460
+ ### Why This Hybrid Approach?
461
+
462
+ | Approach | Pros | Cons |
463
+ | --------------------------------- | -------------------------------------------------- | --------------------------------- |
464
+ | **Built-in** (jwt, 2fa, passkey) | TypeScript types, IDE autocomplete, documentation | Package update needed for changes |
465
+ | **Dynamic** (plugins array) | Any plugin works immediately, future-proof | No typed config in IBetterAuth |
466
+
467
+ **Best of both worlds:**
468
+
469
+ - Core auth plugins with great developer experience
470
+ - Full flexibility for specialized plugins
471
+ - No package updates needed for new Better-Auth plugins
472
+
473
+ ## Module Setup
474
+
475
+ The Better-Auth module is **automatically enabled by default** - no configuration required (true zero-config). It will only be disabled if you explicitly set `enabled: false`.
476
+
477
+ ### Using with CoreModule
478
+
479
+ ```typescript
480
+ // In your ServerModule
481
+ @Module({
482
+ imports: [
483
+ CoreModule.forRoot(environment),
484
+ // BetterAuthModule is automatically included and configured
485
+ // No betterAuth config block needed - works out of the box!
486
+ ],
487
+ })
488
+ export class ServerModule {}
489
+ ```
490
+
491
+ ### Standalone Usage
492
+
493
+ ```typescript
494
+ import { BetterAuthModule } from '@lenne.tech/nest-server';
495
+
496
+ @Module({
497
+ imports: [
498
+ BetterAuthModule.forRoot({
499
+ config: environment.betterAuth,
500
+ }),
501
+ ],
502
+ })
503
+ export class AppModule {}
504
+ ```
505
+
506
+ ## REST API Endpoints
507
+
508
+ When enabled, Better-Auth exposes the following endpoints at the configured `basePath` (default: `/iam`):
509
+
510
+ | Endpoint | Method | Description |
511
+ | --------------------- | ------ | ---------------------------- |
512
+ | `/iam/sign-up` | POST | Register new user |
513
+ | `/iam/sign-in` | POST | Sign in with email/password |
514
+ | `/iam/sign-out` | POST | Sign out (invalidate session)|
515
+ | `/iam/session` | GET | Get current session |
516
+ | `/iam/forgot-password`| POST | Request password reset |
517
+ | `/iam/reset-password` | POST | Reset password with token |
518
+ | `/iam/verify-email` | POST | Verify email address |
519
+
520
+ ### Social Login Endpoints
521
+
522
+ | Endpoint | Method | Description |
523
+ | -------------------------- | ------ | --------------------- |
524
+ | `/iam/sign-in/social` | POST | Initiate social login |
525
+ | `/iam/callback/:provider` | GET | OAuth callback |
526
+
527
+ ### Two-Factor Authentication Endpoints
528
+
529
+ | Endpoint | Method | Description |
530
+ | ------------------------- | ------ | ---------------- |
531
+ | `/iam/two-factor/enable` | POST | Enable 2FA |
532
+ | `/iam/two-factor/disable` | POST | Disable 2FA |
533
+ | `/iam/two-factor/verify` | POST | Verify 2FA code |
534
+
535
+ ### Passkey Endpoints
536
+
537
+ | Endpoint | Method | Description |
538
+ | ---------------------------- | ------ | ------------------------- |
539
+ | `/iam/passkey/register` | POST | Register new passkey |
540
+ | `/iam/passkey/authenticate` | POST | Authenticate with passkey |
541
+
542
+ ## GraphQL API
543
+
544
+ In addition to REST endpoints, Better-Auth provides GraphQL queries and mutations:
545
+
546
+ ### Queries
547
+
548
+ | Query | Arguments | Return Type | Description |
549
+ | ------------------------ | --------- | ---------------------------- | ------------------------------- |
550
+ | `betterAuthEnabled` | - | `Boolean` | Check if Better-Auth is enabled |
551
+ | `betterAuthFeatures` | - | `BetterAuthFeaturesModel` | Get enabled features status |
552
+ | `betterAuthSession` | - | `BetterAuthSessionModel` | Get current session (auth req.) |
553
+ | `betterAuthListPasskeys` | - | `[BetterAuthPasskeyModel]` | List user's passkeys (auth req.)|
554
+
555
+ ### Mutations
556
+
557
+ #### Authentication
558
+
559
+ | Mutation | Arguments | Return Type | Description |
560
+ | --------------------- | ---------------------------- | -------------------- | ------------------------- |
561
+ | `betterAuthSignIn` | `email`, `password` | `BetterAuthAuthModel`| Sign in with email/pass |
562
+ | `betterAuthSignUp` | `email`, `password`, `name?` | `BetterAuthAuthModel`| Register new account |
563
+ | `betterAuthSignOut` | - | `Boolean` | Sign out (requires auth) |
564
+ | `betterAuthVerify2FA` | `code` | `BetterAuthAuthModel`| Verify 2FA code |
565
+
566
+ #### 2FA Management (requires authentication)
567
+
568
+ | Mutation | Arguments | Return Type | Description |
569
+ | ------------------------------ | ---------- | ------------------------ | ------------------------------ |
570
+ | `betterAuthEnable2FA` | `password` | `BetterAuth2FASetupModel`| Enable 2FA, get TOTP URI |
571
+ | `betterAuthDisable2FA` | `password` | `Boolean` | Disable 2FA for user |
572
+ | `betterAuthGenerateBackupCodes`| - | `[String]` | Generate new backup codes |
573
+
574
+ #### Passkey Management (requires authentication)
575
+
576
+ | Mutation | Arguments | Return Type | Description |
577
+ | ------------------------------ | ----------- | -------------------------------- | --------------------------- |
578
+ | `betterAuthGetPasskeyChallenge`| - | `BetterAuthPasskeyChallengeModel`| Get WebAuthn challenge |
579
+ | `betterAuthDeletePasskey` | `passkeyId` | `Boolean` | Delete a passkey |
580
+
581
+ ### Response Types
582
+
583
+ #### BetterAuthAuthModel
584
+
585
+ ```graphql
586
+ type BetterAuthAuthModel {
587
+ success: Boolean!
588
+ requiresTwoFactor: Boolean
589
+ token: String
590
+ user: BetterAuthUserModel
591
+ session: BetterAuthSessionInfoModel
592
+ error: String
593
+ }
594
+ ```
595
+
596
+ #### BetterAuthFeaturesModel
597
+
598
+ ```graphql
599
+ type BetterAuthFeaturesModel {
600
+ enabled: Boolean!
601
+ jwt: Boolean!
602
+ twoFactor: Boolean!
603
+ passkey: Boolean!
604
+ legacyPassword: Boolean!
605
+ socialProviders: [String!]!
606
+ }
607
+ ```
608
+
609
+ #### BetterAuth2FASetupModel
610
+
611
+ ```graphql
612
+ type BetterAuth2FASetupModel {
613
+ success: Boolean!
614
+ totpUri: String
615
+ backupCodes: [String!]
616
+ error: String
617
+ }
618
+ ```
619
+
620
+ #### BetterAuthPasskeyModel
621
+
622
+ ```graphql
623
+ type BetterAuthPasskeyModel {
624
+ id: String!
625
+ name: String
626
+ credentialId: String!
627
+ createdAt: DateTime!
628
+ }
629
+ ```
630
+
631
+ #### BetterAuthPasskeyChallengeModel
632
+
633
+ ```graphql
634
+ type BetterAuthPasskeyChallengeModel {
635
+ success: Boolean!
636
+ challenge: String
637
+ error: String
638
+ }
639
+ ```
640
+
641
+ ### Example Usage
642
+
643
+ ```graphql
644
+ # Check if Better-Auth is enabled
645
+ query {
646
+ betterAuthEnabled
647
+ }
648
+
649
+ # Get available features
650
+ query {
651
+ betterAuthFeatures {
652
+ enabled
653
+ jwt
654
+ twoFactor
655
+ passkey
656
+ socialProviders
657
+ }
658
+ }
659
+
660
+ # Sign in
661
+ mutation {
662
+ betterAuthSignIn(email: "user@example.com", password: "password123") {
663
+ success
664
+ requiresTwoFactor
665
+ token
666
+ user {
667
+ id
668
+ email
669
+ name
670
+ roles
671
+ }
672
+ }
673
+ }
674
+
675
+ # Sign up
676
+ mutation {
677
+ betterAuthSignUp(
678
+ email: "newuser@example.com"
679
+ password: "securePassword123"
680
+ name: "New User"
681
+ ) {
682
+ success
683
+ user {
684
+ id
685
+ email
686
+ }
687
+ }
688
+ }
689
+
690
+ # Verify 2FA (after sign-in with requiresTwoFactor: true)
691
+ mutation {
692
+ betterAuthVerify2FA(code: "123456") {
693
+ success
694
+ token
695
+ user {
696
+ id
697
+ email
698
+ }
699
+ }
700
+ }
701
+
702
+ # Enable 2FA (requires authentication)
703
+ mutation {
704
+ betterAuthEnable2FA(password: "yourPassword") {
705
+ success
706
+ totpUri
707
+ backupCodes
708
+ error
709
+ }
710
+ }
711
+
712
+ # Disable 2FA
713
+ mutation {
714
+ betterAuthDisable2FA(password: "yourPassword")
715
+ }
716
+
717
+ # Generate new backup codes
718
+ mutation {
719
+ betterAuthGenerateBackupCodes
720
+ }
721
+
722
+ # List passkeys
723
+ query {
724
+ betterAuthListPasskeys {
725
+ id
726
+ name
727
+ credentialId
728
+ createdAt
729
+ }
730
+ }
731
+
732
+ # Get passkey registration challenge (for WebAuthn)
733
+ mutation {
734
+ betterAuthGetPasskeyChallenge {
735
+ success
736
+ challenge
737
+ error
738
+ }
739
+ }
740
+
741
+ # Delete a passkey
742
+ mutation {
743
+ betterAuthDeletePasskey(passkeyId: "passkey-id-here")
744
+ }
745
+ ```
746
+
747
+ ## Using BetterAuthService
748
+
749
+ Inject `BetterAuthService` to access Better-Auth functionality:
750
+
751
+ ```typescript
752
+ import { BetterAuthService } from '@lenne.tech/nest-server';
753
+
754
+ @Injectable()
755
+ export class MyService {
756
+ constructor(private readonly betterAuthService: BetterAuthService) {}
757
+
758
+ async checkUser(req: Request) {
759
+ // Check if Better-Auth is enabled
760
+ if (!this.betterAuthService.isEnabled()) {
761
+ return null;
762
+ }
763
+
764
+ // Get current session
765
+ const { session, user } = await this.betterAuthService.getSession(req);
766
+
767
+ if (session) {
768
+ console.log('User:', user.email);
769
+ console.log('Session expires:', session.expiresAt);
770
+
771
+ // Check if session is expiring soon
772
+ if (this.betterAuthService.isSessionExpiringSoon(session)) {
773
+ console.log('Session expiring soon!');
774
+ }
775
+
776
+ // Get remaining time
777
+ const remaining = this.betterAuthService.getSessionTimeRemaining(session);
778
+ console.log(`Session valid for ${remaining} seconds`);
779
+ }
780
+
781
+ return user;
782
+ }
783
+
784
+ async logout(sessionToken: string) {
785
+ const success = await this.betterAuthService.revokeSession(sessionToken);
786
+ return success;
787
+ }
788
+ }
789
+ ```
790
+
791
+ ### Available Methods
792
+
793
+ | Method | Description |
794
+ | ----------------------------------- | -------------------------------------------- |
795
+ | `isEnabled()` | Check if Better-Auth is enabled |
796
+ | `getInstance()` | Get the Better-Auth instance |
797
+ | `getApi()` | Get the Better-Auth API |
798
+ | `getConfig()` | Get the current configuration |
799
+ | `isJwtEnabled()` | Check if JWT plugin is enabled |
800
+ | `isTwoFactorEnabled()` | Check if 2FA is enabled |
801
+ | `isPasskeyEnabled()` | Check if Passkey is enabled |
802
+ | `isLegacyPasswordEnabled()` | Check if legacy password handling is enabled |
803
+ | `getEnabledSocialProviders()` | Get list of enabled social providers |
804
+ | `getBasePath()` | Get the base path for endpoints |
805
+ | `getBaseUrl()` | Get the base URL |
806
+ | `getSession(req)` | Get current session from request |
807
+ | `revokeSession(token)` | Revoke a session (logout) |
808
+ | `isSessionExpiringSoon(session, t?)`| Check if session is expiring soon |
809
+ | `getSessionTimeRemaining(session)` | Get remaining session time in seconds |
810
+
811
+ ## Security Integration
812
+
813
+ Better-Auth users are automatically integrated with the existing security system:
814
+
815
+ ### Role-Based Access Control
816
+
817
+ Better-Auth users work seamlessly with `@Roles()` decorators:
818
+
819
+ ```typescript
820
+ @Roles(RoleEnum.ADMIN)
821
+ @Query(() => [User])
822
+ async findAllUsers() {
823
+ // Only accessible by users with ADMIN role
824
+ }
825
+ ```
826
+
827
+ ### Special Roles
828
+
829
+ | Role | Description |
830
+ | ------------- | ------------------------------------- |
831
+ | `S_EVERYONE` | Accessible by everyone (no auth req.) |
832
+ | `S_USER` | Any authenticated user |
833
+ | `S_VERIFIED` | Users with verified email |
834
+ | `S_NO_ONE` | Never accessible |
835
+
836
+ ### How It Works
837
+
838
+ 1. `BetterAuthMiddleware` validates the session on each request
839
+ 2. `BetterAuthUserMapper` maps the session user to a user with `hasRole()` capability
840
+ 3. The mapped user is set to `req.user` for use with guards and decorators
841
+ 4. `RolesGuard` and `@Restricted()` work as expected
842
+
843
+ ## User Mapping
844
+
845
+ The `BetterAuthUserMapper` handles the conversion between Better-Auth sessions and application users:
846
+
847
+ ```typescript
848
+ import { BetterAuthUserMapper } from '@lenne.tech/nest-server';
849
+
850
+ @Injectable()
851
+ export class MyService {
852
+ constructor(private readonly userMapper: BetterAuthUserMapper) {}
853
+
854
+ async mapUser(sessionUser: BetterAuthSessionUser) {
855
+ // Maps session user to application user with roles
856
+ const user = await this.userMapper.mapSessionUser(sessionUser);
857
+
858
+ if (user) {
859
+ // Check roles
860
+ user.hasRole(RoleEnum.ADMIN); // true/false
861
+ user.hasRole([RoleEnum.ADMIN, 'editor']); // true if any match
862
+ }
863
+ }
864
+
865
+ async linkUser(sessionUser: BetterAuthSessionUser) {
866
+ // Links Better-Auth user with application database
867
+ const dbUser = await this.userMapper.linkOrCreateUser(sessionUser);
868
+ // Creates new user or links existing one via iamId
869
+ }
870
+ }
871
+ ```
872
+
873
+ ### Mapped User Properties
874
+
875
+ | Property | Type | Description |
876
+ | ----------------------------- | ---------- | ----------------------------------------------------- |
877
+ | `id` | string | User ID (from database or Better-Auth ID as fallback) |
878
+ | `iamId` | string | IAM provider user ID (e.g., Better-Auth) |
879
+ | `email` | string | User email |
880
+ | `name` | string | User display name |
881
+ | `roles` | string[] | User roles from database |
882
+ | `verified` | boolean | Whether user is verified |
883
+ | `emailVerified` | boolean | Better-Auth email verification status |
884
+ | `hasRole(roles)` | function | Check if user has any of the specified roles |
885
+ | `_authenticatedViaBetterAuth` | true | Marker for Better-Auth authenticated users |
886
+
887
+ ## Parallel Operation with Legacy Auth
888
+
889
+ Better-Auth runs **parallel to Legacy JWT authentication** without conflicts. Both systems are fully compatible because they use the same password hashing (bcrypt) and share the same users collection.
890
+
891
+ ### How It Works
892
+
893
+ ```typescript
894
+ // Legacy Auth - continues to work as before
895
+ const legacyToken = await authService.signIn({ email, password });
896
+
897
+ // Better-Auth - works with the same users
898
+ // POST /iam/sign-in { email, password }
899
+
900
+ // Both share the same users collection and bcrypt passwords
901
+ ```
902
+
903
+ ### Key Points
904
+
905
+ 1. **Shared Users Collection**: Both systems use the same `users` MongoDB collection
906
+ 2. **bcrypt Compatibility**: Both systems use bcrypt - passwords work with either system
907
+ 3. **User Linking**: When a user logs in via Better-Auth, `iamId` is set to link them
908
+ 4. **Role Preservation**: User roles work with both systems
909
+
910
+ ### Compatibility Matrix
911
+
912
+ | Scenario | Result |
913
+ | ---------------------------------- | ---------------------------------- |
914
+ | Legacy user → Legacy login | Works |
915
+ | Legacy user → Better-Auth login | Works (bcrypt compatible) |
916
+ | Better-Auth user → Better-Auth | Works |
917
+ | Better-Auth user → Legacy login | Works (password field preserved) |
918
+ | Social-only user → Legacy login | Fails (no password field) |
919
+
920
+ ### User Database Fields
921
+
922
+ | Field | Purpose |
923
+ | ---------- | ------------------------------------------------- |
924
+ | `password` | Password hash (bcrypt) - used by both systems |
925
+ | `iamId` | IAM provider user ID (set on first Better-Auth login) |
926
+
927
+ **No migration is required** - users can authenticate with either system immediately.
928
+
929
+ ## Testing
930
+
931
+ The module provides a `reset()` method for testing:
932
+
933
+ ```typescript
934
+ import { BetterAuthModule } from '@lenne.tech/nest-server';
935
+
936
+ describe('My Tests', () => {
937
+ beforeEach(() => {
938
+ // Reset static state between tests
939
+ BetterAuthModule.reset();
940
+ });
941
+
942
+ afterAll(() => {
943
+ BetterAuthModule.reset();
944
+ });
945
+ });
946
+ ```
947
+
948
+ ## Troubleshooting
949
+
950
+ ### Better-Auth endpoints return 404
951
+
952
+ - Ensure `betterAuth.enabled` is NOT explicitly set to `false`
953
+ - Check that a valid secret is available
954
+ - Verify MongoDB connection is established
955
+ - Check logs for initialization errors during startup
956
+
957
+ ### Session not being set on requests
958
+
959
+ - Check that `BetterAuthMiddleware` is being applied (automatic with module)
960
+ - Verify cookies are being sent with requests
961
+ - Check browser developer tools for session cookies
962
+
963
+ ### Social login not working
964
+
965
+ - Verify `clientId` and `clientSecret` are correct
966
+ - Check that redirect URLs are configured in provider settings
967
+ - Ensure `trustedOrigins` includes your application URL
968
+
969
+ ### 2FA/Passkey not working
970
+
971
+ - Ensure the respective plugin is enabled in configuration
972
+ - For Passkey, verify `rpId` matches your domain
973
+ - Check browser console for WebAuthn errors
974
+
975
+ ## Rate Limiting
976
+
977
+ The Better-Auth module includes built-in rate limiting to protect against brute-force attacks.
978
+
979
+ ### Configuration
980
+
981
+ ```typescript
982
+ const config = {
983
+ betterAuth: {
984
+ rateLimit: {
985
+ enabled: true,
986
+ max: 10,
987
+ windowSeconds: 60,
988
+ message: 'Too many requests, please try again later.',
989
+ strictEndpoints: ['/sign-in', '/sign-up', '/forgot-password', '/reset-password'],
990
+ skipEndpoints: ['/session', '/callback'],
991
+ },
992
+ },
993
+ };
994
+ ```
995
+
996
+ ### Configuration Options
997
+
998
+ | Option | Type | Default | Description |
999
+ | ----------------- | -------- | -------------------------- | ------------------------------------- |
1000
+ | `enabled` | boolean | `false` | Enable/disable rate limiting |
1001
+ | `max` | number | `10` | Maximum requests per time window |
1002
+ | `windowSeconds` | number | `60` | Time window in seconds |
1003
+ | `message` | string | `'Too many requests...'` | Error message when limit exceeded |
1004
+ | `strictEndpoints` | string[] | See below | Endpoints with half the normal limit |
1005
+ | `skipEndpoints` | string[] | See below | Endpoints that skip rate limiting |
1006
+
1007
+ ### Default Strict Endpoints
1008
+
1009
+ Strict endpoints receive half the configured `max` limit to provide extra protection:
1010
+
1011
+ - `/sign-in` - Login attempts
1012
+ - `/sign-up` - Registration attempts
1013
+ - `/forgot-password` - Password reset requests
1014
+ - `/reset-password` - Password reset submissions
1015
+
1016
+ ### Default Skip Endpoints
1017
+
1018
+ These endpoints bypass rate limiting entirely:
1019
+
1020
+ - `/session` - Session checks (frequent client-side calls)
1021
+ - `/callback` - OAuth callbacks
1022
+
1023
+ ### Response Headers
1024
+
1025
+ When rate limiting is enabled, the following headers are added to responses:
1026
+
1027
+ | Header | Description |
1028
+ | ---------------------- | ---------------------------------------- |
1029
+ | `X-RateLimit-Limit` | Maximum requests allowed in the window |
1030
+ | `X-RateLimit-Remaining`| Remaining requests in current window |
1031
+ | `X-RateLimit-Reset` | Seconds until the rate limit resets |
1032
+ | `Retry-After` | (429 only) Seconds to wait before retry |
1033
+
1034
+ ### Rate Limit Exceeded Response
1035
+
1036
+ When the rate limit is exceeded, a `429 Too Many Requests` response is returned:
1037
+
1038
+ ```json
1039
+ {
1040
+ "statusCode": 429,
1041
+ "error": "Too Many Requests",
1042
+ "message": "Too many requests, please try again later.",
1043
+ "retryAfter": 45
1044
+ }
1045
+ ```
1046
+
1047
+ ### Using BetterAuthRateLimiter Programmatically
1048
+
1049
+ ```typescript
1050
+ import { BetterAuthRateLimiter } from '@lenne.tech/nest-server';
1051
+
1052
+ @Injectable()
1053
+ export class MyService {
1054
+ constructor(private readonly rateLimiter: BetterAuthRateLimiter) {}
1055
+
1056
+ checkCustomLimit(ip: string) {
1057
+ // Check rate limit for custom endpoint
1058
+ const result = this.rateLimiter.check(ip, '/custom-endpoint');
1059
+
1060
+ if (!result.allowed) {
1061
+ console.log(`Rate limit exceeded. Retry in ${result.resetIn} seconds`);
1062
+ }
1063
+ }
1064
+
1065
+ resetUserLimit(ip: string) {
1066
+ // Reset rate limit for specific IP (e.g., after successful captcha)
1067
+ this.rateLimiter.reset(ip);
1068
+ }
1069
+
1070
+ getStats() {
1071
+ // Get rate limiter statistics
1072
+ return this.rateLimiter.getStats();
1073
+ }
1074
+ }
1075
+ ```
1076
+
1077
+ ### Production Recommendations
1078
+
1079
+ For production environments, consider:
1080
+
1081
+ 1. **Enable rate limiting** - Always enable in production
1082
+ 2. **Lower limits** - Use stricter limits (e.g., `max: 5`) for production
1083
+ 3. **Environment variables** - Configure via environment:
1084
+
1085
+ ```typescript
1086
+ const config = {
1087
+ rateLimit: {
1088
+ enabled: process.env.RATE_LIMIT_ENABLED !== 'false',
1089
+ max: parseInt(process.env.RATE_LIMIT_MAX || '10', 10),
1090
+ windowSeconds: parseInt(process.env.RATE_LIMIT_WINDOW_SECONDS || '60', 10),
1091
+ },
1092
+ };
1093
+ ```
1094
+
1095
+ 4. **Monitor rate limit events** - The service logs warnings when limits are exceeded
1096
+ 5. **Consider Redis** - For multi-instance deployments, implement Redis-based rate limiting