@lenne.tech/nest-server 11.5.0 → 11.6.1
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/core/common/decorators/graphql-populate.decorator.d.ts +2 -2
- package/dist/core/common/interceptors/check-security.interceptor.js +4 -3
- package/dist/core/common/interceptors/check-security.interceptor.js.map +1 -1
- package/dist/core/modules/auth/services/core-auth.service.d.ts +10 -5
- package/dist/core/modules/auth/services/core-auth.service.js +20 -12
- package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +29 -36
- package/src/core/common/interceptors/check-security.interceptor.ts +6 -5
- package/src/core/modules/auth/services/core-auth.service.ts +43 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.6.1",
|
|
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",
|
|
@@ -77,24 +77,22 @@
|
|
|
77
77
|
"node": ">= 20"
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
|
-
"@apollo/gateway": "2.12.0",
|
|
81
80
|
"@getbrevo/brevo": "3.0.1",
|
|
82
81
|
"@nestjs/apollo": "13.1.0",
|
|
83
|
-
"@nestjs/common": "11.1.
|
|
84
|
-
"@nestjs/core": "11.1.
|
|
85
|
-
"@nestjs/graphql": "13.
|
|
86
|
-
"@nestjs/jwt": "11.0.
|
|
87
|
-
"@nestjs/mongoose": "11.0.
|
|
82
|
+
"@nestjs/common": "11.1.9",
|
|
83
|
+
"@nestjs/core": "11.1.9",
|
|
84
|
+
"@nestjs/graphql": "13.2.0",
|
|
85
|
+
"@nestjs/jwt": "11.0.2",
|
|
86
|
+
"@nestjs/mongoose": "11.0.4",
|
|
88
87
|
"@nestjs/passport": "11.0.5",
|
|
89
|
-
"@nestjs/platform-express": "11.1.
|
|
90
|
-
"@nestjs/schedule": "6.0
|
|
91
|
-
"@nestjs/swagger": "11.2.
|
|
88
|
+
"@nestjs/platform-express": "11.1.9",
|
|
89
|
+
"@nestjs/schedule": "6.1.0",
|
|
90
|
+
"@nestjs/swagger": "11.2.3",
|
|
92
91
|
"@nestjs/terminus": "11.0.0",
|
|
93
92
|
"apollo-server-core": "3.13.0",
|
|
94
|
-
"apollo-server-express": "3.13.0",
|
|
95
93
|
"bcrypt": "6.0.0",
|
|
96
94
|
"class-transformer": "0.5.1",
|
|
97
|
-
"class-validator": "0.14.
|
|
95
|
+
"class-validator": "0.14.3",
|
|
98
96
|
"compression": "1.8.1",
|
|
99
97
|
"cookie-parser": "1.4.7",
|
|
100
98
|
"dotenv": "17.2.3",
|
|
@@ -110,41 +108,38 @@
|
|
|
110
108
|
"mongoose": "8.19.3",
|
|
111
109
|
"multer": "2.0.2",
|
|
112
110
|
"node-mailjet": "6.0.11",
|
|
113
|
-
"nodemailer": "7.0.
|
|
111
|
+
"nodemailer": "7.0.11",
|
|
114
112
|
"passport": "0.7.0",
|
|
115
113
|
"passport-jwt": "4.0.1",
|
|
116
114
|
"reflect-metadata": "0.2.2",
|
|
117
115
|
"rfdc": "1.4.1",
|
|
118
116
|
"rxjs": "7.8.2",
|
|
117
|
+
"ts-jest": "29.4.6",
|
|
119
118
|
"yuml-diagram": "1.2.0"
|
|
120
119
|
},
|
|
121
120
|
"devDependencies": {
|
|
122
|
-
"@babel/plugin-proposal-private-methods": "7.18.6",
|
|
123
121
|
"@compodoc/compodoc": "1.1.32",
|
|
124
122
|
"@lenne.tech/eslint-config-ts": "2.1.4",
|
|
125
|
-
"@nestjs/cli": "11.0.
|
|
123
|
+
"@nestjs/cli": "11.0.14",
|
|
126
124
|
"@nestjs/schematics": "11.0.9",
|
|
127
|
-
"@nestjs/testing": "11.1.
|
|
125
|
+
"@nestjs/testing": "11.1.9",
|
|
128
126
|
"@swc/cli": "0.7.9",
|
|
129
|
-
"@swc/core": "1.15.
|
|
130
|
-
"@swc/jest": "0.2.39",
|
|
127
|
+
"@swc/core": "1.15.3",
|
|
131
128
|
"@types/compression": "1.8.1",
|
|
132
129
|
"@types/cookie-parser": "1.4.10",
|
|
133
130
|
"@types/ejs": "3.1.5",
|
|
134
131
|
"@types/express": "4.17.21",
|
|
135
|
-
"@types/
|
|
136
|
-
"@types/lodash": "4.17.20",
|
|
132
|
+
"@types/lodash": "4.17.21",
|
|
137
133
|
"@types/multer": "2.0.0",
|
|
138
|
-
"@types/node": "
|
|
139
|
-
"@types/nodemailer": "7.0.
|
|
134
|
+
"@types/node": "25.0.1",
|
|
135
|
+
"@types/nodemailer": "7.0.4",
|
|
140
136
|
"@types/passport": "1.0.17",
|
|
141
137
|
"@types/supertest": "6.0.3",
|
|
142
|
-
"@typescript-eslint/eslint-plugin": "8.
|
|
143
|
-
"@typescript-eslint/parser": "8.
|
|
144
|
-
"@vitest/coverage-v8": "4.0.
|
|
145
|
-
"@vitest/ui": "4.0.
|
|
138
|
+
"@typescript-eslint/eslint-plugin": "8.49.0",
|
|
139
|
+
"@typescript-eslint/parser": "8.49.0",
|
|
140
|
+
"@vitest/coverage-v8": "4.0.15",
|
|
141
|
+
"@vitest/ui": "4.0.15",
|
|
146
142
|
"ansi-colors": "4.1.3",
|
|
147
|
-
"coffeescript": "2.7.0",
|
|
148
143
|
"eslint": "9.39.1",
|
|
149
144
|
"eslint-config-prettier": "10.1.8",
|
|
150
145
|
"eslint-plugin-unused-imports": "4.3.0",
|
|
@@ -155,25 +150,23 @@
|
|
|
155
150
|
"grunt-contrib-watch": "1.1.0",
|
|
156
151
|
"grunt-sync": "0.8.2",
|
|
157
152
|
"husky": "9.1.7",
|
|
158
|
-
"
|
|
159
|
-
"nodemon": "3.1.10",
|
|
153
|
+
"nodemon": "3.1.11",
|
|
160
154
|
"npm-watch": "0.13.0",
|
|
161
|
-
"pm2": "6.0.
|
|
162
|
-
"prettier": "3.
|
|
155
|
+
"pm2": "6.0.14",
|
|
156
|
+
"prettier": "3.7.4",
|
|
163
157
|
"pretty-quick": "4.2.2",
|
|
164
|
-
"rimraf": "6.1.
|
|
158
|
+
"rimraf": "6.1.2",
|
|
165
159
|
"supertest": "7.1.4",
|
|
166
|
-
"ts-jest": "29.4.5",
|
|
167
160
|
"ts-loader": "9.5.4",
|
|
168
161
|
"ts-morph": "27.0.2",
|
|
169
162
|
"ts-node": "10.9.2",
|
|
170
163
|
"tsconfig-paths": "4.2.0",
|
|
171
164
|
"typescript": "5.9.3",
|
|
172
|
-
"unplugin-swc": "1.5.
|
|
173
|
-
"vite": "7.2.
|
|
165
|
+
"unplugin-swc": "1.5.9",
|
|
166
|
+
"vite": "7.2.7",
|
|
174
167
|
"vite-plugin-node": "7.0.0",
|
|
175
168
|
"vite-tsconfig-paths": "5.1.4",
|
|
176
|
-
"vitest": "4.0.
|
|
169
|
+
"vitest": "4.0.15",
|
|
177
170
|
"yalc": "1.0.0-pre.53"
|
|
178
171
|
},
|
|
179
172
|
"overrides": {
|
|
@@ -31,12 +31,13 @@ export class CheckSecurityInterceptor implements NestInterceptor {
|
|
|
31
31
|
// Get current user
|
|
32
32
|
const user = getContextData(context)?.currentUser || null;
|
|
33
33
|
|
|
34
|
-
// Set force mode for sign in and sign up
|
|
34
|
+
// Set force mode for sign in and sign up (both GraphQL and REST)
|
|
35
35
|
let force = false;
|
|
36
36
|
if (!user) {
|
|
37
37
|
// Here the name is used and not the class itself, because the concrete class is located in the respective project.
|
|
38
|
-
// In case of an override it is better to use the concrete class directly (context.getClass() instead of context.
|
|
39
|
-
|
|
38
|
+
// In case of an override it is better to use the concrete class directly (context.getClass() instead of context.getClass()?.name).
|
|
39
|
+
const className = context.getClass()?.name;
|
|
40
|
+
if (className === 'AuthResolver' || className === 'AuthController') {
|
|
40
41
|
force = true;
|
|
41
42
|
}
|
|
42
43
|
}
|
|
@@ -76,7 +77,7 @@ export class CheckSecurityInterceptor implements NestInterceptor {
|
|
|
76
77
|
|
|
77
78
|
// Check if data is writeable (e.g. objects from direct access to json files via http are not writable)
|
|
78
79
|
if (data && typeof data === 'object') {
|
|
79
|
-
const writeable = !Object.keys(data).find(key => !Object.getOwnPropertyDescriptor(data, key).writable);
|
|
80
|
+
const writeable = !Object.keys(data).find((key) => !Object.getOwnPropertyDescriptor(data, key).writable);
|
|
80
81
|
if (!writeable) {
|
|
81
82
|
return data;
|
|
82
83
|
}
|
|
@@ -88,7 +89,7 @@ export class CheckSecurityInterceptor implements NestInterceptor {
|
|
|
88
89
|
(item) => {
|
|
89
90
|
if (!item || typeof item !== 'object' || typeof item.securityCheck !== 'function') {
|
|
90
91
|
if (Array.isArray(item)) {
|
|
91
|
-
return item.filter(i => i !== undefined);
|
|
92
|
+
return item.filter((i) => i !== undefined);
|
|
92
93
|
}
|
|
93
94
|
return item;
|
|
94
95
|
}
|
|
@@ -15,6 +15,18 @@ import { ICoreAuthUser } from '../interfaces/core-auth-user.interface';
|
|
|
15
15
|
import { JwtPayload } from '../interfaces/jwt-payload.interface';
|
|
16
16
|
import { CoreAuthUserService } from './core-auth-user.service';
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Options for getResult method
|
|
20
|
+
*/
|
|
21
|
+
export interface GetResultOptions {
|
|
22
|
+
/** Current refresh token (for renewal) */
|
|
23
|
+
currentRefreshToken?: string;
|
|
24
|
+
/** Additional data (deviceId, deviceDescription, etc.) */
|
|
25
|
+
data?: { [key: string]: any; deviceId?: string };
|
|
26
|
+
/** Service options including currentUser for securityCheck */
|
|
27
|
+
serviceOptions?: ServiceOptions;
|
|
28
|
+
}
|
|
29
|
+
|
|
18
30
|
/**
|
|
19
31
|
* CoreAuthService to handle user authentication
|
|
20
32
|
*/
|
|
@@ -71,16 +83,17 @@ export class CoreAuthService {
|
|
|
71
83
|
/**
|
|
72
84
|
* Refresh tokens
|
|
73
85
|
*/
|
|
74
|
-
async refreshTokens(user: ICoreAuthUser, currentRefreshToken: string) {
|
|
86
|
+
async refreshTokens(user: ICoreAuthUser, currentRefreshToken: string, serviceOptions?: ServiceOptions) {
|
|
75
87
|
// Create new tokens
|
|
76
88
|
const { deviceDescription, deviceId } = this.decodeJwt(currentRefreshToken);
|
|
77
89
|
const tokens = await this.createTokens(user.id, { deviceDescription, deviceId });
|
|
78
90
|
tokens.refreshToken = await this.updateRefreshToken(user, currentRefreshToken, tokens.refreshToken);
|
|
79
91
|
|
|
80
|
-
// Return
|
|
81
|
-
return
|
|
82
|
-
|
|
83
|
-
|
|
92
|
+
// Return with currentUser set so securityCheck knows user is requesting own data
|
|
93
|
+
return this.getResult(user, {
|
|
94
|
+
currentRefreshToken,
|
|
95
|
+
data: { deviceDescription, deviceId },
|
|
96
|
+
serviceOptions: { ...serviceOptions, currentUser: user },
|
|
84
97
|
});
|
|
85
98
|
}
|
|
86
99
|
|
|
@@ -114,8 +127,11 @@ export class CoreAuthService {
|
|
|
114
127
|
throw new UnauthorizedException('Wrong password');
|
|
115
128
|
}
|
|
116
129
|
|
|
117
|
-
// Return tokens and user
|
|
118
|
-
return this.getResult(user, {
|
|
130
|
+
// Return tokens and user with currentUser set so securityCheck knows user is requesting own data
|
|
131
|
+
return this.getResult(user, {
|
|
132
|
+
data: { deviceDescription, deviceId },
|
|
133
|
+
serviceOptions: { ...serviceOptions, currentUser: user },
|
|
134
|
+
});
|
|
119
135
|
}
|
|
120
136
|
|
|
121
137
|
/**
|
|
@@ -138,8 +154,11 @@ export class CoreAuthService {
|
|
|
138
154
|
// Set device ID
|
|
139
155
|
const { deviceDescription, deviceId } = input;
|
|
140
156
|
|
|
141
|
-
// Return tokens and user
|
|
142
|
-
return this.getResult(user, {
|
|
157
|
+
// Return tokens and user with currentUser set so securityCheck knows user is requesting own data
|
|
158
|
+
return this.getResult(user, {
|
|
159
|
+
data: { deviceDescription, deviceId },
|
|
160
|
+
serviceOptions: { ...serviceOptions, currentUser: user },
|
|
161
|
+
});
|
|
143
162
|
} catch (err) {
|
|
144
163
|
if (err?.message === 'Unprocessable Entity') {
|
|
145
164
|
throw new BadRequestException('Email address already in use');
|
|
@@ -171,12 +190,16 @@ export class CoreAuthService {
|
|
|
171
190
|
|
|
172
191
|
/**
|
|
173
192
|
* Rest result with user and tokens
|
|
193
|
+
*
|
|
194
|
+
* @param user - The authenticated user
|
|
195
|
+
* @param options - Optional configuration for result generation
|
|
196
|
+
* @param options.data - Additional data (deviceId, deviceDescription, etc.)
|
|
197
|
+
* @param options.currentRefreshToken - Current refresh token (for renewal)
|
|
198
|
+
* @param options.serviceOptions - Service options including currentUser for securityCheck
|
|
174
199
|
*/
|
|
175
|
-
protected async getResult(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
currentRefreshToken?: string,
|
|
179
|
-
) {
|
|
200
|
+
protected async getResult(user: ICoreAuthUser, options?: GetResultOptions) {
|
|
201
|
+
const { currentRefreshToken, data, serviceOptions } = options || {};
|
|
202
|
+
|
|
180
203
|
// Create new tokens
|
|
181
204
|
const tokens = await this.createTokens(user.id, data);
|
|
182
205
|
|
|
@@ -184,9 +207,10 @@ export class CoreAuthService {
|
|
|
184
207
|
tokens.refreshToken = await this.updateRefreshToken(user, currentRefreshToken, tokens.refreshToken, data);
|
|
185
208
|
|
|
186
209
|
// Return tokens and user
|
|
210
|
+
// Pass serviceOptions to prepareOutput so currentUser is available for securityCheck
|
|
187
211
|
return CoreAuthModel.map({
|
|
188
212
|
...tokens,
|
|
189
|
-
user: await this.userService.prepareOutput(user),
|
|
213
|
+
user: await this.userService.prepareOutput(user, serviceOptions),
|
|
190
214
|
});
|
|
191
215
|
}
|
|
192
216
|
|
|
@@ -199,10 +223,10 @@ export class CoreAuthService {
|
|
|
199
223
|
path += '.refresh';
|
|
200
224
|
}
|
|
201
225
|
return (
|
|
202
|
-
this.configService.getFastButReadOnly(`${path}.signInOptions.secret`)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
226
|
+
this.configService.getFastButReadOnly(`${path}.signInOptions.secret`) ||
|
|
227
|
+
this.configService.getFastButReadOnly(`${path}.signInOptions.secretOrPrivateKey`) ||
|
|
228
|
+
this.configService.getFastButReadOnly(`${path}.secret`) ||
|
|
229
|
+
this.configService.getFastButReadOnly(`${path}.secretOrPrivateKey`)
|
|
206
230
|
);
|
|
207
231
|
}
|
|
208
232
|
|