Express Basic Auth Middleware

1.2.1 · maintenance · verified Wed Apr 22

express-basic-auth is a lightweight, plug-and-play middleware designed for adding HTTP Basic Authentication to Express applications. Its current stable version is 1.2.1, with the latest release in October 2021. The package has seen sporadic updates, with v1.0.0 (production ready) in 2017 and v1.1.0 (TypeScript support) in 2020. It offers flexibility through static user configurations or custom synchronous/asynchronous authorizer functions. A key differentiator is the inclusion of a `safeCompare` utility, which aids in mitigating timing attacks for secure credential comparison. The middleware also allows customization of unauthorized responses, including JSON, and exposes parsed credentials on `req.auth`.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to set up `express-basic-auth` middleware in an Express application using both static user definitions and a custom asynchronous authorizer function, showcasing secure credential comparison with `safeCompare` and custom unauthorized responses.

import express from 'express';
import basicAuth from 'express-basic-auth';
import type { IOptions } from 'express-basic-auth'; // Import the type for better DX

const app = express();
const port = 3000;

// Example 1: Static users
app.use('/admin-static', basicAuth({
    users: { 'admin': 'supersecret', 'editor': 'editpass' },
    challenge: true, // Always challenge if credentials are not provided or incorrect
    realm: 'Static Admin Area' // Custom realm
}));

// Example 2: Custom asynchronous authorizer
const myAsyncAuthorizer: IOptions['authorizer'] = (username, password, cb) => {
    // In a real app, you'd fetch from a DB, check hashed passwords, etc.
    // Simulate async operation
    setTimeout(() => {
        const userMatches = basicAuth.safeCompare(username, 'customuser');
        const passwordMatches = basicAuth.safeCompare(password, 'custompassword');
        const authorized = userMatches && passwordMatches; // Use logical for final decision, bitwise for comparisons

        if (authorized) {
            cb(null, true); // No error, authorized
        } else {
            cb(null, false); // No error, not authorized
        }
    }, 100);
};

app.use('/admin-custom', basicAuth({
    authorizer: myAsyncAuthorizer,
    authorizeAsync: true, // Crucial for async authorizers
    challenge: true,
    realm: 'Custom Auth Area',
    unauthorizedResponse: (req) => {
        return req.auth ?
            ('Credentials ' + req.auth.user + ':' + req.auth.password + ' rejected.') :
            'No credentials provided';
    }
}));

// Route for static users
app.get('/admin-static', (req, res) => {
    res.send(`Hello, ${req.auth?.user}! Welcome to the static admin area.`);
});

// Route for custom authorizer
app.get('/admin-custom', (req, res) => {
    res.send(`Hello, ${req.auth?.user}! Welcome to the custom authorized area.`);
});

// Public route
app.get('/', (req, res) => {
    res.send('This is a public page.');
});

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}`);
    console.log('Try accessing:');
    console.log(` - http://localhost:${port}/admin-static (user: admin, pass: supersecret)`);
    console.log(` - http://localhost:${port}/admin-custom (user: customuser, pass: custompassword)`);
    console.log(` - http://localhost:${port}/`);
});

view raw JSON →