@lenne.tech/nest-server 11.6.2 → 11.7.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.
- package/dist/config.env.js +19 -12
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/helpers/filter.helper.d.ts +9 -9
- package/dist/core/common/helpers/filter.helper.js +2 -4
- package/dist/core/common/helpers/filter.helper.js.map +1 -1
- package/dist/core/common/helpers/gridfs.helper.js +3 -3
- package/dist/core/common/helpers/gridfs.helper.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +21 -3
- package/dist/core/common/services/crud.service.d.ts +16 -16
- package/dist/core/common/services/crud.service.js +1 -1
- package/dist/core/common/services/crud.service.js.map +1 -1
- package/dist/core/modules/auth/core-auth.controller.d.ts +1 -0
- package/dist/core/modules/auth/core-auth.controller.js +28 -2
- package/dist/core/modules/auth/core-auth.controller.js.map +1 -1
- package/dist/core/modules/auth/core-auth.module.js +14 -1
- package/dist/core/modules/auth/core-auth.module.js.map +1 -1
- package/dist/core/modules/auth/core-auth.resolver.d.ts +1 -0
- package/dist/core/modules/auth/core-auth.resolver.js +20 -2
- package/dist/core/modules/auth/core-auth.resolver.js.map +1 -1
- package/dist/core/modules/auth/exceptions/legacy-auth-disabled.exception.d.ts +4 -0
- package/dist/core/modules/auth/exceptions/legacy-auth-disabled.exception.js +17 -0
- package/dist/core/modules/auth/exceptions/legacy-auth-disabled.exception.js.map +1 -0
- package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.d.ts +9 -0
- package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.js +74 -0
- package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.js.map +1 -0
- package/dist/core/modules/auth/interfaces/auth-provider.interface.d.ts +7 -0
- package/dist/core/modules/auth/interfaces/auth-provider.interface.js +5 -0
- package/dist/core/modules/auth/interfaces/auth-provider.interface.js.map +1 -0
- package/dist/core/modules/auth/interfaces/core-auth-user.interface.d.ts +1 -0
- package/dist/core/modules/auth/services/core-auth.service.d.ts +10 -1
- package/dist/core/modules/auth/services/core-auth.service.js +141 -9
- package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
- package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.d.ts +31 -0
- package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.js +153 -0
- package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth-migration-status.model.d.ts +10 -0
- package/dist/core/modules/better-auth/better-auth-migration-status.model.js +57 -0
- package/dist/core/modules/better-auth/better-auth-migration-status.model.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth-models.d.ts +0 -1
- package/dist/core/modules/better-auth/better-auth-models.js +0 -4
- package/dist/core/modules/better-auth/better-auth-models.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth-user.mapper.d.ts +33 -0
- package/dist/core/modules/better-auth/better-auth-user.mapper.js +443 -0
- package/dist/core/modules/better-auth/better-auth-user.mapper.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.config.js +3 -0
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.module.d.ts +10 -2
- package/dist/core/modules/better-auth/better-auth.module.js +40 -52
- package/dist/core/modules/better-auth/better-auth.module.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.resolver.d.ts +8 -12
- package/dist/core/modules/better-auth/better-auth.resolver.js +33 -351
- package/dist/core/modules/better-auth/better-auth.resolver.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.service.d.ts +0 -1
- package/dist/core/modules/better-auth/better-auth.service.js +0 -3
- package/dist/core/modules/better-auth/better-auth.service.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.types.d.ts +9 -8
- package/dist/core/modules/better-auth/better-auth.types.js +14 -3
- package/dist/core/modules/better-auth/better-auth.types.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.controller.d.ts +67 -0
- package/dist/core/modules/better-auth/core-better-auth.controller.js +504 -0
- package/dist/core/modules/better-auth/core-better-auth.controller.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth.resolver.d.ts +61 -0
- package/dist/core/modules/better-auth/core-better-auth.resolver.js +552 -0
- package/dist/core/modules/better-auth/core-better-auth.resolver.js.map +1 -0
- package/dist/core/modules/better-auth/index.d.ts +3 -0
- package/dist/core/modules/better-auth/index.js +3 -0
- package/dist/core/modules/better-auth/index.js.map +1 -1
- package/dist/core/modules/user/core-user.service.d.ts +7 -1
- package/dist/core/modules/user/core-user.service.js +57 -3
- package/dist/core/modules/user/core-user.service.js.map +1 -1
- package/dist/core/modules/user/interfaces/core-user-service-options.interface.d.ts +4 -0
- package/dist/core/modules/user/interfaces/core-user-service-options.interface.js +3 -0
- package/dist/core/modules/user/interfaces/core-user-service-options.interface.js.map +1 -0
- package/dist/core.module.d.ts +3 -0
- package/dist/core.module.js +132 -54
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/server/modules/auth/auth.resolver.js +2 -0
- package/dist/server/modules/auth/auth.resolver.js.map +1 -1
- package/dist/server/modules/better-auth/better-auth.controller.d.ts +10 -0
- package/dist/server/modules/better-auth/better-auth.controller.js +36 -0
- package/dist/server/modules/better-auth/better-auth.controller.js.map +1 -0
- package/dist/server/modules/better-auth/better-auth.module.d.ts +9 -0
- package/dist/server/modules/better-auth/better-auth.module.js +44 -0
- package/dist/server/modules/better-auth/better-auth.module.js.map +1 -0
- package/dist/server/modules/better-auth/better-auth.resolver.d.ts +47 -0
- package/dist/server/modules/better-auth/better-auth.resolver.js +234 -0
- package/dist/server/modules/better-auth/better-auth.resolver.js.map +1 -0
- package/dist/server/modules/file/file-info.model.d.ts +71 -3
- package/dist/server/modules/user/user.model.d.ts +169 -3
- package/dist/server/modules/user/user.service.d.ts +3 -1
- package/dist/server/modules/user/user.service.js +7 -3
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/server/server.module.js +6 -1
- package/dist/server/server.module.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +20 -29
- package/src/config.env.ts +34 -13
- package/src/core/common/helpers/filter.helper.ts +15 -17
- package/src/core/common/helpers/gridfs.helper.ts +5 -5
- package/src/core/common/interfaces/server-options.interface.ts +222 -14
- package/src/core/common/services/crud.service.ts +22 -22
- package/src/core/modules/auth/core-auth.controller.ts +93 -5
- package/src/core/modules/auth/core-auth.module.ts +15 -1
- package/src/core/modules/auth/core-auth.resolver.ts +70 -2
- package/src/core/modules/auth/exceptions/legacy-auth-disabled.exception.ts +35 -0
- package/src/core/modules/auth/guards/legacy-auth-rate-limit.guard.ts +109 -0
- package/src/core/modules/auth/interfaces/auth-provider.interface.ts +86 -0
- package/src/core/modules/auth/interfaces/core-auth-user.interface.ts +6 -0
- package/src/core/modules/auth/services/core-auth.service.ts +245 -6
- package/src/core/modules/auth/services/legacy-auth-rate-limiter.service.ts +283 -0
- package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +254 -0
- package/src/core/modules/better-auth/README.md +698 -54
- package/src/core/modules/better-auth/better-auth-migration-status.model.ts +73 -0
- package/src/core/modules/better-auth/better-auth-models.ts +0 -3
- package/src/core/modules/better-auth/better-auth-user.mapper.ts +805 -0
- package/src/core/modules/better-auth/better-auth.config.ts +5 -0
- package/src/core/modules/better-auth/better-auth.module.ts +107 -66
- package/src/core/modules/better-auth/better-auth.resolver.ts +88 -553
- package/src/core/modules/better-auth/better-auth.service.ts +0 -9
- package/src/core/modules/better-auth/better-auth.types.ts +25 -10
- package/src/core/modules/better-auth/core-better-auth.controller.ts +646 -0
- package/src/core/modules/better-auth/core-better-auth.resolver.ts +730 -0
- package/src/core/modules/better-auth/index.ts +9 -1
- package/src/core/modules/user/core-user.service.ts +131 -4
- package/src/core/modules/user/interfaces/core-user-service-options.interface.ts +15 -0
- package/src/core.module.ts +257 -74
- package/src/index.ts +5 -0
- package/src/server/modules/auth/auth.resolver.ts +8 -0
- package/src/server/modules/better-auth/better-auth.controller.ts +41 -0
- package/src/server/modules/better-auth/better-auth.module.ts +88 -0
- package/src/server/modules/better-auth/better-auth.resolver.ts +210 -0
- package/src/server/modules/user/user.service.ts +4 -2
- package/src/server/server.module.ts +10 -1
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
Integration of the [better-auth](https://better-auth.com) authentication framework with @lenne.tech/nest-server.
|
|
4
4
|
|
|
5
|
+
## TL;DR
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
// 1. Follow INTEGRATION-CHECKLIST.md to create required files
|
|
9
|
+
// 2. Add to server.module.ts:
|
|
10
|
+
CoreModule.forRoot(envConfig), // IAM-only (new projects)
|
|
11
|
+
BetterAuthModule.forRoot({ config: envConfig.betterAuth, fallbackSecrets: [envConfig.jwt?.secret] }),
|
|
12
|
+
|
|
13
|
+
// 3. Configure in config.env.ts:
|
|
14
|
+
betterAuth: { jwt: {}, twoFactor: {}, passkey: {} }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Quick Links:** [Integration Checklist](./INTEGRATION-CHECKLIST.md) | [REST API](#rest-api-endpoints) | [GraphQL API](#graphql-api) | [Configuration](#configuration)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Table of Contents
|
|
22
|
+
|
|
23
|
+
- [Features](#features)
|
|
24
|
+
- [Quick Integration](#quick-integration-for-claude-code--ai-assistants)
|
|
25
|
+
- [Project Integration Guide](#project-integration-guide-required-steps)
|
|
26
|
+
- [Configuration](#configuration)
|
|
27
|
+
- [REST API Endpoints](#rest-api-endpoints)
|
|
28
|
+
- [GraphQL API](#graphql-api)
|
|
29
|
+
- [CoreModule Signatures](#coremoduleforroot-signatures)
|
|
30
|
+
- [Migration Roadmap](#migration-roadmap-legacy-auth--betterauth)
|
|
31
|
+
- [Password Synchronization](#bidirectional-password-synchronization)
|
|
32
|
+
- [Security Integration](#security-integration)
|
|
33
|
+
- [Troubleshooting](#troubleshooting)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
5
37
|
## Features
|
|
6
38
|
|
|
7
39
|
### Built-in Plugins (Explicit Configuration)
|
|
@@ -9,13 +41,13 @@ Integration of the [better-auth](https://better-auth.com) authentication framewo
|
|
|
9
41
|
- **JWT Tokens** - For API clients and stateless authentication
|
|
10
42
|
- **Two-Factor Authentication (2FA)** - TOTP-based second factor
|
|
11
43
|
- **Passkey/WebAuthn** - Passwordless authentication
|
|
12
|
-
- **Legacy Password Handling** - Migration support for existing users
|
|
13
44
|
|
|
14
45
|
### Core Features
|
|
15
46
|
|
|
16
47
|
- **Email/Password Authentication** - Standard username/password login
|
|
17
48
|
- **Social Login** - Any OAuth provider (Google, GitHub, Apple, Discord, etc.)
|
|
18
49
|
- **Parallel Operation** - Runs alongside Legacy Auth without side effects
|
|
50
|
+
- **Bidirectional Sync** - Users can sign in via Legacy Auth or Better-Auth interchangeably
|
|
19
51
|
|
|
20
52
|
### Extensible via Plugins
|
|
21
53
|
|
|
@@ -24,24 +56,99 @@ Integration of the [better-auth](https://better-auth.com) authentication framewo
|
|
|
24
56
|
- **SSO** - Single Sign-On (OIDC, SAML)
|
|
25
57
|
- **And many more** - See [Plugins and Extensions](#plugins-and-extensions)
|
|
26
58
|
|
|
27
|
-
|
|
59
|
+
---
|
|
28
60
|
|
|
29
|
-
|
|
61
|
+
## Quick Integration (for Claude Code / AI Assistants)
|
|
30
62
|
|
|
31
|
-
|
|
32
|
-
// Works automatically - no betterAuth config needed!
|
|
33
|
-
// Better-Auth will use sensible defaults and fallback secrets
|
|
63
|
+
**Use [INTEGRATION-CHECKLIST.md](./INTEGRATION-CHECKLIST.md)** for a concise checklist with references to the actual implementation files.
|
|
34
64
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Reference Implementation
|
|
68
|
+
|
|
69
|
+
A complete working implementation exists in this package:
|
|
70
|
+
|
|
71
|
+
**Local (in your node_modules):**
|
|
72
|
+
```
|
|
73
|
+
node_modules/@lenne.tech/nest-server/src/server/modules/better-auth/
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**GitHub:**
|
|
77
|
+
https://github.com/lenneTech/nest-server/tree/develop/src/server/modules/better-auth
|
|
78
|
+
|
|
79
|
+
**UserService integration:**
|
|
80
|
+
- Local: `node_modules/@lenne.tech/nest-server/src/server/modules/user/user.service.ts`
|
|
81
|
+
- GitHub: https://github.com/lenneTech/nest-server/blob/develop/src/server/modules/user/user.service.ts
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Project Integration Guide (Required Steps)
|
|
86
|
+
|
|
87
|
+
### Step 1: Create BetterAuth Module
|
|
88
|
+
**Create:** `src/server/modules/better-auth/better-auth.module.ts`
|
|
89
|
+
**Copy from:** Reference implementation (see above)
|
|
90
|
+
|
|
91
|
+
### Step 2: Create BetterAuth Resolver (CRITICAL!)
|
|
92
|
+
**Create:** `src/server/modules/better-auth/better-auth.resolver.ts`
|
|
93
|
+
**Copy from:** Reference implementation
|
|
94
|
+
|
|
95
|
+
**WHY must ALL decorators be re-declared?**
|
|
96
|
+
GraphQL schema is built from decorators at compile time. The parent class (`CoreBetterAuthResolver`) is marked as `isAbstract: true`, so its methods are not registered in the schema. You MUST re-declare `@Query`, `@Mutation`, `@Roles`, `@UseGuards` decorators in the child class for the methods to appear in the GraphQL schema.
|
|
97
|
+
|
|
98
|
+
### Step 3: Create BetterAuth Controller
|
|
99
|
+
**Create:** `src/server/modules/better-auth/better-auth.controller.ts`
|
|
100
|
+
**Copy from:** Reference implementation
|
|
101
|
+
|
|
102
|
+
### Step 4: Inject BetterAuthUserMapper in UserService (CRITICAL!)
|
|
103
|
+
**Modify:** `src/server/modules/user/user.service.ts`
|
|
104
|
+
**Reference:** See UserService in reference implementation
|
|
105
|
+
|
|
106
|
+
**Required changes:**
|
|
107
|
+
|
|
108
|
+
1. Add import:
|
|
109
|
+
```typescript
|
|
110
|
+
import { BetterAuthUserMapper } from '@lenne.tech/nest-server';
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
2. Add constructor parameter:
|
|
114
|
+
```typescript
|
|
115
|
+
@Optional() private readonly betterAuthUserMapper?: BetterAuthUserMapper,
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
3. Pass to super() via options object:
|
|
119
|
+
```typescript
|
|
120
|
+
super(configService, emailService, mainDbModel, mainModelConstructor, { betterAuthUserMapper });
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Why is this critical?**
|
|
124
|
+
The `BetterAuthUserMapper` enables bidirectional password synchronization:
|
|
125
|
+
- User signs up via BetterAuth → password synced to Legacy Auth (bcrypt hash)
|
|
126
|
+
- User changes password → synced between both systems
|
|
127
|
+
- **Without this, users can only authenticate via ONE system!**
|
|
128
|
+
|
|
129
|
+
### Step 5: Import in ServerModule
|
|
130
|
+
**Modify:** `src/server/server.module.ts`
|
|
131
|
+
**Reference:** See ServerModule in reference implementation
|
|
132
|
+
|
|
133
|
+
Add import and include BetterAuthModule in imports array with `fallbackSecrets`:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
BetterAuthModule.forRoot({
|
|
137
|
+
config: envConfig.betterAuth,
|
|
138
|
+
fallbackSecrets: [envConfig.jwt?.secret, envConfig.jwt?.refresh?.secret],
|
|
139
|
+
}),
|
|
43
140
|
```
|
|
44
141
|
|
|
142
|
+
### Step 6: Configure in config.env.ts
|
|
143
|
+
**Modify:** `src/config.env.ts`
|
|
144
|
+
**Reference:** See config.env.ts in reference implementation
|
|
145
|
+
|
|
146
|
+
Add `betterAuth` configuration block. See reference for all available options including `jwt`, `passkey`, `twoFactor`, and `socialProviders`.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Quick Reference
|
|
151
|
+
|
|
45
152
|
**Default values (used when not configured):**
|
|
46
153
|
|
|
47
154
|
- **Secret**: Falls back to `jwt.secret` → `jwt.refresh.secret` → auto-generated
|
|
@@ -51,18 +158,20 @@ const config = {
|
|
|
51
158
|
- **Passkey rpId**: `localhost`
|
|
52
159
|
- **Passkey rpName**: `Nest Server`
|
|
53
160
|
|
|
54
|
-
To **explicitly disable** Better-Auth
|
|
161
|
+
To **explicitly disable** Better-Auth:
|
|
55
162
|
|
|
56
163
|
```typescript
|
|
57
164
|
const config = {
|
|
58
165
|
betterAuth: {
|
|
59
|
-
enabled: false,
|
|
166
|
+
enabled: false,
|
|
60
167
|
},
|
|
61
168
|
};
|
|
62
169
|
```
|
|
63
170
|
|
|
64
171
|
Read the security section below for production deployments.
|
|
65
172
|
|
|
173
|
+
---
|
|
174
|
+
|
|
66
175
|
## Understanding Core Settings
|
|
67
176
|
|
|
68
177
|
### Base URL (`baseUrl`)
|
|
@@ -262,10 +371,6 @@ export default {
|
|
|
262
371
|
},
|
|
263
372
|
},
|
|
264
373
|
|
|
265
|
-
// Legacy Password Handling (enabled by default when config block is present)
|
|
266
|
-
// Set enabled: false to explicitly disable
|
|
267
|
-
legacyPassword: {},
|
|
268
|
-
|
|
269
374
|
// Trusted Origins for CORS
|
|
270
375
|
trustedOrigins: ['http://localhost:3000', 'https://your-app.com'],
|
|
271
376
|
|
|
@@ -282,6 +387,127 @@ export default {
|
|
|
282
387
|
};
|
|
283
388
|
```
|
|
284
389
|
|
|
390
|
+
## Advanced Configuration
|
|
391
|
+
|
|
392
|
+
### Email/Password Authentication
|
|
393
|
+
|
|
394
|
+
Email/password authentication is enabled by default. You can disable it if you only want social login:
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
const config = {
|
|
398
|
+
betterAuth: {
|
|
399
|
+
emailAndPassword: {
|
|
400
|
+
enabled: false, // Disable email/password, only allow social login
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
};
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Additional User Fields
|
|
407
|
+
|
|
408
|
+
Add custom fields to the Better-Auth user schema:
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
const config = {
|
|
412
|
+
betterAuth: {
|
|
413
|
+
additionalUserFields: {
|
|
414
|
+
phoneNumber: { type: 'string', defaultValue: null },
|
|
415
|
+
department: { type: 'string', required: true },
|
|
416
|
+
preferences: { type: 'string', defaultValue: '{}' },
|
|
417
|
+
isActive: { type: 'boolean', defaultValue: true },
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Available field types:** `'string'`, `'number'`, `'boolean'`, `'date'`, `'json'`, `'string[]'`, `'number[]'`
|
|
424
|
+
|
|
425
|
+
### Module Integration (Recommended Pattern)
|
|
426
|
+
|
|
427
|
+
By default (`autoRegister: false`), projects integrate BetterAuth via an **extended module** in their project. This follows the same pattern as Legacy Auth and allows for custom resolvers, controllers, and project-specific authentication logic.
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
// src/server/modules/better-auth/better-auth.module.ts
|
|
431
|
+
import { Module, DynamicModule } from '@nestjs/common';
|
|
432
|
+
import { BetterAuthModule as CoreBetterAuthModule } from '@lenne.tech/nest-server';
|
|
433
|
+
|
|
434
|
+
@Module({})
|
|
435
|
+
export class BetterAuthModule {
|
|
436
|
+
static forRoot(options): DynamicModule {
|
|
437
|
+
return {
|
|
438
|
+
module: BetterAuthModule,
|
|
439
|
+
imports: [CoreBetterAuthModule.forRoot(options)],
|
|
440
|
+
// Add custom providers, resolvers, etc.
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// src/server/server.module.ts
|
|
446
|
+
import { BetterAuthModule } from './modules/better-auth/better-auth.module';
|
|
447
|
+
|
|
448
|
+
@Module({
|
|
449
|
+
imports: [
|
|
450
|
+
CoreModule.forRoot(environment),
|
|
451
|
+
BetterAuthModule.forRoot({
|
|
452
|
+
config: environment.betterAuth,
|
|
453
|
+
resolver: CustomBetterAuthResolver, // Optional custom resolver
|
|
454
|
+
}),
|
|
455
|
+
],
|
|
456
|
+
})
|
|
457
|
+
export class ServerModule {}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Auto-Registration (Simple Projects)
|
|
461
|
+
|
|
462
|
+
For simple projects that don't need customization, you can enable auto-registration:
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
// In config.env.ts
|
|
466
|
+
const config = {
|
|
467
|
+
betterAuth: {
|
|
468
|
+
autoRegister: true, // Enable auto-registration in CoreModule
|
|
469
|
+
},
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
// No manual import needed - CoreModule handles everything
|
|
473
|
+
@Module({
|
|
474
|
+
imports: [CoreModule.forRoot(environment)],
|
|
475
|
+
})
|
|
476
|
+
export class ServerModule {}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Options Passthrough
|
|
480
|
+
|
|
481
|
+
For full Better-Auth customization, use the `options` passthrough. These options are passed directly to Better-Auth:
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
const config = {
|
|
485
|
+
betterAuth: {
|
|
486
|
+
options: {
|
|
487
|
+
emailAndPassword: {
|
|
488
|
+
requireEmailVerification: true,
|
|
489
|
+
sendResetPassword: async ({ user, url }) => {
|
|
490
|
+
// Custom password reset email logic
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
account: {
|
|
494
|
+
accountLinking: { enabled: true },
|
|
495
|
+
},
|
|
496
|
+
session: {
|
|
497
|
+
expiresIn: 60 * 60 * 24 * 7, // 7 days
|
|
498
|
+
updateAge: 60 * 60 * 24, // 1 day
|
|
499
|
+
},
|
|
500
|
+
advanced: {
|
|
501
|
+
cookiePrefix: 'my-app',
|
|
502
|
+
useSecureCookies: true,
|
|
503
|
+
},
|
|
504
|
+
},
|
|
505
|
+
},
|
|
506
|
+
};
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
See [Better-Auth Options Reference](https://www.better-auth.com/docs/reference/options) for all available options.
|
|
510
|
+
|
|
285
511
|
## Plugins and Extensions
|
|
286
512
|
|
|
287
513
|
Better-Auth provides a rich plugin ecosystem. This module uses a **hybrid approach**:
|
|
@@ -298,7 +524,6 @@ These plugins are enabled by default when their config block is present. **All p
|
|
|
298
524
|
| **JWT** | `jwt: {}` | `expiresIn: '15m'` |
|
|
299
525
|
| **Two-Factor** | `twoFactor: {}` | `appName: 'Nest Server'` |
|
|
300
526
|
| **Passkey** | `passkey: {}` | `origin: 'http://localhost:3000'`, `rpId: 'localhost'`, `rpName: 'Nest Server'` |
|
|
301
|
-
| **Legacy Password**| `legacyPassword: {}` | No config needed |
|
|
302
527
|
|
|
303
528
|
#### Minimal Syntax (Recommended for Development)
|
|
304
529
|
|
|
@@ -309,7 +534,6 @@ const config = {
|
|
|
309
534
|
jwt: {},
|
|
310
535
|
twoFactor: {},
|
|
311
536
|
passkey: {},
|
|
312
|
-
legacyPassword: {},
|
|
313
537
|
},
|
|
314
538
|
};
|
|
315
539
|
```
|
|
@@ -326,7 +550,6 @@ const config = {
|
|
|
326
550
|
rpName: 'My App',
|
|
327
551
|
origin: 'https://example.com',
|
|
328
552
|
},
|
|
329
|
-
legacyPassword: {},
|
|
330
553
|
},
|
|
331
554
|
};
|
|
332
555
|
```
|
|
@@ -472,50 +695,63 @@ const config = {
|
|
|
472
695
|
|
|
473
696
|
## Module Setup
|
|
474
697
|
|
|
475
|
-
|
|
698
|
+
See the [Project Integration Guide](#project-integration-guide-required-steps) at the top of this document for complete step-by-step instructions.
|
|
699
|
+
|
|
700
|
+
### Summary
|
|
701
|
+
|
|
702
|
+
Better-Auth is **enabled by default** but requires explicit module integration (`autoRegister: false` by default). This follows the same pattern as Legacy Auth, giving projects full control over customization.
|
|
703
|
+
|
|
704
|
+
**Required components:**
|
|
705
|
+
|
|
706
|
+
1. `BetterAuthModule` - Wraps CoreBetterAuthModule with custom resolver/controller
|
|
707
|
+
2. `BetterAuthResolver` - Extends CoreBetterAuthResolver for GraphQL operations
|
|
708
|
+
3. `BetterAuthController` - Extends CoreBetterAuthController for REST endpoints
|
|
709
|
+
4. `UserService` - Inject `BetterAuthUserMapper` for bidirectional auth sync
|
|
476
710
|
|
|
477
|
-
###
|
|
711
|
+
### Simple: Auto-Registration
|
|
712
|
+
|
|
713
|
+
For simple projects without customization needs (not recommended for production):
|
|
478
714
|
|
|
479
715
|
```typescript
|
|
480
|
-
// In
|
|
716
|
+
// In config.env.ts
|
|
717
|
+
const config = {
|
|
718
|
+
betterAuth: {
|
|
719
|
+
autoRegister: true, // Let CoreModule handle registration
|
|
720
|
+
},
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
// In server.module.ts - no manual import needed
|
|
481
724
|
@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
|
-
],
|
|
725
|
+
imports: [CoreModule.forRoot(environment)],
|
|
487
726
|
})
|
|
488
727
|
export class ServerModule {}
|
|
489
728
|
```
|
|
490
729
|
|
|
491
|
-
###
|
|
730
|
+
### Disable Better-Auth
|
|
492
731
|
|
|
493
|
-
|
|
494
|
-
import { BetterAuthModule } from '@lenne.tech/nest-server';
|
|
732
|
+
To explicitly disable Better-Auth:
|
|
495
733
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
})
|
|
503
|
-
export class AppModule {}
|
|
734
|
+
```typescript
|
|
735
|
+
const config = {
|
|
736
|
+
betterAuth: {
|
|
737
|
+
enabled: false,
|
|
738
|
+
},
|
|
739
|
+
};
|
|
504
740
|
```
|
|
505
741
|
|
|
506
742
|
## REST API Endpoints
|
|
507
743
|
|
|
508
744
|
When enabled, Better-Auth exposes the following endpoints at the configured `basePath` (default: `/iam`):
|
|
509
745
|
|
|
510
|
-
| Endpoint
|
|
511
|
-
|
|
|
512
|
-
| `/iam/sign-up` | POST | Register new user |
|
|
513
|
-
| `/iam/sign-in` | POST | Sign in with email/password |
|
|
514
|
-
| `/iam/sign-out`
|
|
515
|
-
| `/iam/session`
|
|
516
|
-
| `/iam/forgot-password
|
|
517
|
-
| `/iam/reset-password`
|
|
518
|
-
| `/iam/verify-email`
|
|
746
|
+
| Endpoint | Method | Description |
|
|
747
|
+
| --------------------------- | ------ | ---------------------------- |
|
|
748
|
+
| `/iam/sign-up/email` | POST | Register new user |
|
|
749
|
+
| `/iam/sign-in/email` | POST | Sign in with email/password |
|
|
750
|
+
| `/iam/sign-out` | GET | Sign out (invalidate session)|
|
|
751
|
+
| `/iam/session` | GET | Get current session |
|
|
752
|
+
| `/iam/forgot-password` | POST | Request password reset |
|
|
753
|
+
| `/iam/reset-password` | POST | Reset password with token |
|
|
754
|
+
| `/iam/verify-email` | POST | Verify email address |
|
|
519
755
|
|
|
520
756
|
### Social Login Endpoints
|
|
521
757
|
|
|
@@ -601,7 +837,6 @@ type BetterAuthFeaturesModel {
|
|
|
601
837
|
jwt: Boolean!
|
|
602
838
|
twoFactor: Boolean!
|
|
603
839
|
passkey: Boolean!
|
|
604
|
-
legacyPassword: Boolean!
|
|
605
840
|
socialProviders: [String!]!
|
|
606
841
|
}
|
|
607
842
|
```
|
|
@@ -799,7 +1034,6 @@ export class MyService {
|
|
|
799
1034
|
| `isJwtEnabled()` | Check if JWT plugin is enabled |
|
|
800
1035
|
| `isTwoFactorEnabled()` | Check if 2FA is enabled |
|
|
801
1036
|
| `isPasskeyEnabled()` | Check if Passkey is enabled |
|
|
802
|
-
| `isLegacyPasswordEnabled()` | Check if legacy password handling is enabled |
|
|
803
1037
|
| `getEnabledSocialProviders()` | Get list of enabled social providers |
|
|
804
1038
|
| `getBasePath()` | Get the base path for endpoints |
|
|
805
1039
|
| `getBaseUrl()` | Get the base URL |
|
|
@@ -884,9 +1118,132 @@ export class MyService {
|
|
|
884
1118
|
| `hasRole(roles)` | function | Check if user has any of the specified roles |
|
|
885
1119
|
| `_authenticatedViaBetterAuth` | true | Marker for Better-Auth authenticated users |
|
|
886
1120
|
|
|
1121
|
+
## CoreModule.forRoot() Signatures
|
|
1122
|
+
|
|
1123
|
+
Two signatures are available for different use cases:
|
|
1124
|
+
|
|
1125
|
+
### IAM-Only Signature (Recommended for New Projects)
|
|
1126
|
+
|
|
1127
|
+
```typescript
|
|
1128
|
+
// server.module.ts
|
|
1129
|
+
@Module({
|
|
1130
|
+
imports: [
|
|
1131
|
+
CoreModule.forRoot(envConfig),
|
|
1132
|
+
BetterAuthModule.forRoot({
|
|
1133
|
+
config: envConfig.betterAuth,
|
|
1134
|
+
fallbackSecrets: [envConfig.jwt?.secret],
|
|
1135
|
+
}),
|
|
1136
|
+
],
|
|
1137
|
+
})
|
|
1138
|
+
export class ServerModule {}
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
**Features:**
|
|
1142
|
+
- Simplified setup - no Legacy Auth overhead
|
|
1143
|
+
- GraphQL Subscription authentication via BetterAuth sessions
|
|
1144
|
+
- BetterAuthModule is auto-registered when using this signature
|
|
1145
|
+
|
|
1146
|
+
**Requirements:**
|
|
1147
|
+
- Create BetterAuthModule, Resolver, and Controller in your project
|
|
1148
|
+
- Inject BetterAuthUserMapper in UserService
|
|
1149
|
+
- Set `auth.legacyEndpoints.enabled: false` in config
|
|
1150
|
+
|
|
1151
|
+
### Legacy + IAM Signature (For Existing Projects)
|
|
1152
|
+
|
|
1153
|
+
```typescript
|
|
1154
|
+
// server.module.ts
|
|
1155
|
+
@Module({
|
|
1156
|
+
imports: [
|
|
1157
|
+
CoreModule.forRoot(AuthService, AuthModule.forRoot(envConfig.jwt), envConfig),
|
|
1158
|
+
BetterAuthModule.forRoot({
|
|
1159
|
+
config: envConfig.betterAuth,
|
|
1160
|
+
fallbackSecrets: [envConfig.jwt?.secret],
|
|
1161
|
+
}),
|
|
1162
|
+
],
|
|
1163
|
+
})
|
|
1164
|
+
export class ServerModule {}
|
|
1165
|
+
```
|
|
1166
|
+
|
|
1167
|
+
> **@deprecated** This 3-parameter signature is deprecated for new projects.
|
|
1168
|
+
> Use the single-parameter signature for new projects.
|
|
1169
|
+
|
|
1170
|
+
**Features:**
|
|
1171
|
+
- Both Legacy Auth and BetterAuth run in parallel
|
|
1172
|
+
- Bidirectional password synchronization
|
|
1173
|
+
- Gradual user migration to IAM
|
|
1174
|
+
|
|
1175
|
+
---
|
|
1176
|
+
|
|
1177
|
+
## Migration Roadmap (Legacy Auth → BetterAuth)
|
|
1178
|
+
|
|
1179
|
+
Better-Auth is designed as the successor to Legacy Auth. This section describes the migration path.
|
|
1180
|
+
|
|
1181
|
+
### Scenario Overview
|
|
1182
|
+
|
|
1183
|
+
| Scenario | Signature | Description |
|
|
1184
|
+
|----------|-----------|-------------|
|
|
1185
|
+
| **1. Legacy Only** | 3-parameter | Existing projects, no IAM integration |
|
|
1186
|
+
| **2. Migration** | 3-parameter | Legacy + IAM parallel operation |
|
|
1187
|
+
| **3. IAM Only** | 1-parameter | New projects, BetterAuth only |
|
|
1188
|
+
|
|
1189
|
+
### Migration Steps (Scenario 2)
|
|
1190
|
+
|
|
1191
|
+
1. **Enable BetterAuth**
|
|
1192
|
+
- Follow the [Project Integration Guide](#project-integration-guide-required-steps)
|
|
1193
|
+
- Both systems run in parallel
|
|
1194
|
+
|
|
1195
|
+
2. **Monitor Migration Progress**
|
|
1196
|
+
```graphql
|
|
1197
|
+
query {
|
|
1198
|
+
betterAuthMigrationStatus {
|
|
1199
|
+
totalUsers
|
|
1200
|
+
fullyMigratedUsers
|
|
1201
|
+
pendingMigrationUsers
|
|
1202
|
+
migrationPercentage
|
|
1203
|
+
canDisableLegacyAuth
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
```
|
|
1207
|
+
Users migrate automatically when signing in via BetterAuth (IAM).
|
|
1208
|
+
|
|
1209
|
+
3. **Disable Legacy Endpoints** (when `canDisableLegacyAuth: true`)
|
|
1210
|
+
```typescript
|
|
1211
|
+
// config.env.ts
|
|
1212
|
+
auth: {
|
|
1213
|
+
legacyEndpoints: {
|
|
1214
|
+
enabled: false // Disables signIn, signUp, logout, refreshToken
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
```
|
|
1218
|
+
|
|
1219
|
+
4. **Switch to IAM-Only Signature** (optional)
|
|
1220
|
+
```typescript
|
|
1221
|
+
// Before (deprecated)
|
|
1222
|
+
CoreModule.forRoot(AuthService, AuthModule.forRoot(envConfig.jwt), envConfig)
|
|
1223
|
+
|
|
1224
|
+
// After (recommended)
|
|
1225
|
+
CoreModule.forRoot(envConfig)
|
|
1226
|
+
```
|
|
1227
|
+
|
|
1228
|
+
### Why No Automatic Migration Script?
|
|
1229
|
+
|
|
1230
|
+
| System | Password Hash Algorithm |
|
|
1231
|
+
|--------|------------------------|
|
|
1232
|
+
| Legacy Auth | `bcrypt(sha256(password))` |
|
|
1233
|
+
| BetterAuth | `scrypt(sha256(password))` |
|
|
1234
|
+
|
|
1235
|
+
These are **one-way hashes** - there's no way to convert between them without the plain password.
|
|
1236
|
+
Users must sign in at least once via BetterAuth to create a compatible hash.
|
|
1237
|
+
|
|
1238
|
+
### Detailed Documentation
|
|
1239
|
+
|
|
1240
|
+
See `.claude/rules/module-deprecation.md` for complete migration documentation.
|
|
1241
|
+
|
|
1242
|
+
---
|
|
1243
|
+
|
|
887
1244
|
## Parallel Operation with Legacy Auth
|
|
888
1245
|
|
|
889
|
-
Better-Auth runs **parallel to Legacy JWT authentication** without conflicts. Both systems are fully compatible because they
|
|
1246
|
+
Better-Auth runs **parallel to Legacy JWT authentication** without conflicts. Both systems are fully compatible because they share the same users collection.
|
|
890
1247
|
|
|
891
1248
|
### How It Works
|
|
892
1249
|
|
|
@@ -926,6 +1283,224 @@ const legacyToken = await authService.signIn({ email, password });
|
|
|
926
1283
|
|
|
927
1284
|
**No migration is required** - users can authenticate with either system immediately.
|
|
928
1285
|
|
|
1286
|
+
## Bidirectional Password Synchronization
|
|
1287
|
+
|
|
1288
|
+
### Overview
|
|
1289
|
+
|
|
1290
|
+
When both Legacy Auth and BetterAuth (IAM) are active, passwords are automatically synchronized between the systems. This ensures users can sign in via either system after any password change or reset.
|
|
1291
|
+
|
|
1292
|
+
### How Password Sync Works
|
|
1293
|
+
|
|
1294
|
+
| Scenario | Source | Target | Auto-Synced? |
|
|
1295
|
+
|----------|--------|--------|--------------|
|
|
1296
|
+
| Sign up via BetterAuth | IAM | Legacy | ✅ Yes |
|
|
1297
|
+
| Sign up via Legacy Auth | Legacy | IAM | ⚠️ On first IAM sign-in |
|
|
1298
|
+
| Password reset via Legacy | Legacy | IAM | ✅ Yes |
|
|
1299
|
+
| Password reset via BetterAuth | IAM | Legacy | ⚠️ See below |
|
|
1300
|
+
| Password change via user update | Legacy | IAM | ✅ Yes |
|
|
1301
|
+
|
|
1302
|
+
#### IAM Password Reset → Legacy Sync
|
|
1303
|
+
|
|
1304
|
+
When a user resets their password via BetterAuth's native `/iam/reset-password` endpoint, the password is hashed with scrypt before storage. Since we don't have access to the plain password after hashing, we **cannot** automatically sync to Legacy Auth.
|
|
1305
|
+
|
|
1306
|
+
**Solutions:**
|
|
1307
|
+
|
|
1308
|
+
1. **Recommended: Custom Password Reset Flow**
|
|
1309
|
+
Override the password reset to capture the plain password for sync. See [Custom Password Reset with Sync](#custom-password-reset-with-sync).
|
|
1310
|
+
|
|
1311
|
+
2. **Use Legacy Password Reset Only**
|
|
1312
|
+
Direct users to the Legacy Auth password reset flow, which syncs to IAM automatically.
|
|
1313
|
+
|
|
1314
|
+
3. **Re-authenticate After Reset**
|
|
1315
|
+
After IAM password reset, users can sign in via IAM. On next Legacy sign-in attempt, they'll need to reset via Legacy too.
|
|
1316
|
+
|
|
1317
|
+
### Automatic Sync (No Configuration Required)
|
|
1318
|
+
|
|
1319
|
+
The following sync operations happen automatically when `BetterAuthUserMapper` is injected in `UserService`:
|
|
1320
|
+
|
|
1321
|
+
#### 1. IAM Sign-Up → Legacy
|
|
1322
|
+
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.
|
|
1323
|
+
|
|
1324
|
+
#### 2. Legacy Password Reset → IAM
|
|
1325
|
+
When a user resets their password via `CoreUserService.resetPassword()`, the new password is synced to the BetterAuth `account` collection (if the user has a credential account).
|
|
1326
|
+
|
|
1327
|
+
#### 3. Legacy Password Update → IAM
|
|
1328
|
+
When a user changes their password via `UserService.update()`, the new password is synced to BetterAuth (if the user has a credential account).
|
|
1329
|
+
|
|
1330
|
+
#### 4. Legacy → IAM Migration (On First Sign-In)
|
|
1331
|
+
When a legacy user signs in via BetterAuth for the first time, their account is migrated:
|
|
1332
|
+
- Their `iamId` is set
|
|
1333
|
+
- A credential account is created in the `account` collection with the scrypt hash
|
|
1334
|
+
|
|
1335
|
+
### BetterAuth Password Reset Configuration
|
|
1336
|
+
|
|
1337
|
+
BetterAuth provides native password reset via `/iam/forgot-password` and `/iam/reset-password` endpoints. To enable this, configure the `sendResetPassword` callback:
|
|
1338
|
+
|
|
1339
|
+
```typescript
|
|
1340
|
+
// config.env.ts
|
|
1341
|
+
betterAuth: {
|
|
1342
|
+
options: {
|
|
1343
|
+
emailAndPassword: {
|
|
1344
|
+
sendResetPassword: async ({ user, url, token }) => {
|
|
1345
|
+
// Send password reset email
|
|
1346
|
+
// 'url' contains the full reset URL with token
|
|
1347
|
+
await emailService.sendEmail({
|
|
1348
|
+
to: user.email,
|
|
1349
|
+
subject: 'Reset Your Password',
|
|
1350
|
+
html: `<a href="${url}">Click here to reset your password</a>`,
|
|
1351
|
+
});
|
|
1352
|
+
},
|
|
1353
|
+
},
|
|
1354
|
+
},
|
|
1355
|
+
},
|
|
1356
|
+
```
|
|
1357
|
+
|
|
1358
|
+
#### Password Reset Flow (BetterAuth)
|
|
1359
|
+
|
|
1360
|
+
1. User requests reset: `POST /iam/forgot-password` with `{ email }`
|
|
1361
|
+
2. BetterAuth generates token and calls `sendResetPassword` callback
|
|
1362
|
+
3. User clicks link in email → navigates to reset page
|
|
1363
|
+
4. Frontend submits: `POST /iam/reset-password` with `{ token, newPassword }`
|
|
1364
|
+
5. BetterAuth updates password in `account` collection
|
|
1365
|
+
6. Password is automatically synced to Legacy Auth (`users.password`)
|
|
1366
|
+
|
|
1367
|
+
### Password Hashing Algorithms
|
|
1368
|
+
|
|
1369
|
+
| System | Algorithm | Format |
|
|
1370
|
+
|--------|-----------|--------|
|
|
1371
|
+
| Legacy Auth | bcrypt(sha256(password)) | `$2b$10$...` (60 chars) |
|
|
1372
|
+
| BetterAuth (IAM) | scrypt | `salt:hash` (hex encoded) |
|
|
1373
|
+
|
|
1374
|
+
**Important:** These hashes are NOT interchangeable. Password sync requires re-hashing the plain password with the target algorithm.
|
|
1375
|
+
|
|
1376
|
+
### Email Change Synchronization
|
|
1377
|
+
|
|
1378
|
+
Email changes are also synchronized bidirectionally:
|
|
1379
|
+
|
|
1380
|
+
| Scenario | Effect |
|
|
1381
|
+
|----------|--------|
|
|
1382
|
+
| Email changed via Legacy (`UserService.update()`) | IAM sessions invalidated (forces re-auth) |
|
|
1383
|
+
| Email changed via IAM | Legacy refresh tokens cleared |
|
|
1384
|
+
|
|
1385
|
+
### User Deletion Cleanup
|
|
1386
|
+
|
|
1387
|
+
When a user is deleted:
|
|
1388
|
+
|
|
1389
|
+
| Via | Effect |
|
|
1390
|
+
|-----|--------|
|
|
1391
|
+
| Legacy (`UserService.delete()`) | IAM accounts and sessions are cleaned up |
|
|
1392
|
+
| IAM | Legacy user record is removed |
|
|
1393
|
+
|
|
1394
|
+
### Troubleshooting Password Sync
|
|
1395
|
+
|
|
1396
|
+
#### Password not syncing to IAM
|
|
1397
|
+
|
|
1398
|
+
1. Verify `BetterAuthUserMapper` is injected in `UserService`:
|
|
1399
|
+
```typescript
|
|
1400
|
+
constructor(
|
|
1401
|
+
@Optional() private readonly betterAuthUserMapper?: BetterAuthUserMapper,
|
|
1402
|
+
) {
|
|
1403
|
+
super(..., { betterAuthUserMapper });
|
|
1404
|
+
}
|
|
1405
|
+
```
|
|
1406
|
+
|
|
1407
|
+
2. Check if user has an IAM credential account:
|
|
1408
|
+
```javascript
|
|
1409
|
+
// MongoDB query
|
|
1410
|
+
db.account.findOne({ userId: ObjectId("..."), providerId: "credential" })
|
|
1411
|
+
```
|
|
1412
|
+
|
|
1413
|
+
3. Check server logs for sync warnings:
|
|
1414
|
+
```
|
|
1415
|
+
[CoreUserService] Failed to sync password to IAM...
|
|
1416
|
+
```
|
|
1417
|
+
|
|
1418
|
+
#### Password reset email not sending
|
|
1419
|
+
|
|
1420
|
+
1. Configure `sendResetPassword` callback in `betterAuth.options`
|
|
1421
|
+
2. Check that your email service is working
|
|
1422
|
+
3. Verify the callback receives `user`, `url`, and `token` parameters
|
|
1423
|
+
|
|
1424
|
+
#### Legacy user can't sign in via IAM
|
|
1425
|
+
|
|
1426
|
+
The user needs to sign in via Legacy Auth first with their password. This triggers the automatic migration on first IAM sign-in attempt.
|
|
1427
|
+
|
|
1428
|
+
Alternatively, use `BetterAuthUserMapper.migrateAccountToIam()` to migrate the user programmatically:
|
|
1429
|
+
|
|
1430
|
+
```typescript
|
|
1431
|
+
await betterAuthUserMapper.migrateAccountToIam(email, plainPassword);
|
|
1432
|
+
```
|
|
1433
|
+
|
|
1434
|
+
### Custom Password Reset with Sync
|
|
1435
|
+
|
|
1436
|
+
To enable bidirectional password reset sync, implement a custom password reset endpoint that captures the plain password and syncs to both systems:
|
|
1437
|
+
|
|
1438
|
+
```typescript
|
|
1439
|
+
// src/server/modules/better-auth/better-auth.controller.ts
|
|
1440
|
+
import { Body, Controller, Post } from '@nestjs/common';
|
|
1441
|
+
import { CoreBetterAuthController, BetterAuthUserMapper, Roles, RoleEnum } from '@lenne.tech/nest-server';
|
|
1442
|
+
|
|
1443
|
+
@Controller('iam')
|
|
1444
|
+
export class BetterAuthController extends CoreBetterAuthController {
|
|
1445
|
+
constructor(
|
|
1446
|
+
// ... other dependencies
|
|
1447
|
+
private readonly betterAuthUserMapper: BetterAuthUserMapper,
|
|
1448
|
+
) {
|
|
1449
|
+
super(...);
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
/**
|
|
1453
|
+
* Custom password reset that syncs to both auth systems
|
|
1454
|
+
*/
|
|
1455
|
+
@Post('reset-password-sync')
|
|
1456
|
+
@Roles(RoleEnum.S_EVERYONE)
|
|
1457
|
+
async resetPasswordWithSync(
|
|
1458
|
+
@Body() input: { token: string; newPassword: string },
|
|
1459
|
+
): Promise<{ success: boolean }> {
|
|
1460
|
+
// 1. Reset password via BetterAuth native API
|
|
1461
|
+
const api = this.betterAuthService.getApi();
|
|
1462
|
+
await api.resetPassword({
|
|
1463
|
+
body: { token: input.token, newPassword: input.newPassword },
|
|
1464
|
+
});
|
|
1465
|
+
|
|
1466
|
+
// 2. Sync to Legacy Auth using the plain password
|
|
1467
|
+
// Get user email from the token (you may need to decode it or lookup)
|
|
1468
|
+
const userEmail = await this.getUserEmailFromToken(input.token);
|
|
1469
|
+
if (userEmail) {
|
|
1470
|
+
await this.betterAuthUserMapper.syncPasswordToLegacy(
|
|
1471
|
+
'', // iamUserId not needed for email lookup
|
|
1472
|
+
userEmail,
|
|
1473
|
+
input.newPassword,
|
|
1474
|
+
);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
return { success: true };
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
private async getUserEmailFromToken(token: string): Promise<string | null> {
|
|
1481
|
+
// Implementation depends on your token structure
|
|
1482
|
+
// You may need to query the database or decode the token
|
|
1483
|
+
return null;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
```
|
|
1487
|
+
|
|
1488
|
+
**Alternative: Use Legacy Password Reset**
|
|
1489
|
+
|
|
1490
|
+
The simpler approach is to use the Legacy Auth password reset flow, which automatically syncs to IAM:
|
|
1491
|
+
|
|
1492
|
+
```typescript
|
|
1493
|
+
// Frontend points to Legacy Auth password reset
|
|
1494
|
+
const passwordResetUrl = config.email.passwordResetLink;
|
|
1495
|
+
// e.g., 'http://localhost:4200/user/password-reset'
|
|
1496
|
+
|
|
1497
|
+
// User submits new password via Legacy Auth
|
|
1498
|
+
// → CoreUserService.resetPassword() is called
|
|
1499
|
+
// → Automatically syncs to IAM via syncPasswordChangeToIam()
|
|
1500
|
+
```
|
|
1501
|
+
|
|
1502
|
+
This is the recommended approach for projects in migration phase where both auth systems are active.
|
|
1503
|
+
|
|
929
1504
|
## Testing
|
|
930
1505
|
|
|
931
1506
|
The module provides a `reset()` method for testing:
|
|
@@ -1094,3 +1669,72 @@ const config = {
|
|
|
1094
1669
|
|
|
1095
1670
|
4. **Monitor rate limit events** - The service logs warnings when limits are exceeded
|
|
1096
1671
|
5. **Consider Redis** - For multi-instance deployments, implement Redis-based rate limiting
|
|
1672
|
+
|
|
1673
|
+
## Extending Better-Auth (Custom Resolver)
|
|
1674
|
+
|
|
1675
|
+
See the [Project Integration Guide](#project-integration-guide-required-steps) for complete setup instructions. This section covers additional customization options.
|
|
1676
|
+
|
|
1677
|
+
### Adding Custom Logic
|
|
1678
|
+
|
|
1679
|
+
Override any method to add custom behavior (e.g., sending welcome emails, analytics):
|
|
1680
|
+
|
|
1681
|
+
```typescript
|
|
1682
|
+
// In your BetterAuthResolver (see Step 2 in Integration Guide)
|
|
1683
|
+
|
|
1684
|
+
@Mutation(() => BetterAuthAuthModel, { description: 'Sign up via Better-Auth (email/password)' })
|
|
1685
|
+
@Roles(RoleEnum.S_EVERYONE)
|
|
1686
|
+
override async betterAuthSignUp(
|
|
1687
|
+
@Args('email') email: string,
|
|
1688
|
+
@Args('password') password: string,
|
|
1689
|
+
@Args('name', { nullable: true }) name?: string,
|
|
1690
|
+
): Promise<BetterAuthAuthModel> {
|
|
1691
|
+
// Call original implementation
|
|
1692
|
+
const result = await super.betterAuthSignUp(email, password, name);
|
|
1693
|
+
|
|
1694
|
+
// Add custom logic after successful sign-up
|
|
1695
|
+
if (result.success && result.user) {
|
|
1696
|
+
await this.emailService.sendWelcomeEmail(result.user.email, result.user.name);
|
|
1697
|
+
await this.analyticsService.trackSignUp(result.user.id);
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
return result;
|
|
1701
|
+
}
|
|
1702
|
+
```
|
|
1703
|
+
|
|
1704
|
+
### Available Override Methods
|
|
1705
|
+
|
|
1706
|
+
All methods in `CoreBetterAuthResolver` can be overridden:
|
|
1707
|
+
|
|
1708
|
+
| Method | Description |
|
|
1709
|
+
| ------ | ----------- |
|
|
1710
|
+
| `betterAuthSignIn(email, password, ctx)` | Sign in with email/password |
|
|
1711
|
+
| `betterAuthSignUp(email, password, name?)` | Register new user |
|
|
1712
|
+
| `betterAuthSignOut(ctx)` | Sign out current session |
|
|
1713
|
+
| `betterAuthVerify2FA(code, ctx)` | Verify 2FA code |
|
|
1714
|
+
| `betterAuthEnable2FA(password, ctx)` | Enable 2FA for user |
|
|
1715
|
+
| `betterAuthDisable2FA(password, ctx)` | Disable 2FA for user |
|
|
1716
|
+
| `betterAuthGenerateBackupCodes(ctx)` | Generate new backup codes |
|
|
1717
|
+
| `betterAuthGetPasskeyChallenge(ctx)` | Get WebAuthn challenge |
|
|
1718
|
+
| `betterAuthListPasskeys(ctx)` | List user's passkeys |
|
|
1719
|
+
| `betterAuthDeletePasskey(passkeyId, ctx)` | Delete a passkey |
|
|
1720
|
+
| `betterAuthSession(ctx)` | Get current session |
|
|
1721
|
+
| `betterAuthEnabled()` | Check if Better-Auth is enabled |
|
|
1722
|
+
| `betterAuthFeatures()` | Get enabled features |
|
|
1723
|
+
|
|
1724
|
+
### Helper Methods (Protected)
|
|
1725
|
+
|
|
1726
|
+
These protected methods are available for use in your custom resolver:
|
|
1727
|
+
|
|
1728
|
+
```typescript
|
|
1729
|
+
// Check if Better-Auth is enabled (throws if not)
|
|
1730
|
+
this.ensureEnabled();
|
|
1731
|
+
|
|
1732
|
+
// Convert Express headers to Web API Headers
|
|
1733
|
+
const headers = this.convertHeaders(ctx.req.headers);
|
|
1734
|
+
|
|
1735
|
+
// Map session info
|
|
1736
|
+
const sessionInfo = this.mapSessionInfo(response.session);
|
|
1737
|
+
|
|
1738
|
+
// Map user to model
|
|
1739
|
+
const userModel = this.mapToUserModel(mappedUser);
|
|
1740
|
+
```
|