Fastify Basic Auth Middleware
This package provides a plug-and-play HTTP Basic Authentication solution designed for use with the Fastify web framework. It supports checking credentials against static user lists or through custom synchronous or asynchronous authorizer functions. As of version 0.0.2, it emphasizes secure credential comparison with a `safeCompare` utility to prevent timing attacks. While its README initially showed Express.js examples, the package is explicitly built for Fastify. It ships with TypeScript types, ensuring type safety for Fastify applications. This plugin is distinct from the `@fastify/basic-auth` core plugin, offering a different API for integrating basic authentication.
Common errors
-
TypeError: app.use is not a function
cause Attempting to use `app.use()` with a Fastify instance, as shown in the README's Express example, instead of Fastify's `fastify.register()` or `fastify.addHook()`. Fastify does not have a native `.use()` method for middleware unless `@fastify/express` or `middie` are registered.fixReplace `app.use(basicAuth(...))` with `fastify.register(basicAuthPlugin, { ...options... })` to correctly register the plugin with Fastify. Hooks like `fastify.addHook('preHandler', ...)` can then utilize the plugin's functionality. -
Error: Unauthorized (HTTP 401 response) when using custom async authorizer
cause Your custom `authorizer` function is asynchronous but you forgot to set `authorizeAsync: true` in the plugin options, or the async function is not correctly handling the callback/Promise resolution.fixEnsure that `authorizeAsync: true` is set in the options object when registering the plugin. Additionally, verify that your asynchronous authorizer correctly calls the provided `cb(null, true)` for success or `cb(null, false)` for failure (or resolves/rejects a Promise if using `async/await`). -
Property 'auth' does not exist on type 'FastifyRequest'
cause When using TypeScript, the `request.auth` property, which is decorated by the plugin, is not automatically recognized by Fastify's default `FastifyRequest` type.fixAugment the `FastifyRequest` interface to include the `auth` property. Create a declaration file (e.g., `src/types/fastify.d.ts`) with: ```typescript declare module 'fastify' { interface FastifyRequest { auth: { user: string; password?: string; }; } } ```
Warnings
- breaking The README examples explicitly use `require('express')()` and `app.use()`, which are incorrect for Fastify applications. Fastify uses its own plugin system (`fastify.register`) and hooks (`fastify.addHook` or `onRequest`) for middleware integration. Following the README's Express examples will lead to errors.
- gotcha When implementing a custom `authorizer` function, direct string comparison (`==` or `===`) for credentials is vulnerable to timing attacks. This can expose sensitive information about credentials based on response times.
- gotcha Asynchronous custom `authorizer` functions require setting `authorizeAsync: true` in the plugin options. If this flag is not set, the plugin will treat your asynchronous function as synchronous, leading to incorrect authorization behavior or errors.
- gotcha This `fastify-basic-auth-middleware` package is distinct from the official Fastify core plugin `@fastify/basic-auth`. They have different APIs and usage patterns. Be careful not to confuse the two when integrating basic authentication into your Fastify application.
Install
-
npm install fastify-basic-auth-middleware -
yarn add fastify-basic-auth-middleware -
pnpm add fastify-basic-auth-middleware
Imports
- basicAuth
const basicAuth = require('fastify-basic-auth-middleware')import basicAuth from 'fastify-basic-auth-middleware'
- safeCompare
import { safeCompare } from 'fastify-basic-auth-middleware'import basicAuth from 'fastify-basic-auth-middleware'; const safeCompare = basicAuth.safeCompare;
- FastifyRequest
import type { FastifyRequest } from 'fastify'
Quickstart
import Fastify from 'fastify';
import basicAuthPlugin from 'fastify-basic-auth-middleware';
const fastify = Fastify({ logger: true });
// Register the basic auth plugin with static users
fastify.register(basicAuthPlugin, {
users: { 'admin': 'supersecret', 'user': 'password123' },
});
// Add a preHandler hook to protect routes with basic authentication
fastify.addHook('preHandler', async (request, reply) => {
try {
// The plugin adds 'auth' to the request if credentials are provided
// and checks them against the configured users/authorizer.
// If unauthorized, it will automatically send a 401 response.
if (!request.auth || !request.auth.user) {
reply.code(401).send('Unauthorized'); // This part is handled by the plugin, but explicitly showing the check
}
request.log.info(`Authenticated user: ${request.auth.user}`);
} catch (err) {
request.log.error('Authentication error:', err);
reply.code(500).send('Internal Server Error');
}
});
// Define a protected route
fastify.get('/protected', async (request, reply) => {
return { message: `Hello, ${request.auth.user}! You accessed a protected route.` };
});
// Define an unprotected route
fastify.get('/public', async (request, reply) => {
return { message: 'This is a public route.' };
});
const start = async () => {
try {
await fastify.listen({ port: 3000 });
fastify.log.info(`Server listening on ${fastify.server.address().port}`);
console.log('Try accessing: http://localhost:3000/protected with admin:supersecret');
console.log('Try accessing: http://localhost:3000/public');
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
start();