How to use secret key for on-demand revalidation for ISR (Next.js) in the frontend without exposing it?

Viewed 203

According to the documentation, you should use a SECRET_TOKEN to prevent unauthorized access to your revalidate API route i.e.

https://<your-site.com>/api/revalidate?secret=<token>

But how are you supposed to call that route from the frontend and keep the token secret?

For example, if you have a simple POST that you then want to trigger the revalidate off of, you'd have to expose your secret token via NEXT_PUBLIC to be able to use it:

function handleSubmit(payload) {
  axios.post(POST_URL, payload)
  .then(() => {
    axios.get(`/api/revalidate?secret=${process.env.NEXT_PUBLIC_SECRET_TOKEN}`)
  })
  .then(() => {
    // redirect to on-demand revalidated page
  })
}

What am I missing here? How can you call the API route through the frontend without exposing the SECRET_TOKEN?

3 Answers

I've been trying out On-Demand ISR and stumbled on a similar problem. I was trying to revalidate data after CRUD actions from my Admin dashboard living on the client, behind protected routes ("/admin/...").

If you have an authentication process setup and you're using Next-Auth's JWT strategy, it gives you access to the getToken() method, which decrypts the JWT of the current authenticated user.

You can then use whatever information you have passed through your callbacks to validate the request instead of relying on a SECRET_TOKEN.

import type { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt";

const secret = process.env.NEXTAUTH_SECRET;

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const user = await getToken({ req, secret });
  if (!user || user.role !== "ADMIN") {
    return res.status(401).json({ message: "Revalidation not authorized"});
  }

  try {
    // unstable_revalidate is being used in Next 12.1
    // I'm passing the revalidation url through the query params
    await res.unstable_revalidate(req.query.url as string);
    return res.json({ revalidated: true });
  } catch (err) {
    return res.status(500).send("Error revalidating");
  }
}

I think you need to create one file called ".env".

Inside the file, you put the params .env like this:

NEXT_PUBLIC_SECRET_TOKEN=123password

You must install the dependency dotenv:

npm i dotenv

and then you can call inside your function like this

function handleSubmit(payload) {
  axios.post(POST_URL, payload)
    .then(() => {
      axios.get(`/api/revalidate?secret=${process.env.NEXT_PUBLIC_SECRET_TOKEN}`)
    })
    .then(() => {
      // redirect to on-demand revalidated page
    })
}
Related