← back to Dear Bubbe Admin

backend/routes/auth.js

218 lines

const express = require('express')
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const rateLimit = require('express-rate-limit')
const router = express.Router()

// Admin credentials (in production, store in database)
const ADMIN_CREDENTIALS = [
  {
    username: 'admin',
    password: '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
    role: 'super_admin',
    id: 'admin_1'
  },
  {
    username: 'dw-admin',
    password: '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password  
    role: 'admin',
    id: 'admin_dw'
  },
  {
    username: 'monitor',
    password: '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
    role: 'monitor',
    id: 'admin_monitor'
  }
]

const JWT_SECRET = process.env.JWT_SECRET || 'dear-bubbe-admin-secret-key-change-in-production'

// Rate limiting for auth endpoints
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // limit each IP to 5 requests per windowMs
  message: 'Too many login attempts, please try again later',
  standardHeaders: true,
  legacyHeaders: false
})

// Login endpoint
router.post('/login', authLimiter, async (req, res) => {
  try {
    const { username, password } = req.body
    
    if (!username || !password) {
      return res.status(400).json({ 
        success: false, 
        error: 'Username and password required' 
      })
    }
    
    // Find admin user
    const admin = ADMIN_CREDENTIALS.find(a => a.username === username)
    if (!admin) {
      return res.status(401).json({ 
        success: false, 
        error: 'Invalid credentials' 
      })
    }
    
    // Check password
    const passwordMatch = await bcrypt.compare(password, admin.password)
    if (!passwordMatch) {
      return res.status(401).json({ 
        success: false, 
        error: 'Invalid credentials' 
      })
    }
    
    // Generate JWT token
    const token = jwt.sign(
      { 
        userId: admin.id,
        username: admin.username,
        role: admin.role 
      },
      JWT_SECRET,
      { expiresIn: '24h' }
    )
    
    // Log successful login
    console.log(`Admin login successful: ${username} (${admin.role}) from ${req.ip}`)
    
    res.json({
      success: true,
      token,
      user: {
        id: admin.id,
        username: admin.username,
        role: admin.role
      },
      expiresIn: '24h'
    })
    
  } catch (error) {
    console.error('Login error:', error)
    res.status(500).json({ 
      success: false, 
      error: 'Authentication failed' 
    })
  }
})

// Verify token endpoint
router.post('/verify', (req, res) => {
  try {
    const { token } = req.body
    
    if (!token) {
      return res.status(400).json({ 
        success: false, 
        error: 'Token required' 
      })
    }
    
    const decoded = jwt.verify(token, JWT_SECRET)
    
    res.json({
      success: true,
      valid: true,
      user: {
        id: decoded.userId,
        username: decoded.username,
        role: decoded.role
      }
    })
    
  } catch (error) {
    res.status(401).json({ 
      success: false, 
      valid: false,
      error: 'Invalid token' 
    })
  }
})

// Refresh token endpoint
router.post('/refresh', (req, res) => {
  try {
    const { token } = req.body
    
    if (!token) {
      return res.status(400).json({ 
        success: false, 
        error: 'Token required' 
      })
    }
    
    const decoded = jwt.verify(token, JWT_SECRET)
    
    // Generate new token
    const newToken = jwt.sign(
      { 
        userId: decoded.userId,
        username: decoded.username,
        role: decoded.role 
      },
      JWT_SECRET,
      { expiresIn: '24h' }
    )
    
    res.json({
      success: true,
      token: newToken,
      expiresIn: '24h'
    })
    
  } catch (error) {
    res.status(401).json({ 
      success: false, 
      error: 'Invalid token' 
    })
  }
})

// Logout endpoint (mainly for logging purposes)
router.post('/logout', (req, res) => {
  // In a real app, you might blacklist the token
  console.log(`Admin logout from ${req.ip}`)
  res.json({ 
    success: true, 
    message: 'Logged out successfully' 
  })
})

// Get current user info
router.get('/me', (req, res) => {
  try {
    const authHeader = req.headers['authorization']
    const token = authHeader && authHeader.split(' ')[1]
    
    if (!token) {
      return res.status(401).json({ 
        success: false, 
        error: 'Access token required' 
      })
    }
    
    const decoded = jwt.verify(token, JWT_SECRET)
    
    res.json({
      success: true,
      user: {
        id: decoded.userId,
        username: decoded.username,
        role: decoded.role
      }
    })
    
  } catch (error) {
    res.status(401).json({ 
      success: false, 
      error: 'Invalid token' 
    })
  }
})

module.exports = router