Backup: 2025-06-07 19:31 - Dockerhub prep

[Restore from backup: vip-coordinator-backup-2025-06-07-19-31-dockerhub-prep]
This commit is contained in:
2025-06-07 19:31:00 +02:00
parent ae3702c3b1
commit 8fb00ec041
6 changed files with 224 additions and 42 deletions

View File

@@ -1,49 +1,16 @@
import jwt from 'jsonwebtoken';
import jwtKeyManager, { User } from '../services/jwtKeyManager';
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
// JWT Key Manager now handles all token operations with automatic rotation
// No more static JWT_SECRET needed!
export interface User {
id: string;
google_id: string;
email: string;
name: string;
profile_picture_url?: string;
role: 'driver' | 'coordinator' | 'administrator';
created_at?: string;
last_login?: string;
is_active?: boolean;
updated_at?: string;
}
export { User } from '../services/jwtKeyManager';
export function generateToken(user: User): string {
return jwt.sign(
{
id: user.id,
google_id: user.google_id,
email: user.email,
name: user.name,
profile_picture_url: user.profile_picture_url,
role: user.role
},
JWT_SECRET,
{ expiresIn: '24h' }
);
return jwtKeyManager.generateToken(user);
}
export function verifyToken(token: string): User | null {
try {
const decoded = jwt.verify(token, JWT_SECRET) as any;
return {
id: decoded.id,
google_id: decoded.google_id,
email: decoded.email,
name: decoded.name,
profile_picture_url: decoded.profile_picture_url,
role: decoded.role
};
} catch (error) {
return null;
}
return jwtKeyManager.verifyToken(token);
}
// Simple Google OAuth2 client using fetch

View File

