← 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