← back to Homesonspec

apps/admin/src/middleware.ts

38 lines

import { NextRequest, NextResponse } from "next/server";

/**
 * HTTP Basic Auth for the whole admin app (fleet next-auth-gate convention).
 * Credentials come ONLY from the BASIC_AUTH env ("user:pass") — there is NO
 * hardcoded fallback. If BASIC_AUTH is unset/malformed the admin fails CLOSED
 * (denies every request) rather than accepting a committed default credential
 * (TK-10 security remediation).
 */
const RAW = process.env.BASIC_AUTH ?? "";
const SEP = RAW.indexOf(":");
const USER = SEP >= 0 ? RAW.slice(0, SEP) : "";
const PASS = SEP >= 0 ? RAW.slice(SEP + 1) : "";
const CONFIGURED = USER.length > 0 && PASS.length > 0;

export function middleware(request: NextRequest) {
  if (CONFIGURED) {
    const header = request.headers.get("authorization");
    if (header?.startsWith("Basic ")) {
      // Split on the FIRST colon only — symmetric with USER/PASS parsing above,
      // so passwords may contain ':'.
      const decoded = Buffer.from(header.slice(6), "base64").toString();
      const sep = decoded.indexOf(":");
      const user = sep >= 0 ? decoded.slice(0, sep) : decoded;
      const pass = sep >= 0 ? decoded.slice(sep + 1) : "";
      if (user === USER && pass === PASS) return NextResponse.next();
    }
  }
  return new NextResponse("Authentication required", {
    status: 401,
    headers: { "WWW-Authenticate": 'Basic realm="HomesOnSpec Admin"' },
  });
}

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};