Basic Authentication and Authorization for Password Protection
The app has simple authentication with an optional PASSWORD in the .env file. When the user enters the password, the app creates a session cookie that authorizes the next requests. The session’s userId is a hash of the password, so changing the PASSWORD variable in production ends all sessions.
The flow is a simpler version of the one in the official Next.js authentication documentation . A login page has a form that calls a login server action . src/lib/session.ts creates the session, and src/lib/dal.ts is the Data Access Layer (DAL).
The DAL file exports verifySession. page.tsx calls it, and it redirects the user to the login page when the session is invalid.
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
import { cache } from 'react';
import { decrypt, SESSION_USER_ID } 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();
return session?.userId === SESSION_USER_ID;
};
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();
// ...The DAL also exports isLoggedIn. The sessionGuard decorator uses it to check that the user is logged in before a procedure runs.
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.
Every procedure of the root segment has the sessionGuard decorator. The typeof req.url !== 'undefined' check tells HTTP requests apart from fn calls.
The Telegram webhook doesn’t use sessionGuard, because Telegram sends no session cookie. It checks Telegram’s secret token instead (see the Telegram article). The MCP server has its own MCP_ACCESS_KEY. The static segment (/api/static/openapi.json and /api/static/hello.json) and the /openapi page have no check, so they stay public when PASSWORD is set.