ecrs-auth-core 1.0.108 → 1.0.112

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.
@@ -1,6 +1,6 @@
1
- import { JwtService } from '@nestjs/jwt';
2
- import { AuthCoreOptions } from './interfaces/auth-core-options.interface';
3
- import { User } from './entities/user.entity';
1
+ import { JwtService } from "@nestjs/jwt";
2
+ import { AuthCoreOptions } from "./interfaces/auth-core-options.interface";
3
+ import { User } from "./entities/user.entity";
4
4
  export type RoutePermissionSet = {
5
5
  view?: boolean;
6
6
  create?: boolean;
@@ -44,7 +44,7 @@ export declare class AuthService {
44
44
  * Save user last login details
45
45
  * Updates the tbl_user_last_login table with latest login info
46
46
  */
47
- saveLastLogin(user: User, clientIp: string, loginStatus?: 'success' | 'failed' | 'blocked', failureReason?: string, additionalData?: {
47
+ saveLastLogin(user: User, clientIp: string, loginStatus?: "success" | "failed" | "blocked", failureReason?: string, additionalData?: {
48
48
  browser?: string;
49
49
  deviceType?: string;
50
50
  operatingSystem?: string;
@@ -59,7 +59,7 @@ export declare class AuthService {
59
59
  * If a record exists for the user on the same date, update it
60
60
  * Otherwise, create a new record
61
61
  */
62
- saveLoginDetailsJson(user: User, clientIp: string, loginStatus?: 'success' | 'failed' | 'blocked', failureReason?: string, additionalData?: {
62
+ saveLoginDetailsJson(user: User, clientIp: string, loginStatus?: "success" | "failed" | "blocked", failureReason?: string, additionalData?: {
63
63
  browser?: string;
64
64
  deviceType?: string;
65
65
  operatingSystem?: string;
@@ -55,7 +55,7 @@ let AuthService = class AuthService {
55
55
  constructor(jwtService, options) {
56
56
  this.jwtService = jwtService;
57
57
  this.options = options;
58
- this.uploadPhotoDir = 'uploads/organization/photos';
58
+ this.uploadPhotoDir = "uploads/organization/photos";
59
59
  const { repositories } = options;
60
60
  this.userRepo = repositories.userRepo;
61
61
  this.roleRepo = repositories.roleRepo;
@@ -75,12 +75,17 @@ let AuthService = class AuthService {
75
75
  // Guard: reject missing or oversized inputs before any DB/bcrypt work.
76
76
  // Oversized passwords cause bcrypt to hang (DoS); oversized emails are never valid.
77
77
  if (!email || !password || password.length > 72 || email.length > 254) {
78
- throw new common_1.UnauthorizedException('Invalid credentials');
78
+ throw new common_1.UnauthorizedException("Invalid credentials");
79
79
  }
80
80
  // Normalize email for consistent comparison, then use ILike so the DB
81
81
  // matches regardless of how the email was originally stored (mixed/upper/lower).
82
82
  const normalizedEmail = email.trim().toLowerCase();
83
- const whereClause = { email: (0, typeorm_1.ILike)(normalizedEmail), deletedBy: null, deletedAt: null, status: 1 };
83
+ const whereClause = {
84
+ email: (0, typeorm_1.ILike)(normalizedEmail),
85
+ deletedBy: (0, typeorm_1.IsNull)(),
86
+ deletedAt: (0, typeorm_1.IsNull)(),
87
+ status: 1,
88
+ };
84
89
  //role-id
85
90
  //1-"Superadmin" 2-"Admin" 3-"Employee" 5-"CUSTOMER_BOOKER" 6-"SECURITY" 7-"SUPPLIER"
86
91
  //module-id
@@ -102,15 +107,21 @@ let AuthService = class AuthService {
102
107
  // const DUMMY_HASH = '$2b$10$abcdefghijklmnopqrstuuABCDEFGHIJKLMNOPQRSTUVWXYZ012345';
103
108
  // const hashToCompare = user?.password ?? DUMMY_HASH;
104
109
  // const isValid = await bcrypt.compare(password, hashToCompare);
105
- const isValid = user ? await bcrypt.compare(password, user.password) : false;
110
+ // console.log(`🔐 Comparing password for user ${user?.id}`);
111
+ // console.log(`🔐 Comparing password for user ${user?.password}`);
112
+ const isValid = user
113
+ ? await bcrypt.compare(password, user.password)
114
+ : false;
106
115
  // Single generic message — never reveal whether the email exists
107
116
  if (!user || !isValid) {
108
- throw new common_1.UnauthorizedException('Invalid credentials');
117
+ console.log(`🔍 Validating user with email: ${normalizedEmail}, moduleId: ${moduleId}, whereClause:`, whereClause);
118
+ console.log(`🔐 Comparing password for user ${user?.id}`);
119
+ throw new common_1.UnauthorizedException("Invalid credentials");
109
120
  }
110
121
  if (clientIp && this.ipRestrictionsRepo) {
111
122
  const ipAllowed = await this.validateIpRestriction(user.id, clientIp);
112
123
  if (!ipAllowed) {
113
- throw new common_1.UnauthorizedException('Login not allowed from this IP address');
124
+ throw new common_1.UnauthorizedException("Login not allowed from this IP address");
114
125
  }
115
126
  }
116
127
  return user;
@@ -128,17 +139,17 @@ let AuthService = class AuthService {
128
139
  normalizeIp(ip) {
129
140
  // Remove descriptive text if present (e.g., "IPv4 Address. . . . . . : 192.167.0.173")
130
141
  let cleanIp = ip.trim();
131
- if (cleanIp.includes(':')) {
132
- const parts = cleanIp.split(':');
142
+ if (cleanIp.includes(":")) {
143
+ const parts = cleanIp.split(":");
133
144
  cleanIp = parts[parts.length - 1].trim();
134
145
  }
135
146
  // Convert IPv6-mapped IPv4 addresses (::ffff:x.x.x.x) to plain IPv4 (x.x.x.x)
136
- if (cleanIp.startsWith('::ffff:')) {
147
+ if (cleanIp.startsWith("::ffff:")) {
137
148
  return cleanIp.substring(7);
138
149
  }
139
150
  // Also handle other IPv6 prefixes
140
- if (cleanIp.startsWith('::1')) {
141
- return '127.0.0.1'; // IPv6 loopback to IPv4 loopback
151
+ if (cleanIp.startsWith("::1")) {
152
+ return "127.0.0.1"; // IPv6 loopback to IPv4 loopback
142
153
  }
143
154
  return cleanIp;
144
155
  }
@@ -151,11 +162,11 @@ let AuthService = class AuthService {
151
162
  // Normalize the incoming IP
152
163
  const normalizedRequestIp = this.normalizeIp(requestIp);
153
164
  // Get all active IP restrictions for this user
154
- const restrictions = await this.ipRestrictionsRepo.find({
165
+ const restrictions = (await this.ipRestrictionsRepo.find({
155
166
  where: {
156
167
  user_id: userId,
157
168
  },
158
- });
169
+ }));
159
170
  // If no restrictions exist, allow login
160
171
  if (!restrictions || restrictions.length === 0) {
161
172
  console.log(`✅ User ${userId}: No IP restrictions configured - Allow login`);
@@ -173,16 +184,18 @@ let AuthService = class AuthService {
173
184
  return true;
174
185
  }
175
186
  // IP doesn't match any allowed IP
176
- const allowedIps = restrictions.map((r) => {
187
+ const allowedIps = restrictions
188
+ .map((r) => {
177
189
  const ip = r.allowed_ip_address || r.ip_address;
178
190
  return this.normalizeIp(ip);
179
- }).join(', ');
191
+ })
192
+ .join(", ");
180
193
  console.log(`❌ User ${userId}: IP ${requestIp} (normalized: ${normalizedRequestIp}) does not match allowed IPs - Deny login`);
181
194
  console.log(` Allowed IPs: ${allowedIps}`);
182
195
  return false;
183
196
  }
184
197
  catch (error) {
185
- console.error('Error validating IP restriction:', error);
198
+ console.error("Error validating IP restriction:", error);
186
199
  // On error, allow login (fail open)
187
200
  return true;
188
201
  }
@@ -199,13 +212,13 @@ let AuthService = class AuthService {
199
212
  AND status = 1
200
213
  AND is_deleted = 0
201
214
  LIMIT 1`, [userId, moduleId]);
202
- console.log('Raw Query Result:', result);
215
+ console.log("Raw Query Result:", result);
203
216
  const allowed = result.length > 0;
204
- console.log(`📊 Module access check result for user ID ${userId} and module ID ${moduleId}: ${allowed ? 'Allowed' : 'Denied'}`);
217
+ console.log(`📊 Module access check result for user ID ${userId} and module ID ${moduleId}: ${allowed ? "Allowed" : "Denied"}`);
205
218
  return allowed;
206
219
  }
207
220
  catch (error) {
208
- console.error('❌ Error checking module access:', error);
221
+ console.error("❌ Error checking module access:", error);
209
222
  return false;
210
223
  }
211
224
  }
@@ -217,10 +230,10 @@ let AuthService = class AuthService {
217
230
  * Save user last login details
218
231
  * Updates the tbl_user_last_login table with latest login info
219
232
  */
220
- async saveLastLogin(user, clientIp, loginStatus = 'success', failureReason, additionalData) {
233
+ async saveLastLogin(user, clientIp, loginStatus = "success", failureReason, additionalData) {
221
234
  if (!this.userLastLoginRepo) {
222
235
  // Last login tracking not configured
223
- console.warn('⚠️ userLastLoginRepo not configured. Add loginDetailsRepo to AuthCoreModule repositories.');
236
+ console.warn("⚠️ userLastLoginRepo not configured. Add loginDetailsRepo to AuthCoreModule repositories.");
224
237
  return;
225
238
  }
226
239
  try {
@@ -262,13 +275,13 @@ let AuthService = class AuthService {
262
275
  console.log(`📝 Last login details saved for user ${user.id} (${loginStatus})`);
263
276
  }
264
277
  catch (error) {
265
- if (error?.message?.includes('No metadata')) {
266
- console.error('❌ ERROR: UserLastLoginEntity is not registered in your TypeORM configuration.\n' +
267
- 'Please see ENTITY_SETUP_GUIDE.md for setup instructions.\n' +
268
- 'Entity must be added to TypeOrmModule.forRoot(entities: [...]) in your app.');
278
+ if (error?.message?.includes("No metadata")) {
279
+ console.error("❌ ERROR: UserLastLoginEntity is not registered in your TypeORM configuration.\n" +
280
+ "Please see ENTITY_SETUP_GUIDE.md for setup instructions.\n" +
281
+ "Entity must be added to TypeOrmModule.forRoot(entities: [...]) in your app.");
269
282
  }
270
283
  else {
271
- console.error('Error saving last login details:', error);
284
+ console.error("Error saving last login details:", error);
272
285
  }
273
286
  // Don't throw error - this shouldn't block login
274
287
  }
@@ -278,10 +291,10 @@ let AuthService = class AuthService {
278
291
  * If a record exists for the user on the same date, update it
279
292
  * Otherwise, create a new record
280
293
  */
281
- async saveLoginDetailsJson(user, clientIp, loginStatus = 'success', failureReason, additionalData) {
294
+ async saveLoginDetailsJson(user, clientIp, loginStatus = "success", failureReason, additionalData) {
282
295
  if (!this.loginDetailsRepo) {
283
296
  // Login details tracking not configured
284
- console.warn('⚠️ loginDetailsRepo not configured. Add loginDetailsRepo to AuthCoreModule repositories.');
297
+ console.warn("⚠️ loginDetailsRepo not configured. Add loginDetailsRepo to AuthCoreModule repositories.");
285
298
  return;
286
299
  }
287
300
  try {
@@ -320,10 +333,10 @@ let AuthService = class AuthService {
320
333
  // Append new login detail
321
334
  loginData.push(loginDetailData);
322
335
  // Update counters
323
- if (loginStatus === 'success') {
336
+ if (loginStatus === "success") {
324
337
  successCount++;
325
338
  }
326
- else if (loginStatus === 'failed' || loginStatus === 'blocked') {
339
+ else if (loginStatus === "failed" || loginStatus === "blocked") {
327
340
  failedCount++;
328
341
  }
329
342
  await this.loginDetailsRepo.update({ id: existingRecord.id }, {
@@ -338,8 +351,9 @@ let AuthService = class AuthService {
338
351
  else {
339
352
  // Create new record
340
353
  loginData = [loginDetailData];
341
- successCount = loginStatus === 'success' ? 1 : 0;
342
- failedCount = loginStatus === 'failed' || loginStatus === 'blocked' ? 1 : 0;
354
+ successCount = loginStatus === "success" ? 1 : 0;
355
+ failedCount =
356
+ loginStatus === "failed" || loginStatus === "blocked" ? 1 : 0;
343
357
  const newLoginDetails = this.loginDetailsRepo.create({
344
358
  user_id: user.id,
345
359
  date: today,
@@ -356,13 +370,13 @@ let AuthService = class AuthService {
356
370
  }
357
371
  }
358
372
  catch (error) {
359
- if (error?.message?.includes('No metadata')) {
360
- console.error('❌ ERROR: LoginDetailsEntity is not registered in your TypeORM configuration.\n' +
361
- 'Please see ENTITY_SETUP_GUIDE.md for setup instructions.\n' +
362
- 'Entity must be added to TypeOrmModule.forRoot(entities: [...]) in your app.');
373
+ if (error?.message?.includes("No metadata")) {
374
+ console.error("❌ ERROR: LoginDetailsEntity is not registered in your TypeORM configuration.\n" +
375
+ "Please see ENTITY_SETUP_GUIDE.md for setup instructions.\n" +
376
+ "Entity must be added to TypeOrmModule.forRoot(entities: [...]) in your app.");
363
377
  }
364
378
  else {
365
- console.error('Error saving login details JSON:', error);
379
+ console.error("Error saving login details JSON:", error);
366
380
  }
367
381
  // Don't throw error - this shouldn't block login
368
382
  }
@@ -412,12 +426,12 @@ let AuthService = class AuthService {
412
426
  console.log(`🚪 Logout details updated for user ${userId} on ${today.toDateString()}`);
413
427
  }
414
428
  catch (error) {
415
- if (error?.message?.includes('No metadata')) {
416
- console.error('❌ ERROR: LoginDetailsEntity is not registered in your TypeORM configuration.\n' +
417
- 'Please see ENTITY_SETUP_GUIDE.md for setup instructions.');
429
+ if (error?.message?.includes("No metadata")) {
430
+ console.error("❌ ERROR: LoginDetailsEntity is not registered in your TypeORM configuration.\n" +
431
+ "Please see ENTITY_SETUP_GUIDE.md for setup instructions.");
418
432
  }
419
433
  else {
420
- console.error('Error updating logout details:', error);
434
+ console.error("Error updating logout details:", error);
421
435
  }
422
436
  // Don't throw error - this shouldn't block logout
423
437
  }
@@ -445,12 +459,12 @@ let AuthService = class AuthService {
445
459
  console.log(`🚪 Last login logout time updated for user ${userId}`);
446
460
  }
447
461
  catch (error) {
448
- if (error?.message?.includes('No metadata')) {
449
- console.error('❌ ERROR: UserLastLoginEntity is not registered in your TypeORM configuration.\n' +
450
- 'Please see ENTITY_SETUP_GUIDE.md for setup instructions.');
462
+ if (error?.message?.includes("No metadata")) {
463
+ console.error("❌ ERROR: UserLastLoginEntity is not registered in your TypeORM configuration.\n" +
464
+ "Please see ENTITY_SETUP_GUIDE.md for setup instructions.");
451
465
  }
452
466
  else {
453
- console.error('Error updating last login logout time:', error);
467
+ console.error("Error updating last login logout time:", error);
454
468
  }
455
469
  // Don't throw error - this shouldn't block logout
456
470
  }
@@ -461,7 +475,9 @@ let AuthService = class AuthService {
461
475
  console.log(`📊 Loaded permissions for user ID ${user.id}:`, permissionTree);
462
476
  const role = await this.roleRepo.findOne({ where: { id: user.roleId } });
463
477
  const roleName = role?.roleName || null;
464
- const effectiveModuleId = Number.isFinite(selectedModuleId) ? selectedModuleId : user.moduleId ?? null;
478
+ const effectiveModuleId = Number.isFinite(selectedModuleId)
479
+ ? selectedModuleId
480
+ : (user.moduleId ?? null);
465
481
  // Fetch workprofile/employee details from EmployeeWorkProfileEntity
466
482
  let branchId = null;
467
483
  let dispatchCenterId = null;
@@ -480,7 +496,7 @@ let AuthService = class AuthService {
480
496
  }
481
497
  }
482
498
  catch (error) {
483
- console.error('Error fetching work profile:', error);
499
+ console.error("Error fetching work profile:", error);
484
500
  // Continue with null values if fetch fails
485
501
  }
486
502
  }
@@ -492,14 +508,14 @@ let AuthService = class AuthService {
492
508
  try {
493
509
  const lastLogin = await this.userLastLoginRepo.findOne({
494
510
  where: { user_id: user.id },
495
- order: { login_time: 'DESC' },
511
+ order: { login_time: "DESC" },
496
512
  });
497
513
  if (lastLogin) {
498
514
  lastLoginTime = lastLogin?.login_time || null;
499
515
  }
500
516
  }
501
517
  catch (error) {
502
- console.error('Error fetching last login details:', error);
518
+ console.error("Error fetching last login details:", error);
503
519
  // Continue with null values if fetch fails
504
520
  }
505
521
  }
@@ -535,7 +551,7 @@ let AuthService = class AuthService {
535
551
  };
536
552
  return {
537
553
  status: true,
538
- message: 'Login successful',
554
+ message: "Login successful",
539
555
  data: {
540
556
  user: {
541
557
  id: user.id,
@@ -571,7 +587,7 @@ let AuthService = class AuthService {
571
587
  */
572
588
  extractUserIpv4(clientIp) {
573
589
  if (!clientIp)
574
- return '';
590
+ return "";
575
591
  return this.normalizeIp(clientIp);
576
592
  }
577
593
  async findUserById(id) {
@@ -582,11 +598,11 @@ let AuthService = class AuthService {
582
598
  if (this.userLastLoginRepo) {
583
599
  try {
584
600
  const result = await this.userLastLoginRepo
585
- .createQueryBuilder('login')
586
- .select('login.login_time', 'login_time')
587
- .where('login.user_id = :userId', { userId })
588
- .andWhere('login.logout_time IS NULL')
589
- .orderBy('login.login_time', 'DESC')
601
+ .createQueryBuilder("login")
602
+ .select("login.login_time", "login_time")
603
+ .where("login.user_id = :userId", { userId })
604
+ .andWhere("login.logout_time IS NULL")
605
+ .orderBy("login.login_time", "DESC")
590
606
  .limit(1)
591
607
  .getRawOne();
592
608
  if (result) {
@@ -594,7 +610,7 @@ let AuthService = class AuthService {
594
610
  }
595
611
  }
596
612
  catch (error) {
597
- console.error('Error fetching last login details:', error);
613
+ console.error("Error fetching last login details:", error);
598
614
  // Continue with null values if fetch fails
599
615
  }
600
616
  }
@@ -610,7 +626,7 @@ let AuthService = class AuthService {
610
626
  }
611
627
  }
612
628
  catch (error) {
613
- console.error('Error updating last activity:', error);
629
+ console.error("Error updating last activity:", error);
614
630
  }
615
631
  }
616
632
  // private async loadPermissions(userId: number): Promise<PermissionsTree> {
@@ -54,7 +54,7 @@ __decorate([
54
54
  __metadata("design:type", String)
55
55
  ], UserCustomer.prototype, "userImage", void 0);
56
56
  __decorate([
57
- (0, typeorm_1.Column)(),
57
+ (0, typeorm_1.Column)({ type: "int" }),
58
58
  __metadata("design:type", Number)
59
59
  ], UserCustomer.prototype, "roleId", void 0);
60
60
  __decorate([
@@ -115,15 +115,15 @@ __decorate([
115
115
  __metadata("design:type", Number)
116
116
  ], UserCustomer.prototype, "token_version", void 0);
117
117
  __decorate([
118
- (0, typeorm_1.Column)(),
118
+ (0, typeorm_1.Column)({ type: "int" }),
119
119
  __metadata("design:type", Number)
120
120
  ], UserCustomer.prototype, "createdBy", void 0);
121
121
  __decorate([
122
- (0, typeorm_1.Column)({ nullable: true }),
122
+ (0, typeorm_1.Column)({ type: "int", nullable: true }),
123
123
  __metadata("design:type", Number)
124
124
  ], UserCustomer.prototype, "updatedBy", void 0);
125
125
  __decorate([
126
- (0, typeorm_1.Column)({ nullable: true }),
126
+ (0, typeorm_1.Column)({ type: "int", nullable: true }),
127
127
  __metadata("design:type", Object)
128
128
  ], UserCustomer.prototype, "deletedBy", void 0);
129
129
  __decorate([
package/package.json CHANGED
@@ -1,52 +1,52 @@
1
- {
2
- "name": "ecrs-auth-core",
3
- "version": "1.0.108",
4
- "description": "Centralized authentication and authorization module for ECRS apps",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "files": [
8
- "dist"
9
- ],
10
- "scripts": {
11
- "build": "tsc -p tsconfig.json"
12
- },
13
- "keywords": [
14
- "nestjs",
15
- "auth",
16
- "jwt",
17
- "module",
18
- "ecrs"
19
- ],
20
- "author": "Chetan Yadnik",
21
- "license": "MIT",
22
- "peerDependencies": {
23
- "@nestjs/common": "^10.2.7 || ^11.0.0",
24
- "@nestjs/core": "^10.2.7 || ^11.0.0",
25
- "@nestjs/passport": "^10.0.3 || ^11.0.0",
26
- "bcrypt": "^5.1.1",
27
- "passport": "^0.6.0",
28
- "passport-jwt": "^4.0.1",
29
- "typeorm": "^0.3.25"
30
- },
31
- "dependencies": {
32
- "@nestjs/jwt": "^11.0.0",
33
- "@nestjs/swagger": "^7.1.14",
34
- "@nestjs/typeorm": "^11.0.0",
35
- "axios": "^1.16.1",
36
- "class-transformer": "^0.5.1",
37
- "class-validator": "^0.14.3",
38
- "jsonwebtoken": "^9.0.2",
39
- "rxjs": "^7.8.1",
40
- "swagger-ui-express": "^5.0.0"
41
- },
42
- "devDependencies": {
43
- "@nestjs/common": "^10.2.7",
44
- "@nestjs/core": "^10.2.7",
45
- "@nestjs/passport": "^10.0.3",
46
- "@types/bcrypt": "^6.0.0",
47
- "@types/passport-jwt": "^4.0.1",
48
- "reflect-metadata": "^0.1.13",
49
- "typeorm": "^0.3.17",
50
- "typescript": "^5.8.3"
51
- }
52
- }
1
+ {
2
+ "name": "ecrs-auth-core",
3
+ "version": "1.0.112",
4
+ "description": "Centralized authentication and authorization module for ECRS apps",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc -p tsconfig.json"
12
+ },
13
+ "keywords": [
14
+ "nestjs",
15
+ "auth",
16
+ "jwt",
17
+ "module",
18
+ "ecrs"
19
+ ],
20
+ "author": "Chetan Yadnik",
21
+ "license": "MIT",
22
+ "peerDependencies": {
23
+ "@nestjs/common": "^10.2.7 || ^11.0.0",
24
+ "@nestjs/core": "^10.2.7 || ^11.0.0",
25
+ "@nestjs/passport": "^10.0.3 || ^11.0.0",
26
+ "bcrypt": "^5.1.1",
27
+ "passport": "^0.6.0",
28
+ "passport-jwt": "^4.0.1",
29
+ "typeorm": "^0.3.25"
30
+ },
31
+ "dependencies": {
32
+ "@nestjs/jwt": "^11.0.0",
33
+ "@nestjs/swagger": "^7.1.14",
34
+ "@nestjs/typeorm": "^11.0.0",
35
+ "axios": "^1.16.1",
36
+ "class-transformer": "^0.5.1",
37
+ "class-validator": "^0.14.3",
38
+ "jsonwebtoken": "^9.0.2",
39
+ "rxjs": "^7.8.1",
40
+ "swagger-ui-express": "^5.0.0"
41
+ },
42
+ "devDependencies": {
43
+ "@nestjs/common": "^10.2.7",
44
+ "@nestjs/core": "^10.2.7",
45
+ "@nestjs/passport": "^10.0.3",
46
+ "@types/bcrypt": "^6.0.0",
47
+ "@types/passport-jwt": "^4.0.1",
48
+ "reflect-metadata": "^0.1.13",
49
+ "typeorm": "^0.3.17",
50
+ "typescript": "^5.8.3"
51
+ }
52
+ }