@lenne.tech/nest-server 11.9.0 → 11.10.1

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
@@ -8,7 +8,7 @@ Integration of the [better-auth](https://better-auth.com) authentication framewo
8
8
  // 1. Follow INTEGRATION-CHECKLIST.md to create required files
9
9
  // 2. Add to server.module.ts:
10
10
  CoreModule.forRoot(envConfig), // IAM-only (new projects)
11
- BetterAuthModule.forRoot({ config: envConfig.betterAuth, fallbackSecrets: [envConfig.jwt?.secret] }),
11
+ CoreBetterAuthModule.forRoot({ config: envConfig.betterAuth, fallbackSecrets: [envConfig.jwt?.secret] }),
12
12
 
13
13
  // 3. Configure in config.env.ts (minimal - JWT enabled by default):
14
14
  betterAuth: true // or betterAuth: {} for same effect
@@ -112,12 +112,12 @@ GraphQL schema is built from decorators at compile time. The parent class (`Core
112
112
 
113
113
  1. Add import:
114
114
  ```typescript
115
- import { BetterAuthUserMapper } from '@lenne.tech/nest-server';
115
+ import { CoreBetterAuthUserMapper } from '@lenne.tech/nest-server';
116
116
  ```
117
117
 
118
118
  2. Add constructor parameter:
119
119
  ```typescript
120
- @Optional() private readonly betterAuthUserMapper?: BetterAuthUserMapper,
120
+ @Optional() private readonly betterAuthUserMapper?: CoreBetterAuthUserMapper,
121
121
  ```
122
122
 
123
123
  3. Pass to super() via options object:
@@ -126,7 +126,7 @@ GraphQL schema is built from decorators at compile time. The parent class (`Core
126
126
  ```
127
127
 
128
128
  **Why is this critical?**
129
- The `BetterAuthUserMapper` enables bidirectional password synchronization:
129
+ The `CoreBetterAuthUserMapper` enables bidirectional password synchronization:
130
130
  - User signs up via BetterAuth → password synced to Legacy Auth (bcrypt hash)
131
131
  - User changes password → synced between both systems
132
132
  - **Without this, users can only authenticate via ONE system!**
@@ -135,10 +135,10 @@ The `BetterAuthUserMapper` enables bidirectional password synchronization:
135
135
  **Modify:** `src/server/server.module.ts`
136
136
  **Reference:** See ServerModule in reference implementation
137
137
 
138
- Add import and include BetterAuthModule in imports array with `fallbackSecrets`:
138
+ Add import and include CoreBetterAuthModule in imports array with `fallbackSecrets`:
139
139
 
140
140
  ```typescript
141
- BetterAuthModule.forRoot({
141
+ CoreBetterAuthModule.forRoot({
142
142
  config: envConfig.betterAuth,
143
143
  fallbackSecrets: [envConfig.jwt?.secret, envConfig.jwt?.refresh?.secret],
144
144
  }),
@@ -216,7 +216,7 @@ Read the security section below for production deployments.
216
216
  /iam/session
217
217
  /iam/callback/:provider
218
218
  ```
219
- - **Middleware Matching**: `BetterAuthMiddleware` only forwards requests to paths starting with `basePath`
219
+ - **Middleware Matching**: `CoreBetterAuthMiddleware` only forwards requests to paths starting with `basePath`
220
220
 
221
221
  **Default `/iam`** avoids collisions with existing `/auth` routes from Legacy Auth.
222
222
 
@@ -441,7 +441,7 @@ By default (`autoRegister: false`), projects integrate BetterAuth via an **exten
441
441
  ```typescript
442
442
  // src/server/modules/better-auth/better-auth.module.ts
443
443
  import { Module, DynamicModule } from '@nestjs/common';
444
- import { BetterAuthModule as CoreBetterAuthModule } from '@lenne.tech/nest-server';
444
+ import { CoreBetterAuthModule } from '@lenne.tech/nest-server';
445
445
 
446
446
  @Module({})
447
447
  export class BetterAuthModule {
@@ -723,7 +723,7 @@ Better-Auth is **enabled by default** but requires explicit module integration (
723
723
  1. `BetterAuthModule` - Wraps CoreBetterAuthModule with custom resolver/controller
724
724
  2. `BetterAuthResolver` - Extends CoreBetterAuthResolver for GraphQL operations
725
725
  3. `BetterAuthController` - Extends CoreBetterAuthController for REST endpoints
726
- 4. `UserService` - Inject `BetterAuthUserMapper` for bidirectional auth sync
726
+ 4. `UserService` - Inject `CoreBetterAuthUserMapper` for bidirectional auth sync
727
727
 
728
728
  ### Simple: Auto-Registration
729
729
 
@@ -797,20 +797,31 @@ curl -X GET https://api.example.com/iam/token \
797
797
  | `/iam/sign-in/social` | POST | Initiate social login |
798
798
  | `/iam/callback/:provider` | GET | OAuth callback |
799
799
 
800
- ### Two-Factor Authentication Endpoints
800
+ ### Two-Factor Authentication Endpoints (Native Better Auth)
801
801
 
802
- | Endpoint | Method | Description |
803
- | ------------------------- | ------ | ---------------- |
804
- | `/iam/two-factor/enable` | POST | Enable 2FA |
805
- | `/iam/two-factor/disable` | POST | Disable 2FA |
806
- | `/iam/two-factor/verify` | POST | Verify 2FA code |
802
+ These endpoints are handled by Better Auth's native `twoFactor` plugin:
807
803
 
808
- ### Passkey Endpoints
804
+ | Endpoint | Method | Description |
805
+ | ----------------------------------- | ------ | ------------------------------ |
806
+ | `/iam/two-factor/enable` | POST | Enable 2FA, get TOTP URI |
807
+ | `/iam/two-factor/disable` | POST | Disable 2FA |
808
+ | `/iam/two-factor/verify-totp` | POST | Verify TOTP code |
809
+ | `/iam/two-factor/generate-backup-codes` | POST | Generate backup codes |
810
+ | `/iam/two-factor/verify-backup-code`| POST | Verify backup code |
809
811
 
810
- | Endpoint | Method | Description |
811
- | ---------------------------- | ------ | ------------------------- |
812
- | `/iam/passkey/register` | POST | Register new passkey |
813
- | `/iam/passkey/authenticate` | POST | Authenticate with passkey |
812
+ ### Passkey Endpoints (Native Better Auth)
813
+
814
+ These endpoints are handled by Better Auth's native `passkey` plugin:
815
+
816
+ | Endpoint | Method | Description |
817
+ | ----------------------------------------- | ------ | ----------------------------------- |
818
+ | `/iam/passkey/generate-register-options` | POST | Get WebAuthn registration options |
819
+ | `/iam/passkey/verify-registration` | POST | Verify and store passkey |
820
+ | `/iam/passkey/generate-authenticate-options` | POST | Get WebAuthn authentication options |
821
+ | `/iam/passkey/verify-authentication` | POST | Verify passkey authentication |
822
+ | `/iam/passkey/list-user-passkeys` | POST | List user's passkeys |
823
+ | `/iam/passkey/delete-passkey` | POST | Delete a passkey |
824
+ | `/iam/passkey/update-passkey` | POST | Update passkey name |
814
825
 
815
826
  ## GraphQL API
816
827
 
@@ -855,10 +866,10 @@ In addition to REST endpoints, Better-Auth provides GraphQL queries and mutation
855
866
 
856
867
  ### Response Types
857
868
 
858
- #### BetterAuthAuthModel
869
+ #### CoreBetterAuthAuthModel
859
870
 
860
871
  ```graphql
861
- type BetterAuthAuthModel {
872
+ type CoreBetterAuthAuthModel {
862
873
  success: Boolean!
863
874
  requiresTwoFactor: Boolean
864
875
  token: String
@@ -868,10 +879,10 @@ type BetterAuthAuthModel {
868
879
  }
869
880
  ```
870
881
 
871
- #### BetterAuthFeaturesModel
882
+ #### CoreBetterAuthFeaturesModel
872
883
 
873
884
  ```graphql
874
- type BetterAuthFeaturesModel {
885
+ type CoreBetterAuthFeaturesModel {
875
886
  enabled: Boolean!
876
887
  jwt: Boolean!
877
888
  twoFactor: Boolean!
@@ -880,10 +891,10 @@ type BetterAuthFeaturesModel {
880
891
  }
881
892
  ```
882
893
 
883
- #### BetterAuth2FASetupModel
894
+ #### CoreBetterAuth2FASetupModel
884
895
 
885
896
  ```graphql
886
- type BetterAuth2FASetupModel {
897
+ type CoreBetterAuth2FASetupModel {
887
898
  success: Boolean!
888
899
  totpUri: String
889
900
  backupCodes: [String!]
@@ -891,10 +902,10 @@ type BetterAuth2FASetupModel {
891
902
  }
892
903
  ```
893
904
 
894
- #### BetterAuthPasskeyModel
905
+ #### CoreBetterAuthPasskeyModel
895
906
 
896
907
  ```graphql
897
- type BetterAuthPasskeyModel {
908
+ type CoreBetterAuthPasskeyModel {
898
909
  id: String!
899
910
  name: String
900
911
  credentialId: String!
@@ -902,10 +913,10 @@ type BetterAuthPasskeyModel {
902
913
  }
903
914
  ```
904
915
 
905
- #### BetterAuthPasskeyChallengeModel
916
+ #### CoreBetterAuthPasskeyChallengeModel
906
917
 
907
918
  ```graphql
908
- type BetterAuthPasskeyChallengeModel {
919
+ type CoreBetterAuthPasskeyChallengeModel {
909
920
  success: Boolean!
910
921
  challenge: String
911
922
  error: String
@@ -1018,16 +1029,16 @@ mutation {
1018
1029
  }
1019
1030
  ```
1020
1031
 
1021
- ## Using BetterAuthService
1032
+ ## Using CoreBetterAuthService
1022
1033
 
1023
- Inject `BetterAuthService` to access Better-Auth functionality:
1034
+ Inject `CoreBetterAuthService` to access Better-Auth functionality:
1024
1035
 
1025
1036
  ```typescript
1026
- import { BetterAuthService } from '@lenne.tech/nest-server';
1037
+ import { CoreBetterAuthService } from '@lenne.tech/nest-server';
1027
1038
 
1028
1039
  @Injectable()
1029
1040
  export class MyService {
1030
- constructor(private readonly betterAuthService: BetterAuthService) {}
1041
+ constructor(private readonly betterAuthService: CoreBetterAuthService) {}
1031
1042
 
1032
1043
  async checkUser(req: Request) {
1033
1044
  // Check if Better-Auth is enabled
@@ -1108,21 +1119,21 @@ async findAllUsers() {
1108
1119
 
1109
1120
  ### How It Works
1110
1121
 
1111
- 1. `BetterAuthMiddleware` validates the session on each request
1112
- 2. `BetterAuthUserMapper` maps the session user to a user with `hasRole()` capability
1122
+ 1. `CoreBetterAuthMiddleware` validates the session on each request
1123
+ 2. `CoreBetterAuthUserMapper` maps the session user to a user with `hasRole()` capability
1113
1124
  3. The mapped user is set to `req.user` for use with guards and decorators
1114
1125
  4. `RolesGuard` and `@Restricted()` work as expected
1115
1126
 
1116
1127
  ## User Mapping
1117
1128
 
1118
- The `BetterAuthUserMapper` handles the conversion between Better-Auth sessions and application users:
1129
+ The `CoreBetterAuthUserMapper` handles the conversion between Better-Auth sessions and application users:
1119
1130
 
1120
1131
  ```typescript
1121
- import { BetterAuthUserMapper } from '@lenne.tech/nest-server';
1132
+ import { CoreBetterAuthUserMapper } from '@lenne.tech/nest-server';
1122
1133
 
1123
1134
  @Injectable()
1124
1135
  export class MyService {
1125
- constructor(private readonly userMapper: BetterAuthUserMapper) {}
1136
+ constructor(private readonly userMapper: CoreBetterAuthUserMapper) {}
1126
1137
 
1127
1138
  async mapUser(sessionUser: BetterAuthSessionUser) {
1128
1139
  // Maps session user to application user with roles
@@ -1355,7 +1366,7 @@ When a user resets their password via BetterAuth's native `/iam/reset-password`
1355
1366
 
1356
1367
  ### Automatic Sync (No Configuration Required)
1357
1368
 
1358
- The following sync operations happen automatically when `BetterAuthUserMapper` is injected in `UserService`:
1369
+ The following sync operations happen automatically when `CoreBetterAuthUserMapper` is injected in `UserService`:
1359
1370
 
1360
1371
  #### 1. IAM Sign-Up → Legacy
1361
1372
  When a user signs up via BetterAuth (`/iam/sign-up/email`), the password is hashed with bcrypt and stored in `users.password`, enabling Legacy Auth sign-in.
@@ -1434,10 +1445,10 @@ When a user is deleted:
1434
1445
 
1435
1446
  #### Password not syncing to IAM
1436
1447
 
1437
- 1. Verify `BetterAuthUserMapper` is injected in `UserService`:
1448
+ 1. Verify `CoreBetterAuthUserMapper` is injected in `UserService`:
1438
1449
  ```typescript
1439
1450
  constructor(
1440
- @Optional() private readonly betterAuthUserMapper?: BetterAuthUserMapper,
1451
+ @Optional() private readonly betterAuthUserMapper?: CoreBetterAuthUserMapper,
1441
1452
  ) {
1442
1453
  super(..., { betterAuthUserMapper });
1443
1454
  }
@@ -1464,7 +1475,7 @@ When a user is deleted:
1464
1475
 
1465
1476
  The user needs to sign in via Legacy Auth first with their password. This triggers the automatic migration on first IAM sign-in attempt.
1466
1477
 
1467
- Alternatively, use `BetterAuthUserMapper.migrateAccountToIam()` to migrate the user programmatically:
1478
+ Alternatively, use `CoreBetterAuthUserMapper.migrateAccountToIam()` to migrate the user programmatically:
1468
1479
 
1469
1480
  ```typescript
1470
1481
  await betterAuthUserMapper.migrateAccountToIam(email, plainPassword);
@@ -1477,13 +1488,13 @@ To enable bidirectional password reset sync, implement a custom password reset e
1477
1488
  ```typescript
1478
1489
  // src/server/modules/better-auth/better-auth.controller.ts
1479
1490
  import { Body, Controller, Post } from '@nestjs/common';
1480
- import { CoreBetterAuthController, BetterAuthUserMapper, Roles, RoleEnum } from '@lenne.tech/nest-server';
1491
+ import { CoreBetterAuthController, CoreBetterAuthUserMapper, Roles, RoleEnum } from '@lenne.tech/nest-server';
1481
1492
 
1482
1493
  @Controller('iam')
1483
1494
  export class BetterAuthController extends CoreBetterAuthController {
1484
1495
  constructor(
1485
1496
  // ... other dependencies
1486
- private readonly betterAuthUserMapper: BetterAuthUserMapper,
1497
+ private readonly betterAuthUserMapper: CoreBetterAuthUserMapper,
1487
1498
  ) {
1488
1499
  super(...);
1489
1500
  }
@@ -1545,20 +1556,26 @@ This is the recommended approach for projects in migration phase where both auth
1545
1556
  The module provides a `reset()` method for testing:
1546
1557
 
1547
1558
  ```typescript
1548
- import { BetterAuthModule } from '@lenne.tech/nest-server';
1559
+ import { CoreBetterAuthModule } from '@lenne.tech/nest-server';
1549
1560
 
1550
1561
  describe('My Tests', () => {
1551
1562
  beforeEach(() => {
1552
1563
  // Reset static state between tests
1553
- BetterAuthModule.reset();
1564
+ CoreBetterAuthModule.reset();
1554
1565
  });
1555
1566
 
1556
1567
  afterAll(() => {
1557
- BetterAuthModule.reset();
1568
+ CoreBetterAuthModule.reset();
1558
1569
  });
1559
1570
  });
1560
1571
  ```
1561
1572
 
1573
+ ## Architecture: Why Custom Controllers?
1574
+
1575
+ See **[ARCHITECTURE.md](./ARCHITECTURE.md)** for detailed documentation on why custom controllers are necessary for the hybrid auth system.
1576
+
1577
+ ---
1578
+
1562
1579
  ## Troubleshooting
1563
1580
 
1564
1581
  ### Better-Auth endpoints return 404
@@ -1658,14 +1675,14 @@ When the rate limit is exceeded, a `429 Too Many Requests` response is returned:
1658
1675
  }
1659
1676
  ```
1660
1677
 
1661
- ### Using BetterAuthRateLimiter Programmatically
1678
+ ### Using CoreBetterAuthRateLimiter Programmatically
1662
1679
 
1663
1680
  ```typescript
1664
- import { BetterAuthRateLimiter } from '@lenne.tech/nest-server';
1681
+ import { CoreBetterAuthRateLimiter } from '@lenne.tech/nest-server';
1665
1682
 
1666
1683
  @Injectable()
1667
1684
  export class MyService {
1668
- constructor(private readonly rateLimiter: BetterAuthRateLimiter) {}
1685
+ constructor(private readonly rateLimiter: CoreBetterAuthRateLimiter) {}
1669
1686
 
1670
1687
  checkCustomLimit(ip: string) {
1671
1688
  // Check rate limit for custom endpoint
@@ -1720,13 +1737,13 @@ Override any method to add custom behavior (e.g., sending welcome emails, analyt
1720
1737
  ```typescript
1721
1738
  // In your BetterAuthResolver (see Step 2 in Integration Guide)
1722
1739
 
1723
- @Mutation(() => BetterAuthAuthModel, { description: 'Sign up via Better-Auth (email/password)' })
1740
+ @Mutation(() => CoreBetterAuthAuthModel, { description: 'Sign up via Better-Auth (email/password)' })
1724
1741
  @Roles(RoleEnum.S_EVERYONE)
1725
1742
  override async betterAuthSignUp(
1726
1743
  @Args('email') email: string,
1727
1744
  @Args('password') password: string,
1728
1745
  @Args('name', { nullable: true }) name?: string,
1729
- ): Promise<BetterAuthAuthModel> {
1746
+ ): Promise<CoreBetterAuthAuthModel> {
1730
1747
  // Call original implementation
1731
1748
  const result = await super.betterAuthSignUp(email, password, name);
1732
1749
 
@@ -1777,3 +1794,30 @@ const sessionInfo = this.mapSessionInfo(response.session);
1777
1794
  // Map user to model
1778
1795
  const userModel = this.mapToUserModel(mappedUser);
1779
1796
  ```
1797
+
1798
+ ---
1799
+
1800
+ ## Client-Side Integration
1801
+
1802
+ For frontend integration with Better-Auth, see the **[Integration Checklist](./INTEGRATION-CHECKLIST.md#client-side-configuration)** which includes:
1803
+
1804
+ - Complete auth-client configuration with password hashing
1805
+ - 2FA login flow handling (`twoFactorRedirect`)
1806
+ - Passkey authentication with `validateSession()` fallback
1807
+ - Passkey registration and management
1808
+
1809
+ ### Quick Reference
1810
+
1811
+ | Feature | Client Method | Server Endpoint |
1812
+ |---------|---------------|-----------------|
1813
+ | Email Sign-In | `authClient.signIn.email()` | `POST /iam/sign-in/email` |
1814
+ | Passkey Sign-In | `authClient.signIn.passkey()` | `POST /iam/passkey/verify-authentication` |
1815
+ | 2FA Verify | `authClient.twoFactor.verifyTotp()` | `POST /iam/two-factor/verify-totp` |
1816
+ | Session Validation | `authClient.$fetch('/session')` | `GET /iam/session` |
1817
+
1818
+ ### Key Points
1819
+
1820
+ 1. **Password Hashing**: Always hash passwords with SHA256 client-side before sending
1821
+ 2. **2FA Redirect**: Check for `twoFactorRedirect: true` in sign-in response
1822
+ 3. **Passkey Session**: Passkey auth returns session without user - call `validateSession()` to fetch user data
1823
+ 4. **Credentials**: Use `credentials: 'include'` for cross-origin cookie handling
@@ -135,7 +135,7 @@ export function createBetterAuthInstance(options: CreateBetterAuthOptions): Bett
135
135
  const additionalFields = buildUserFields(config);
136
136
 
137
137
  // Build the base Better-Auth configuration
138
- const betterAuthConfig = {
138
+ const betterAuthConfig: Record<string, unknown> = {
139
139
  basePath: config.basePath || '/iam',
140
140
  baseURL: config.baseUrl || 'http://localhost:3000',
141
141
  database: mongodbAdapter(db),
@@ -147,13 +147,18 @@ export function createBetterAuthInstance(options: CreateBetterAuthOptions): Bett
147
147
  plugins,
148
148
  secret: config.secret,
149
149
  socialProviders,
150
- trustedOrigins,
151
150
  user: {
152
151
  additionalFields,
153
152
  modelName: 'users',
154
153
  },
155
154
  };
156
155
 
156
+ // Only add trustedOrigins if explicitly configured
157
+ // When undefined, Better-Auth uses its default CORS behavior (allows all origins)
158
+ if (trustedOrigins) {
159
+ betterAuthConfig.trustedOrigins = trustedOrigins;
160
+ }
161
+
157
162
  // Merge with custom options passthrough
158
163
  // This allows projects to configure any Better-Auth option not explicitly defined
159
164
  const finalConfig = config.options ? { ...betterAuthConfig, ...config.options } : betterAuthConfig;
@@ -205,13 +210,28 @@ function buildPlugins(config: IBetterAuth): BetterAuthPlugin[] {
205
210
  // Passkey/WebAuthn Plugin
206
211
  const passkeyConfig = getPluginConfig(config.passkey);
207
212
  if (passkeyConfig) {
208
- plugins.push(
209
- passkey({
210
- origin: passkeyConfig.origin || 'http://localhost:3000',
211
- rpID: passkeyConfig.rpId || 'localhost',
212
- rpName: passkeyConfig.rpName || 'Nest Server',
213
- }),
214
- );
213
+ // Build passkey options, only including defined values
214
+ const passkeyOptions: Record<string, unknown> = {
215
+ origin: passkeyConfig.origin || 'http://localhost:3000',
216
+ rpID: passkeyConfig.rpId || 'localhost',
217
+ rpName: passkeyConfig.rpName || 'Nest Server',
218
+ };
219
+
220
+ // Add optional WebAuthn configuration if specified
221
+ if (passkeyConfig.authenticatorAttachment) {
222
+ passkeyOptions.authenticatorAttachment = passkeyConfig.authenticatorAttachment;
223
+ }
224
+ if (passkeyConfig.residentKey) {
225
+ passkeyOptions.residentKey = passkeyConfig.residentKey;
226
+ }
227
+ if (passkeyConfig.userVerification) {
228
+ passkeyOptions.userVerification = passkeyConfig.userVerification;
229
+ }
230
+ if (passkeyConfig.webAuthnChallengeCookie) {
231
+ passkeyOptions.webAuthnChallengeCookie = passkeyConfig.webAuthnChallengeCookie;
232
+ }
233
+
234
+ plugins.push(passkey(passkeyOptions));
215
235
  }
216
236
 
217
237
  // Merge custom plugins from configuration
@@ -249,16 +269,33 @@ function buildSocialProviders(config: IBetterAuth): Record<string, SocialProvide
249
269
  }
250
270
 
251
271
  /**
252
- * Builds the trusted origins array
272
+ * Builds the trusted origins array for CORS configuration.
273
+ *
274
+ * Behavior:
275
+ * - If trustedOrigins is explicitly configured → use those origins
276
+ * - If Passkey disabled and baseUrl set → fallback to [baseUrl] (backwards compatible)
277
+ * - Otherwise → return undefined (allows all origins via Better-Auth's default)
278
+ *
279
+ * NOTE: When Passkey is enabled, trustedOrigins MUST be configured because
280
+ * Passkey uses credentials: 'include' which doesn't work with CORS '*' wildcard.
281
+ * This is validated in validateConfig() which throws an error if missing.
253
282
  */
254
- function buildTrustedOrigins(config: IBetterAuth): string[] {
283
+ function buildTrustedOrigins(config: IBetterAuth): string[] | undefined {
284
+ // If trustedOrigins is explicitly configured, use it
255
285
  if (config.trustedOrigins?.length) {
256
286
  return config.trustedOrigins;
257
287
  }
258
- if (config.baseUrl) {
288
+
289
+ // Backwards-compatible fallback for non-Passkey configs
290
+ // When Passkey is enabled, validateConfig() will throw an error anyway
291
+ const isPasskeyEnabled = isPluginEnabled(config.passkey);
292
+ if (!isPasskeyEnabled && config.baseUrl) {
259
293
  return [config.baseUrl];
260
294
  }
261
- return ['http://localhost:3000'];
295
+
296
+ // Otherwise, let Better-Auth handle CORS with its default behavior
297
+ // This allows all origins for requests without credentials
298
+ return undefined;
262
299
  }
263
300
 
264
301
  /**
@@ -415,16 +452,16 @@ function validateConfig(config: IBetterAuth, fallbackSecrets?: (string | undefin
415
452
  // Log information about secret source
416
453
  switch (secretSource) {
417
454
  case 'auto-generated':
418
- warnings.push('⚠️ BETTER_AUTH: No secret configured - using auto-generated secret.');
419
- warnings.push('⚠️ CONSEQUENCE: All user sessions will be invalidated on server restart!');
455
+ warnings.push('BETTER_AUTH: No secret configured - using auto-generated secret.');
456
+ warnings.push('CONSEQUENCE: All user sessions will be invalidated on server restart!');
420
457
  warnings.push(
421
- '💡 FOR PRODUCTION: Set betterAuth.secret in config or provide a valid fallback secret (min 32 chars).',
458
+ 'FOR PRODUCTION: Set betterAuth.secret in config or provide a valid fallback secret (min 32 chars).',
422
459
  );
423
- warnings.push("💡 Generate with: node -e \"console.log(require('crypto').randomBytes(32).toString('base64'))\"");
460
+ warnings.push("Generate with: node -e \"console.log(require('crypto').randomBytes(32).toString('base64'))\"");
424
461
  break;
425
462
  case 'fallback':
426
463
  warnings.push(
427
- '💡 BETTER_AUTH: Using fallback secret (backwards compatible). Consider setting betterAuth.secret explicitly.',
464
+ 'BETTER_AUTH: Using fallback secret (backwards compatible). Consider setting betterAuth.secret explicitly.',
428
465
  );
429
466
  break;
430
467
  // 'explicit' - no warning needed, explicitly configured
@@ -444,6 +481,17 @@ function validateConfig(config: IBetterAuth, fallbackSecrets?: (string | undefin
444
481
  }
445
482
  }
446
483
 
484
+ // ERROR if Passkey is enabled but trustedOrigins is not configured
485
+ // Passkey uses credentials: 'include', which doesn't work with CORS '*' wildcard
486
+ const isPasskeyEnabled = isPluginEnabled(config.passkey);
487
+ if (isPasskeyEnabled && !config.trustedOrigins?.length) {
488
+ errors.push(
489
+ 'PASSKEY CORS: trustedOrigins is REQUIRED when Passkey is enabled. ' +
490
+ 'Passkey uses credentials which requires explicit CORS origins (wildcard "*" is not allowed). ' +
491
+ 'Configure betterAuth.trustedOrigins with your frontend URLs, e.g.: ["http://localhost:3001", "https://app.example.com"]',
492
+ );
493
+ }
494
+
447
495
  // Validate passkey origin
448
496
  const passkeyConfig = typeof config.passkey === 'object' ? config.passkey : null;
449
497
  if (passkeyConfig?.enabled && passkeyConfig.origin && !isValidUrl(passkeyConfig.origin)) {