|
| 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) |
0 commit comments