Skip to content

Commit d67878e

Browse files
authored
Merge pull request #477 from lenneTech/develop
Release 11.10.2
2 parents facfa80 + 2c2efa4 commit d67878e

21 files changed

+1541
-287
lines changed

.claude/commands/create-migration-guide.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ Always analyze:
7474

7575
Use the template at `migration-guides/TEMPLATE.md` and follow the process in `.claude/rules/migration-guides.md`.
7676

77+
**IMPORTANT: All migration guides MUST be written in English.** This includes all section titles, descriptions, code comments, and explanations. Technical terms and code identifiers remain unchanged.
78+
7779
Required sections:
7880
1. Overview table
7981
2. Quick Migration

.claude/rules/migration-guides.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ Use `migration-guides/TEMPLATE.md` as starting point.
7878
7. **Module Documentation** - Links to affected module docs (README.md, INTEGRATION-CHECKLIST.md)
7979

8080
**Rules:**
81+
- **Write ALL content in English** - This includes section titles, descriptions, explanations, and code comments. Technical terms and code identifiers remain unchanged.
8182
- Do NOT name specific customer projects (except nest-server-starter as reference)
8283
- Keep information general and pattern-based
8384
- Include code examples for all changes
@@ -107,6 +108,7 @@ ls src/core/modules/**/INTEGRATION-CHECKLIST.md
107108

108109
## Guide Quality Checklist
109110

111+
- [ ] **Written entirely in English**
110112
- [ ] Overview table is complete
111113
- [ ] Quick migration path provided
112114
- [ ] All breaking changes documented with before/after

