Basic Authentication and Authorization for Password Protection
The app implements a simple authentication mechanism with an optional PASSWORD stored in the .env file. Once the user enters the password, a session cookie is created that authorizes the user for subsequent requests. The userId is a hashed version of the password, which allows all sessions to be invalidated by changing the PASSWORD env variable in production.
The authentication flow is a simplified version of the solution provided in the official Next.js authentication documentation . It implements a login page with a form that invokes a login server action . The session is created in src/lib/session.ts , and the Data Access Layer file is defined in src/lib/dal.ts .
The DAL file exports the verifySession function, which is invoked in page.tsx and redirects the user to the login page if the session is invalid.
import { cookies } from "next/headers";
import { cache } from "react";
import { redirect } from "next/navigation";
import crypto from "crypto";
import { decrypt } from "./session";
const getSession = async () => {
const cookie = (await cookies()).get("session")?.value;
const session = await decrypt(cookie);
return session;
};
export const isLoggedIn = async () => {
if (!process.env.PASSWORD) return true;
const session = await getSession();
const userId = crypto
.createHash("md5")
.update(process.env.PASSWORD)
.digest("hex");
return session?.userId === userId;
};
export const verifySession = cache(async () => {
if (!(await isLoggedIn())) {
redirect("/login");
}
});The code above is fetched from GitHub repository.
import { verifySession } from "@/lib/dal";
export default async function Home() {
await verifySession();
// ...It also exports the isLoggedIn function, which is used by the sessionGuard decorator to check if the user is logged in when invoking procedures.
import { createDecorator, HttpException, HttpStatus } from "vovk";
import { isLoggedIn } from "@/lib/dal";
export const sessionGuard = createDecorator(async (req, next) => {
if (typeof req.url !== "undefined" && !(await isLoggedIn())) {
throw new HttpException(HttpStatus.UNAUTHORIZED, "Unauthorized");
}
return next();
});The code above is fetched from GitHub repository.
The sessionGuard decorator is applied to all procedures. The typeof req.url !== 'undefined' check is required to distinguish between HTTP requests and fn invocations.