Passport HTTP Basic and Digest Strategies

raw JSON →
1.1.1 verified Thu Apr 23 auth: no javascript maintenance

Passport-HTTP-2 provides HTTP Basic and Digest authentication strategies for Passport.js, enabling stateless authentication for Node.js applications that support Connect-style middleware like Express. Currently at version 1.1.1, the library's release cadence appears to be on-demand, with the last notable update in 2019 and last commit in 2022, suggesting a maintenance rather than actively developed status. It serves as a fork of the original `jaredhanson/passport-http` module, which itself is less actively maintained. This fork aims to offer continued support for these fundamental HTTP authentication schemes, often used for protecting API endpoints where sessions are not desired. Developers leverage this module to integrate standard basic (username/password over plaintext, requires HTTPS) and digest (challenge-response, avoids cleartext password) authentication into their Passport-based applications.

error TypeError: BasicStrategy is not a constructor
cause The `BasicStrategy` (or `DigestStrategy`) was not imported correctly, likely due to a mixed CommonJS/ESM environment or an incorrect named/default import.
fix
For ESM, use import { BasicStrategy } from 'passport-http-2';. For CommonJS, ensure const { BasicStrategy } = require('passport-http-2'); or const BasicStrategy = require('passport-http-2').BasicStrategy; if it's not a direct named export.
error Error: Unknown authentication strategy "basic"
cause The Passport strategy has not been registered with Passport using `passport.use()` before being referenced in `passport.authenticate()` middleware.
fix
Ensure passport.use(new BasicStrategy(...)) (or DigestStrategy) is called to configure and register the strategy before any routes attempt to use passport.authenticate('basic', ...).
error 401 Unauthorized (without credential prompt)
cause This typically occurs when the strategy's `verify` (Basic) or `secret` (Digest) callback returns `done(null, false)`, indicating that the provided credentials were invalid or the user was not found, but no `WWW-Authenticate` header was sent or processed by the client.
fix
Debug your verify/secret callback logic to ensure it correctly identifies valid users and passwords. Also, check browser/client cache, especially in development, as browsers might cache HTTP auth credentials.
error RangeError: Invalid digest parameter 'qop'
cause The `qop` (Quality of Protection) option for `DigestStrategy` is misconfigured or an unsupported value is provided in the strategy options. The standard values are 'auth' or 'auth-int'.
fix
Ensure the qop option in new DigestStrategy({ qop: 'auth' }, ...) is correctly set to a valid string like 'auth' or 'auth-int' as per RFC 2617.
gotcha As a fork of `passport-http`, be aware that `passport-http-2` might introduce subtle behavioral differences or have a different maintenance schedule. Always consult the specific documentation for this fork.
fix Review the GitHub repository's issues and PRs for active development and specific changes from the original `passport-http`.
breaking HTTP Basic authentication transmits credentials in plain text. It is critically important to use HTTPS in production environments to prevent eavesdropping and credential theft.
fix Always deploy applications using HTTP Basic authentication behind a TLS/SSL (HTTPS) layer to encrypt communications.
gotcha Forgetting to set `session: false` in `passport.authenticate()` when using HTTP Basic or Digest strategies will cause Passport to attempt to establish a login session. This is often undesired for stateless API authentication and can lead to unexpected behavior or errors if session middleware is not configured.
fix Always include `{ session: false }` as an option in `passport.authenticate('strategy', { session: false })` for stateless HTTP authentication.
gotcha The `done` callback in Passport strategies has a specific signature: `done(err, user, info)`. Incorrectly passing parameters (e.g., `done(user)` instead of `done(null, user)`) can lead to authentication failures or server errors.
fix Ensure the `done` callback is invoked with `done(null, user)` for success, `done(null, false)` for authentication failure, or `done(error)` for server errors during credential verification.
gotcha The `DigestStrategy`'s `secret` callback (for retrieving the user's password/secret from the database) has a different signature than `BasicStrategy`'s `verify` callback. It expects `done(err, user, password)`, providing the actual server-side password for hash computation, unlike Basic which just needs `done(err, user)`.
fix For `DigestStrategy`, ensure your `secret` callback correctly returns `done(null, user, user.password)` (or equivalent) for successful user lookup, supplying the secret for digest calculation.
npm install passport-http-2
yarn add passport-http-2
pnpm add passport-http-2

This quickstart demonstrates setting up both HTTP Basic and Digest authentication using Passport-HTTP-2 with an Express application, showing strategy configuration and route protection.

import express from 'express';
import passport from 'passport';
import { BasicStrategy, DigestStrategy } from 'passport-http-2';

const app = express();

// In a real application, replace this with a database or secure user store
const users = new Map<string, { password?: string, digestSecret?: string, id: string }>([
  ['alice', { password: 'password', id: 'alice' }],
  ['bob', { digestSecret: 'secret', id: 'bob' }]
]);

passport.use(new BasicStrategy(
  async (userid, password, done) => {
    console.log(`Basic attempt for user: ${userid}`);
    const user = users.get(userid);
    if (!user || user.password !== password) {
      return done(null, false); // Authentication failed
    }
    return done(null, user); // Authentication successful
  }
));

passport.use(new DigestStrategy(
  { qop: 'auth' }, // Quality of Protection (qop) for Digest authentication
  async (username, done) => {
    console.log(`Digest attempt for user: ${username}`);
    const user = users.get(username);
    if (!user) {
      return done(null, false); // User not found
    }
    // Digest strategy expects user, then the shared secret (password)
    return done(null, user, user.digestSecret); 
  },
  async (params, done) => {
    // Optional: Validate nonce-related parameters here to prevent replay attacks
    // For this example, we just say 'true'
    console.log('Digest nonce params:', params);
    done(null, true);
  }
));

app.use(passport.initialize());

app.get('/', (req, res) => {
  res.send('Hello, you can try accessing /basic or /digest');
});

app.get('/basic', passport.authenticate('basic', { session: false }), (req, res) => {
  res.json({ message: `Hello Basic authenticated user: ${(req.user as any).id}` });
});

app.get('/digest', passport.authenticate('digest', { session: false }), (req, res) => {
  res.json({ message: `Hello Digest authenticated user: ${(req.user as any).id}` });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
  console.log('Try: curl -u alice:password http://localhost:3000/basic');
  console.log('Try: curl --digest -u bob:secret http://localhost:3000/digest');
});