Skip to content

Commit 80827a5

Browse files
committed
11.10.0: Passkey
1 parent 76bd8ab commit 80827a5

25 files changed

+2673
-482
lines changed
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
# Migration Guide: 11.9.x → 11.10.x
2+
3+
## Overview
4+
5+
| Category | Details |
6+
|----------|---------|
7+
| **Breaking Changes** | 1. `trustedOrigins` now required when Passkey enabled (TypeScript compile-time check) |
8+
| **New Features** | Logging helpers, improved session lookup, enhanced cookie handling, native Better Auth plugin endpoints |
9+
| **Bugfixes** | Session token forwarding to Better Auth plugins |
10+
| **Migration Effort** | Low (~10 minutes) - Only affects projects using Passkey |
11+
12+
---
13+
14+
## Quick Migration
15+
16+
### Projects WITHOUT Passkey
17+
18+
```bash
19+
# Update package
20+
npm install @lenne.tech/nest-server@11.10.x
21+
22+
# Verify build
23+
npm run build
24+
25+
# Run tests
26+
npm test
27+
```
28+
29+
**That's it!** No code changes required.
30+
31+
### Projects WITH Passkey
32+
33+
If you use `passkey: true` in your BetterAuth config, you must now provide `trustedOrigins`:
34+
35+
```typescript
36+
// config.env.ts - BEFORE (11.9.x)
37+
betterAuth: {
38+
passkey: true, // This worked without trustedOrigins
39+
}
40+
41+
// config.env.ts - AFTER (11.10.x)
42+
betterAuth: {
43+
passkey: true,
44+
trustedOrigins: ['http://localhost:3001', 'https://app.example.com'], // REQUIRED
45+
}
46+
```
47+
48+
---
49+
50+
## What's New in 11.10.x
51+
52+
### 1. TypeScript Compile-Time Validation for Passkey CORS
53+
54+
The `IBetterAuth` type is now a **Discriminated Union** that enforces `trustedOrigins` when Passkey is enabled.
55+
56+
**Why this change?**
57+
Passkey (WebAuthn) uses `credentials: 'include'` for API calls. Browsers don't allow CORS wildcard `*` with credentials, so explicit origins must be configured. This was previously a runtime error - now it's caught at compile time.
58+
59+
```typescript
60+
// TypeScript ERROR: Property 'trustedOrigins' is missing
61+
const config: IBetterAuth = {
62+
passkey: true,
63+
// ❌ Compile error!
64+
};
65+
66+
// CORRECT: trustedOrigins provided
67+
const config: IBetterAuth = {
68+
passkey: true,
69+
trustedOrigins: ['http://localhost:3001'], //
70+
};
71+
72+
// CORRECT: No Passkey, trustedOrigins optional
73+
const config: IBetterAuth = {
74+
twoFactor: true,
75+
// trustedOrigins is optional when passkey is disabled
76+
};
77+
```
78+
79+
### 2. Logging Helper for Secure Logging
80+
81+
New helper functions for safely logging sensitive data (GDPR compliant):
82+
83+
```typescript
84+
import { maskToken, maskEmail, maskCookieHeader, isProduction } from '@lenne.tech/nest-server';
85+
86+
// Mask tokens: 'abc123xyz789' → 'abc1...9789'
87+
this.logger.debug(`Token: ${maskToken(token)}`);
88+
89+
// Mask emails: 'john.doe@example.com' → 'jo***@example.com'
90+
this.logger.debug(`User: ${maskEmail(user.email)}`);
91+
92+
// Mask cookie headers: 'session=abc123; token=xyz' → 'session=***; token=***'
93+
this.logger.debug(`Cookies: ${maskCookieHeader(req.headers.cookie)}`);
94+
95+
// Conditional logging for non-production only
96+
if (!isProduction()) {
97+
this.logger.debug('Debug info...');
98+
}
99+
```
100+
101+
### 3. Improved Session Lookup Performance
102+
103+
The `getSessionByToken()` method now uses a MongoDB aggregation pipeline for single-query session+user lookup, improving performance and handling both ObjectId and string userId formats automatically.
104+
105+
### 4. Enhanced Cookie Handling for Plugin Compatibility
106+
107+
Session tokens are now set in multiple cookies to ensure compatibility with Better Auth plugins (especially Passkey):
108+
109+
| Cookie | Purpose |
110+
|--------|---------|
111+
| `token` | nest-server compatibility |
112+
| `{basePath}.session_token` | Better Auth native (e.g., `iam.session_token`) |
113+
| `better-auth.session_token` | Legacy Better Auth |
114+
115+
### 5. Native Better Auth Plugin Endpoints
116+
117+
2FA and Passkey endpoints now use Better Auth's native plugin handlers, providing more features:
118+
119+
**Two-Factor Authentication (2FA):**
120+
121+
| Endpoint | Method | Description |
122+
|----------|--------|-------------|
123+
| `/iam/two-factor/enable` | POST | Enable 2FA, get TOTP URI |
124+
| `/iam/two-factor/disable` | POST | Disable 2FA |
125+
| `/iam/two-factor/verify-totp` | POST | Verify TOTP code |
126+
| `/iam/two-factor/generate-backup-codes` | POST | Generate backup codes |
127+
| `/iam/two-factor/verify-backup-code` | POST | Verify backup code |
128+
129+
**Passkey (WebAuthn):**
130+
131+
| Endpoint | Method | Description |
132+
|----------|--------|-------------|
133+
| `/iam/passkey/generate-register-options` | POST | Get WebAuthn registration options |
134+
| `/iam/passkey/verify-registration` | POST | Verify and store passkey |
135+
| `/iam/passkey/generate-authenticate-options` | POST | Get WebAuthn authentication options |
136+
| `/iam/passkey/verify-authentication` | POST | Verify passkey authentication |
137+
| `/iam/passkey/list-user-passkeys` | POST | List user's passkeys |
138+
| `/iam/passkey/delete-passkey` | POST | Delete a passkey |
139+
| `/iam/passkey/update-passkey` | POST | Update passkey name |
140+
141+
---
142+
143+
## Breaking Changes
144+
145+
### 1. `trustedOrigins` Required for Passkey
146+
147+
**Impact:** Projects using `passkey: true` without `trustedOrigins`
148+
149+
**Symptom:** TypeScript compile error:
150+
```
151+
Property 'trustedOrigins' is missing in type '{ passkey: true; }' but required in type 'IBetterAuthWithPasskey'.
152+
```
153+
154+
**Before (11.9.x):**
155+
```typescript
156+
betterAuth: {
157+
passkey: true,
158+
// trustedOrigins was optional (caused runtime CORS errors)
159+
}
160+
```
161+
162+
**After (11.10.x):**
163+
```typescript
164+
betterAuth: {
165+
passkey: true,
166+
trustedOrigins: ['http://localhost:3001', 'https://app.example.com'],
167+
}
168+
```
169+
170+
### 2. 2FA Endpoint Changes
171+
172+
**Impact:** Projects calling 2FA endpoints directly
173+
174+
The endpoint for verifying TOTP codes changed:
175+
176+
| Before (11.9.x) | After (11.10.x) |
177+
|-----------------|-----------------|
178+
| `POST /iam/two-factor/verify` | `POST /iam/two-factor/verify-totp` |
179+
180+
Additionally, new endpoints are available for backup codes.
181+
182+
---
183+
184+
## Detailed Migration Steps
185+
186+
### Step 1: Update Package
187+
188+
```bash
189+
npm install @lenne.tech/nest-server@11.10.x
190+
```
191+
192+
### Step 2: Add trustedOrigins (If Using Passkey)
193+
194+
If you have `passkey: true` in your config, add `trustedOrigins`:
195+
196+
```typescript
197+
// config.env.ts
198+
betterAuth: {
199+
passkey: true,
200+
trustedOrigins: [
201+
'http://localhost:3001', // Development frontend
202+
'https://app.example.com', // Production frontend
203+
],
204+
}
205+
```
206+
207+
### Step 3: Update 2FA API Calls (If Using 2FA)
208+
209+
If your frontend calls the 2FA verify endpoint, update the path:
210+
211+
```typescript
212+
// Before
213+
await fetch('/iam/two-factor/verify', { body: { code: '123456' } });
214+
215+
// After
216+
await fetch('/iam/two-factor/verify-totp', { body: { code: '123456' } });
217+
```
218+
219+
### Step 4: Verify Build and Tests
220+
221+
```bash
222+
npm run build
223+
npm test
224+
```
225+
226+
---
227+
228+
## Compatibility Notes
229+
230+
### Existing Projects Without Passkey
231+
232+
No changes required. The migration is seamless.
233+
234+
### Existing Projects With 2FA Only
235+
236+
No breaking changes for basic 2FA usage. The new endpoints provide additional features (backup codes) but the core flow remains the same.
237+
238+
### Custom BetterAuthController Extensions
239+
240+
If you extend `CoreBetterAuthController`, note that the `handleBetterAuthPlugins()` method is now used for plugin endpoints. Your existing overrides should continue to work.
241+
242+
---
243+
244+
## Troubleshooting
245+
246+
### TypeScript Error: trustedOrigins Missing
247+
248+
**Symptom:**
249+
```
250+
Property 'trustedOrigins' is missing in type '{ passkey: true; }'
251+
```
252+
253+
**Solution:** Add `trustedOrigins` array to your betterAuth config:
254+
```typescript
255+
betterAuth: {
256+
passkey: true,
257+
trustedOrigins: ['http://localhost:3001'],
258+
}
259+
```
260+
261+
### CORS Error with Passkey
262+
263+
**Symptom:** Browser console shows CORS error when using Passkey
264+
265+
**Solution:** Ensure `trustedOrigins` includes your frontend URL (including protocol and port):
266+
```typescript
267+
trustedOrigins: ['http://localhost:3001'], // Include port!
268+
```
269+
270+
### 2FA Verify Returns 404
271+
272+
**Symptom:** `POST /iam/two-factor/verify` returns 404
273+
274+
**Solution:** Update to new endpoint: `POST /iam/two-factor/verify-totp`
275+
276+
### Debug Logging Not Appearing
277+
278+
**Symptom:** Debug logs missing in development
279+
280+
**Solution:** Debug logs are now suppressed in production. Ensure `NODE_ENV` is not set to `production` during development.
281+
282+
---
283+
284+
## Module Documentation
285+
286+
### BetterAuth Module
287+
288+
- **README:** [src/core/modules/better-auth/README.md](../src/core/modules/better-auth/README.md)
289+
- **Integration Checklist:** [src/core/modules/better-auth/INTEGRATION-CHECKLIST.md](../src/core/modules/better-auth/INTEGRATION-CHECKLIST.md)
290+
- **Reference Implementation:** `src/server/modules/better-auth/`
291+
292+
---
293+
294+
## New Exports
295+
296+
The following are now exported from `@lenne.tech/nest-server`:
297+
298+
```typescript
299+
// Logging helpers (new)
300+
export { isProduction, maskCookieHeader, maskEmail, maskId, maskSensitive, maskToken } from './core/common/helpers/logging.helper';
301+
302+
// BetterAuth types (updated)
303+
export { IBetterAuth, IBetterAuthWithPasskey, IBetterAuthWithoutPasskey } from './core/common/interfaces/server-options.interface';
304+
```
305+
306+
---
307+
308+
## References
309+
310+
- [BetterAuth README](../src/core/modules/better-auth/README.md)
311+
- [BetterAuth Integration Checklist](../src/core/modules/better-auth/INTEGRATION-CHECKLIST.md)
312+
- [nest-server-starter](https://github.com/lenneTech/nest-server-starter) (reference implementation)
313+
- [Better Auth Documentation](https://www.better-auth.com/docs)

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@lenne.tech/nest-server",
3-
"version": "11.9.0",
3+
"version": "11.10.0",
44
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
55
"keywords": [
66
"node",
@@ -62,6 +62,7 @@
6262
"vitest:watch": "NODE_ENV=local vitest --config vitest-e2e.config.ts",
6363
"vitest:unit": "vitest run --config vitest.config.ts",
6464
"test:unit:watch": "vitest --config vitest.config.ts",
65+
"test:types": "tsc --noEmit --skipLibCheck -p tests/types/tsconfig.json",
6566
"watch": "npm-watch",
6667
"link:eslint": "yalc add @lenne.tech/eslint-config-ts && yalc link @lenne.tech/eslint-config-ts && npm install",
6768
"unlink:eslint": "yalc remove @lenne.tech/eslint-config-ts && npm install"

src/config.env.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ const config: { [env: string]: IServerOptions } = {
6969
appName: 'Nest Server Development',
7070
enabled: false,
7171
},
72+
// CORS trustedOrigins configuration:
73+
// - Not set + Passkey disabled: All origins allowed (default)
74+
// - Not set + Passkey enabled: Server startup FAILS (trustedOrigins required)
75+
// - Set explicitly: Only configured origins allowed
76+
// Uncomment and configure when enabling Passkey:
77+
// trustedOrigins: ['http://localhost:3000', 'http://localhost:3001'],
7278
},
7379
compression: true,
7480
cookies: false,
@@ -223,6 +229,10 @@ const config: { [env: string]: IServerOptions } = {
223229
enabled: false,
224230
},
225231
},
232+
// REQUIRED when Passkey is enabled!
233+
// Passkey uses credentials: 'include' which requires explicit CORS origins.
234+
// Server startup will fail if Passkey is enabled without trustedOrigins.
235+
trustedOrigins: ['http://localhost:3000', 'http://localhost:3001'],
226236
twoFactor: {
227237
appName: 'Nest Server Local',
228238
enabled: true,
@@ -399,10 +409,15 @@ const config: { [env: string]: IServerOptions } = {
399409
enabled: !!process.env.SOCIAL_GOOGLE_CLIENT_ID,
400410
},
401411
},
412+
// REQUIRED for Passkey in production!
413+
// Passkey uses credentials: 'include' which requires explicit origins (no wildcard '*')
414+
// Configure all frontend URLs that need Passkey authentication:
415+
trustedOrigins: process.env.TRUSTED_ORIGINS?.split(',') || [],
402416
twoFactor: {
403417
appName: process.env.TWO_FACTOR_APP_NAME || 'Nest Server',
404418
enabled: process.env.TWO_FACTOR_ENABLED === 'true',
405419
},
420+
// Example: TRUSTED_ORIGINS=https://app.example.com,https://admin.example.com
406421
},
407422
compression: true,
408423
cookies: false,

0 commit comments

Comments
 (0)