Next.js Discord

Discord Forum

How do I prevent my client token from being exposed to the browser?

Unanswered
Northern Mockingbird posted this in #help-forum
Open in Discord
Northern MockingbirdOP
In order to update my sanity content, I placed a 'token' inside the 'createClient' function:
export const client = createClient({
  apiVersion,
  dataset,
  projectId,
  useCdn,
  token: process.env.NEXT_PUBLIC_SANITY_TOKEN
});


When I patch() an update to Sanity, I get this error on the console:
You have configured Sanity client to use a token in the browser. This may cause unintentional security issues. See https://www.sanity.io/help/js-client-browser-token for more information and how to hide this warning.


Visiting that Link I read that a recommendation is Making the backend fetch the data from the Sanity APIs

So my attempt at that was to create an /api route in the /app directory:
./app/api/sanity/route.js
_____
import { NextResponse } from 'next/server';

export async function GET(req) {
  try {
    const token = process.env.SANITY_SECRET_TOKEN;
    return NextResponse.json(token);
  } catch (error) {
    console.log(error);
    return NextResponse.json({ error: 'Failed to fetch token' });
  }
}


...and fetch that from within the sanity client
export const client = createClient({
  apiVersion,
  dataset,
  projectId,
  useCdn,
  token: () => fetch('/api/sanity').then((res) => res.json()),
});


The error I'm getting is:
Error: Unauthorized - Session not found

35 Replies

Northern MockingbirdOP
One of my components is client-side rendered and calls a function that uses the client to fetch and patch an update
Interesting.. so if I do that, it will then be safe to have my token value in the createClient ?
@Northern Mockingbird Interesting.. so if I do that, it will then be safe to have my `token` value in the `createClient` ?
yes like this process.env.SANITY_SECRET_TOKEN without NEXT_PUBLIC_
and only use the client on server side
Northern MockingbirdOP
Ok let me give that shot
Actually.. before I do.

This is the function that is being called in my client-side component:

export async function updateAmountInStock(items) {
  items.forEach(async (item) => {
    const sanityProduct = await client.fetch(
      `*[_type == 'storeItem' && title == "${item.description}"][0]`
    );
    await client
      .patch(sanityProduct._id)
      .dec({ amountInStock: item.quantity })
      .commit();
  });
}


What would that be like in an API route?
I do have an API route for handling revalidation of documents by type...
export async function POST(req) {
  try {
    const { isValidSignature, body } = await parseBody(
      req,
      process.env.SANITY_REVALIDATE_SECRET
    );

    if (!isValidSignature) {
      const message = 'Invalid signature';
      return new Response(JSON.stringify({ message, isValidSignature, body }), {
        status: 401,
      });
    }

    if (!body?._type) {
      const message = 'Bad Request';
      return new Response({ message, body }, { status: 400 });
    }

    console.log(`${body._type} revalidated`);
    revalidateTag(body._type);
    return NextResponse.json({ body });
  } catch (err) {
    console.error(err);
    return new Response(err.message, { status: 500 });
  }
}


Would I just handle the client login below that second if-statement?
Northern MockingbirdOP
I have updateAmountInStock defined in ./rootDir/lib which is where I put a couple of patch() functions
Ah.. hmm
@Northern Mockingbird I have `updateAmountInStock` defined in ./rootDir/lib which is where I put a couple of `patch()` functions
create a action.ts in your lib folder and put 'use server' on top of the file then export updateAmountInStock
Northern MockingbirdOP
Alright that's clear instructions. Doing this and will test it out
I placed 'use server' at the top of the file exporting that update method and tested it in my app by performing the action that calls it.

Got this error:
To use Server Actions, please enable the feature flag in your Next.js config


Looking into it..
Northern MockingbirdOP
v13.4.19
put this in your next.config.js
/** @type {import('next').NextConfig} */
const config = {
  experimental: {
    serverActions: true,
  },
}
 
module.exports = config
Northern MockingbirdOP
I think I got it.
Oh you replied first
thanks, yeah I had just found that somewhere
I sent the error to you just in case I couldn't find an answer quick. Sorry about that. Under time pressure atm
Northern MockingbirdOP
Interestingly it worked until I removed NEXTPUBLIC from the env variable name of the token.

Simply putting updateAmountInStock as a server action worked, and the browser did not throw the warning telling me my token key is exposed.

But removing NEXTPUBLIC is making it throw an error when I try to perform the action in my app.

It's probably fine to keep it right? If the browser warning goes away?
(I could check manually if the key is exposed but I'd have to look into seeing how)
Changed NEXT_PUBLIC_SANITY_TOKEN to SANITY_SECRET_TOKEN, that is
so what is the variable name for the token?
NEXT_PUBLIC_SANITY_TOKEN or SANITY_SECRET_TOKEN?
Northern MockingbirdOP
NEXT_PUBLIC_SANITY_TOKEN was it's name originally.

I tested what you told me to do in two steps:
1) keep everything the same, just add 'use server' to the update method
2) rename NEXT_PUBLIC_SANITY_TOKEN to SANITY_SECRET_TOKEN

I did #1 and then performed the action in my app (it's a cart checkout feature that usually gives me that token exposure warning when I do it). It worked fine and the warning didn't appear.

Then I did #2 but that actually causes the error when I try to do the cart checkout
so you might be using it on client side on cart checkout
NEXTPUBLIC will make the variable available on browser which is not good for your case
Northern MockingbirdOP
I see.. ok. I'll have to do some digging then.

But so far this has been great help and your replies are quick and clear. Thanks man. If I need help I'll message here again if that's fine with you.
yes sure. check the code on cart checkout
do the same updateAmountInStock
Northern MockingbirdOP
Yeah oddly enough it's throwing an error from just navigating to the /cart page (which is not even the page containing the component that called updateAmountInStock)

And it also errors when I go to the /studio
what is the error?
Northern MockingbirdOP
Okay! Nevermind! It was erroring because I was still referencing process.env.SANITY_SECRET_TOKEN somewhere
I took care of it. It's working now
Thanks Ray, you're the man
no prob