@lenne.tech/nest-server 11.10.1 → 11.10.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.
Files changed (34) hide show
  1. package/dist/core/modules/auth/guards/auth.guard.d.ts +2 -2
  2. package/dist/core/modules/auth/guards/auth.guard.js +68 -8
  3. package/dist/core/modules/auth/guards/auth.guard.js.map +1 -1
  4. package/dist/core/modules/auth/guards/roles.guard.d.ts +3 -4
  5. package/dist/core/modules/auth/guards/roles.guard.js +64 -159
  6. package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
  7. package/dist/core/modules/better-auth/better-auth-token.service.d.ts +21 -0
  8. package/dist/core/modules/better-auth/better-auth-token.service.js +153 -0
  9. package/dist/core/modules/better-auth/better-auth-token.service.js.map +1 -0
  10. package/dist/core/modules/better-auth/better-auth.types.d.ts +13 -0
  11. package/dist/core/modules/better-auth/better-auth.types.js.map +1 -1
  12. package/dist/core/modules/better-auth/core-better-auth.module.d.ts +2 -0
  13. package/dist/core/modules/better-auth/core-better-auth.module.js +33 -4
  14. package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -1
  15. package/dist/core/modules/better-auth/core-better-auth.service.d.ts +1 -0
  16. package/dist/core/modules/better-auth/core-better-auth.service.js +4 -0
  17. package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -1
  18. package/dist/core/modules/better-auth/index.d.ts +1 -0
  19. package/dist/core/modules/better-auth/index.js +1 -0
  20. package/dist/core/modules/better-auth/index.js.map +1 -1
  21. package/dist/core.module.js +1 -0
  22. package/dist/core.module.js.map +1 -1
  23. package/dist/tsconfig.build.tsbuildinfo +1 -1
  24. package/package.json +1 -1
  25. package/src/core/modules/auth/guards/auth.guard.ts +136 -23
  26. package/src/core/modules/auth/guards/roles.guard.ts +119 -239
  27. package/src/core/modules/better-auth/better-auth-token.service.ts +241 -0
  28. package/src/core/modules/better-auth/better-auth.types.ts +37 -0
  29. package/src/core/modules/better-auth/core-better-auth.controller.ts +1 -1
  30. package/src/core/modules/better-auth/core-better-auth.module.ts +51 -4
  31. package/src/core/modules/better-auth/core-better-auth.resolver.ts +1 -1
  32. package/src/core/modules/better-auth/core-better-auth.service.ts +13 -0
  33. package/src/core/modules/better-auth/index.ts +1 -0
  34. package/src/core.module.ts +3 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "11.10.1",
3
+ "version": "11.10.2",
4
4
  "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).",