@@ -8,6 +8,7 @@ import scheduleValidationService from './services/scheduleValidationService';
import FlightTrackingScheduler from './services/flightTrackingScheduler';
import enhancedDataService from './services/enhancedDataService';
import databaseService from './services/databaseService';
import jwtKeyManager from './services/jwtKeyManager'; // Initialize JWT Key Manager
dotenv.config();
@@ -745,6 +746,34 @@ app.post('/api/admin/test-api/:apiType', async (req: Request, res: Response) =>
}
});
// JWT Key Management endpoints (admin only)
app.get('/api/admin/jwt-status', requireAuth, requireRole(['administrator']), (req: Request, res: Response) => {
const jwtKeyManager = require('./services/jwtKeyManager').default;
const status = jwtKeyManager.getStatus();
res.json({
keyRotationEnabled: true,
rotationInterval: '24 hours',
gracePeriod: '24 hours',
...status,
message: 'JWT keys are automatically rotated every 24 hours for enhanced security'
});
});
app.post('/api/admin/jwt-rotate', requireAuth, requireRole(['administrator']), (req: Request, res: Response) => {
const jwtKeyManager = require('./services/jwtKeyManager').default;
try {
jwtKeyManager.forceRotation();
res.json({
success: true,
message: 'JWT key rotation triggered successfully. New tokens will use the new key.'
});
} catch (error) {
res.status(500).json({ error: 'Failed to rotate JWT keys' });
}
});
// Initialize database and start server
async function startServer() {
try {

View File

@@ -0,0 +1,183 @@
import crypto from 'crypto';
import jwt from 'jsonwebtoken';
export interface User {
id: string;
google_id: string;
email: string;
name: string;
profile_picture_url?: string;
role: 'driver' | 'coordinator' | 'administrator';
created_at?: string;
last_login?: string;
is_active?: boolean;
updated_at?: string;
}
class JWTKeyManager {
private currentSecret: string;
private previousSecret: string | null = null;
private rotationInterval: NodeJS.Timeout | null = null;
private gracePeriodTimeout: NodeJS.Timeout | null = null;
constructor() {
console.log('🔑 Initializing JWT Key Manager with automatic rotation');
this.currentSecret = this.generateSecret();
this.startRotation();
}
private generateSecret(): string {
const secret = crypto.randomBytes(64).toString('hex');
console.log('🔄 Generated new JWT signing key (length:', secret.length, 'chars)');
return secret;
}
private startRotation() {
// Rotate every 24 hours (86400000 ms)
this.rotationInterval = setInterval(() => {
this.rotateKey();
}, 24 * 60 * 60 * 1000);
console.log('⏰ JWT key rotation scheduled every 24 hours');
// Also rotate on startup after 1 hour to test the system
setTimeout(() => {
console.log('🧪 Performing initial key rotation test...');
this.rotateKey();
}, 60 * 60 * 1000); // 1 hour
}
private rotateKey() {
console.log('🔄 Rotating JWT signing key...');
// Store current secret as previous
this.previousSecret = this.currentSecret;
// Generate new current secret
this.currentSecret = this.generateSecret();
console.log('✅ JWT key rotation completed. Grace period: 24 hours');
// Clear any existing grace period timeout
if (this.gracePeriodTimeout) {
clearTimeout(this.gracePeriodTimeout);
}
// Clean up previous secret after 24 hours (grace period)
this.gracePeriodTimeout = setTimeout(() => {
this.previousSecret = null;
console.log('🧹 Grace period ended. Previous JWT key cleaned up');
}, 24 * 60 * 60 * 1000);
}
generateToken(user: User): string {
const payload = {
id: user.id,
google_id: user.google_id,
email: user.email,
name: user.name,
profile_picture_url: user.profile_picture_url,
role: user.role,
iat: Math.floor(Date.now() / 1000) // Issued at time
};
return jwt.sign(payload, this.currentSecret, {
expiresIn: '24h',
issuer: 'vip-coordinator',
audience: 'vip-coordinator-users'
});
}
verifyToken(token: string): User | null {
try {
// Try current secret first
const decoded = jwt.verify(token, this.currentSecret, {
issuer: 'vip-coordinator',
audience: 'vip-coordinator-users'
}) as any;
return {
id: decoded.id,
google_id: decoded.google_id,
email: decoded.email,
name: decoded.name,
profile_picture_url: decoded.profile_picture_url,
role: decoded.role
};
} catch (error) {
// Try previous secret during grace period
if (this.previousSecret) {
try {
const decoded = jwt.verify(token, this.previousSecret, {
issuer: 'vip-coordinator',
audience: 'vip-coordinator-users'
}) as any;
console.log('🔄 Token verified using previous key (grace period)');
return {
id: decoded.id,
google_id: decoded.google_id,
email: decoded.email,
name: decoded.name,
profile_picture_url: decoded.profile_picture_url,
role: decoded.role
};
} catch (gracePeriodError) {
console.log('❌ Token verification failed with both current and previous keys');
return null;
}
}
console.log('❌ Token verification failed:', error instanceof Error ? error.message : 'Unknown error');
return null;
}
}
// Get status for monitoring/debugging
getStatus() {
return {
hasCurrentKey: !!this.currentSecret,
hasPreviousKey: !!this.previousSecret,
rotationActive: !!this.rotationInterval,
gracePeriodActive: !!this.gracePeriodTimeout
};
}
// Cleanup on shutdown
destroy() {
console.log('🛑 Shutting down JWT Key Manager...');
if (this.rotationInterval) {
clearInterval(this.rotationInterval);
this.rotationInterval = null;
}
if (this.gracePeriodTimeout) {
clearTimeout(this.gracePeriodTimeout);
this.gracePeriodTimeout = null;
}
console.log('✅ JWT Key Manager shutdown complete');
}
// Manual rotation for testing/emergency
forceRotation() {
console.log('🚨 Manual key rotation triggered');
this.rotateKey();
}
}
// Singleton instance
export const jwtKeyManager = new JWTKeyManager();
// Graceful shutdown handling
process.on('SIGTERM', () => {
jwtKeyManager.destroy();
});
process.on('SIGINT', () => {
jwtKeyManager.destroy();
});
export default jwtKeyManager;