const jwt = require('jsonwebtoken'); import { Request, Response, NextFunction } from 'express'; import { OAuth2Client } from 'google-auth-library'; import dataService from './unifiedDataService'; // Simplified authentication service - removes excessive logging and complexity class AuthService { private jwtSecret: string; private jwtExpiry: string = '24h'; private googleClient: OAuth2Client; constructor() { // Auto-generate a secure JWT secret if not provided if (process.env.JWT_SECRET) { this.jwtSecret = process.env.JWT_SECRET; console.log('Using JWT_SECRET from environment'); } else { // Generate a cryptographically secure random secret const crypto = require('crypto'); this.jwtSecret = crypto.randomBytes(64).toString('hex'); console.log('Generated new JWT_SECRET (this will change on restart)'); console.log('To persist sessions across restarts, set JWT_SECRET in .env'); } // Initialize Google OAuth client this.googleClient = new OAuth2Client(process.env.GOOGLE_CLIENT_ID); } // Generate JWT token generateToken(user: any): string { const payload = { id: user.id, email: user.email, role: user.role }; return jwt.sign(payload, this.jwtSecret, { expiresIn: this.jwtExpiry }) as string; } // Verify Google ID token from frontend async verifyGoogleToken(credential: string): Promise<{ user: any; token: string }> { try { // Verify the token with Google const ticket = await this.googleClient.verifyIdToken({ idToken: credential, audience: process.env.GOOGLE_CLIENT_ID, }); const payload = ticket.getPayload(); if (!payload || !payload.email) { throw new Error('Invalid token payload'); } // Find or create user let user = await dataService.getUserByEmail(payload.email); if (!user) { // Auto-create user with coordinator role user = await dataService.createUser({ email: payload.email, name: payload.name || payload.email, role: 'coordinator', googleId: payload.sub }); } // Generate our JWT const token = this.generateToken(user); return { user, token }; } catch (error) { console.error('Token verification error:', error); throw new Error('Failed to verify Google token'); } } // Verify JWT token verifyToken(token: string): any { try { return jwt.verify(token, this.jwtSecret); } catch (error) { return null; } } // Middleware to check authentication requireAuth = async (req: Request & { user?: any }, res: Response, next: NextFunction) => { const token = req.headers.authorization?.replace('Bearer ', ''); if (!token) { return res.status(401).json({ error: 'Authentication required' }); } const decoded = this.verifyToken(token); if (!decoded) { return res.status(401).json({ error: 'Invalid or expired token' }); } // Get fresh user data const user = await dataService.getUserById(decoded.id); if (!user) { return res.status(401).json({ error: 'User not found' }); } req.user = user; next(); }; // Middleware to check role requireRole = (roles: string[]) => { return (req: Request & { user?: any }, res: Response, next: NextFunction) => { if (!req.user) { return res.status(401).json({ error: 'Authentication required' }); } if (!roles.includes(req.user.role)) { return res.status(403).json({ error: 'Insufficient permissions' }); } next(); }; }; // Google OAuth helpers getGoogleAuthUrl(): string { if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_REDIRECT_URI) { throw new Error('Google OAuth not configured. Please set GOOGLE_CLIENT_ID and GOOGLE_REDIRECT_URI in .env file'); } const params = new URLSearchParams({ client_id: process.env.GOOGLE_CLIENT_ID, redirect_uri: process.env.GOOGLE_REDIRECT_URI, response_type: 'code', scope: 'email profile', access_type: 'offline', prompt: 'consent' }); return `https://accounts.google.com/o/oauth2/v2/auth?${params}`; } async exchangeGoogleCode(code: string): Promise { const response = await fetch('https://oauth2.googleapis.com/token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code, client_id: process.env.GOOGLE_CLIENT_ID, client_secret: process.env.GOOGLE_CLIENT_SECRET, redirect_uri: process.env.GOOGLE_REDIRECT_URI, grant_type: 'authorization_code' }) }); if (!response.ok) { throw new Error('Failed to exchange authorization code'); } return response.json(); } async getGoogleUserInfo(accessToken: string): Promise { const response = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', { headers: { Authorization: `Bearer ${accessToken}` } }); if (!response.ok) { throw new Error('Failed to get user info'); } return response.json(); } // Simplified login/signup async handleGoogleAuth(code: string): Promise<{ user: any; token: string }> { // Exchange code for tokens const tokens = await this.exchangeGoogleCode(code); // Get user info const googleUser = await this.getGoogleUserInfo(tokens.access_token); // Find or create user let user = await dataService.getUserByEmail(googleUser.email); if (!user) { // Auto-create user with coordinator role user = await dataService.createUser({ email: googleUser.email, name: googleUser.name, role: 'coordinator', googleId: googleUser.id }); } // Generate JWT const token = this.generateToken(user); return { user, token }; } } export default new AuthService();