5
5
  "keywords": [
6
6
  "node",
@@ -1,10 +1,14 @@
1
1
  import { CanActivate, ExecutionContext, Logger, mixin, Optional } from '@nestjs/common';
2
+ import { ModuleRef } from '@nestjs/core';
2
3
  import { GqlExecutionContext } from '@nestjs/graphql';
3
4
  import { AuthModuleOptions, Type } from '@nestjs/passport';
4
5
  import { defaultOptions } from '@nestjs/passport/dist/options';
5
6
  import { memoize } from '@nestjs/passport/dist/utils/memoize.util';
6
7
  import passport = require('passport');
7
8
 
9
+ import { BetterAuthTokenService } from '../../better-auth/better-auth-token.service';
10
+ import { BetterAuthenticatedUser } from '../../better-auth/better-auth.types';
11
+ import { CoreBetterAuthService } from '../../better-auth/core-better-auth.service';
8
12
  import { AuthGuardStrategy } from '../auth-guard-strategy.enum';
9
13
  import { ExpiredRefreshTokenException } from '../exceptions/expired-refresh-token.exception';
10
14
  import { ExpiredTokenException } from '../exceptions/expired-token.exception';
@@ -21,7 +25,7 @@ const NO_STRATEGY_ERROR =
21
25
  * Interface for auth guard
22
26
  */
23
27
  export type IAuthGuard = CanActivate & {
24
- handleRequest<TUser = any>(err, user, info, context): TUser;
28
+ handleRequest<TUser = any>(err: Error | null, user: any, info: any, context: ExecutionContext): TUser;
25
29
  };
26
30
 
27
31
  /**
@@ -29,39 +33,82 @@ export type IAuthGuard = CanActivate & {
29
33
  * @param request
30
34
  * @param response
31
35
  */
32
- const createPassportContext = (request, response) => (type, options, callback: (...params) => any) =>
33
- new Promise((resolve, reject) =>
34
- passport.authenticate(type, options, (err, user, info) => {
35
- try {
36
- request.authInfo = info;
37
- return resolve(callback(err, user, info));
38
- } catch (err) {
39
- reject(err);
40
- }
41
- })(request, response, (err) => (err ? reject(err) : resolve)),
42
- );
36
+ const createPassportContext =
37
+ (request: any, response: any) => (type: any, options: any, callback: (...params: any[]) => any) =>
38
+ new Promise((resolve, reject) =>
39
+ passport.authenticate(type, options, (err: any, user: any, info: any) => {
40
+ try {
41
+ request.authInfo = info;
42
+ return resolve(callback(err, user, info));
43
+ } catch (err) {
44
+ reject(err);
45
+ }
46
+ })(request, response, (err: any) => (err ? reject(err) : resolve(undefined))),
47
+ );
43
48
 
44
49
  /**
45
50
  * Extension of AuthGuard to get context in handleRequest method
46
51
  * See: https://github.com/nestjs/passport/blob/master/lib/auth.guard.ts
47
52
  *
53
+ * MULTI-TOKEN SUPPORT:
54
+ * This guard supports multiple authentication strategies:
55
+ * 1. JWT (Legacy Auth) - Uses Passport JWT strategy
56
+ * 2. JWT_REFRESH (Legacy Auth) - Uses Passport JWT refresh strategy
57
+ * 3. BETTER_AUTH (IAM) - Uses BetterAuthTokenService directly (no Passport)
58
+ *
59
+ * For BETTER_AUTH strategy:
60
+ * - First checks if user is already authenticated via middleware (_authenticatedViaBetterAuth)
61
+ * - If not, validates the token via BetterAuthTokenService (JWT or session token)
62
+ * - Loads the full user from MongoDB with hasRole() capability
63
+ *
48
64
  * Can be removed when pull request is merged:
49
65
  * https://github.com/nestjs/passport/pull/66
50
66
  */
51
67
  function createAuthGuard(type?: AuthGuardStrategy | string | string[]): Type<IAuthGuard> {
52
68
  class MixinAuthGuard<TUser = any> {
69
+ private readonly logger = new Logger('AuthGuard');
70
+ private betterAuthService: CoreBetterAuthService | null = null;
71
+ private tokenService: BetterAuthTokenService | null = null;
72
+ private servicesResolved = false;
73
+
53
74
  /**
54
75
  * Integrate options
55
76
  */
56
- constructor(@Optional() protected readonly options?: AuthModuleOptions) {
77
+ constructor(
78
+ @Optional() protected readonly options?: AuthModuleOptions,
79
+ @Optional() private readonly moduleRef?: ModuleRef,
80
+ ) {
57
81
  this.options = this.options || {};
58
82
  if (!type && !this.options.defaultStrategy) {
59
- new Logger('AuthGuard').error(NO_STRATEGY_ERROR);
83
+ this.logger.error(NO_STRATEGY_ERROR);
60
84
  }
61
85
  }
62
86
 
63
87
  /**
64
- * Integrate options
88
+ * Lazily resolve BetterAuth services
89
+ */
90
+ private resolveServices(): void {
91
+ if (this.servicesResolved || !this.moduleRef) {
92
+ return;
93
+ }
94
+
95
+ try {
96
+ this.betterAuthService = this.moduleRef.get(CoreBetterAuthService, { strict: false });
97
+ } catch {
98
+ // BetterAuth not available - that's fine for JWT-only setups
99
+ }
100
+
101
+ try {
102
+ this.tokenService = this.moduleRef.get(BetterAuthTokenService, { strict: false });
103
+ } catch {
104
+ // BetterAuthTokenService not available
105
+ }
106
+
107
+ this.servicesResolved = true;
108
+ }
109
+
110
+ /**
111
+ * Check if user can activate the route
65
112
  */
66
113
  async canActivate(context: ExecutionContext): Promise<boolean> {
67
114
  const args = context.getArgs();
@@ -84,16 +131,81 @@ function createAuthGuard(type?: AuthGuardStrategy | string | string[]): Type<IAu
84
131
  return true;
85
132
  }
86
133
 
87
- // Proceed with Passport authentication
134
+ // For BETTER_AUTH strategy, use BetterAuthTokenService directly (no Passport)
135
+ if (type === AuthGuardStrategy.BETTER_AUTH) {
136
+ return this.handleBetterAuthStrategy(context, request, options);
137
+ }
138
+
139
+ // Proceed with Passport authentication for other strategies
88
140
  const response = context?.switchToHttp()?.getResponse();
89
141
  const passportFn = createPassportContext(request, response);
90
- const user = await passportFn(type || this.options.defaultStrategy, options, (err, currentUser, info) =>
142
+ const user = await passportFn(type || this.options?.defaultStrategy, options, (err: any, currentUser: any, info: any) =>
91
143
  this.handleRequest(err, currentUser, info, context),
92
144
  );
93
145
  request[options.property || defaultOptions.property] = user;
94
146
  return true;
95
147
  }
96
148
 
149
+ /**
150
+ * Handle BETTER_AUTH strategy authentication.
151
+ * Validates tokens via BetterAuthTokenService without using Passport.
152
+ */
153
+ private async handleBetterAuthStrategy(
154
+ context: ExecutionContext,
155
+ request: any,
156
+ options: AuthModuleOptions,
157
+ ): Promise<boolean> {
158
+ // Resolve services lazily
159
+ this.resolveServices();
160
+
161
+ if (!this.betterAuthService?.isEnabled()) {
162
+ this.logger.warn('BETTER_AUTH strategy used but BetterAuth is not enabled');
163
+ throw new InvalidTokenException();
164
+ }
165
+
166
+ // Try to validate token via BetterAuthTokenService
167
+ const user = await this.verifyBetterAuthToken(request);
168
+
169
+ if (!user) {
170
+ throw new InvalidTokenException();
171
+ }
172
+
173
+ // Validate through handleRequest and set user on request
174
+ const validatedUser = this.handleRequest(null, user, null, context);
175
+ request[options.property || defaultOptions.property] = validatedUser;
176
+ return true;
177
+ }
178
+
179
+ /**
180
+ * Verify BetterAuth token (JWT or session) and load the corresponding user.
181
+ *
182
+ * Delegates to BetterAuthTokenService for token verification and user loading.
183
+ *
184
+ * @param request - HTTP request object
185
+ * @returns User object if verification succeeds, null otherwise
186
+ */
187
+ private async verifyBetterAuthToken(request: any): Promise<BetterAuthenticatedUser | null> {
188
+ if (!this.tokenService) {
189
+ return null;
190
+ }
191
+
192
+ try {
193
+ // Extract token from request
194
+ const { token } = this.tokenService.extractTokenFromRequest(request);
195
+ if (!token) {
196
+ return null;
197
+ }
198
+
199
+ // Verify token and load user
200
+ return await this.tokenService.verifyAndLoadUser(token);
201
+ } catch (error) {
202
+ this.logger.debug(
203
+ `BetterAuth token verification failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
204
+ );
205
+ return null;
206
+ }
207
+ }
208
+
97
209
  /**
98
210
  * Prepare request
99
211
  */
@@ -104,7 +216,9 @@ function createAuthGuard(type?: AuthGuardStrategy | string | string[]): Type<IAu
104
216
  if (ctx?.req) {
105
217
  return ctx.req;
106
218
  }
107
- } catch (e) {}
219
+ } catch {
220
+ // GraphQL context not available
221
+ }
108
222
 
109
223
  // Else return HTTP request
110
224
  return context && context.switchToHttp() ? context.switchToHttp().getRequest() : null;
@@ -113,16 +227,15 @@ function createAuthGuard(type?: AuthGuardStrategy | string | string[]): Type<IAu
113
227
  /**
114
228
  * Login for session handling
115
229
  */
116
- async logIn<TRequest extends { logIn: (...params) => any } = any>(request: TRequest) {
117
- const user = request[this.options.property || defaultOptions.property];
118
- await new Promise<void>((resolve, reject) => request.logIn(user, (err) => (err ? reject(err) : resolve())));
230
+ async logIn<TRequest extends { logIn: (...params: any[]) => any } = any>(request: TRequest) {
231
+ const user = request[this.options?.property || defaultOptions.property];
232
+ await new Promise<void>((resolve, reject) => request.logIn(user, (err: any) => (err ? reject(err) : resolve())));
119
233
  }
120
234
 
121
235
  /**
122
236
  * Process request
123
237
  */
124
- // eslint-disable-next-line unused-imports/no-unused-vars
125
- handleRequest(err, user, info, context): TUser {
238
+ handleRequest(err: Error | null, user: any, info: any, _context: ExecutionContext): TUser {
126
239
  if (err) {
127
240
  throw new InvalidTokenException();
128
241
  }