Use this guide when you already have a preview page and want to restrict access with a login step before rendering the preview.
This is a simple authentication pattern for demo or controlled preview access. It is especially useful for BFSI and other high-security sectors that need restricted preview access before content can be reviewed more broadly.
Problem statement
In some apps, a preview page should not be openly accessible. Editors or reviewers must authenticate before the app loads and renders preview content.
What you are doing
You will add a server-side auth check to your preview page. If the request does not have a valid preview session, the page returns 401 and shows a login form. After login, the user is redirected back to the preview URL.
This pattern is based on the simple auth flow used in your app:
pages/post/[id].tsx
lib/server/post-access-auth.ts
- a thin server entry point that sets the cookie
Simple auth logic
The password authentication here is simple:
- validate a username and password on the server
- create a signed token for the authenticated user
- store that token in an
HttpOnly cookie
- read the cookie in
getServerSideProps
- show the login form when the cookie is missing or invalid
Your repo already implements most of this flow in lib/server/post-access-auth.ts.
1. Create the auth setup
Keep the auth setup small. The helper should validate credentials, create a signed token, return the cookie value to set, and read the cookie back later in getServerSideProps.
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
export const POST_ACCESS_AUTH_COOKIE_NAME = "post_access_auth";
export const validatePostAccessCredentials = (username: string, password: string) => {
const matched = POST_ACCESS_CREDENTIALS.find((item) => {
return item.username === username && item.password === password;
});
return matched ?? null;
};
export const createPostAccessToken = (username: string) => {
const nonce = randomBytes(32).toString("hex");
const payload = Buffer.from(JSON.stringify({ username, nonce }), "utf8").toString("base64url");
const signature = createHmac("sha256", TOKEN_SALT).update(payload).digest("hex");
return `${payload}.${signature}`;
};
export const buildPostAccessCookie = (token: string) => {
return `${POST_ACCESS_AUTH_COOKIE_NAME}=${encodeURIComponent(token)}; Path=/post; HttpOnly; SameSite=Lax`;
};
export const getPostAccessSession = (token?: string) => {
if (!token) return null;
const [payload, signature] = token.split(".");
if (!payload || !signature) return null;
const expected = createHmac("sha256", TOKEN_SALT).update(payload).digest("hex");
if (!timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"))) {
return null;
}
const decoded = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
return decoded?.username ? { username: decoded.username } : null;
};
export const authenticatePostAccess = (username: string, password: string) => {
const credential = validatePostAccessCredentials(username, password);
if (!credential) {
return null;
}
const token = createPostAccessToken(credential.username);
return {
token,
cookie: buildPostAccessCookie(token),
username: credential.username,
};
};
This is intentionally simple. The token proves the cookie was issued by your server, and getPostAccessSession decides whether preview access should be granted.
2. Guard the preview page in getServerSideProps
Read the auth cookie on the server. If the session is missing, return 401 and send the page into login mode.
const AUTH_COOKIE_NAME = "preview_auth";
const getPreviewSession = (token?: string) => {
if (!token || token !== "demo-token") {
return null;
}
return { username: "demo-user" };
};
export const getServerSideProps = async (context) => {
const id = context.params?.id as string;
const authToken = context.req.cookies?.[AUTH_COOKIE_NAME];
const session = getPreviewSession(authToken);
if (!session) {
context.res.statusCode = 401;
return {
props: {
needsAuth: true,
requestedPath: `/post/${id}`,
},
};
}
const identifiedPost = await publive.content.identify(`/post/${id}`);
if (!identifiedPost.data?.content) {
return { notFound: true };
}
return {
props: {
post: identifiedPost.data.content,
},
};
};
3. Render the login UI
When needsAuth is true, show a small login form. That form should call a thin server entry point, such as an API route or server action, that uses authenticatePostAccess and sets the returned cookie on the response before redirecting back to requestedPath.
function AuthLoginForm({ requestedPath }) {
const onSubmit = async (event) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const response = await fetch("/your-auth-entry-point", {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username: formData.get("username"),
password: formData.get("password"),
}),
});
if (response.ok) {
window.location.href = requestedPath ?? window.location.pathname;
}
};
return (
<form onSubmit={onSubmit}>
<input name="username" placeholder="Username" />
<input name="password" type="password" placeholder="Password" />
<button type="submit">Sign in</button>
</form>
);
}
function PreviewPage({ needsAuth, requestedPath, post }) {
if (needsAuth) {
return <AuthLoginForm requestedPath={requestedPath} />;
}
return <PostPage post={post} />;
}
Keep this logic minimal. The important part is that the form does not set auth state in the browser directly. The server-side auth function produces the cookie, the server sets it on the response, and the next page request reads it.
Continue with normal preview rendering
After the user is authenticated, the page follows the same server-side preview flow as the basic preview doc. The auth layer only controls access. It should stay separate from the actual content rendering logic.
Core pieces you need
- a server-side credential validator
- a signed auth token
- an
HttpOnly cookie for the preview session
- a reusable auth function such as
authenticatePostAccess
getServerSideProps to enforce access on every request
- a login UI rendered when access is missing
- redirect back to the requested preview URL after login
Notes
- Keep preview logic server-side.
- Return
401 when authentication is required, not 200.
- Use an
HttpOnly cookie so the browser can send the session automatically on the next request.
- Keep the actual content rendering logic separate from the auth layer.