How to implement authentication in Node.js

Implementing secure authentication in Node.js is fundamental for protecting API endpoints and managing user access in web applications and services. As the creator of CoreUI with over 25 years of backend development experience, I’ve built authentication systems for countless enterprise applications. The most effective approach is using JWT tokens with bcrypt password hashing and middleware-based route protection. This provides secure, stateless authentication that scales well and integrates seamlessly with modern frontend applications.

Use JWT tokens with bcrypt password hashing and authentication middleware to secure Node.js API endpoints and manage user sessions.

const express = require('express')
const bcrypt = require('bcrypt')
const jwt = require('jsonwebtoken')
const app = express()

app.use(express.json())

// Sample users database (use real database in production)
const users = []

// Environment variables
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'
const SALT_ROUNDS = 10

// Register endpoint
app.post('/api/auth/register', async (req, res) => {
  try {
    const { email, password, name } = req.body

    // Validate input
    if (!email || !password || !name) {
      return res.status(400).json({ error: 'All fields required' })
    }

    // Check if user exists
    const existingUser = users.find(user => user.email === email)
    if (existingUser) {
      return res.status(400).json({ error: 'User already exists' })
    }

    // Hash password
    const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS)

    // Create user
    const newUser = {
      id: users.length + 1,
      email,
      name,
      password: hashedPassword,
      createdAt: new Date()
    }

    users.push(newUser)

    // Generate JWT
    const token = jwt.sign(
      { userId: newUser.id, email: newUser.email },
      JWT_SECRET,
      { expiresIn: '7d' }
    )

    res.status(201).json({
      message: 'User registered successfully',
      token,
      user: {
        id: newUser.id,
        email: newUser.email,
        name: newUser.name
      }
    })
  } catch (error) {
    res.status(500).json({ error: 'Registration failed' })
  }
})

// Login endpoint
app.post('/api/auth/login', async (req, res) => {
  try {
    const { email, password } = req.body

    // Find user
    const user = users.find(user => user.email === email)
    if (!user) {
      return res.status(401).json({ error: 'Invalid credentials' })
    }

    // Verify password
    const isValidPassword = await bcrypt.compare(password, user.password)
    if (!isValidPassword) {
      return res.status(401).json({ error: 'Invalid credentials' })
    }

    // Generate JWT
    const token = jwt.sign(
      { userId: user.id, email: user.email },
      JWT_SECRET,
      { expiresIn: '7d' }
    )

    res.json({
      message: 'Login successful',
      token,
      user: {
        id: user.id,
        email: user.email,
        name: user.name
      }
    })
  } catch (error) {
    res.status(500).json({ error: 'Login failed' })
  }
})

// Authentication middleware
const authenticateToken = (req, res, next) => {
  const authHeader = req.headers.authorization
  const token = authHeader && authHeader.split(' ')[1] // Bearer TOKEN

  if (!token) {
    return res.status(401).json({ error: 'Access token required' })
  }

  jwt.verify(token, JWT_SECRET, (err, user) => {
    if (err) {
      return res.status(403).json({ error: 'Invalid token' })
    }
    req.user = user
    next()
  })
}

// Protected route
app.get('/api/profile', authenticateToken, (req, res) => {
  const user = users.find(user => user.id === req.user.userId)
  if (!user) {
    return res.status(404).json({ error: 'User not found' })
  }

  res.json({
    id: user.id,
    email: user.email,
    name: user.name,
    createdAt: user.createdAt
  })
})

app.listen(3000, () => {
  console.log('Authentication server running on port 3000')
})

This authentication system uses bcrypt for secure password hashing, JWT for stateless tokens, and middleware for protecting routes. The register endpoint creates users with hashed passwords, login verifies credentials and returns tokens, and the middleware validates tokens on protected routes.

Best Practice Note:

This authentication pattern is the foundation of CoreUI’s secure backend templates for enterprise applications. Always use environment variables for secrets, implement rate limiting on auth endpoints, and consider adding refresh tokens for enhanced security in production applications.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author