@lenne.tech/nest-server 11.9.0 → 11.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.env.js +2 -0
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/helpers/logging.helper.d.ts +6 -0
- package/dist/core/common/helpers/logging.helper.js +55 -0
- package/dist/core/common/helpers/logging.helper.js.map +1 -0
- package/dist/core/common/interfaces/server-options.interface.d.ts +37 -19
- package/dist/core/modules/auth/guards/roles.guard.js +33 -2
- package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
- package/dist/core/modules/auth/services/core-auth.service.d.ts +5 -5
- package/dist/core/modules/auth/services/core-auth.service.js +4 -4
- package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
- package/dist/core/modules/auth/tokens.decorator.d.ts +1 -1
- package/dist/core/modules/better-auth/better-auth.config.js +32 -10
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.resolver.d.ts +16 -16
- package/dist/core/modules/better-auth/better-auth.resolver.js +34 -34
- package/dist/core/modules/better-auth/better-auth.resolver.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.types.d.ts +2 -1
- package/dist/core/modules/better-auth/better-auth.types.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.d.ts +10 -0
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js +91 -0
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-auth.model.d.ts +9 -0
- package/dist/core/modules/better-auth/{better-auth-auth.model.js → core-better-auth-auth.model.js} +17 -17
- package/dist/core/modules/better-auth/core-better-auth-auth.model.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth-migration-status.model.d.ts → core-better-auth-migration-status.model.d.ts} +1 -1
- package/dist/core/modules/better-auth/{better-auth-migration-status.model.js → core-better-auth-migration-status.model.js} +14 -14
- package/dist/core/modules/better-auth/core-better-auth-migration-status.model.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth-models.d.ts → core-better-auth-models.d.ts} +8 -8
- package/dist/core/modules/better-auth/{better-auth-models.js → core-better-auth-models.js} +61 -61
- package/dist/core/modules/better-auth/core-better-auth-models.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-rate-limit.middleware.d.ts +12 -0
- package/dist/core/modules/better-auth/{better-auth-rate-limit.middleware.js → core-better-auth-rate-limit.middleware.js} +10 -10
- package/dist/core/modules/better-auth/core-better-auth-rate-limit.middleware.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth-rate-limiter.service.d.ts → core-better-auth-rate-limiter.service.d.ts} +1 -1
- package/dist/core/modules/better-auth/{better-auth-rate-limiter.service.js → core-better-auth-rate-limiter.service.js} +8 -8
- package/dist/core/modules/better-auth/core-better-auth-rate-limiter.service.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth-user.mapper.d.ts → core-better-auth-user.mapper.d.ts} +1 -1
- package/dist/core/modules/better-auth/{better-auth-user.mapper.js → core-better-auth-user.mapper.js} +10 -9
- package/dist/core/modules/better-auth/core-better-auth-user.mapper.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-web.helper.d.ts +19 -0
- package/dist/core/modules/better-auth/core-better-auth-web.helper.js +152 -0
- package/dist/core/modules/better-auth/core-better-auth-web.helper.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth.controller.d.ts +23 -32
- package/dist/core/modules/better-auth/core-better-auth.controller.js +184 -201
- package/dist/core/modules/better-auth/core-better-auth.controller.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.middleware.d.ts +22 -0
- package/dist/core/modules/better-auth/{better-auth.middleware.js → core-better-auth.middleware.js} +45 -18
- package/dist/core/modules/better-auth/core-better-auth.middleware.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth.module.d.ts → core-better-auth.module.d.ts} +6 -6
- package/dist/core/modules/better-auth/{better-auth.module.js → core-better-auth.module.js} +65 -60
- package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth.resolver.d.ts +19 -19
- package/dist/core/modules/better-auth/core-better-auth.resolver.js +18 -18
- package/dist/core/modules/better-auth/core-better-auth.resolver.js.map +1 -1
- package/dist/core/modules/better-auth/{better-auth.service.d.ts → core-better-auth.service.d.ts} +3 -2
- package/dist/core/modules/better-auth/{better-auth.service.js → core-better-auth.service.js} +75 -35
- package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -0
- package/dist/core/modules/better-auth/index.d.ts +11 -9
- package/dist/core/modules/better-auth/index.js +11 -9
- package/dist/core/modules/better-auth/index.js.map +1 -1
- package/dist/core/modules/error-code/core-error-code.controller.js.map +1 -1
- package/dist/core/modules/user/interfaces/core-user-service-options.interface.d.ts +2 -2
- package/dist/core.module.js +6 -6
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/server/modules/better-auth/better-auth.controller.d.ts +5 -5
- package/dist/server/modules/better-auth/better-auth.controller.js +4 -4
- package/dist/server/modules/better-auth/better-auth.controller.js.map +1 -1
- package/dist/server/modules/better-auth/better-auth.module.js +3 -3
- package/dist/server/modules/better-auth/better-auth.module.js.map +1 -1
- package/dist/server/modules/better-auth/better-auth.resolver.d.ts +17 -17
- package/dist/server/modules/better-auth/better-auth.resolver.js +18 -18
- package/dist/server/modules/better-auth/better-auth.resolver.js.map +1 -1
- package/dist/server/modules/user/user.service.d.ts +2 -2
- package/dist/server/modules/user/user.service.js +2 -2
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/test/test.helper.d.ts +1 -0
- package/dist/test/test.helper.js +5 -1
- package/dist/test/test.helper.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +5 -3
- package/src/config.env.ts +15 -0
- package/src/core/common/helpers/logging.helper.ts +134 -0
- package/src/core/common/interfaces/server-options.interface.ts +419 -234
- package/src/core/modules/auth/guards/roles.guard.ts +44 -3
- package/src/core/modules/auth/services/core-auth.service.ts +4 -4
- package/src/core/modules/better-auth/ARCHITECTURE.md +102 -0
- package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +277 -8
- package/src/core/modules/better-auth/README.md +97 -53
- package/src/core/modules/better-auth/better-auth.config.ts +66 -18
- package/src/core/modules/better-auth/better-auth.resolver.ts +32 -32
- package/src/core/modules/better-auth/better-auth.types.ts +3 -2
- package/src/core/modules/better-auth/core-better-auth-api.middleware.ts +134 -0
- package/src/core/modules/better-auth/{better-auth-auth.model.ts → core-better-auth-auth.model.ts} +6 -6
- package/src/core/modules/better-auth/{better-auth-migration-status.model.ts → core-better-auth-migration-status.model.ts} +1 -1
- package/src/core/modules/better-auth/{better-auth-models.ts → core-better-auth-models.ts} +9 -9
- package/src/core/modules/better-auth/{better-auth-rate-limit.middleware.ts → core-better-auth-rate-limit.middleware.ts} +5 -5
- package/src/core/modules/better-auth/{better-auth-rate-limiter.service.ts → core-better-auth-rate-limiter.service.ts} +2 -2
- package/src/core/modules/better-auth/{better-auth-user.mapper.ts → core-better-auth-user.mapper.ts} +4 -3
- package/src/core/modules/better-auth/core-better-auth-web.helper.ts +272 -0
- package/src/core/modules/better-auth/core-better-auth.controller.ts +386 -230
- package/src/core/modules/better-auth/{better-auth.middleware.ts → core-better-auth.middleware.ts} +57 -17
- package/src/core/modules/better-auth/{better-auth.module.ts → core-better-auth.module.ts} +77 -66
- package/src/core/modules/better-auth/core-better-auth.resolver.ts +42 -42
- package/src/core/modules/better-auth/{better-auth.service.ts → core-better-auth.service.ts} +86 -40
- package/src/core/modules/better-auth/index.ts +18 -11
- package/src/core/modules/error-code/INTEGRATION-CHECKLIST.md +4 -1
- package/src/core/modules/error-code/core-error-code.controller.ts +3 -2
- package/src/core/modules/user/interfaces/core-user-service-options.interface.ts +3 -3
- package/src/core.module.ts +12 -12
- package/src/index.ts +1 -0
- package/src/server/modules/better-auth/better-auth.controller.ts +4 -4
- package/src/server/modules/better-auth/better-auth.module.ts +1 -1
- package/src/server/modules/better-auth/better-auth.resolver.ts +31 -31
- package/src/server/modules/user/user.service.ts +2 -2
- package/src/test/test.helper.ts +13 -1
- package/dist/core/modules/better-auth/better-auth-auth.model.d.ts +0 -9
- package/dist/core/modules/better-auth/better-auth-auth.model.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-migration-status.model.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-models.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.d.ts +0 -12
- package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-user.mapper.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth.middleware.d.ts +0 -21
- package/dist/core/modules/better-auth/better-auth.middleware.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth.module.js.map +0 -1
- 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
|
-
|
|
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 {
|
|
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?:
|
|
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 `
|
|
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
|
|
138
|
+
Add import and include CoreBetterAuthModule in imports array with `fallbackSecrets`:
|
|
139
139
|
|
|
140
140
|
```typescript
|
|
141
|
-
|
|
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**: `
|
|
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 {
|
|
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 `
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
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
|
-
####
|
|
869
|
+
#### CoreBetterAuthAuthModel
|
|
859
870
|
|
|
860
871
|
```graphql
|
|
861
|
-
type
|
|
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
|
-
####
|
|
882
|
+
#### CoreBetterAuthFeaturesModel
|
|
872
883
|
|
|
873
884
|
```graphql
|
|
874
|
-
type
|
|
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
|
-
####
|
|
894
|
+
#### CoreBetterAuth2FASetupModel
|
|
884
895
|
|
|
885
896
|
```graphql
|
|
886
|
-
type
|
|
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
|
-
####
|
|
905
|
+
#### CoreBetterAuthPasskeyModel
|
|
895
906
|
|
|
896
907
|
```graphql
|
|
897
|
-
type
|
|
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
|
-
####
|
|
916
|
+
#### CoreBetterAuthPasskeyChallengeModel
|
|
906
917
|
|
|
907
918
|
```graphql
|
|
908
|
-
type
|
|
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
|
|
1032
|
+
## Using CoreBetterAuthService
|
|
1022
1033
|
|
|
1023
|
-
Inject `
|
|
1034
|
+
Inject `CoreBetterAuthService` to access Better-Auth functionality:
|
|
1024
1035
|
|
|
1025
1036
|
```typescript
|
|
1026
|
-
import {
|
|
1037
|
+
import { CoreBetterAuthService } from '@lenne.tech/nest-server';
|
|
1027
1038
|
|
|
1028
1039
|
@Injectable()
|
|
1029
1040
|
export class MyService {
|
|
1030
|
-
constructor(private readonly 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. `
|
|
1112
|
-
2. `
|
|
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 `
|
|
1129
|
+
The `CoreBetterAuthUserMapper` handles the conversion between Better-Auth sessions and application users:
|
|
1119
1130
|
|
|
1120
1131
|
```typescript
|
|
1121
|
-
import {
|
|
1132
|
+
import { CoreBetterAuthUserMapper } from '@lenne.tech/nest-server';
|
|
1122
1133
|
|
|
1123
1134
|
@Injectable()
|
|
1124
1135
|
export class MyService {
|
|
1125
|
-
constructor(private readonly userMapper:
|
|
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 `
|
|
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 `
|
|
1448
|
+
1. Verify `CoreBetterAuthUserMapper` is injected in `UserService`:
|
|
1438
1449
|
```typescript
|
|
1439
1450
|
constructor(
|
|
1440
|
-
@Optional() private readonly 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 `
|
|
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,
|
|
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:
|
|
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 {
|
|
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
|
-
|
|
1564
|
+
CoreBetterAuthModule.reset();
|
|
1554
1565
|
});
|
|
1555
1566
|
|
|
1556
1567
|
afterAll(() => {
|
|
1557
|
-
|
|
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
|
|
1678
|
+
### Using CoreBetterAuthRateLimiter Programmatically
|
|
1662
1679
|
|
|
1663
1680
|
```typescript
|
|
1664
|
-
import {
|
|
1681
|
+
import { CoreBetterAuthRateLimiter } from '@lenne.tech/nest-server';
|
|
1665
1682
|
|
|
1666
1683
|
@Injectable()
|
|
1667
1684
|
export class MyService {
|
|
1668
|
-
constructor(private readonly rateLimiter:
|
|
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(() =>
|
|
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<
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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('
|
|
419
|
-
warnings.push('
|
|
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
|
-
'
|
|
458
|
+
'FOR PRODUCTION: Set betterAuth.secret in config or provide a valid fallback secret (min 32 chars).',
|
|
422
459
|
);
|
|
423
|
-
warnings.push("
|
|
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
|
-
'
|
|
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)) {
|