← 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).*)"],
};