UNPKG

simple-express-react-auth

Version:

A lightweight authentication package for Express/React apps with single password protection using cookie-session

119 lines (101 loc) 3.54 kB
const express = require('express'); const bcrypt = require('bcrypt'); /** * Creates authentication middleware and routes for Express * @param {Object} options - Configuration options * @param {string} options.password - The plain text password to hash and use for authentication * @param {string} [options.hashedPassword] - Pre-hashed password (alternative to password) * @param {string} [options.loginPath='/login'] - Login endpoint path (relative to router mount) * @param {string} [options.logoutPath='/logout'] - Logout endpoint path (relative to router mount) * @param {string} [options.statusPath='/status'] - Status endpoint path (relative to router mount) * @param {number} [options.saltRounds=12] - BCrypt salt rounds (increased for better security) * @returns {Object} Express router and middleware functions */ function createAuth(options = {}) { const { password, hashedPassword, loginPath = '/login', logoutPath = '/logout', statusPath = '/status', saltRounds = 12 } = options; if (!password && !hashedPassword) { throw new Error('Either password or hashedPassword must be provided'); } let bcryptPassword; // Initialize the password hash const initPassword = async () => { if (hashedPassword) { bcryptPassword = hashedPassword; } else { bcryptPassword = await bcrypt.hash(password, saltRounds); } }; // Initialize password immediately initPassword(); const router = express.Router(); // Status endpoint router.get(statusPath, (req, res) => { if (req.session && req.session.authenticated) { res.status(200).json({ status: 'Authenticated', authenticated: true }); } else { res.status(200).json({ status: 'Unauthenticated', authenticated: false }); } }); // Login endpoint router.post(loginPath, async (req, res) => { const { password: inputPassword } = req.body; if (!inputPassword) { return res.status(400).json({ error: 'Password is required', authenticated: false }); } try { // Ensure password is initialized if (!bcryptPassword) { await initPassword(); } const result = await bcrypt.compare(inputPassword, bcryptPassword); if (result) { req.session.authenticated = true; res.status(200).json({ message: 'Login successful', authenticated: true }); } else { res.status(401).json({ error: 'Invalid password', authenticated: false }); } } catch (err) { console.error('Login error:', err); res.status(500).json({ error: 'Internal server error', authenticated: false }); } }); // Logout endpoint router.get(logoutPath, (req, res) => { req.session = null; // Clear the session res.status(200).json({ message: 'Logged out successfully', authenticated: false }); }); // Middleware to protect routes const requireAuth = (req, res, next) => { if (req.session && req.session.authenticated) { next(); } else { res.status(401).json({ error: 'Authentication required', authenticated: false }); } }; // Middleware to redirect unauthenticated users (for HTML responses) const requireAuthRedirect = (redirectUrl = '/login') => { return (req, res, next) => { if (req.session && req.session.authenticated) { next(); } else { res.redirect(redirectUrl); } }; }; return { router, requireAuth, requireAuthRedirect, initPassword }; } module.exports = { createAuth };