> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thepublive.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Have Password Protected Preview

> Add password protection to a preview page in your Next.js app before rendering Publive content

Use this guide when you already have a preview page and want to restrict access with a login step before rendering the preview.

<Info>
  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.
</Info>

## 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`.

```ts theme={null}
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.

```ts theme={null}
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`.

```tsx theme={null}
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} />;
}
```

## Login form behavior

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.
