Basic Auth with Next.JS
I recently needed to add a temporary section to a website which should only be visible to me. This site didn't have any other auth configured, and it wouldn't need it after this, so I was hesitant to pull in a full auth system.
Instead, I opted for basic.. Basic Auth :) This is that old-school login where the browser prompts for a username/password in a native dialog and persists it for your session.
With Next.js, it was simple enough - just one manual route.ts
file to bounce between.
Protect an endpoint with a function which'll throw a redirect to a login page:
// src/app/some/directory/page.tsx
import {ensureAuthorized} from '@/server/auth';
export default function SomePage() {
ensureAuthorized(); // <-- throw if not logged in, redirect to /auth
// ...
}
Implementing that "login" page, we check the base64'd username/password that may have been submitted. If it's incorrect, or hasn't been provided yet, we return a WWW-Authenticate response which'll prompt again for a username/password:
// src/auth/route.ts
import {checkBasicAuthHeader} from '@/server/auth';
import {NextResponse} from 'next/server';
import {redirect} from 'next/navigation';
export async function GET(request: Request) {
const authHeader = request.headers.get('authorization');
if (!checkBasicAuthHeader(authHeader)) {
return new NextResponse('please login...', {
status: 401,
headers: {
'WWW-Authenticate': 'Basic realm="protected"',
},
});
}
return redirect('/new');
}
And a simple implementation of the helper methods - checkBasicAuthHeader
verifies if the header string contains the correct username & password.
// src/server/auth.ts
import {headers} from 'next/headers';
import {redirect} from 'next/navigation';
const THE_USERNAME = 'abc';
const THE_PASSWORD = '123'; // you could put these in env, or you can yolo it
export function ensureAuthorized() {
if (!isAuthorized()) {
redirect('/auth');
}
}
export function isAuthorized() {
const authHeader = headers().get('authorization');
return checkBasicAuthHeader(authHeader);
}
export function checkBasicAuthHeader(authHeader: string | null) {
if (!authHeader || !authHeader.startsWith('Basic ')) {
return false;
}
const [username, password] = Buffer.from(authHeader.split(' ')[1], 'base64')
.toString()
.split(':');
return username === THE_USERNAME && password === THE_PASSWORD;
}
Who needs Auth.js, OAuth, databases, sessions, when you have Basic Auth? lol