@govish/shared-services 1.4.0 → 1.6.0

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.
@@ -20,7 +20,7 @@ const deviceService_1 = require("../services/deviceService");
20
20
  const officerService_1 = require("../services/officerService");
21
21
  const logMode_1 = require("../utils/logMode");
22
22
  /**
23
- * Middleware to authenticate either a device, an officer, or a microservice (via API key)
23
+ * Middleware to authenticate either a device, an officer, a microservice (via API key), or combinations thereof
24
24
  * Checks JWT token and validates against Device or Officer table
25
25
  * Also checks for API keys for microservice authentication
26
26
  *
@@ -29,7 +29,18 @@ const logMode_1 = require("../utils/logMode");
29
29
  * - Device-Token: <device_jwt_token> (for device authentication)
30
30
  * - Authorization: Bearer <user_jwt_token> (for officer/user authentication)
31
31
  *
32
- * Priority: API Key > Device Token > Authorization Token
32
+ * Supports combined authentication:
33
+ * - API Key + Officer: X-API-Key + Authorization header (sets authType: 'api_key_and_officer')
34
+ * - Device + Officer: Device-Token + Authorization header (sets authType: 'both')
35
+ *
36
+ * Authentication Priority/Combination Logic:
37
+ * 1. API Key + Officer (if both headers present) -> authType: 'api_key_and_officer'
38
+ * 2. API Key only (if only X-API-Key present) -> authType: 'api_key'
39
+ * 3. Device + Officer (if both headers present) -> authType: 'both'
40
+ * 4. Device only (if only Device-Token present) -> authType: 'device'
41
+ * 5. Officer only (if only Authorization present) -> authType: 'officer'
42
+ *
43
+ * At least one authentication method must succeed for the request to proceed.
33
44
  */
