import express from 'express'; import bcrypt from 'bcrypt'; import { prisma } from '../db'; import axios from 'axios'; import crypto from 'crypto'; import { encrypt, decrypt } from '../utils/encryption'; const router = express.Router(); const SALT_ROUNDS = 12; const SESSION_DURATION = 7 * 24 * 60 * 60 * 1000; // 7 days // Middleware to check if user is authenticated export async function requireAuth(req: any, res: any, next: any) { const sessionToken = req.cookies?.sessionToken; if (!sessionToken) { return res.status(401).json({ error: 'Not authenticated' }); } try { const session = await prisma.session.findUnique({ where: { token: sessionToken }, include: { user: true } }); if (!session || session.expiresAt < new Date()) { return res.status(401).json({ error: 'Session expired' }); } req.user = session.user; next(); } catch (error) { return res.status(500).json({ error: 'Authentication error' }); } } // Register new user router.post('/register', async (req, res) => { const { username, password } = req.body; if (!username || !password) { return res.status(400).json({ error: 'Username and password required' }); } if (password.length < 8) { return res.status(400).json({ error: 'Password must be at least 8 characters' }); } const normalizedUsername = username.toLowerCase().trim(); try { const existingUser = await prisma.user.findFirst({ where: { username: { equals: normalizedUsername, mode: 'insensitive' } } }); if (existingUser) { return res.status(409).json({ error: 'Username already exists' }); } // Verify credentials with Skyward via port 3000 API console.log('Verifying Skyward credentials...'); try { const authCheck = await axios.post('http://localhost:3000/check-auth', { username, password }, { timeout: 30000 }); if (!authCheck.data.success) { return res.status(401).json({ error: 'Invalid Skyward credentials. Please verify your username and password.' }); } console.log('Skyward credentials verified successfully'); } catch (authError: any) { if (authError.response?.status === 401) { return res.status(401).json({ error: 'Invalid Skyward credentials. Please verify your username and password.' }); } console.error('Skyward auth check failed:', authError.message); return res.status(500).json({ error: 'Unable to verify credentials with Skyward. Please try again.' }); } // Hash password for login authentication const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS); // Encrypt password for Skyward API calls const encryptedSkywardPassword = encrypt(password); const user = await prisma.user.create({ data: { username: normalizedUsername, password: hashedPassword, skywardPassword: encryptedSkywardPassword } }); console.log('User created successfully'); const sessionToken = crypto.randomBytes(32).toString('hex'); const expiresAt = new Date(Date.now() + SESSION_DURATION); await prisma.session.create({ data: { token: sessionToken, userId: user.id, expiresAt } }); res.cookie('sessionToken', sessionToken, { httpOnly: true, secure: false, // set to true in production sameSite: 'lax', maxAge: SESSION_DURATION }); return res.json({ success: true, user: { id: user.id, username: user.username } }); } catch (error) { console.error('Registration error:', error); return res.status(500).json({ error: 'Registration failed' }); } }); // Login existing user router.post('/login', async (req, res) => { const { username, password } = req.body; if (!username || !password) { return res.status(400).json({ error: 'Username and password required' }); } const normalizedUsername = username.toLowerCase().trim(); try { const user = await prisma.user.findFirst({ where: { username: { equals: normalizedUsername, mode: 'insensitive' } } }); if (!user) { return res.status(401).json({ error: 'Invalid credentials' }); } const validPassword = await bcrypt.compare(password, user.password); if (!validPassword) { return res.status(401).json({ error: 'Invalid credentials' }); } const sessionToken = crypto.randomBytes(32).toString('hex'); const expiresAt = new Date(Date.now() + SESSION_DURATION); await prisma.session.create({ data: { token: sessionToken, userId: user.id, expiresAt } }); res.cookie('sessionToken', sessionToken, { httpOnly: true, secure: false, sameSite: 'lax', maxAge: SESSION_DURATION }); return res.json({ success: true, user: { id: user.id, username: user.username } }); } catch (error) { console.error('Login error:', error); return res.status(500).json({ error: 'Login failed' }); } }); // Logout router.post('/logout', async (req, res) => { const sessionToken = req.cookies?.sessionToken; if (sessionToken) { try { await prisma.session.delete({ where: { token: sessionToken } }); } catch (error) { // Session might not exist } } res.clearCookie('sessionToken'); return res.json({ success: true }); }); // Check auth status router.get('/me', requireAuth, async (req: any, res) => { return res.json({ user: { id: req.user.id, username: req.user.username } }); }); export default router;