eslint.config.mjs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ const patched = baseConfig.map(config => {
1010
if (config.rules?.['unused-imports/no-unused-vars']) {
1111
config.rules['unused-imports/no-unused-vars'] = [
1212
'warn',
13-
{caughtErrors: 'none'},
13+
{
14+
argsIgnorePattern: '^_',
15+
caughtErrors: 'none',
16+
varsIgnorePattern: '^_',
17+
},
1418
];
1519
}
1620
return config;

migration-guides/11.9.x-to-11.10.x.md

Lines changed: 129 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
| Category | Details |
66
|----------|---------|
77
| **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 |
8+
| **New Features** | Logging helpers, improved session lookup, enhanced cookie handling, native Better Auth plugin endpoints, **BetterAuthTokenService**, **registerRolesGuardGlobally** for IAM-only setups |
9+
| **Bugfixes** | Session token forwarding to Better Auth plugins, **RolesGuard now enforced in IAM-only mode** |
10+
| **Migration Effort** | Low (~10 minutes) - Affects projects using Passkey or IAM-only setups |
1111

1212
---
1313

@@ -45,6 +45,25 @@ betterAuth: {
4545
}
4646
```
4747

48+
### IAM-only Projects (Security Fix)
49+
50+
If you use the 1-parameter `CoreModule.forRoot(environment)` signature (IAM-only, no Legacy Auth), add `registerRolesGuardGlobally: true`:
51+
52+
```typescript
53+
// server.module.ts - BEFORE (11.9.x)
54+
CoreBetterAuthModule.forRoot({
55+
config: environment.betterAuth,
56+
})
57+
58+
// server.module.ts - AFTER (11.10.x)
59+
CoreBetterAuthModule.forRoot({
60+
config: environment.betterAuth,
61+
registerRolesGuardGlobally: true, // IMPORTANT: Ensures @Roles() decorators work
62+
})
63+
```
64+
65+
**Why:** Without this option, `@Roles()` decorators are not enforced in IAM-only mode, potentially exposing protected endpoints.
66+
4867
---
4968

5069
## What's New in 11.10.x
@@ -138,6 +157,60 @@ Session tokens are now set in multiple cookies to ensure compatibility with Bett
138157
| `/iam/passkey/delete-passkey` | POST | Delete a passkey |
139158
| `/iam/passkey/update-passkey` | POST | Update passkey name |
140159

160+
### 6. BetterAuthTokenService
161+
162+
New centralized service for token extraction and user loading in BetterAuth authentication.
163+
164+
**What the service provides:**
165+
- Token extraction from Authorization header or cookies
166+
- JWT token verification via BetterAuth
167+
- Session token verification via database lookup
168+
- User loading from MongoDB with `hasRole()` capability
169+
170+
**Benefits:**
171+
- Consolidates token verification logic that was previously duplicated in AuthGuard and RolesGuard
172+
- Lazy resolution: Guards use `ModuleRef.get()` with `{ strict: false }` for optional dependencies
173+
- Improved testability and maintainability
174+
175+
```typescript
176+
import { BetterAuthTokenService } from '@lenne.tech/nest-server';
177+
178+
// In a guard or service
179+
const { token, source } = this.tokenService.extractTokenFromRequest(request);
180+
if (token) {
181+
const user = await this.tokenService.verifyAndLoadUser(token);
182+
if (user) {
183+
request.user = user;
184+
}
185+
}
186+
```
187+
188+
### 7. IAM-only Mode Support (registerRolesGuardGlobally)
189+
190+
New option `registerRolesGuardGlobally` in `CoreBetterAuthModule.forRoot()` for IAM-only setups.
191+
192+
**Problem:**
193+
In IAM-only setups (CoreModule.forRoot with only 1 parameter), CoreAuthModule is not imported. CoreAuthModule normally registers the RolesGuard globally. Without this global registration, `@Roles()` decorators are not automatically enforced.
194+
195+
**Solution:**
196+
```typescript
197+
// server.module.ts - IAM-only Setup
198+
@Module({
199+
imports: [
200+
CoreModule.forRoot(environment), // 1-parameter signature = IAM-only
201+
CoreBetterAuthModule.forRoot({
202+
config: environment.betterAuth,
203+
registerRolesGuardGlobally: true, // RolesGuard is registered globally
204+
}),
205+
],
206+
})
207+
export class ServerModule {}
208+
```
209+
210+
**When to use:**
211+
- `registerRolesGuardGlobally: true` - For IAM-only setups (1-parameter CoreModule.forRoot)
212+
- `registerRolesGuardGlobally: false` (default) - For Legacy + IAM setups (3-parameter CoreModule.forRoot)
213+
141214
---
142215

143216
## Breaking Changes
@@ -201,6 +274,25 @@ Additionally, new endpoints are available for backup codes.
201274

202275
**No action required** - this is a bugfix that improves 2FA functionality without requiring any code changes.
203276

277+
### RolesGuard Enforcement in IAM-only Mode
278+
279+
**Fixed Issue:** In IAM-only setups (CoreModule.forRoot with 1 parameter), `@Roles()` decorators were not automatically enforced because the RolesGuard was not globally registered.
280+
281+
**Symptom:** Endpoints with `@Roles(RoleEnum.ADMIN)` were accessible without authentication.
282+
283+
**Cause:** CoreAuthModule (which globally registers the RolesGuard) is not imported in IAM-only setups.
284+
285+
**Solution:** Use the new option `registerRolesGuardGlobally: true` in CoreBetterAuthModule.forRoot():
286+
287+
```typescript
288+
CoreBetterAuthModule.forRoot({
289+
config: environment.betterAuth,
290+
registerRolesGuardGlobally: true,
291+
})
292+
```
293+
294+
**Important:** All IAM-only projects should add this option to ensure `@Roles()` decorators are correctly enforced.
295+
204296
---
205297

206298
## Detailed Migration Steps
@@ -238,7 +330,23 @@ await fetch('/iam/two-factor/verify', { body: { code: '123456' } });
238330
await fetch('/iam/two-factor/verify-totp', { body: { code: '123456' } });
239331
```
240332

241-
### Step 4: Verify Build and Tests
333+
### Step 4: Add registerRolesGuardGlobally (If IAM-only Setup)
334+
335+
If you use the 1-parameter `CoreModule.forRoot(environment)` signature (without Legacy Auth), add the `registerRolesGuardGlobally` option:
336+
337+
```typescript
338+
// server.module.ts
339+
CoreBetterAuthModule.forRoot({
340+
config: environment.betterAuth,
341+
registerRolesGuardGlobally: true, // Required for IAM-only setups
342+
})
343+
```
344+
345+
**How to check if you're IAM-only:**
346+
- 1-parameter: `CoreModule.forRoot(environment)` → IAM-only, needs `registerRolesGuardGlobally: true`
347+
- 3-parameter: `CoreModule.forRoot(CoreAuthService, AuthModule.forRoot(...), environment)` → Legacy + IAM, no change needed
348+
349+
### Step 5: Verify Build and Tests
242350

243351
```bash
244352
npm run build
@@ -301,6 +409,20 @@ trustedOrigins: ['http://localhost:3001'], // Include port!
301409

302410
**Solution:** Debug logs are now suppressed in production. Ensure `NODE_ENV` is not set to `production` during development.
303411

412+
### Protected Endpoints Accessible Without Authentication (IAM-only)
413+
414+
**Symptom:** Endpoints with `@Roles(RoleEnum.ADMIN)` are accessible without authentication in IAM-only setups.
415+
416+
**Cause:** RolesGuard is not globally registered when using 1-parameter `CoreModule.forRoot()`.
417+
418+
**Solution:** Add `registerRolesGuardGlobally: true` to CoreBetterAuthModule:
419+
```typescript
420+
CoreBetterAuthModule.forRoot({
421+
config: environment.betterAuth,
422+
registerRolesGuardGlobally: true,
423+
})
424+
```
425+
304426
---
305427

306428
## Module Documentation
@@ -323,6 +445,9 @@ export { isProduction, maskCookieHeader, maskEmail, maskId, maskSensitive, maskT
323445

324446
// BetterAuth types (updated)
325447
export { IBetterAuth, IBetterAuthWithPasskey, IBetterAuthWithoutPasskey } from './core/common/interfaces/server-options.interface';
448+
449+
// BetterAuthTokenService (new)
450+
export { BetterAuthTokenService, TokenExtractionResult } from './core/modules/better-auth/better-auth-token.service';
326451
```
327452

328453
---

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@lenne.tech/nest-server",
3-
"version": "11.10.1",
3+
"version": "11.10.2",
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",

spectaql.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ servers:
1111
info:
1212
title: lT Nest Server
1313
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).
14-
version: 11.10.1
14+
version: 11.10.2
1515
contact:
1616
name: lenne.Tech GmbH
1717
url: https://lenne.tech

src/core.module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,9 @@ export class CoreModule implements NestModule {
262262
config: betterAuthConfig === true ? {} : betterAuthConfig || {},
263263
// Pass JWT secrets for backwards compatibility fallback
264264
fallbackSecrets: [config.jwt?.secret, config.jwt?.refresh?.secret],
265+
// In IAM-only mode, register RolesGuard globally to enforce @Roles() decorators
266+
// In Legacy mode (autoRegister), RolesGuard is already registered via CoreAuthModule
267+
registerRolesGuardGlobally: isIamOnlyMode,
265268
}),
266269
);
267270
}

0 commit comments

Comments
 (0)