34
45
  const createAuthenticateDeviceOrOfficer = (deps) => {
35
46
  const apiKeyService = new apiKeyService_1.ApiKeyService(deps);
@@ -69,32 +80,122 @@ const createAuthenticateDeviceOrOfficer = (deps) => {
69
80
  // Always initialize so we can log to console/logger even if Kafka is not available
70
81
  const auditService = new auditService_1.AuditService(deps);
71
82
  return (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
83
+ var _a, _b, _c, _d, _e, _f, _g, _h;
72
84
  try {
73
- // First, check for API key (for microservice authentication)
74
- const apiKeyHeader = req.headers['x-api-key'];
85
+ // Track authentication attempts and failures
86
+ const authAttempts = [];
87
+ const authFailures = [];
88
+ // Step 1: Check for API key (for microservice authentication)
89
+ // Check both normalized headers and rawHeaders to handle case variations
90
+ let apiKeyHeader;
91
+ // Check normalized headers first (Express normalizes to lowercase)
92
+ apiKeyHeader = req.headers['x-api-key'];
93
+ // If not found, check rawHeaders (case-insensitive search)
94
+ if (!apiKeyHeader && req.rawHeaders) {
95
+ const index = req.rawHeaders.findIndex((h) => h.toLowerCase() === 'x-api-key');
96
+ if (index >= 0 && index < req.rawHeaders.length - 1) {
97
+ apiKeyHeader = req.rawHeaders[index + 1];
98
+ }
99
+ }
100
+ let apiKeyAuthenticated = false;
101
+ let apiKeyFailureReason = null;
75
102
  if (apiKeyHeader) {
76
- const apiKey = yield apiKeyService.validateApiKey(apiKeyHeader);
77
- if (apiKey) {
78
- // Attach API key info to request
79
- req.apiKey = apiKey;
80
- req.microservice = apiKey.microservice;
81
- req.authType = 'api_key';
82
- // Log audit event
83
- if (auditService) {
84
- auditService.logAuditEvent(req, {
85
- // Event type will be determined automatically
86
- }).catch((err) => {
87
- logger.error('Failed to log audit event', { error: err.message });
103
+ authAttempts.push('api_key');
104
+ debugLog('[Authentication Flow] Step 1: X-API-Key header detected, starting API key validation');
105
+ console.log('🔑 [Authentication Flow] Step 1: X-API-Key header detected, starting API key validation', {
106
+ headerLength: apiKeyHeader.length,
107
+ headerPreview: apiKeyHeader.substring(0, 15) + '...',
108
+ endpoint: req.originalUrl || req.url
109
+ });
110
+ logger.info('[Authentication Flow] Step 1: X-API-Key header detected', {
111
+ endpoint: req.originalUrl || req.url,
112
+ method: req.method,
113
+ apiKeyPreview: apiKeyHeader.substring(0, 10) + '...',
114
+ headerLength: apiKeyHeader.length
115
+ });
116
+ try {
117
+ const apiKey = yield apiKeyService.validateApiKey(apiKeyHeader);
118
+ if (apiKey) {
119
+ apiKeyAuthenticated = true;
120
+ console.log('✅ [Authentication Flow] Step 1a: API key validation successful');
121
+ console.log(' 📋 API Key Details:', {
122
+ id: apiKey.id,
123
+ microservice: apiKey.microservice,
124
+ keyPreview: apiKey.key ? apiKey.key.substring(0, 15) + '...' : 'N/A',
125
+ isActive: apiKey.is_active,
126
+ expiresAt: apiKey.expires_at ? new Date(apiKey.expires_at).toISOString() : 'Never'
127
+ });
128
+ logger.info('[Authentication Flow] Step 1a: API key validation successful', {
129
+ apiKeyId: apiKey.id,
130
+ microservice: apiKey.microservice,
131
+ keyPreview: apiKey.key ? apiKey.key.substring(0, 15) + '...' : 'N/A',
132
+ isActive: apiKey.is_active,
133
+ expiresAt: apiKey.expires_at,
134
+ endpoint: req.originalUrl || req.url
88
135
  });
136
+ // Attach API key info to request
137
+ req.apiKey = apiKey;
138
+ req.microservice = apiKey.microservice;
139
+ // Don't set authType yet - wait to see if officer is also authenticated
140
+ // This allows API key + officer authentication (similar to device + officer)
141
+ // Log debug info
142
+ debugLog('API Key authentication successful', {
143
+ apiKeyId: apiKey.id,
144
+ microservice: apiKey.microservice,
145
+ endpoint: req.originalUrl || req.url,
146
+ method: req.method
147
+ });
148
+ console.log('[Authentication Flow] Step 1b: API key attached to request, continuing to check for officer authentication');
149
+ console.log(' 🔍 Checking for Bearer token (Authorization header) to authenticate officer...');
150
+ logger.info('[Authentication Flow] Step 1b: API key attached to request, continuing to check for officer authentication');
151
+ // Continue to check for officer authentication (similar to device authentication)
152
+ // Don't return early - let it go through the same flow as device + officer
153
+ }
154
+ else {
155
+ apiKeyFailureReason = 'Invalid, expired, inactive, or not found';
156
+ authFailures.push({ method: 'api_key', reason: apiKeyFailureReason });
157
+ console.error('❌ [Authentication Flow] Step 1a: API key validation failed', {
158
+ reason: apiKeyFailureReason,
159
+ endpoint: req.originalUrl || req.url,
160
+ method: req.method,
161
+ apiKeyPreview: apiKeyHeader.substring(0, 10) + '...'
162
+ });
163
+ logger.warn('[Authentication Flow] Step 1a: API key validation failed', {
164
+ reason: apiKeyFailureReason,
165
+ endpoint: req.originalUrl || req.url,
166
+ method: req.method,
167
+ apiKeyPreview: apiKeyHeader.substring(0, 10) + '...'
168
+ });
169
+ // Don't return 401 yet - continue to check Bearer token
89
170
  }
90
- return next();
91
171
  }
92
- else {
93
- return res.status(401).json({
94
- message: 'Invalid, expired, or inactive API key'
172
+ catch (apiKeyError) {
173
+ const errorMessage = apiKeyError instanceof Error ? apiKeyError.message : 'Unknown error';
174
+ const errorStack = apiKeyError instanceof Error ? apiKeyError.stack : undefined;
175
+ apiKeyFailureReason = `Network or validation error: ${errorMessage}`;
176
+ authFailures.push({ method: 'api_key', reason: apiKeyFailureReason });
177
+ console.error('❌ [Authentication Flow] Step 1a: API key validation error', {
178
+ error: errorMessage,
179
+ errorStack,
180
+ endpoint: req.originalUrl || req.url,
181
+ apiKeyPreview: apiKeyHeader.substring(0, 10) + '...'
182
+ });
183
+ logger.error('[Authentication Flow] Step 1a: API key validation error', {
184
+ error: errorMessage,
185
+ errorStack,
186
+ endpoint: req.originalUrl || req.url,
187
+ apiKeyPreview: apiKeyHeader.substring(0, 10) + '...'
95
188
  });
189
+ // Don't return 401 yet - continue to check Bearer token
96
190
  }
97
191
  }
192
+ else {
193
+ // Log when API key header is not present (for debugging)
194
+ debugLog('[Authentication Flow] Step 1: No X-API-Key header found', {
195
+ availableHeaders: Object.keys(req.headers).filter(h => h.toLowerCase().includes('api') || h.toLowerCase().includes('key')),
196
+ endpoint: req.originalUrl || req.url
197
+ });
198
+ }
98
199
  // Check for device-token header (case-insensitive)
99
200
  // Express normalizes headers to lowercase, but check multiple variations
100
201
  // Also check rawHeaders in case Express hasn't normalized it
@@ -110,7 +211,6 @@ const createAuthenticateDeviceOrOfficer = (deps) => {
110
211
  deviceTokenHeader = req.rawHeaders[index + 1];
111
212
  }
112
213
  }
113
- const authHeader = req.headers.authorization;
114
214
  let deviceAuthenticated = false;
115
215
  let officerAuthenticated = false;
116
216
  // Authenticate device if Device-Token header is present
@@ -156,6 +256,14 @@ const createAuthenticateDeviceOrOfficer = (deps) => {
156
256
  // Attach device to request
157
257
  req.device = device;
158
258
  deviceAuthenticated = true;
259
+ // Log debug info
260
+ debugLog('Device authentication successful', {
261
+ deviceId: device.id,
262
+ deviceDeviceId: device.device_id,
263
+ deviceName: device.device_name,
264
+ endpoint: req.originalUrl || req.url,
265
+ method: req.method
266
+ });
159
267
  }
160
268
  catch (error) {
161
269
  // Log audit event for invalid device token
@@ -173,101 +281,471 @@ const createAuthenticateDeviceOrOfficer = (deps) => {
173
281
  return res.status(401).json({ message: 'Invalid or expired device token' });
174
282
  }
175
283
  }
176
- // Authenticate officer if Authorization header is present
284
+ // Step 2: Authenticate officer if Authorization header is present
285
+ let bearerTokenFailureReason = null;
286
+ const authHeader = req.headers.authorization;
177
287
  if (authHeader && authHeader.startsWith('Bearer ')) {
288
+ authAttempts.push('bearer_token');
289
+ console.log('🔐 [Authentication Flow] Step 2: Authorization (Bearer) header detected, starting officer authentication');
290
+ logger.info('[Authentication Flow] Step 2: Authorization header detected, starting officer authentication', {
291
+ endpoint: req.originalUrl || req.url,
292
+ method: req.method,
293
+ hasApiKey: apiKeyAuthenticated,
294
+ hasDevice: deviceAuthenticated,
295
+ apiKeyFailed: !!apiKeyFailureReason
296
+ });
178
297
  const token = authHeader.substring(7); // Remove 'Bearer ' prefix
179
298
  try {
299
+ console.log('[Authentication Flow] Step 2a: Verifying JWT token');
300
+ logger.info('[Authentication Flow] Step 2a: Verifying JWT token');
180
301
  const decoded = jsonwebtoken_1.default.verify(token, jwtConfig.secret, {
181
302
  issuer: jwtConfig.issuer,
182
303
  audience: jwtConfig.audience,
183
304
  });
305
+ console.log('✅ [Authentication Flow] Step 2b: JWT token verified successfully', {
306
+ tokenType: decoded.type,
307
+ hasId: !!decoded.id,
308
+ hasSub: !!decoded.sub
309
+ });
310
+ logger.info('[Authentication Flow] Step 2b: JWT token verified successfully', {
311
+ tokenType: decoded.type,
312
+ hasId: !!decoded.id,
313
+ hasSub: !!decoded.sub
314
+ });
184
315
  // Skip if this is a device token (should use Device-Token header instead)
185
316
  if (decoded.type === 'device') {
317
+ bearerTokenFailureReason = 'Token is a device token, should use Device-Token header';
318
+ authFailures.push({ method: 'bearer_token', reason: bearerTokenFailureReason });
319
+ console.warn('⚠️ [Authentication Flow] Step 2c: Token is a device token, should use Device-Token header');
320
+ logger.warn('[Authentication Flow] Step 2c: Token is a device token, should use Device-Token header');
186
321
  if (!deviceAuthenticated) {
187
- return res.status(401).json({ message: 'Device tokens should use Device-Token header' });
322
+ // Don't return 401 yet - check if we have other valid auth
323
+ // Will be handled in final check
324
+ }
325
+ else {
326
+ // If device already authenticated via Device-Token, continue
327
+ console.log('[Authentication Flow] Step 2c: Device already authenticated via Device-Token header, continuing');
328
+ logger.info('[Authentication Flow] Step 2c: Device already authenticated via Device-Token header, continuing');
188
329
  }
189
- // If device already authenticated via Device-Token, continue
190
330
  }
191
331
  else {
192
- // Authenticate as officer
332
+ // Step 2d: Authenticate as officer
333
+ console.log('[Authentication Flow] Step 2d: Processing as officer token');
334
+ logger.info('[Authentication Flow] Step 2d: Processing as officer token');
193
335
  const officerId = decoded.id || decoded.sub;
194
336
  if (!officerId) {
195
- return res.status(401).json({ message: 'Invalid token payload' });
337
+ bearerTokenFailureReason = 'Invalid token payload - no officer ID found';
338
+ authFailures.push({ method: 'bearer_token', reason: bearerTokenFailureReason });
339
+ console.error('❌ [Authentication Flow] Step 2d: Invalid token payload - no officer ID found');
340
+ logger.warn('[Authentication Flow] Step 2d: Invalid token payload - no officer ID found');
341
+ // Don't return 401 yet - will be handled in final check
196
342
  }
197
- // Get officer from service (uses Redis cache with auth service fallback)
198
- const officer = yield officerService.getOfficerById(officerId);
199
- if (!officer) {
200
- // Log audit event for officer not found
201
- if (auditService) {
202
- req.authType = 'officer';
203
- auditService.logAuditEvent(req, {
204
- event_type: undefined, // Will be determined automatically
205
- response_status: 401,
206
- error_message: 'Officer not found',
207
- error_code: 'OFFICER_NOT_FOUND',
208
- }).catch((err) => {
209
- logger.debug('Failed to log audit event', { error: err.message });
343
+ else {
344
+ console.log('[Authentication Flow] Step 2e: Fetching officer from service', {
345
+ officerId
346
+ });
347
+ logger.info('[Authentication Flow] Step 2e: Fetching officer from service', {
348
+ officerId
349
+ });
350
+ // Get officer from service (uses Redis cache with auth service fallback)
351
+ const officer = yield officerService.getOfficerById(officerId);
352
+ if (!officer) {
353
+ bearerTokenFailureReason = 'Officer not found';
354
+ authFailures.push({ method: 'bearer_token', reason: bearerTokenFailureReason });
355
+ console.error(' [Authentication Flow] Step 2e: Officer not found', { officerId });
356
+ logger.warn('[Authentication Flow] Step 2e: Officer not found', { officerId });
357
+ // Don't return 401 yet - will be handled in final check
358
+ }
359
+ else {
360
+ console.log('✅ [Authentication Flow] Step 2f: Officer found and authenticated', {
361
+ officerId: officer.id,
362
+ officerName: officer.name,
363
+ serviceNumber: officer.service_number
364
+ });
365
+ logger.info('[Authentication Flow] Step 2f: Officer found and authenticated', {
366
+ officerId: officer.id,
367
+ officerName: officer.name,
368
+ serviceNumber: officer.service_number
369
+ });
370
+ // Attach officer to request
371
+ req.officer = officer;
372
+ req.user = officer;
373
+ officerAuthenticated = true;
374
+ // Log debug info
375
+ const hasApiKey = !!req.apiKey;
376
+ debugLog('Officer authentication successful', {
377
+ officerId: officer.id,
378
+ officerName: officer.name,
379
+ serviceNumber: officer.service_number,
380
+ endpoint: req.originalUrl || req.url,
381
+ method: req.method,
382
+ combinedWithApiKey: hasApiKey,
383
+ apiKeyMicroservice: hasApiKey ? req.apiKey.microservice : undefined
384
+ });
385
+ console.log('[Authentication Flow] Step 2g: Officer attached to request', {
386
+ officerId: officer.id,
387
+ officerName: officer.name,
388
+ serviceNumber: officer.service_number,
389
+ combinedWithApiKey: hasApiKey,
390
+ combinedWithDevice: deviceAuthenticated
391
+ });
392
+ // Enhanced logging when officer is authenticated with API key
393
+ if (hasApiKey) {
394
+ console.log('═══════════════════════════════════════════════════════════');
395
+ console.log('✅ API KEY + OFFICER AUTHENTICATION SUCCESSFUL:');
396
+ console.log(' 🔑 API Key:', {
397
+ id: req.apiKey.id,
398
+ microservice: req.apiKey.microservice,
399
+ keyPreview: req.apiKey.key ? req.apiKey.key.substring(0, 15) + '...' : 'N/A'
400
+ });
401
+ console.log(' 👤 Officer:', {
402
+ id: officer.id,
403
+ name: officer.name,
404
+ serviceNumber: officer.service_number,
405
+ email: officer.email
406
+ });
407
+ console.log('═══════════════════════════════════════════════════════════');
408
+ }
409
+ logger.info('[Authentication Flow] Step 2g: Officer attached to request', {
410
+ officerId: officer.id,
411
+ officerName: officer.name,
412
+ serviceNumber: officer.service_number,
413
+ combinedWithApiKey: hasApiKey,
414
+ combinedWithDevice: deviceAuthenticated
210
415
  });
211
416
  }
212
- return res.status(401).json({ message: 'Officer not found' });
213
417
  }
214
- // Attach officer to request
215
- req.officer = officer;
216
- req.user = officer;
217
- officerAuthenticated = true;
218
418
  }
219
419
  }
220
420
  catch (error) {
221
- // Log audit event for invalid authorization token
222
- if (auditService) {
223
- req.authType = 'officer';
224
- auditService.logAuditEvent(req, {
225
- event_type: undefined, // Will be determined automatically
226
- response_status: 401,
227
- error_message: error instanceof Error ? error.message : 'Invalid or expired authorization token',
228
- error_code: 'INVALID_AUTH_TOKEN',
229
- }).catch((err) => {
230
- logger.debug('Failed to log audit event', { error: err.message });
231
- });
232
- }
233
- return res.status(401).json({ message: 'Invalid or expired authorization token' });
421
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
422
+ bearerTokenFailureReason = `Invalid or expired token: ${errorMessage}`;
423
+ authFailures.push({ method: 'bearer_token', reason: bearerTokenFailureReason });
424
+ console.error('❌ [Authentication Flow] Step 2: Bearer token validation error', {
425
+ error: errorMessage,
426
+ errorStack: error instanceof Error ? error.stack : undefined,
427
+ errorName: error instanceof Error ? error.name : undefined
428
+ });
429
+ logger.error('[Authentication Flow] Step 2: Bearer token validation error', {
430
+ error: errorMessage,
431
+ errorStack: error instanceof Error ? error.stack : undefined
432
+ });
433
+ // Don't return 401 yet - will be handled in final check
234
434
  }
235
435
  }
436
+ // Step 3: Determine final authentication status
437
+ const finalApiKeyAuthenticated = !!req.apiKey;
438
+ console.log('[Authentication Flow] Step 3: Determining final authentication status', {
439
+ apiKeyAuthenticated: finalApiKeyAuthenticated,
440
+ deviceAuthenticated,
441
+ officerAuthenticated,
442
+ authAttempts,
443
+ authFailures: authFailures.length > 0 ? authFailures : 'None'
444
+ });
445
+ logger.info('[Authentication Flow] Step 3: Determining final authentication status', {
446
+ apiKeyAuthenticated: finalApiKeyAuthenticated,
447
+ deviceAuthenticated,
448
+ officerAuthenticated,
449
+ authAttempts,
450
+ authFailures
451
+ });
236
452
  // At least one authentication method must succeed
237
- if (!deviceAuthenticated && !officerAuthenticated) {
453
+ if (!finalApiKeyAuthenticated && !deviceAuthenticated && !officerAuthenticated) {
454
+ // Build detailed error message
455
+ let errorMessage = 'Authentication failed';
456
+ const failedMethods = [];
457
+ if (apiKeyFailureReason) {
458
+ failedMethods.push(`API key (${apiKeyFailureReason})`);
459
+ }
460
+ if (bearerTokenFailureReason) {
461
+ failedMethods.push(`Bearer token (${bearerTokenFailureReason})`);
462
+ }
463
+ if (failedMethods.length > 0) {
464
+ if (failedMethods.length === 1) {
465
+ errorMessage = `Authentication failed: ${failedMethods[0]}`;
466
+ }
467
+ else {
468
+ errorMessage = `Authentication failed: Both ${failedMethods.join(' and ')}`;
469
+ }
470
+ }
471
+ else if (authAttempts.length === 0) {
472
+ errorMessage = 'No valid authentication provided. Provide X-API-Key, Device-Token, or Authorization header';
473
+ }
474
+ else {
475
+ errorMessage = 'All provided authentication methods failed';
476
+ }
477
+ console.error('❌ [Authentication Flow] Step 3a: No authentication method succeeded', {
478
+ attemptedMethods: authAttempts,
479
+ failedMethods: authFailures,
480
+ errorMessage
481
+ });
482
+ logger.warn('[Authentication Flow] Step 3a: No authentication method succeeded', {
483
+ attemptedMethods: authAttempts,
484
+ failedMethods: authFailures
485
+ });
238
486
  // Log audit event for missing authentication
239
487
  if (auditService) {
240
488
  auditService.logAuditEvent(req, {
241
489
  event_type: undefined, // Will be determined automatically
242
490
  response_status: 401,
243
- error_message: 'No valid token provided. Provide either Device-Token or Authorization header',
244
- error_code: 'NO_AUTH_TOKEN',
491
+ error_message: errorMessage,
492
+ error_code: authAttempts.length === 0 ? 'NO_AUTH_TOKEN' : 'ALL_AUTH_FAILED',
245
493
  }).catch((err) => {
246
494
  logger.debug('Failed to log audit event', { error: err.message });
247
495
  });
248
496
  }
249
- return res.status(401).json({ message: 'No valid token provided. Provide either Device-Token or Authorization header' });
497
+ return res.status(401).json({
498
+ message: errorMessage,
499
+ failedMethods: authFailures.map(f => f.method),
500
+ attemptedMethods: authAttempts
501
+ });
502
+ }
503
+ // Step 3b: Set auth type based on what was authenticated
504
+ // Priority: API Key + Device + Officer > API Key + Officer > API Key + Device > API Key > Device + Officer > Device > Officer
505
+ console.log('[Authentication Flow] Step 3b: Setting authentication type');
506
+ logger.info('[Authentication Flow] Step 3b: Setting authentication type');
507
+ // Check for all combinations including device + API key
508
+ if (finalApiKeyAuthenticated && deviceAuthenticated && officerAuthenticated) {
509
+ // All three: API Key + Device + Officer
510
+ req.authType = 'api_key_and_officer'; // Use existing type, device info will be preserved
511
+ const apiKey = req.apiKey;
512
+ const device = req.device;
513
+ const officer = req.officer;
514
+ console.log('═══════════════════════════════════════════════════════════');
515
+ console.log('✅ [Authentication Flow] Step 3c: Authentication type set to api_key_and_officer (with device)');
516
+ console.log(' 🔑 API Key Being Used:', {
517
+ id: apiKey === null || apiKey === void 0 ? void 0 : apiKey.id,
518
+ microservice: apiKey === null || apiKey === void 0 ? void 0 : apiKey.microservice,
519
+ keyPreview: (apiKey === null || apiKey === void 0 ? void 0 : apiKey.key) ? apiKey.key.substring(0, 15) + '...' : 'N/A',
520
+ isActive: apiKey === null || apiKey === void 0 ? void 0 : apiKey.is_active,
521
+ expiresAt: (apiKey === null || apiKey === void 0 ? void 0 : apiKey.expires_at) ? new Date(apiKey.expires_at).toISOString() : 'Never'
522
+ });
523
+ console.log(' 📱 Device Being Used:', {
524
+ id: device === null || device === void 0 ? void 0 : device.id,
525
+ device_id: device === null || device === void 0 ? void 0 : device.device_id,
526
+ device_name: device === null || device === void 0 ? void 0 : device.device_name
527
+ });
528
+ console.log(' 👤 Officer Using This API Key:', {
529
+ id: officer === null || officer === void 0 ? void 0 : officer.id,
530
+ name: officer === null || officer === void 0 ? void 0 : officer.name,
531
+ serviceNumber: officer === null || officer === void 0 ? void 0 : officer.service_number,
532
+ email: officer === null || officer === void 0 ? void 0 : officer.email
533
+ });
534
+ console.log('═══════════════════════════════════════════════════════════');
535
+ logger.info('[Authentication Flow] Step 3c: Authentication type set to api_key_and_officer (with device)', {
536
+ apiKeyId: apiKey === null || apiKey === void 0 ? void 0 : apiKey.id,
537
+ microservice: apiKey === null || apiKey === void 0 ? void 0 : apiKey.microservice,
538
+ deviceId: device === null || device === void 0 ? void 0 : device.id,
539
+ deviceDeviceId: device === null || device === void 0 ? void 0 : device.device_id,
540
+ officerId: officer === null || officer === void 0 ? void 0 : officer.id,
541
+ officerName: officer === null || officer === void 0 ? void 0 : officer.name,
542
+ officerServiceNumber: officer === null || officer === void 0 ? void 0 : officer.service_number
543
+ });
544
+ }
545
+ else if (finalApiKeyAuthenticated && deviceAuthenticated) {
546
+ // API Key + Device (no officer)
547
+ req.authType = 'api_key'; // Use api_key type, device info will be preserved
548
+ const apiKey = req.apiKey;
549
+ const device = req.device;
550
+ console.log('═══════════════════════════════════════════════════════════');
551
+ console.log('✅ [Authentication Flow] Step 3c: Authentication type set to api_key (with device)');
552
+ console.log(' 🔑 API Key Being Used:', {
553
+ id: apiKey === null || apiKey === void 0 ? void 0 : apiKey.id,
554
+ microservice: apiKey === null || apiKey === void 0 ? void 0 : apiKey.microservice,
555
+ keyPreview: (apiKey === null || apiKey === void 0 ? void 0 : apiKey.key) ? apiKey.key.substring(0, 15) + '...' : 'N/A',
556
+ isActive: apiKey === null || apiKey === void 0 ? void 0 : apiKey.is_active,
557
+ expiresAt: (apiKey === null || apiKey === void 0 ? void 0 : apiKey.expires_at) ? new Date(apiKey.expires_at).toISOString() : 'Never'
558
+ });
559
+ console.log(' 📱 Device Being Used:', {
560
+ id: device === null || device === void 0 ? void 0 : device.id,
561
+ device_id: device === null || device === void 0 ? void 0 : device.device_id,
562
+ device_name: device === null || device === void 0 ? void 0 : device.device_name
563
+ });
564
+ console.log(' ⚠️ No Officer Authenticated (API key + device only)');
565
+ console.log('═══════════════════════════════════════════════════════════');
566
+ logger.info('[Authentication Flow] Step 3c: Authentication type set to api_key (with device)', {
567
+ apiKeyId: apiKey === null || apiKey === void 0 ? void 0 : apiKey.id,
568
+ microservice: apiKey === null || apiKey === void 0 ? void 0 : apiKey.microservice,
569
+ deviceId: device === null || device === void 0 ? void 0 : device.id,
570
+ deviceDeviceId: device === null || device === void 0 ? void 0 : device.device_id
571
+ });
572
+ // Clear officer if not authenticated
573
+ req.officer = null;
574
+ req.user = null;
575
+ }
576
+ else if (finalApiKeyAuthenticated && officerAuthenticated) {
577
+ req.authType = 'api_key_and_officer';
578
+ const apiKey = req.apiKey;
579
+ const officer = req.officer;
580
+ console.log('═══════════════════════════════════════════════════════════');
581
+ console.log('✅ [Authentication Flow] Step 3c: Authentication type set to api_key_and_officer');
582
+ console.log(' 🔑 API Key Being Used:', {
583
+ id: apiKey === null || apiKey === void 0 ? void 0 : apiKey.id,
584
+ microservice: apiKey === null || apiKey === void 0 ? void 0 : apiKey.microservice,
585
+ keyPreview: (apiKey === null || apiKey === void 0 ? void 0 : apiKey.key) ? apiKey.key.substring(0, 15) + '...' : 'N/A',
586
+ isActive: apiKey === null || apiKey === void 0 ? void 0 : apiKey.is_active,
587
+ expiresAt: (apiKey === null || apiKey === void 0 ? void 0 : apiKey.expires_at) ? new Date(apiKey.expires_at).toISOString() : 'Never'
588
+ });
589
+ console.log(' 👤 Officer Using This API Key:', {
590
+ id: officer === null || officer === void 0 ? void 0 : officer.id,
591
+ name: officer === null || officer === void 0 ? void 0 : officer.name,
592
+ serviceNumber: officer === null || officer === void 0 ? void 0 : officer.service_number,
593
+ email: officer === null || officer === void 0 ? void 0 : officer.email
594
+ });
595
+ console.log('═══════════════════════════════════════════════════════════');
596
+ logger.info('[Authentication Flow] Step 3c: Authentication type set to api_key_and_officer', {
597
+ apiKeyId: apiKey === null || apiKey === void 0 ? void 0 : apiKey.id,
598
+ microservice: apiKey === null || apiKey === void 0 ? void 0 : apiKey.microservice,
599
+ keyPreview: (apiKey === null || apiKey === void 0 ? void 0 : apiKey.key) ? apiKey.key.substring(0, 15) + '...' : 'N/A',
600
+ officerId: officer === null || officer === void 0 ? void 0 : officer.id,
601
+ officerName: officer === null || officer === void 0 ? void 0 : officer.name,
602
+ officerServiceNumber: officer === null || officer === void 0 ? void 0 : officer.service_number
603
+ });
604
+ // Officer info is already set from officer authentication above
250
605
  }
251
- // Set auth type
252
- if (deviceAuthenticated && officerAuthenticated) {
606
+ else if (finalApiKeyAuthenticated) {
607
+ req.authType = 'api_key';
608
+ const apiKey = req.apiKey;
609
+ console.log('═══════════════════════════════════════════════════════════');
610
+ console.log('✅ [Authentication Flow] Step 3c: Authentication type set to api_key');
611
+ console.log(' 🔑 API Key Being Used:', {
612
+ id: apiKey === null || apiKey === void 0 ? void 0 : apiKey.id,
613
+ microservice: apiKey === null || apiKey === void 0 ? void 0 : apiKey.microservice,
614
+ keyPreview: (apiKey === null || apiKey === void 0 ? void 0 : apiKey.key) ? apiKey.key.substring(0, 15) + '...' : 'N/A',
615
+ isActive: apiKey === null || apiKey === void 0 ? void 0 : apiKey.is_active,
616
+ expiresAt: (apiKey === null || apiKey === void 0 ? void 0 : apiKey.expires_at) ? new Date(apiKey.expires_at).toISOString() : 'Never'
617
+ });
618
+ console.log(' ⚠️ No Officer Authenticated (API key only - service-to-service)');
619
+ console.log('═══════════════════════════════════════════════════════════');
620
+ logger.info('[Authentication Flow] Step 3c: Authentication type set to api_key', {
621
+ apiKeyId: apiKey === null || apiKey === void 0 ? void 0 : apiKey.id,
622
+ microservice: apiKey === null || apiKey === void 0 ? void 0 : apiKey.microservice,
623
+ keyPreview: (apiKey === null || apiKey === void 0 ? void 0 : apiKey.key) ? apiKey.key.substring(0, 15) + '...' : 'N/A'
624
+ });
625
+ // Clear officer if only API key is authenticated
626
+ req.officer = null;
627
+ req.user = null;
628
+ }
629
+ else if (deviceAuthenticated && officerAuthenticated) {
253
630
  req.authType = 'both';
631
+ console.log('[Authentication Flow] Step 3c: Authentication type set to both (device + officer)', {
632
+ deviceId: (_a = req.device) === null || _a === void 0 ? void 0 : _a.id,
633
+ officerId: (_b = req.officer) === null || _b === void 0 ? void 0 : _b.id
634
+ });
635
+ logger.info('[Authentication Flow] Step 3c: Authentication type set to both (device + officer)', {
636
+ deviceId: (_c = req.device) === null || _c === void 0 ? void 0 : _c.id,
637
+ officerId: (_d = req.officer) === null || _d === void 0 ? void 0 : _d.id
638
+ });
254
639
  }
255
640
  else if (deviceAuthenticated) {
256
641
  req.authType = 'device';
642
+ console.log('[Authentication Flow] Step 3c: Authentication type set to device', {
643
+ deviceId: (_e = req.device) === null || _e === void 0 ? void 0 : _e.id
644
+ });
645
+ logger.info('[Authentication Flow] Step 3c: Authentication type set to device', {
646
+ deviceId: (_f = req.device) === null || _f === void 0 ? void 0 : _f.id
647
+ });
257
648
  req.user = null;
258
649
  }
259
650
  else {
260
651
  req.authType = 'officer';
652
+ console.log('[Authentication Flow] Step 3c: Authentication type set to officer', {
653
+ officerId: (_g = req.officer) === null || _g === void 0 ? void 0 : _g.id
654
+ });
655
+ logger.info('[Authentication Flow] Step 3c: Authentication type set to officer', {
656
+ officerId: (_h = req.officer) === null || _h === void 0 ? void 0 : _h.id
657
+ });
261
658
  req.device = null;
262
659
  }
263
- (0, logMode_1.devLog)('Authentication successful', {
660
+ // Log authentication success with consistent format for all auth types
661
+ const authInfo = {
264
662
  authType: req.authType,
265
- device: req.device,
266
- officer: req.officer,
267
- apiKey: req.apiKey,
268
- microservice: req.microservice,
269
- });
663
+ };
664
+ // Enhanced API key logging
665
+ if (req.apiKey) {
666
+ const apiKey = req.apiKey;
667
+ authInfo.apiKey = {
668
+ id: apiKey.id,
669
+ microservice: apiKey.microservice,
670
+ keyPreview: apiKey.key ? apiKey.key.substring(0, 15) + '...' : 'N/A',
671
+ isActive: apiKey.is_active,
672
+ expiresAt: apiKey.expires_at || 'Never'
673
+ };
674
+ authInfo.sourceMicroservice = apiKey.microservice;
675
+ // Prominent console log for API key usage
676
+ console.log('═══════════════════════════════════════════════════════════');
677
+ console.log('🔑 API KEY AUTHENTICATION DETAILS:');
678
+ console.log(' API Key ID:', apiKey.id);
679
+ console.log(' Microservice:', apiKey.microservice);
680
+ console.log(' API Key Preview:', apiKey.key ? apiKey.key.substring(0, 15) + '...' : 'N/A');
681
+ console.log(' Is Active:', apiKey.is_active);
682
+ console.log(' Expires At:', apiKey.expires_at ? new Date(apiKey.expires_at).toISOString() : 'Never');
683
+ if (req.device) {
684
+ console.log(' 📱 Device Also Authenticated:');
685
+ console.log(' Device ID:', req.device.id);
686
+ console.log(' Device Device ID:', req.device.device_id);
687
+ console.log(' Device Name:', req.device.device_name);
688
+ }
689
+ if (req.officer) {
690
+ console.log(' ✅ Officer Also Authenticated:');
691
+ console.log(' Officer ID:', req.officer.id);
692
+ console.log(' Officer Name:', req.officer.name);
693
+ console.log(' Service Number:', req.officer.service_number);
694
+ }
695
+ else {
696
+ console.log(' ⚠️ No Officer Authenticated (API key only)');
697
+ }
698
+ console.log('═══════════════════════════════════════════════════════════');
699
+ }
700
+ if (req.device) {
701
+ authInfo.device = {
702
+ id: req.device.id,
703
+ device_id: req.device.device_id,
704
+ device_name: req.device.device_name,
705
+ };
706
+ }
707
+ if (req.officer) {
708
+ authInfo.officer = {
709
+ id: req.officer.id,
710
+ name: req.officer.name,
711
+ service_number: req.officer.service_number,
712
+ };
713
+ }
714
+ (0, logMode_1.devLog)('Authentication successful', authInfo);
715
+ // Enhanced console logging for authentication summary
716
+ console.log('═══════════════════════════════════════════════════════════');
717
+ console.log('✅ AUTHENTICATION SUMMARY:');
718
+ console.log(' Auth Type:', req.authType);
719
+ console.log(' Endpoint:', req.originalUrl || req.url);
720
+ console.log(' Method:', req.method);
721
+ if (req.apiKey) {
722
+ console.log(' 🔑 API Key:', {
723
+ id: req.apiKey.id,
724
+ microservice: req.apiKey.microservice,
725
+ keyPreview: req.apiKey.key ? req.apiKey.key.substring(0, 15) + '...' : 'N/A'
726
+ });
727
+ }
728
+ if (req.device) {
729
+ console.log(' 📱 Device:', {
730
+ id: req.device.id,
731
+ device_id: req.device.device_id,
732
+ device_name: req.device.device_name
733
+ });
734
+ }
735
+ if (req.officer) {
736
+ console.log(' 👤 Officer:', {
737
+ id: req.officer.id,
738
+ name: req.officer.name,
739
+ service_number: req.officer.service_number,
740
+ email: req.officer.email
741
+ });
742
+ }
743
+ else if (req.authType === 'api_key' || req.authType === 'api_key_and_officer') {
744
+ console.log(' ⚠️ No Officer Information (API key authentication only)');
745
+ }
746
+ console.log('═══════════════════════════════════════════════════════════');
270
747
  // Log audit event for endpoint access (non-blocking)
748
+ // This now handles ALL authentication types consistently: API Key, Device, Officer, or Both
271
749
  if (auditService) {
272
750
  (0, logMode_1.devLog)('Calling audit service for endpoint access', {
273
751
  path: req.path,
@@ -275,6 +753,9 @@ const createAuthenticateDeviceOrOfficer = (deps) => {
275
753
  baseUrl: req.baseUrl,
276
754
  method: req.method,
277
755
  authType: req.authType,
756
+ hasApiKey: !!req.apiKey,
757
+ hasDevice: !!req.device,
758
+ hasOfficer: !!req.officer,
278
759
  });
279
760
  auditService.logAuditEvent(req, {
280
761
  event_type: undefined, // Will be determined automatically
@@ -285,6 +766,7 @@ const createAuthenticateDeviceOrOfficer = (deps) => {
285
766
  stack: err.stack,
286
767
  path: req.path,
287
768
  originalUrl: req.originalUrl,
769
+ authType: req.authType,
288
770
  });
289
771
  (0, logMode_1.devError)('Audit logging failed', err);
290
772
  });