How To Add HTTP Basic Auth to Next.js

March 9, 2023

I recently started building some basic admin support tools for BudgetSheet. These tools needed to be behind a login, but I did not want to build a whole internal user system just to do this. Enter HTTP Basic Auth. A simple and easy way to protect pages and directories that is already built into browsers.

To add HTTP Basic Auth to a Next.js project, we need to write some custom middleware and configure it.

Three Steps to Implement Basic Auth in Next.js

  1. Send a WWW-Authenticate: Basic header to present the HTTP Basic Auth challenge login box to the end user.
  2. Read the Authorization header for the user-supplied username and password, and check that against whatever you need to. In this example, it is a static ENV variable.
  3. Configure which paths to protect with HTTP Basic Auth

Example Middleware Code

Name the file middleware.ts and ensure it exists at the root of your Next.js project.

This example includes the auth challenge and response with a environment variable named HTTP_BASIC_AUTH with a value like user:pass. Feel free to change the user/pass auth to a database lookup or whatever is appropriate for your project.

Don’t forget to change the config variable at the bottom to the route paths you want to password protect!

TypeScript
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

const [AUTH_USER, AUTH_PASS] = (process.env.HTTP_BASIC_AUTH || ':').split(':');

// Step 1. HTTP Basic Auth Middleware for Challenge
export function middleware(req: NextRequest) {
  if (!isAuthenticated(req)) {
    return new NextResponse('Authentication required', {
      status: 401,
      headers: { 'WWW-Authenticate': 'Basic' },
    });
  }

  return NextResponse.next();
}

// Step 2. Check HTTP Basic Auth header if present
function isAuthenticated(req: NextRequest) {
  const authheader = req.headers.get('authorization') || req.headers.get('Authorization');

  if (!authheader) {
    return false;
  }

  const auth = Buffer.from(authheader.split(' ')[1], 'base64').toString().split(':');
  const user = auth[0];
  const pass = auth[1];

  if (user == AUTH_USER && pass == AUTH_PASS) {
    return true;
  } else {
    return false;
  }
}

// Step 3. Configure "Matching Paths" below to protect routes with HTTP Basic Auth
export const config = {
  matcher: '/some/admin/page/:path*',
};

Tags: , , , ,

Categories: