const express = require("express"); const router = express.Router(); const bcrypt = require("bcrypt"); const jwt = require("jsonwebtoken"); const { query } = require("../db"); const JWT_SECRET = process.env.JWT_SECRET || "your-super-secret-jwt-key"; // Login router.post("/login", async (req, res) => { try { const { username, password } = req.body; if (!username || !password) { return res.status(400).json({ success: false, message: "Username and password are required", }); } // Find user in database (case-insensitive) const result = await query( "SELECT * FROM users WHERE LOWER(username) = LOWER($1)", [username], ); if (result.rows.length === 0) { return res .status(401) .json({ success: false, message: "Invalid credentials" }); } const user = result.rows[0]; // Check password const validPassword = await bcrypt.compare(password, user.password_hash); if (!validPassword) { return res .status(401) .json({ success: false, message: "Invalid credentials" }); } // Generate JWT token const token = jwt.sign( { id: user.id, username: user.username, role: user.role || "user", }, JWT_SECRET, { expiresIn: "7d" }, ); res.json({ success: true, token, user: { id: user.id, username: user.username, name: user.display_name || user.username, role: user.role || "user", }, }); } catch (err) { console.error("Login error:", err); res.status(500).json({ success: false, message: "Login failed" }); } }); // Verify token router.get("/verify", async (req, res) => { try { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith("Bearer ")) { return res .status(401) .json({ success: false, message: "No token provided" }); } const token = authHeader.split(" ")[1]; const decoded = jwt.verify(token, JWT_SECRET); // Get fresh user data const result = await query("SELECT * FROM users WHERE id = $1", [ decoded.id, ]); if (result.rows.length === 0) { return res .status(401) .json({ success: false, message: "User not found" }); } const user = result.rows[0]; res.json({ success: true, user: { id: user.id, username: user.username, name: user.display_name || user.username, role: user.role || "user", }, }); } catch (err) { console.error("Token verification error:", err); res.status(401).json({ success: false, message: "Invalid token" }); } }); // Logout (client-side token deletion, but we can track here if needed) router.post("/logout", (req, res) => { res.json({ success: true, message: "Logged out" }); }); // Get current user router.get("/me", async (req, res) => { try { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith("Bearer ")) { return res .status(401) .json({ success: false, message: "Not authenticated" }); } const token = authHeader.split(" ")[1]; const decoded = jwt.verify(token, JWT_SECRET); const result = await query("SELECT * FROM users WHERE id = $1", [ decoded.id, ]); if (result.rows.length === 0) { return res .status(401) .json({ success: false, message: "User not found" }); } const user = result.rows[0]; res.json({ success: true, user: { id: user.id, username: user.username, name: user.display_name || user.username, role: user.role || "user", biometric_enabled: user.biometric_enabled || false, }, }); } catch (err) { console.error("Get user error:", err); res.status(401).json({ success: false, message: "Invalid token" }); } }); // Biometric registration - store public key router.post("/biometric-register", async (req, res) => { try { const { username, credentialId, publicKey } = req.body; if (!username || !credentialId || !publicKey) { return res.status(400).json({ success: false, message: "Username, credential ID, and public key required", }); } // Update user with biometric credential const result = await query( `UPDATE users SET biometric_credential_id = $1, biometric_public_key = $2, biometric_enabled = true WHERE username = $3 RETURNING id, username`, [credentialId, publicKey, username.toLowerCase()], ); if (result.rows.length === 0) { return res .status(404) .json({ success: false, message: "User not found" }); } res.json({ success: true, message: "Biometric authentication registered successfully", }); } catch (err) { console.error("Biometric registration error:", err); res.status(500).json({ success: false, message: "Registration failed" }); } }); // Biometric login - verify assertion router.post("/biometric-login", async (req, res) => { try { const { username, assertion } = req.body; if (!username || !assertion) { return res.status(400).json({ success: false, message: "Username and assertion required", }); } // Find user with biometric enabled const result = await query( `SELECT * FROM users WHERE username = $1 AND biometric_enabled = true`, [username.toLowerCase()], ); if (result.rows.length === 0) { return res.status(401).json({ success: false, message: "Biometric authentication not enabled", }); } const user = result.rows[0]; // In a production environment, verify the assertion signature here // For now, we'll trust the client-side verification // TODO: Implement server-side WebAuthn assertion verification // Generate JWT token const token = jwt.sign( { id: user.id, username: user.username, role: user.role || "user", }, JWT_SECRET, { expiresIn: "7d" }, ); res.json({ success: true, token, user: { id: user.id, username: user.username, name: user.display_name || user.username, role: user.role || "user", }, }); } catch (err) { console.error("Biometric login error:", err); res.status(500).json({ success: false, message: "Biometric login failed" }); } }); module.exports = router;