HVRDHVRD
ExpressJS

Authentication

Guide on implementing basic Token-based Authentication in ExpressJS, covering Auth Workflow, example workflow, and creating authenticated endpoints using a custom generateToken approach.

Authentication in ExpressJS

Authentication is the process of verifying the identity of a user or system interacting with your application. In ExpressJS, you can implement authentication by generating a simple token, storing it, and verifying it on protected endpoints.

This guide focuses on a simple custom token approach using generateToken.


Auth Workflow Overview

A typical authentication flow consists of the following steps:

  1. User Login – The user submits credentials (username and password).
  2. Token Generation – On successful validation, the server generates a random token.
  3. Token Storage – The token is stored server-side (e.g., in memory).
  4. Authenticated Requests – The client sends the token in headers with each request.
  5. Token Verification – Middleware verifies the token’s validity.
  6. Logout (Optional) – The token is removed from storage.

Example Auth Workflow

Step 1: User Submits Credentials

app.post('/login', (req, res) => {
    const { username, password } = req.body;

    // Example hardcoded user for demonstration
    const validUser = { username: 'admin', password: 'password123' };

    if (username === validUser.username && password === validUser.password) {
        const token = generateToken();
        tokens[token] = username; // Store token mapping

        res.json({ token, message: 'Login successful' });
    } else {
        res.status(401).json({ message: 'Invalid credentials' });
    }
});

Step 2: Token Generation Function

const crypto = require('crypto');

function generateToken() {
    return crypto.randomBytes(32).toString('hex');
}

const tokens = {}; // Simple in-memory storage for tokens

Why Send the Token in the Header Instead of the Request Body?

The token should be sent in the Authorization header because:

  • Standard Practice: The HTTP standard defines the Authorization header for authentication purposes.
  • Statelessness: Tokens in headers help maintain a stateless API where each request is independent.
  • Security: Sending tokens in the body is error-prone, especially in GET requests where bodies are typically ignored or unsupported. Headers ensure the token is consistently present in every request.
  • Caching Issues: Some caching proxies or CDNs may cache requests based on the body content, which is not ideal for authentication.
  • Separation of Concerns: Headers are intended for metadata like authorization, whereas the body should carry only the resource data.

Creating Authenticated Endpoints

Use middleware to protect sensitive routes by validating the token.

Authentication Middleware

function authenticateToken(req, res, next) {
    const token = req.headers['authorization'];

    if (token && tokens[token]) {
        req.user = tokens[token]; // Attach user info to request
        next();
    } else {
        res.status(403).json({ message: 'Access denied. Invalid token.' });
    }
}

Protected Route Example

app.get('/profile', authenticateToken, (req, res) => {
    res.json({ message: `Hello, ${req.user}. This is your profile.` });
});

Logout Workflow (Optional)

app.post('/logout', authenticateToken, (req, res) => {
    const token = req.headers['authorization'];
    delete tokens[token];

    res.json({ message: 'Logged out successfully.' });
});

Important Notes

  • Token storage here is in-memory, which is not durable across server restarts. Use persistent storage like a database or Redis for production.
  • HTTPS is mandatory in production to prevent token interception.
  • Implement token expiration logic manually if needed.