const express = require('express'); const router = express.Router(); const passport = require('passport'); const GoogleStrategy = require('passport-google-oauth20').Strategy; const jwt = require('jsonwebtoken'); const db = require('./db'); const { generateUniqueId } = require('../shared/utils/crypto'); // Check user session (GET) router.get('/api/auth/session', (req, res) => { const token = req.cookies.token; if (!token) { return res.json({ userId: null }); } try { // Verify the token const decoded = jwt.verify(token, process.env.JWT_SECRET); res.json({ userId: decoded.userId }); } catch (error) { console.error('Invalid token:', error); res.status(401).json({ userId: null, error: 'Invalid token' }); } }); // Sign in anonymously (POST) router.post('/api/auth/signin-anonymous', async (req, res) => { const userId = generateUniqueId(); // Generate unique ID for anonymous user try { // Insert the anonymous user into the database (no sensitive data, just ID) await db.query('INSERT INTO users (id, provider) VALUES (?, ?)', [ userId, 'anonymous', ]); // Generate JWT token for the anonymous user const token = jwt.sign( { userId, provider: 'anonymous' }, process.env.JWT_SECRET, { expiresIn: '1h' } ); // Set the JWT in an httpOnly cookie for better security res.cookie('token', token, { httpOnly: true }); res.status(200).json({ message: 'Anonymous user signed in' }); } catch (error) { console.error('Error creating anonymous user:', error); res.status(500).json({ error: 'Server error' }); } }); router.post('/api/auth/refresh-token', (req, res) => { const refreshToken = req.cookies.refreshToken; if (!refreshToken) return res.status(403).send('Refresh token required'); // Verify the refresh token and issue a new JWT try { const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET); const newToken = jwt.sign( { userId: decoded.userId }, process.env.JWT_SECRET, { expiresIn: '1h' } ); res.cookie('token', newToken, { httpOnly: true }); res.json({ message: 'Token refreshed' }); } catch (error) { res.status(403).send('Invalid refresh token'); } }); // Initialize Passport router.use(passport.initialize()); // Google OAuth Strategy passport.use( new GoogleStrategy( { clientID: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, callbackURL: '/api/auth/google/callback', }, async (accessToken, refreshToken, profile, done) => { const googleId = profile.id; // This is the unique ID from Google try { // Insert the Google user into the database if they don't exist await db.query( 'INSERT INTO users (id, provider) VALUES (?, ?) ON DUPLICATE KEY UPDATE id = id', [googleId, 'google'] ); // Generate JWT token for the Google user const token = jwt.sign( { userId: googleId, provider: 'google' }, process.env.JWT_SECRET, { expiresIn: '1h' } ); // Pass the token back return done(null, token); } catch (error) { return done(error); } } ) ); // Start Google OAuth login router.get( '/api/auth/google', passport.authenticate('google', { scope: ['profile', 'email'] }) ); // Callback route for Google OAuth router.get( '/api/auth/google/callback', passport.authenticate('google', { failureRedirect: '/' }), (req, res) => { // After successful authentication, generate the JWT (which is in req.user) const token = req.user; // Passport's Google strategy should return the JWT // Set the JWT in an httpOnly cookie res.cookie('token', token, { httpOnly: true }); // Redirect to the frontend dashboard or a secure page without the token in the URL res.redirect('/dashboard'); } ); // Example of a protected route (no need for query token) router.get('/dashboard', (req, res) => { // Extract token from cookies (JWT is automatically sent as a cookie) const token = req.cookies.token; if (!token) { return res.status(403).send('Token is required'); } try { // Verify the token and decode it const decoded = jwt.verify(token, process.env.JWT_SECRET); res.send(`Welcome, user with ID: ${decoded.userId}`); } catch (err) { res.status(403).send('Invalid token'); } }); module.exports = router;