Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preview token is exposed to the frontend bundle #571

Open
1 task done
dsdevries opened this issue Jun 9, 2023 · 6 comments
Open
1 task done

Preview token is exposed to the frontend bundle #571

dsdevries opened this issue Jun 9, 2023 · 6 comments
Labels
enhancement [Issue][PR] New feature

Comments

@dsdevries
Copy link

dsdevries commented Jun 9, 2023

Describe the issue you're facing

When I follow the standard implementation guides for nextjs, I am suppose to pass the preview token to the client side to initiate the api client. This is done to enable live editing and preview mode with zero effort, but it introduces a major security hole.

Once you retrieved the preview token, you are able to fetch all unpublished content using the api. This is a serious concern for clients that have unpublished content of which is vital to remain secret until it is published. Think of financial result of listed companies, product launches or other strategic content.

Please update the library to either use the preview token or user credentials passed by the cms editor and use server side validation for this. Or update the manuals to create a custom api route as a proxy.

Reproduction

https://reinvent-daalse-singel-rebuild.vercel.app

Steps to reproduce

search for the preview token in the _app-[hash].js file

System Info

Nodejs, Nextjs, React

Used Package Manager

npm

Error logs (Optional)

No response

Validations

@dsdevries dsdevries added pending-author [Issue] Add Storyblok employee pending-triage [Issue] Add labels to describe it or prioritise it labels Jun 9, 2023
@fgiuliani
Copy link
Collaborator

Hi @dsdevries, thanks for your request.

The preview token shouldn't be used in a production environment. It's meant to be used only while developing or testing, in controlled staging/QA environments.

When you create an API token on Storyblok you can pick between "preview" and "public". You should use a public token in production, as it only has access to the published content.

cc @arorachakit

@dsdevries
Copy link
Author

hi @fgiuliani,
Thank you for your answer. I followed the guides on:
https://www.storyblok.com/tp/add-a-headless-cms-to-next-js-in-5-minutes
and actually have the same implementation as on:
https://github.com/storyblok/storyblok-react/blob/main/playground-next/pages/index.tsx

They both describe that you should use the preview token. And when you do, the visual editor works out of the box. If I replace the preview token with the public token, and change the version to published by hardcoding, the code builds properly, but the visual editor doesn't work because it won't automatically loads the unpublished version in the preview iframe. When I just change the token to the public token but leave the mode to draft I get 401 and the build fails.

How can I get the visual editor to work in production without exposing the preview token?

@arorachakit
Copy link
Contributor

arorachakit commented Jun 16, 2023

Hey @dsdevries! I will just give a descriptive reply here so that you can take a look at different scenarios and how to proceed forward.

First, let me tell you how the token logic works generally in regard to different data and editing. Then we will take a look at Next.js case.
There are two different tokens, preview and public. I see that you're already aware. The public token can only give you published data and that's why you get a 401 error when using the public token with draft content. Take a look here.

The working of the Visual Editor (live edits) in your case depends on two cases, but since we are talking about the token let me share something which will show you what is different with the draft token - The draft content contains a specific _editable field which helps the bridge to identify the components. You can check the JSON to see how it looks. Please take a look here. This might also help in understanding how it works - https://www.storyblok.com/docs/guide/essentials/visual-editor#enabling-click-events-on-your-html-elements

Coming onto the part where in guides we mention using the preview token - that is because write these guides where the user starts from scratch and should have access to all the content along with live editing mode. The tokens are defined in such a way that the preview token should be used inside the visual editor, which gives access to draft content and allows live editing. And on the production environment, the one that is accessible to the world - you should use public token so that the users can only access published content. This is the same thing that @fgiuliani mentioned. We never recommend using the preview token on your publicly available website.

Now coming to the framework-specific part, I would like to mention that things change depending on the frameworks you choose or work with. Generally speaking, the way we have our tokens can help you anywhere. Let's say you work with a SPA like React. In that case everything is on the client side, and there is no way to hide the token as you are making a call from the frontend to the Storyblok's API. In this case, you would have two different deployments in an ideal case, one with the preview token and the other with the public token. The deployment with preview token and draft content to be used inside Visual Editor and the one with public token and published content for public users.

