@lenne.tech/nest-server 11.7.0 → 11.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.env.js +17 -1
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +35 -15
- package/dist/core/modules/auth/core-auth.controller.d.ts +1 -0
- package/dist/core/modules/auth/core-auth.controller.js +29 -3
- 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 +21 -3
- 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-rate-limiter.service.js +1 -1
- package/dist/core/modules/better-auth/better-auth-rate-limiter.service.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 +395 -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 +29 -10
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.middleware.d.ts +1 -0
- package/dist/core/modules/better-auth/better-auth.middleware.js +55 -1
- package/dist/core/modules/better-auth/better-auth.middleware.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.module.d.ts +1 -1
- package/dist/core/modules/better-auth/better-auth.module.js +46 -18
- package/dist/core/modules/better-auth/better-auth.module.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.resolver.js +0 -11
- package/dist/core/modules/better-auth/better-auth.resolver.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.service.d.ts +22 -1
- package/dist/core/modules/better-auth/better-auth.service.js +209 -8
- package/dist/core/modules/better-auth/better-auth.service.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.types.d.ts +2 -0
- 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 +1 -0
- package/dist/core/modules/better-auth/core-better-auth.controller.js +15 -2
- package/dist/core/modules/better-auth/core-better-auth.controller.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.resolver.d.ts +7 -0
- package/dist/core/modules/better-auth/core-better-auth.resolver.js +72 -12
- package/dist/core/modules/better-auth/core-better-auth.resolver.js.map +1 -1
- package/dist/core/modules/better-auth/index.d.ts +1 -0
- package/dist/core/modules/better-auth/index.js +1 -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 +136 -55
- 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.module.d.ts +1 -1
- package/dist/server/modules/better-auth/better-auth.module.js +2 -1
- package/dist/server/modules/better-auth/better-auth.module.js.map +1 -1
- package/dist/server/modules/better-auth/better-auth.resolver.d.ts +5 -0
- package/dist/server/modules/better-auth/better-auth.resolver.js +27 -11
- package/dist/server/modules/better-auth/better-auth.resolver.js.map +1 -1
- package/dist/server/modules/user/user.controller.js +0 -8
- package/dist/server/modules/user/user.controller.js.map +1 -1
- 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/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/config.env.ts +32 -2
- package/src/core/common/interfaces/server-options.interface.ts +304 -58
- package/src/core/modules/auth/core-auth.controller.ts +94 -6
- package/src/core/modules/auth/core-auth.module.ts +15 -1
- package/src/core/modules/auth/core-auth.resolver.ts +71 -3
- 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 +255 -0
- package/src/core/modules/better-auth/README.md +565 -208
- package/src/core/modules/better-auth/better-auth-migration-status.model.ts +73 -0
- package/src/core/modules/better-auth/better-auth-rate-limiter.service.ts +1 -1
- package/src/core/modules/better-auth/better-auth-user.mapper.ts +737 -0
- package/src/core/modules/better-auth/better-auth.config.ts +45 -15
- package/src/core/modules/better-auth/better-auth.middleware.ts +85 -2
- package/src/core/modules/better-auth/better-auth.module.ts +83 -27
- package/src/core/modules/better-auth/better-auth.resolver.ts +0 -11
- package/src/core/modules/better-auth/better-auth.service.ts +367 -12
- package/src/core/modules/better-auth/better-auth.types.ts +16 -0
- package/src/core/modules/better-auth/core-better-auth.controller.ts +44 -3
- package/src/core/modules/better-auth/core-better-auth.resolver.ts +136 -16
- package/src/core/modules/better-auth/index.ts +1 -0
- 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 +264 -76
- package/src/index.ts +5 -0
- package/src/server/modules/auth/auth.resolver.ts +8 -0
- package/src/server/modules/better-auth/better-auth.module.ts +9 -3
- package/src/server/modules/better-auth/better-auth.resolver.ts +18 -11
- package/src/server/modules/user/user.controller.ts +1 -9
- package/src/server/modules/user/user.service.ts +4 -2
|
@@ -2,19 +2,55 @@
|
|
|
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 (minimal - JWT enabled by default):
|
|
14
|
+
betterAuth: true // or betterAuth: {} for same effect
|
|
15
|
+
|
|
16
|
+
// With optional features:
|
|
17
|
+
betterAuth: { twoFactor: {}, passkey: {} }
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Quick Links:** [Integration Checklist](./INTEGRATION-CHECKLIST.md) | [REST API](#rest-api-endpoints) | [GraphQL API](#graphql-api) | [Configuration](#configuration)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Table of Contents
|
|
25
|
+
|
|
26
|
+
- [Features](#features)
|
|
27
|
+
- [Quick Integration](#quick-integration-for-claude-code--ai-assistants)
|
|
28
|
+
- [Project Integration Guide](#project-integration-guide-required-steps)
|
|
29
|
+
- [Configuration](#configuration)
|
|
30
|
+
- [REST API Endpoints](#rest-api-endpoints)
|
|
31
|
+
- [GraphQL API](#graphql-api)
|
|
32
|
+
- [CoreModule Signatures](#coremoduleforroot-signatures)
|
|
33
|
+
- [Migration Roadmap](#migration-roadmap-legacy-auth--betterauth)
|
|
34
|
+
- [Password Synchronization](#bidirectional-password-synchronization)
|
|
35
|
+
- [Security Integration](#security-integration)
|
|
36
|
+
- [Troubleshooting](#troubleshooting)
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
5
40
|
## Features
|
|
6
41
|
|
|
7
|
-
### Built-in Plugins
|
|
42
|
+
### Built-in Plugins
|
|
8
43
|
|
|
9
|
-
- **JWT Tokens** - For API clients and stateless authentication
|
|
10
|
-
- **Two-Factor Authentication (2FA)** - TOTP-based second factor
|
|
11
|
-
- **Passkey/WebAuthn** - Passwordless authentication
|
|
44
|
+
- **JWT Tokens** - For API clients and stateless authentication (**enabled by default**)
|
|
45
|
+
- **Two-Factor Authentication (2FA)** - TOTP-based second factor (opt-in)
|
|
46
|
+
- **Passkey/WebAuthn** - Passwordless authentication (opt-in)
|
|
12
47
|
|
|
13
48
|
### Core Features
|
|
14
49
|
|
|
15
50
|
- **Email/Password Authentication** - Standard username/password login
|
|
16
51
|
- **Social Login** - Any OAuth provider (Google, GitHub, Apple, Discord, etc.)
|
|
17
52
|
- **Parallel Operation** - Runs alongside Legacy Auth without side effects
|
|
53
|
+
- **Bidirectional Sync** - Users can sign in via Legacy Auth or Better-Auth interchangeably
|
|
18
54
|
|
|
19
55
|
### Extensible via Plugins
|
|
20
56
|
|
|
@@ -23,75 +59,130 @@ Integration of the [better-auth](https://better-auth.com) authentication framewo
|
|
|
23
59
|
- **SSO** - Single Sign-On (OIDC, SAML)
|
|
24
60
|
- **And many more** - See [Plugins and Extensions](#plugins-and-extensions)
|
|
25
61
|
|
|
26
|
-
|
|
62
|
+
---
|
|
27
63
|
|
|
28
|
-
|
|
64
|
+
## Quick Integration (for Claude Code / AI Assistants)
|
|
29
65
|
|
|
30
|
-
|
|
66
|
+
**Use [INTEGRATION-CHECKLIST.md](./INTEGRATION-CHECKLIST.md)** for a concise checklist with references to the actual implementation files.
|
|
31
67
|
|
|
32
|
-
|
|
33
|
-
// src/server/modules/better-auth/better-auth.module.ts
|
|
34
|
-
import { BetterAuthModule as CoreBetterAuthModule } from '@lenne.tech/nest-server';
|
|
68
|
+
---
|
|
35
69
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
}
|
|
70
|
+
## Reference Implementation
|
|
71
|
+
|
|
72
|
+
A complete working implementation exists in this package:
|
|
73
|
+
|
|
74
|
+
**Local (in your node_modules):**
|
|
75
|
+
```
|
|
76
|
+
node_modules/@lenne.tech/nest-server/src/server/modules/better-auth/
|
|
45
77
|
```
|
|
46
78
|
|
|
47
|
-
|
|
79
|
+
**GitHub:**
|
|
80
|
+
https://github.com/lenneTech/nest-server/tree/develop/src/server/modules/better-auth
|
|
48
81
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
82
|
+
**UserService integration:**
|
|
83
|
+
- Local: `node_modules/@lenne.tech/nest-server/src/server/modules/user/user.service.ts`
|
|
84
|
+
- GitHub: https://github.com/lenneTech/nest-server/blob/develop/src/server/modules/user/user.service.ts
|
|
52
85
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Project Integration Guide (Required Steps)
|
|
89
|
+
|
|
90
|
+
### Step 1: Create BetterAuth Module
|
|
91
|
+
**Create:** `src/server/modules/better-auth/better-auth.module.ts`
|
|
92
|
+
**Copy from:** Reference implementation (see above)
|
|
93
|
+
|
|
94
|
+
### Step 2: Create BetterAuth Resolver (CRITICAL!)
|
|
95
|
+
**Create:** `src/server/modules/better-auth/better-auth.resolver.ts`
|
|
96
|
+
**Copy from:** Reference implementation
|
|
97
|
+
|
|
98
|
+
**WHY must ALL decorators be re-declared?**
|
|
99
|
+
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` decorators in the child class for the methods to appear in the GraphQL schema.
|
|
100
|
+
|
|
101
|
+
**Note:** `@UseGuards(AuthGuard(JWT))` is NOT needed when using `@Roles(S_USER)` or `@Roles(ADMIN)` because `RolesGuard` already extends `AuthGuard(JWT)` internally.
|
|
102
|
+
|
|
103
|
+
### Step 3: Create BetterAuth Controller
|
|
104
|
+
**Create:** `src/server/modules/better-auth/better-auth.controller.ts`
|
|
105
|
+
**Copy from:** Reference implementation
|
|
106
|
+
|
|
107
|
+
### Step 4: Inject BetterAuthUserMapper in UserService (CRITICAL!)
|
|
108
|
+
**Modify:** `src/server/modules/user/user.service.ts`
|
|
109
|
+
**Reference:** See UserService in reference implementation
|
|
110
|
+
|
|
111
|
+
**Required changes:**
|
|
112
|
+
|
|
113
|
+
1. Add import:
|
|
114
|
+
```typescript
|
|
115
|
+
import { BetterAuthUserMapper } from '@lenne.tech/nest-server';
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
2. Add constructor parameter:
|
|
119
|
+
```typescript
|
|
120
|
+
@Optional() private readonly betterAuthUserMapper?: BetterAuthUserMapper,
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
3. Pass to super() via options object:
|
|
124
|
+
```typescript
|
|
125
|
+
super(configService, emailService, mainDbModel, mainModelConstructor, { betterAuthUserMapper });
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Why is this critical?**
|
|
129
|
+
The `BetterAuthUserMapper` enables bidirectional password synchronization:
|
|
130
|
+
- User signs up via BetterAuth → password synced to Legacy Auth (bcrypt hash)
|
|
131
|
+
- User changes password → synced between both systems
|
|
132
|
+
- **Without this, users can only authenticate via ONE system!**
|
|
133
|
+
|
|
134
|
+
### Step 5: Import in ServerModule
|
|
135
|
+
**Modify:** `src/server/server.module.ts`
|
|
136
|
+
**Reference:** See ServerModule in reference implementation
|
|
137
|
+
|
|
138
|
+
Add import and include BetterAuthModule in imports array with `fallbackSecrets`:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
BetterAuthModule.forRoot({
|
|
142
|
+
config: envConfig.betterAuth,
|
|
143
|
+
fallbackSecrets: [envConfig.jwt?.secret, envConfig.jwt?.refresh?.secret],
|
|
144
|
+
}),
|
|
60
145
|
```
|
|
61
146
|
|
|
62
|
-
###
|
|
147
|
+
### Step 6: Configure in config.env.ts
|
|
148
|
+
**Modify:** `src/config.env.ts`
|
|
149
|
+
**Reference:** See config.env.ts in reference implementation
|
|
63
150
|
|
|
151
|
+
Add `betterAuth` configuration block. See reference for all available options including `jwt`, `passkey`, `twoFactor`, and `socialProviders`.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Quick Reference
|
|
156
|
+
|
|
157
|
+
**Configuration formats:**
|
|
64
158
|
```typescript
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
},
|
|
71
|
-
};
|
|
159
|
+
betterAuth: true // Enable with all defaults (JWT enabled)
|
|
160
|
+
betterAuth: false // Disable completely
|
|
161
|
+
betterAuth: {} // Same as true
|
|
162
|
+
betterAuth: { ... } // Enable with custom settings
|
|
163
|
+
betterAuth: { enabled: false } // Disable (allows pre-configuration)
|
|
72
164
|
```
|
|
73
165
|
|
|
74
166
|
**Default values (used when not configured):**
|
|
75
167
|
|
|
168
|
+
- **JWT**: Enabled by default
|
|
76
169
|
- **Secret**: Falls back to `jwt.secret` → `jwt.refresh.secret` → auto-generated
|
|
77
170
|
- **Base URL**: `http://localhost:3000`
|
|
78
171
|
- **Base Path**: `/iam`
|
|
79
|
-
- **Passkey
|
|
80
|
-
- **Passkey rpId**: `localhost`
|
|
81
|
-
- **Passkey rpName**: `Nest Server`
|
|
172
|
+
- **2FA/Passkey**: Disabled (opt-in)
|
|
82
173
|
|
|
83
174
|
To **explicitly disable** Better-Auth:
|
|
84
175
|
|
|
85
176
|
```typescript
|
|
86
177
|
const config = {
|
|
87
|
-
betterAuth: {
|
|
88
|
-
enabled: false,
|
|
89
|
-
},
|
|
178
|
+
betterAuth: false, // or betterAuth: { enabled: false }
|
|
90
179
|
};
|
|
91
180
|
```
|
|
92
181
|
|
|
93
182
|
Read the security section below for production deployments.
|
|
94
183
|
|
|
184
|
+
---
|
|
185
|
+
|
|
95
186
|
## Understanding Core Settings
|
|
96
187
|
|
|
97
188
|
### Base URL (`baseUrl`)
|
|
@@ -250,28 +341,29 @@ const config = {
|
|
|
250
341
|
export default {
|
|
251
342
|
// ... other config
|
|
252
343
|
|
|
253
|
-
//
|
|
254
|
-
|
|
344
|
+
// MINIMAL: Just enable BetterAuth (JWT enabled by default)
|
|
345
|
+
betterAuth: true,
|
|
346
|
+
|
|
347
|
+
// OR with customization:
|
|
255
348
|
betterAuth: {
|
|
256
349
|
// enabled: true by default - only set to false to disable
|
|
257
350
|
// secret: auto-generated if not set (see Security section above)
|
|
258
351
|
// baseUrl: 'http://localhost:3000', // Default
|
|
259
352
|
// basePath: '/iam', // Default
|
|
260
353
|
|
|
261
|
-
// JWT Plugin
|
|
262
|
-
//
|
|
354
|
+
// JWT Plugin - ENABLED BY DEFAULT (no config needed)
|
|
355
|
+
// Only add this block to customize or explicitly disable
|
|
263
356
|
jwt: {
|
|
264
|
-
expiresIn: '15m'
|
|
357
|
+
expiresIn: '30m', // Default: '15m'
|
|
358
|
+
// enabled: false, // Uncomment to disable JWT
|
|
265
359
|
},
|
|
266
360
|
|
|
267
|
-
// Two-Factor Authentication (
|
|
268
|
-
// Set enabled: false to explicitly disable
|
|
361
|
+
// Two-Factor Authentication (opt-in - requires config block)
|
|
269
362
|
twoFactor: {
|
|
270
363
|
appName: 'My Application',
|
|
271
364
|
},
|
|
272
365
|
|
|
273
|
-
// Passkey/WebAuthn (
|
|
274
|
-
// Set enabled: false to explicitly disable
|
|
366
|
+
// Passkey/WebAuthn (opt-in - requires config block)
|
|
275
367
|
passkey: {
|
|
276
368
|
rpId: 'localhost',
|
|
277
369
|
rpName: 'My Application',
|
|
@@ -435,23 +527,25 @@ Better-Auth provides a rich plugin ecosystem. This module uses a **hybrid approa
|
|
|
435
527
|
- **Built-in plugins** (JWT, 2FA, Passkey): Explicitly configured with typed options
|
|
436
528
|
- **Additional plugins**: Dynamically added via the `plugins` array
|
|
437
529
|
|
|
438
|
-
### Built-in Plugins
|
|
530
|
+
### Built-in Plugins
|
|
439
531
|
|
|
440
|
-
|
|
532
|
+
| Plugin | Default State | Minimal Config to Enable | Default Values |
|
|
533
|
+
| ------------------ | ------------- | ------------------------ | --------------------------------------------------------------------------------- |
|
|
534
|
+
| **JWT** | **ENABLED** | *(none needed)* | `expiresIn: '15m'` |
|
|
535
|
+
| **Two-Factor** | Disabled | `twoFactor: {}` | `appName: 'Nest Server'` |
|
|
536
|
+
| **Passkey** | Disabled | `passkey: {}` | `origin: 'http://localhost:3000'`, `rpId: 'localhost'`, `rpName: 'Nest Server'` |
|
|
441
537
|
|
|
442
|
-
|
|
443
|
-
| ------------------ | -------------------- | --------------------------------------------------------------------------------- |
|
|
444
|
-
| **JWT** | `jwt: {}` | `expiresIn: '15m'` |
|
|
445
|
-
| **Two-Factor** | `twoFactor: {}` | `appName: 'Nest Server'` |
|
|
446
|
-
| **Passkey** | `passkey: {}` | `origin: 'http://localhost:3000'`, `rpId: 'localhost'`, `rpName: 'Nest Server'` |
|
|
538
|
+
**JWT is enabled by default** - no configuration needed. 2FA and Passkey require explicit configuration.
|
|
447
539
|
|
|
448
540
|
#### Minimal Syntax (Recommended for Development)
|
|
449
541
|
|
|
450
542
|
```typescript
|
|
451
543
|
const config = {
|
|
544
|
+
// JWT is enabled automatically with BetterAuth
|
|
545
|
+
betterAuth: true, // or betterAuth: {}
|
|
546
|
+
|
|
547
|
+
// To also enable 2FA and Passkey:
|
|
452
548
|
betterAuth: {
|
|
453
|
-
// Just add empty blocks - all defaults are applied!
|
|
454
|
-
jwt: {},
|
|
455
549
|
twoFactor: {},
|
|
456
550
|
passkey: {},
|
|
457
551
|
},
|
|
@@ -474,17 +568,20 @@ const config = {
|
|
|
474
568
|
};
|
|
475
569
|
```
|
|
476
570
|
|
|
477
|
-
#### Disabling
|
|
571
|
+
#### Disabling Plugins
|
|
478
572
|
|
|
479
573
|
```typescript
|
|
480
574
|
const config = {
|
|
481
575
|
betterAuth: {
|
|
482
|
-
jwt: { enabled: false }
|
|
483
|
-
twoFactor: {},
|
|
576
|
+
jwt: false, // Disable JWT (or jwt: { enabled: false })
|
|
577
|
+
twoFactor: {}, // 2FA enabled with defaults
|
|
578
|
+
passkey: { enabled: false }, // Passkey explicitly disabled
|
|
484
579
|
},
|
|
485
580
|
};
|
|
486
581
|
```
|
|
487
582
|
|
|
583
|
+
**Note:** JWT is the only plugin enabled by default. To disable it, use `jwt: false` or `jwt: { enabled: false }`.
|
|
584
|
+
|
|
488
585
|
### Dynamic Plugins (plugins Array)
|
|
489
586
|
|
|
490
587
|
For all other Better-Auth plugins, use the `plugins` array. This provides maximum flexibility without requiring updates to this package.
|
|
@@ -615,39 +712,22 @@ const config = {
|
|
|
615
712
|
|
|
616
713
|
## Module Setup
|
|
617
714
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
### Recommended: Extended Module Pattern
|
|
715
|
+
See the [Project Integration Guide](#project-integration-guide-required-steps) at the top of this document for complete step-by-step instructions.
|
|
621
716
|
|
|
622
|
-
|
|
717
|
+
### Summary
|
|
623
718
|
|
|
624
|
-
|
|
625
|
-
// src/server/modules/better-auth/better-auth.module.ts
|
|
626
|
-
import { BetterAuthModule as CoreBetterAuthModule } from '@lenne.tech/nest-server';
|
|
719
|
+
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.
|
|
627
720
|
|
|
628
|
-
|
|
629
|
-
export class BetterAuthModule {
|
|
630
|
-
static forRoot(options): DynamicModule {
|
|
631
|
-
return {
|
|
632
|
-
module: BetterAuthModule,
|
|
633
|
-
imports: [CoreBetterAuthModule.forRoot(options)],
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
}
|
|
721
|
+
**Required components:**
|
|
637
722
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
BetterAuthModule.forRoot({ config: environment.betterAuth }),
|
|
643
|
-
],
|
|
644
|
-
})
|
|
645
|
-
export class ServerModule {}
|
|
646
|
-
```
|
|
723
|
+
1. `BetterAuthModule` - Wraps CoreBetterAuthModule with custom resolver/controller
|
|
724
|
+
2. `BetterAuthResolver` - Extends CoreBetterAuthResolver for GraphQL operations
|
|
725
|
+
3. `BetterAuthController` - Extends CoreBetterAuthController for REST endpoints
|
|
726
|
+
4. `UserService` - Inject `BetterAuthUserMapper` for bidirectional auth sync
|
|
647
727
|
|
|
648
728
|
### Simple: Auto-Registration
|
|
649
729
|
|
|
650
|
-
For simple projects without customization needs:
|
|
730
|
+
For simple projects without customization needs (not recommended for production):
|
|
651
731
|
|
|
652
732
|
```typescript
|
|
653
733
|
// In config.env.ts
|
|
@@ -670,9 +750,9 @@ To explicitly disable Better-Auth:
|
|
|
670
750
|
|
|
671
751
|
```typescript
|
|
672
752
|
const config = {
|
|
673
|
-
betterAuth:
|
|
674
|
-
|
|
675
|
-
},
|
|
753
|
+
betterAuth: false, // Simple boolean
|
|
754
|
+
// or
|
|
755
|
+
betterAuth: { enabled: false }, // Allows pre-configuration
|
|
676
756
|
};
|
|
677
757
|
```
|
|
678
758
|
|
|
@@ -686,10 +766,30 @@ When enabled, Better-Auth exposes the following endpoints at the configured `bas
|
|
|
686
766
|
| `/iam/sign-in/email` | POST | Sign in with email/password |
|
|
687
767
|
| `/iam/sign-out` | GET | Sign out (invalidate session)|
|
|
688
768
|
| `/iam/session` | GET | Get current session |
|
|
769
|
+
| `/iam/token` | GET | Get fresh JWT token |
|
|
689
770
|
| `/iam/forgot-password` | POST | Request password reset |
|
|
690
771
|
| `/iam/reset-password` | POST | Reset password with token |
|
|
691
772
|
| `/iam/verify-email` | POST | Verify email address |
|
|
692
773
|
|
|
774
|
+
### JWT Token Endpoint
|
|
775
|
+
|
|
776
|
+
The `/iam/token` endpoint returns a fresh JWT token for the current session. Use this when your JWT has expired but your session is still valid.
|
|
777
|
+
|
|
778
|
+
**Request:**
|
|
779
|
+
```bash
|
|
780
|
+
curl -X GET https://api.example.com/iam/token \
|
|
781
|
+
-H "Cookie: better-auth.session_token=..."
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
**Response:**
|
|
785
|
+
```json
|
|
786
|
+
{
|
|
787
|
+
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6Ii4uLiJ9..."
|
|
788
|
+
}
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
**Use case:** Microservice authentication - pass the JWT to other services that verify tokens via JWKS (`/iam/jwks`) without database access.
|
|
792
|
+
|
|
693
793
|
### Social Login Endpoints
|
|
694
794
|
|
|
695
795
|
| Endpoint | Method | Description |
|
|
@@ -718,12 +818,14 @@ In addition to REST endpoints, Better-Auth provides GraphQL queries and mutation
|
|
|
718
818
|
|
|
719
819
|
### Queries
|
|
720
820
|
|
|
721
|
-
| Query | Arguments | Return Type | Description
|
|
722
|
-
| ------------------------ | --------- | ---------------------------- |
|
|
723
|
-
| `betterAuthEnabled` | - | `Boolean` | Check if Better-Auth is enabled
|
|
724
|
-
| `betterAuthFeatures` | - | `BetterAuthFeaturesModel` | Get enabled features status
|
|
725
|
-
| `betterAuthSession` | - | `BetterAuthSessionModel` | Get current session (auth req.)
|
|
726
|
-
| `
|
|
821
|
+
| Query | Arguments | Return Type | Description |
|
|
822
|
+
| ------------------------ | --------- | ---------------------------- | --------------------------------- |
|
|
823
|
+
| `betterAuthEnabled` | - | `Boolean` | Check if Better-Auth is enabled |
|
|
824
|
+
| `betterAuthFeatures` | - | `BetterAuthFeaturesModel` | Get enabled features status |
|
|
825
|
+
| `betterAuthSession` | - | `BetterAuthSessionModel` | Get current session (auth req.) |
|
|
826
|
+
| `betterAuthToken` | - | `String` | Get fresh JWT token (auth req.) |
|
|
827
|
+
| `betterAuthListPasskeys` | - | `[BetterAuthPasskeyModel]` | List user's passkeys (auth req.) |
|
|
828
|
+
| `betterAuthMigrationStatus` | - | `BetterAuthMigrationStatusModel` | Migration status (admin only) |
|
|
727
829
|
|
|
728
830
|
### Mutations
|
|
729
831
|
|
|
@@ -1055,9 +1157,132 @@ export class MyService {
|
|
|
1055
1157
|
| `hasRole(roles)` | function | Check if user has any of the specified roles |
|
|
1056
1158
|
| `_authenticatedViaBetterAuth` | true | Marker for Better-Auth authenticated users |
|
|
1057
1159
|
|
|
1160
|
+
## CoreModule.forRoot() Signatures
|
|
1161
|
+
|
|
1162
|
+
Two signatures are available for different use cases:
|
|
1163
|
+
|
|
1164
|
+
### IAM-Only Signature (Recommended for New Projects)
|
|
1165
|
+
|
|
1166
|
+
```typescript
|
|
1167
|
+
// server.module.ts
|
|
1168
|
+
@Module({
|
|
1169
|
+
imports: [
|
|
1170
|
+
CoreModule.forRoot(envConfig),
|
|
1171
|
+
BetterAuthModule.forRoot({
|
|
1172
|
+
config: envConfig.betterAuth,
|
|
1173
|
+
fallbackSecrets: [envConfig.jwt?.secret],
|
|
1174
|
+
}),
|
|
1175
|
+
],
|
|
1176
|
+
})
|
|
1177
|
+
export class ServerModule {}
|
|
1178
|
+
```
|
|
1179
|
+
|
|
1180
|
+
**Features:**
|
|
1181
|
+
- Simplified setup - no Legacy Auth overhead
|
|
1182
|
+
- GraphQL Subscription authentication via BetterAuth sessions
|
|
1183
|
+
- BetterAuthModule is auto-registered when using this signature
|
|
1184
|
+
|
|
1185
|
+
**Requirements:**
|
|
1186
|
+
- Create BetterAuthModule, Resolver, and Controller in your project
|
|
1187
|
+
- Inject BetterAuthUserMapper in UserService
|
|
1188
|
+
- Set `auth.legacyEndpoints.enabled: false` in config
|
|
1189
|
+
|
|
1190
|
+
### Legacy + IAM Signature (For Existing Projects)
|
|
1191
|
+
|
|
1192
|
+
```typescript
|
|
1193
|
+
// server.module.ts
|
|
1194
|
+
@Module({
|
|
1195
|
+
imports: [
|
|
1196
|
+
CoreModule.forRoot(AuthService, AuthModule.forRoot(envConfig.jwt), envConfig),
|
|
1197
|
+
BetterAuthModule.forRoot({
|
|
1198
|
+
config: envConfig.betterAuth,
|
|
1199
|
+
fallbackSecrets: [envConfig.jwt?.secret],
|
|
1200
|
+
}),
|
|
1201
|
+
],
|
|
1202
|
+
})
|
|
1203
|
+
export class ServerModule {}
|
|
1204
|
+
```
|
|
1205
|
+
|
|
1206
|
+
> **@deprecated** This 3-parameter signature is deprecated for new projects.
|
|
1207
|
+
> Use the single-parameter signature for new projects.
|
|
1208
|
+
|
|
1209
|
+
**Features:**
|
|
1210
|
+
- Both Legacy Auth and BetterAuth run in parallel
|
|
1211
|
+
- Bidirectional password synchronization
|
|
1212
|
+
- Gradual user migration to IAM
|
|
1213
|
+
|
|
1214
|
+
---
|
|
1215
|
+
|
|
1216
|
+
## Migration Roadmap (Legacy Auth → BetterAuth)
|
|
1217
|
+
|
|
1218
|
+
Better-Auth is designed as the successor to Legacy Auth. This section describes the migration path.
|
|
1219
|
+
|
|
1220
|
+
### Scenario Overview
|
|
1221
|
+
|
|
1222
|
+
| Scenario | Signature | Description |
|
|
1223
|
+
|----------|-----------|-------------|
|
|
1224
|
+
| **1. Legacy Only** | 3-parameter | Existing projects, no IAM integration |
|
|
1225
|
+
| **2. Migration** | 3-parameter | Legacy + IAM parallel operation |
|
|
1226
|
+
| **3. IAM Only** | 1-parameter | New projects, BetterAuth only |
|
|
1227
|
+
|
|
1228
|
+
### Migration Steps (Scenario 2)
|
|
1229
|
+
|
|
1230
|
+
1. **Enable BetterAuth**
|
|
1231
|
+
- Follow the [Project Integration Guide](#project-integration-guide-required-steps)
|
|
1232
|
+
- Both systems run in parallel
|
|
1233
|
+
|
|
1234
|
+
2. **Monitor Migration Progress**
|
|
1235
|
+
```graphql
|
|
1236
|
+
query {
|
|
1237
|
+
betterAuthMigrationStatus {
|
|
1238
|
+
totalUsers
|
|
1239
|
+
fullyMigratedUsers
|
|
1240
|
+
pendingMigrationUsers
|
|
1241
|
+
migrationPercentage
|
|
1242
|
+
canDisableLegacyAuth
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
```
|
|
1246
|
+
Users migrate automatically when signing in via BetterAuth (IAM).
|
|
1247
|
+
|
|
1248
|
+
3. **Disable Legacy Endpoints** (when `canDisableLegacyAuth: true`)
|
|
1249
|
+
```typescript
|
|
1250
|
+
// config.env.ts
|
|
1251
|
+
auth: {
|
|
1252
|
+
legacyEndpoints: {
|
|
1253
|
+
enabled: false // Disables signIn, signUp, logout, refreshToken
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
```
|
|
1257
|
+
|
|
1258
|
+
4. **Switch to IAM-Only Signature** (optional)
|
|
1259
|
+
```typescript
|
|
1260
|
+
// Before (deprecated)
|
|
1261
|
+
CoreModule.forRoot(AuthService, AuthModule.forRoot(envConfig.jwt), envConfig)
|
|
1262
|
+
|
|
1263
|
+
// After (recommended)
|
|
1264
|
+
CoreModule.forRoot(envConfig)
|
|
1265
|
+
```
|
|
1266
|
+
|
|
1267
|
+
### Why No Automatic Migration Script?
|
|
1268
|
+
|
|
1269
|
+
| System | Password Hash Algorithm |
|
|
1270
|
+
|--------|------------------------|
|
|
1271
|
+
| Legacy Auth | `bcrypt(sha256(password))` |
|
|
1272
|
+
| BetterAuth | `scrypt(sha256(password))` |
|
|
1273
|
+
|
|
1274
|
+
These are **one-way hashes** - there's no way to convert between them without the plain password.
|
|
1275
|
+
Users must sign in at least once via BetterAuth to create a compatible hash.
|
|
1276
|
+
|
|
1277
|
+
### Detailed Documentation
|
|
1278
|
+
|
|
1279
|
+
See `.claude/rules/module-deprecation.md` for complete migration documentation.
|
|
1280
|
+
|
|
1281
|
+
---
|
|
1282
|
+
|
|
1058
1283
|
## Parallel Operation with Legacy Auth
|
|
1059
1284
|
|
|
1060
|
-
Better-Auth runs **parallel to Legacy JWT authentication** without conflicts. Both systems are fully compatible because they
|
|
1285
|
+
Better-Auth runs **parallel to Legacy JWT authentication** without conflicts. Both systems are fully compatible because they share the same users collection.
|
|
1061
1286
|
|
|
1062
1287
|
### How It Works
|
|
1063
1288
|
|
|
@@ -1097,6 +1322,224 @@ const legacyToken = await authService.signIn({ email, password });
|
|
|
1097
1322
|
|
|
1098
1323
|
**No migration is required** - users can authenticate with either system immediately.
|
|
1099
1324
|
|
|
1325
|
+
## Bidirectional Password Synchronization
|
|
1326
|
+
|
|
1327
|
+
### Overview
|
|
1328
|
+
|
|
1329
|
+
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.
|
|
1330
|
+
|
|
1331
|
+
### How Password Sync Works
|
|
1332
|
+
|
|
1333
|
+
| Scenario | Source | Target | Auto-Synced? |
|
|
1334
|
+
|----------|--------|--------|--------------|
|
|
1335
|
+
| Sign up via BetterAuth | IAM | Legacy | ✅ Yes |
|
|
1336
|
+
| Sign up via Legacy Auth | Legacy | IAM | ⚠️ On first IAM sign-in |
|
|
1337
|
+
| Password reset via Legacy | Legacy | IAM | ✅ Yes |
|
|
1338
|
+
| Password reset via BetterAuth | IAM | Legacy | ⚠️ See below |
|
|
1339
|
+
| Password change via user update | Legacy | IAM | ✅ Yes |
|
|
1340
|
+
|
|
1341
|
+
#### IAM Password Reset → Legacy Sync
|
|
1342
|
+
|
|
1343
|
+
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.
|
|
1344
|
+
|
|
1345
|
+
**Solutions:**
|
|
1346
|
+
|
|
1347
|
+
1. **Recommended: Custom Password Reset Flow**
|
|
1348
|
+
Override the password reset to capture the plain password for sync. See [Custom Password Reset with Sync](#custom-password-reset-with-sync).
|
|
1349
|
+
|
|
1350
|
+
2. **Use Legacy Password Reset Only**
|
|
1351
|
+
Direct users to the Legacy Auth password reset flow, which syncs to IAM automatically.
|
|
1352
|
+
|
|
1353
|
+
3. **Re-authenticate After Reset**
|
|
1354
|
+
After IAM password reset, users can sign in via IAM. On next Legacy sign-in attempt, they'll need to reset via Legacy too.
|
|
1355
|
+
|
|
1356
|
+
### Automatic Sync (No Configuration Required)
|
|
1357
|
+
|
|
1358
|
+
The following sync operations happen automatically when `BetterAuthUserMapper` is injected in `UserService`:
|
|
1359
|
+
|
|
1360
|
+
#### 1. IAM Sign-Up → Legacy
|
|
1361
|
+
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.
|
|
1362
|
+
|
|
1363
|
+
#### 2. Legacy Password Reset → IAM
|
|
1364
|
+
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).
|
|
1365
|
+
|
|
1366
|
+
#### 3. Legacy Password Update → IAM
|
|
1367
|
+
When a user changes their password via `UserService.update()`, the new password is synced to BetterAuth (if the user has a credential account).
|
|
1368
|
+
|
|
1369
|
+
#### 4. Legacy → IAM Migration (On First Sign-In)
|
|
1370
|
+
When a legacy user signs in via BetterAuth for the first time, their account is migrated:
|
|
1371
|
+
- Their `iamId` is set
|
|
1372
|
+
- A credential account is created in the `account` collection with the scrypt hash
|
|
1373
|
+
|
|
1374
|
+
### BetterAuth Password Reset Configuration
|
|
1375
|
+
|
|
1376
|
+
BetterAuth provides native password reset via `/iam/forgot-password` and `/iam/reset-password` endpoints. To enable this, configure the `sendResetPassword` callback:
|
|
1377
|
+
|
|
1378
|
+
```typescript
|
|
1379
|
+
// config.env.ts
|
|
1380
|
+
betterAuth: {
|
|
1381
|
+
options: {
|
|
1382
|
+
emailAndPassword: {
|
|
1383
|
+
sendResetPassword: async ({ user, url, token }) => {
|
|
1384
|
+
// Send password reset email
|
|
1385
|
+
// 'url' contains the full reset URL with token
|
|
1386
|
+
await emailService.sendEmail({
|
|
1387
|
+
to: user.email,
|
|
1388
|
+
subject: 'Reset Your Password',
|
|
1389
|
+
html: `<a href="${url}">Click here to reset your password</a>`,
|
|
1390
|
+
});
|
|
1391
|
+
},
|
|
1392
|
+
},
|
|
1393
|
+
},
|
|
1394
|
+
},
|
|
1395
|
+
```
|
|
1396
|
+
|
|
1397
|
+
#### Password Reset Flow (BetterAuth)
|
|
1398
|
+
|
|
1399
|
+
1. User requests reset: `POST /iam/forgot-password` with `{ email }`
|
|
1400
|
+
2. BetterAuth generates token and calls `sendResetPassword` callback
|
|
1401
|
+
3. User clicks link in email → navigates to reset page
|
|
1402
|
+
4. Frontend submits: `POST /iam/reset-password` with `{ token, newPassword }`
|
|
1403
|
+
5. BetterAuth updates password in `account` collection
|
|
1404
|
+
6. Password is automatically synced to Legacy Auth (`users.password`)
|
|
1405
|
+
|
|
1406
|
+
### Password Hashing Algorithms
|
|
1407
|
+
|
|
1408
|
+
| System | Algorithm | Format |
|
|
1409
|
+
|--------|-----------|--------|
|
|
1410
|
+
| Legacy Auth | bcrypt(sha256(password)) | `$2b$10$...` (60 chars) |
|
|
1411
|
+
| BetterAuth (IAM) | scrypt | `salt:hash` (hex encoded) |
|
|
1412
|
+
|
|
1413
|
+
**Important:** These hashes are NOT interchangeable. Password sync requires re-hashing the plain password with the target algorithm.
|
|
1414
|
+
|
|
1415
|
+
### Email Change Synchronization
|
|
1416
|
+
|
|
1417
|
+
Email changes are also synchronized bidirectionally:
|
|
1418
|
+
|
|
1419
|
+
| Scenario | Effect |
|
|
1420
|
+
|----------|--------|
|
|
1421
|
+
| Email changed via Legacy (`UserService.update()`) | IAM sessions invalidated (forces re-auth) |
|
|
1422
|
+
| Email changed via IAM | Legacy refresh tokens cleared |
|
|
1423
|
+
|
|
1424
|
+
### User Deletion Cleanup
|
|
1425
|
+
|
|
1426
|
+
When a user is deleted:
|
|
1427
|
+
|
|
1428
|
+
| Via | Effect |
|
|
1429
|
+
|-----|--------|
|
|
1430
|
+
| Legacy (`UserService.delete()`) | IAM accounts and sessions are cleaned up |
|
|
1431
|
+
| IAM | Legacy user record is removed |
|
|
1432
|
+
|
|
1433
|
+
### Troubleshooting Password Sync
|
|
1434
|
+
|
|
1435
|
+
#### Password not syncing to IAM
|
|
1436
|
+
|
|
1437
|
+
1. Verify `BetterAuthUserMapper` is injected in `UserService`:
|
|
1438
|
+
```typescript
|
|
1439
|
+
constructor(
|
|
1440
|
+
@Optional() private readonly betterAuthUserMapper?: BetterAuthUserMapper,
|
|
1441
|
+
) {
|
|
1442
|
+
super(..., { betterAuthUserMapper });
|
|
1443
|
+
}
|
|
1444
|
+
```
|
|
1445
|
+
|
|
1446
|
+
2. Check if user has an IAM credential account:
|
|
1447
|
+
```javascript
|
|
1448
|
+
// MongoDB query
|
|
1449
|
+
db.account.findOne({ userId: ObjectId("..."), providerId: "credential" })
|
|
1450
|
+
```
|
|
1451
|
+
|
|
1452
|
+
3. Check server logs for sync warnings:
|
|
1453
|
+
```
|
|
1454
|
+
[CoreUserService] Failed to sync password to IAM...
|
|
1455
|
+
```
|
|
1456
|
+
|
|
1457
|
+
#### Password reset email not sending
|
|
1458
|
+
|
|
1459
|
+
1. Configure `sendResetPassword` callback in `betterAuth.options`
|
|
1460
|
+
2. Check that your email service is working
|
|
1461
|
+
3. Verify the callback receives `user`, `url`, and `token` parameters
|
|
1462
|
+
|
|
1463
|
+
#### Legacy user can't sign in via IAM
|
|
1464
|
+
|
|
1465
|
+
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
|
+
|
|
1467
|
+
Alternatively, use `BetterAuthUserMapper.migrateAccountToIam()` to migrate the user programmatically:
|
|
1468
|
+
|
|
1469
|
+
```typescript
|
|
1470
|
+
await betterAuthUserMapper.migrateAccountToIam(email, plainPassword);
|
|
1471
|
+
```
|
|
1472
|
+
|
|
1473
|
+
### Custom Password Reset with Sync
|
|
1474
|
+
|
|
1475
|
+
To enable bidirectional password reset sync, implement a custom password reset endpoint that captures the plain password and syncs to both systems:
|
|
1476
|
+
|
|
1477
|
+
```typescript
|
|
1478
|
+
// src/server/modules/better-auth/better-auth.controller.ts
|
|
1479
|
+
import { Body, Controller, Post } from '@nestjs/common';
|
|
1480
|
+
import { CoreBetterAuthController, BetterAuthUserMapper, Roles, RoleEnum } from '@lenne.tech/nest-server';
|
|
1481
|
+
|
|
1482
|
+
@Controller('iam')
|
|
1483
|
+
export class BetterAuthController extends CoreBetterAuthController {
|
|
1484
|
+
constructor(
|
|
1485
|
+
// ... other dependencies
|
|
1486
|
+
private readonly betterAuthUserMapper: BetterAuthUserMapper,
|
|
1487
|
+
) {
|
|
1488
|
+
super(...);
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
/**
|
|
1492
|
+
* Custom password reset that syncs to both auth systems
|
|
1493
|
+
*/
|
|
1494
|
+
@Post('reset-password-sync')
|
|
1495
|
+
@Roles(RoleEnum.S_EVERYONE)
|
|
1496
|
+
async resetPasswordWithSync(
|
|
1497
|
+
@Body() input: { token: string; newPassword: string },
|
|
1498
|
+
): Promise<{ success: boolean }> {
|
|
1499
|
+
// 1. Reset password via BetterAuth native API
|
|
1500
|
+
const api = this.betterAuthService.getApi();
|
|
1501
|
+
await api.resetPassword({
|
|
1502
|
+
body: { token: input.token, newPassword: input.newPassword },
|
|
1503
|
+
});
|
|
1504
|
+
|
|
1505
|
+
// 2. Sync to Legacy Auth using the plain password
|
|
1506
|
+
// Get user email from the token (you may need to decode it or lookup)
|
|
1507
|
+
const userEmail = await this.getUserEmailFromToken(input.token);
|
|
1508
|
+
if (userEmail) {
|
|
1509
|
+
await this.betterAuthUserMapper.syncPasswordToLegacy(
|
|
1510
|
+
'', // iamUserId not needed for email lookup
|
|
1511
|
+
userEmail,
|
|
1512
|
+
input.newPassword,
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
return { success: true };
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
private async getUserEmailFromToken(token: string): Promise<string | null> {
|
|
1520
|
+
// Implementation depends on your token structure
|
|
1521
|
+
// You may need to query the database or decode the token
|
|
1522
|
+
return null;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
```
|
|
1526
|
+
|
|
1527
|
+
**Alternative: Use Legacy Password Reset**
|
|
1528
|
+
|
|
1529
|
+
The simpler approach is to use the Legacy Auth password reset flow, which automatically syncs to IAM:
|
|
1530
|
+
|
|
1531
|
+
```typescript
|
|
1532
|
+
// Frontend points to Legacy Auth password reset
|
|
1533
|
+
const passwordResetUrl = config.email.passwordResetLink;
|
|
1534
|
+
// e.g., 'http://localhost:4200/user/password-reset'
|
|
1535
|
+
|
|
1536
|
+
// User submits new password via Legacy Auth
|
|
1537
|
+
// → CoreUserService.resetPassword() is called
|
|
1538
|
+
// → Automatically syncs to IAM via syncPasswordChangeToIam()
|
|
1539
|
+
```
|
|
1540
|
+
|
|
1541
|
+
This is the recommended approach for projects in migration phase where both auth systems are active.
|
|
1542
|
+
|
|
1100
1543
|
## Testing
|
|
1101
1544
|
|
|
1102
1545
|
The module provides a `reset()` method for testing:
|
|
@@ -1268,121 +1711,35 @@ const config = {
|
|
|
1268
1711
|
|
|
1269
1712
|
## Extending Better-Auth (Custom Resolver)
|
|
1270
1713
|
|
|
1271
|
-
|
|
1714
|
+
See the [Project Integration Guide](#project-integration-guide-required-steps) for complete setup instructions. This section covers additional customization options.
|
|
1272
1715
|
|
|
1273
|
-
###
|
|
1274
|
-
|
|
1275
|
-
```typescript
|
|
1276
|
-
// src/server/modules/better-auth/better-auth.resolver.ts
|
|
1277
|
-
import { Resolver } from '@nestjs/graphql';
|
|
1278
|
-
import { Roles } from '@lenne.tech/nest-server';
|
|
1279
|
-
import {
|
|
1280
|
-
BetterAuthAuthModel,
|
|
1281
|
-
BetterAuthService,
|
|
1282
|
-
BetterAuthUserMapper,
|
|
1283
|
-
CoreBetterAuthResolver,
|
|
1284
|
-
RoleEnum,
|
|
1285
|
-
} from '@lenne.tech/nest-server';
|
|
1286
|
-
import { EmailService } from '../email/email.service';
|
|
1287
|
-
|
|
1288
|
-
@Resolver(() => BetterAuthAuthModel)
|
|
1289
|
-
@Roles(RoleEnum.ADMIN)
|
|
1290
|
-
export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
1291
|
-
constructor(
|
|
1292
|
-
betterAuthService: BetterAuthService,
|
|
1293
|
-
userMapper: BetterAuthUserMapper,
|
|
1294
|
-
private readonly emailService: EmailService,
|
|
1295
|
-
) {
|
|
1296
|
-
super(betterAuthService, userMapper);
|
|
1297
|
-
}
|
|
1716
|
+
### Adding Custom Logic
|
|
1298
1717
|
|
|
1299
|
-
|
|
1300
|
-
* Override signUp to send welcome email after registration
|
|
1301
|
-
*/
|
|
1302
|
-
override async betterAuthSignUp(
|
|
1303
|
-
email: string,
|
|
1304
|
-
password: string,
|
|
1305
|
-
name?: string,
|
|
1306
|
-
): Promise<BetterAuthAuthModel> {
|
|
1307
|
-
// Call original implementation
|
|
1308
|
-
const result = await super.betterAuthSignUp(email, password, name);
|
|
1309
|
-
|
|
1310
|
-
// Add custom logic after successful sign-up
|
|
1311
|
-
if (result.success && result.user) {
|
|
1312
|
-
await this.emailService.sendWelcomeEmail(result.user.email, result.user.name);
|
|
1313
|
-
await this.analyticsService.trackSignUp(result.user.id);
|
|
1314
|
-
}
|
|
1718
|
+
Override any method to add custom behavior (e.g., sending welcome emails, analytics):
|
|
1315
1719
|
|
|
1316
|
-
|
|
1720
|
+
```typescript
|
|
1721
|
+
// In your BetterAuthResolver (see Step 2 in Integration Guide)
|
|
1722
|
+
|
|
1723
|
+
@Mutation(() => BetterAuthAuthModel, { description: 'Sign up via Better-Auth (email/password)' })
|
|
1724
|
+
@Roles(RoleEnum.S_EVERYONE)
|
|
1725
|
+
override async betterAuthSignUp(
|
|
1726
|
+
@Args('email') email: string,
|
|
1727
|
+
@Args('password') password: string,
|
|
1728
|
+
@Args('name', { nullable: true }) name?: string,
|
|
1729
|
+
): Promise<BetterAuthAuthModel> {
|
|
1730
|
+
// Call original implementation
|
|
1731
|
+
const result = await super.betterAuthSignUp(email, password, name);
|
|
1732
|
+
|
|
1733
|
+
// Add custom logic after successful sign-up
|
|
1734
|
+
if (result.success && result.user) {
|
|
1735
|
+
await this.emailService.sendWelcomeEmail(result.user.email, result.user.name);
|
|
1736
|
+
await this.analyticsService.trackSignUp(result.user.id);
|
|
1317
1737
|
}
|
|
1318
1738
|
|
|
1319
|
-
|
|
1320
|
-
* Override signIn to add custom tracking
|
|
1321
|
-
*/
|
|
1322
|
-
override async betterAuthSignIn(
|
|
1323
|
-
email: string,
|
|
1324
|
-
password: string,
|
|
1325
|
-
ctx: { req: Request; res: Response },
|
|
1326
|
-
): Promise<BetterAuthAuthModel> {
|
|
1327
|
-
// Add pre-login logic
|
|
1328
|
-
this.logger.log(`Login attempt for ${email}`);
|
|
1329
|
-
|
|
1330
|
-
const result = await super.betterAuthSignIn(email, password, ctx);
|
|
1331
|
-
|
|
1332
|
-
// Add post-login logic
|
|
1333
|
-
if (result.success && result.user) {
|
|
1334
|
-
await this.analyticsService.trackLogin(result.user.id);
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
return result;
|
|
1338
|
-
}
|
|
1739
|
+
return result;
|
|
1339
1740
|
}
|
|
1340
1741
|
```
|
|
1341
1742
|
|
|
1342
|
-
### Registering the Custom Resolver
|
|
1343
|
-
|
|
1344
|
-
**Option 1: Via BetterAuthModule options**
|
|
1345
|
-
|
|
1346
|
-
```typescript
|
|
1347
|
-
// src/server/server.module.ts
|
|
1348
|
-
import { BetterAuthModule } from '@lenne.tech/nest-server';
|
|
1349
|
-
import { BetterAuthResolver } from './modules/better-auth/better-auth.resolver';
|
|
1350
|
-
|
|
1351
|
-
@Module({
|
|
1352
|
-
imports: [
|
|
1353
|
-
CoreModule.forRoot(environment),
|
|
1354
|
-
BetterAuthModule.forRoot({
|
|
1355
|
-
config: environment.betterAuth,
|
|
1356
|
-
resolver: BetterAuthResolver, // Your custom resolver
|
|
1357
|
-
}),
|
|
1358
|
-
],
|
|
1359
|
-
})
|
|
1360
|
-
export class ServerModule {}
|
|
1361
|
-
```
|
|
1362
|
-
|
|
1363
|
-
**Option 2: Create your own module (like Legacy Auth)**
|
|
1364
|
-
|
|
1365
|
-
```typescript
|
|
1366
|
-
// src/server/modules/better-auth/better-auth.module.ts
|
|
1367
|
-
import { Module } from '@nestjs/common';
|
|
1368
|
-
import { BetterAuthModule as CoreBetterAuthModule } from '@lenne.tech/nest-server';
|
|
1369
|
-
import { BetterAuthResolver } from './better-auth.resolver';
|
|
1370
|
-
import { EmailModule } from '../email/email.module';
|
|
1371
|
-
|
|
1372
|
-
@Module({
|
|
1373
|
-
imports: [
|
|
1374
|
-
CoreBetterAuthModule.forRoot({
|
|
1375
|
-
config: environment.betterAuth,
|
|
1376
|
-
resolver: BetterAuthResolver,
|
|
1377
|
-
}),
|
|
1378
|
-
EmailModule,
|
|
1379
|
-
],
|
|
1380
|
-
providers: [BetterAuthResolver],
|
|
1381
|
-
exports: [BetterAuthResolver],
|
|
1382
|
-
})
|
|
1383
|
-
export class BetterAuthModule {}
|
|
1384
|
-
```
|
|
1385
|
-
|
|
1386
1743
|
### Available Override Methods
|
|
1387
1744
|
|
|
1388
1745
|
All methods in `CoreBetterAuthResolver` can be overridden:
|