I see that you're using Next.js, it is a framework that allows you to work on the server side as well. Before moving to the way you can make it work here, I would also like to mention that live editing depends on the way you build your website. If you're building it statically, then even when you use the preview token it can be tricky to live edit. This is the other reason why you might not be able to live edit. When you deploy your website, and use that URL, for Next.js you need to use the preview mode, it is legacy now but here is an example assuming you're using Next.js 12 - https://github.com/storyblok/react-next-boilerplate/tree/main/pages/api. If you're using Next.js 13 and building your website statically, you will need to use the new Draft Mode that they introduced.

Now coming to the point where you can even use the preview token, and keep everything on the server side with Next.js. The examples you mentioned are initializing with the storyblokInit function inside the _app.js file. If you store the access token inside the env file without exposing it on the browser (i.e. without adding NEXT_PUBLIC) to it, you will see that it will still fetch the data and enable the live editing. As getStaticProps and getStaticPaths run only on the server, you won't face any errors using this way. This is just to confirm that it can work exactly the way you would want. The variable is on the server side completely and won't expose it to the browser. Just adding the docs for env in case you want to take a look - https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables

But again, I'd like to highlight that these things depend on the frameworks. And in this case too, I would recommend having two different deployments. One with the public token for users to access, that only has published data. And another one that has the preview token and draft content with enabled preview/draft mode to be used inside the editor.

Also, just wanted to give an example in the last - let's say you fetch something on the client side here, you will face an error because the storyblokApi won't get the token on the client. Here is an example of fetching something on the client - https://github.com/storyblok/next.js-ultimate-tutorial/blob/master/components/AllArticles.js. Here we are fetching all articles on the client. To make this work, the token has to be on the client. So you can put it hard coded or maybe add NEXT_PUBLIC to it. Just wanted to mention that this is also one of the reasons that we are using the token on the client and haven't described this approach in the tutorial.

I hope this helps, let me know your thoughts on this.
cc - @fgiuliani

@fgiuliani
Copy link
Collaborator

Hey @dsdevries did the message from @arorachakit help you?

@dsdevries
Copy link
Author

dsdevries commented Jul 4, 2023

hi @fgiuliani and @arorachakit,
Thank you for your responses. The above story makes sense, and it's also what I found out myself. I also came to the conclusion that two different deployments for the public site and for the content editors is the easiest solution for now.

It is just that this isn't clear from the documentation. From reading the documentation, I was under the assumption, that it is safe to use the preview token. And because the the Storyblok-react library is basically a singleton, I also assumed that it was only used server side by nextjs.

I would suggest updating the documentation to advise the two separate deployments. Also, I would stress that, as our client will be using the cms, the preview/staging deployment is also considered production. We basically have 3 separate environments (development, acceptance, production) connected to 3 different spaces in storyblok. Each environment has a preview deployment and a public deployment. When we develop a new feature, we develop against the development environment and we also test whether preview mode is working. When development is ready, we deploy it to acceptance where the PO is testing. She will also test preview mode. Then when the feature is acceptance, it is deployed to production where the content editors will work in preview mode. Thus, preview mode is not a pre-production mode.

It would however be nice if the two separate deployment would not be necessary. After all, when you are logged, you already have a session. It would be very easy to just build in a mechanism in the library that takes the token from your session when you are in preview mode, and takes the public token when you are not logged in. This will take away a lot of confusion.

@arorachakit
Copy link
Contributor

Hey @dsdevries !
Good to know that the comment was helpful.

Thank you so much for your feedback regarding the documentation, we are in the process of improving this part. I am sure it will be useful for many people.

Regarding the changes in the sdk, I understand what you're trying to say. It definitely sounds interesting.
We will need to inspect the complete scenario first, as there can be various use cases and many of the things even depend on the type of deployments. There are also scenarios where you'd need different versions of data depending on the type of deployment. With Next.js, the legacy preview mode and the new draft mode have to be in consideration too.

But I really like the overall concept and idea, if we are able to implement it in such a way where we give the flexibility for users to choose this and keep the things they want - it would be great and maybe we can even think it for more SDKs.
If you want, and if you can - it'd really encourage you to create a PR for the same. Let me know what you think. :)

@fgiuliani fgiuliani added enhancement [Issue][PR] New feature and removed pending-author [Issue] Add Storyblok employee pending-triage [Issue] Add labels to describe it or prioritise it labels Aug 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement [Issue][PR] New feature
Projects
None yet
Development

No branches or pull requests

3 participants