Exclusive new data reveals that content debt costs $4.63 trillion globally. Read the full report and calculate your company's content debt now.

How to verify the preview query parameters of the visual editor?

If the user opens your page in Storyblok, we add a few parameters which you can use to securely validate their use of the edit mode.

You will need that validation to load the right version of your content to the right users. The draft version is for the editor and the published version is for the public.

A simple validation would be to check if there is the _storyblok parameter in url. This could be done in the frontend or in the backend. But for a secure check we recommend to implement the logic in the backend and validate the _storyblok_tk parameter.

Code Examples

Here are some examples of how to securely check if the user is in edit mode:

import crypto from 'crypto'
// getQueryParam = access requests URL param by name

let validationString = getQueryParam('_storyblok_tk[space_id]')
  + ':' + YOUR_PREVIEW_TOKEN
  + ':' + getQueryParam('_storyblok_tk[timestamp]')
let validationToken = crypto.createHash('sha1')
  .update(validationString)
  .digest('hex')

if (getQueryParam('_storyblok_tk[token]') == validationToken &&
    getQueryParam('_storyblok_tk[timestamp]')
    > Math.floor(Date.now()/1000)-3600) { 
  // you're in edit mode.
  this.editMode = true 
}

Zero-downtime Preview Token Rotation

The _storyblok_tk[token] signature is generated with one specific preview token from your space. If your space has more than one preview token, which one gets used is not configurable. Validating against a single hardcoded token will break as soon as that changes.

Accept a list of preview tokens instead of one:

import crypto from 'crypto'

// Every preview token that should be accepted, e.g.
// process.env.STORYBLOK_PREVIEW_TOKENS.split(',')
const PREVIEW_TOKENS = [YOUR_PREVIEW_TOKEN, YOUR_NEW_PREVIEW_TOKEN]

// getQueryParam = access requests URL param by name
let spaceId = getQueryParam('_storyblok_tk[space_id]')
let timestamp = getQueryParam('_storyblok_tk[timestamp]')
let signature = Buffer.from(getQueryParam('_storyblok_tk[token]'), 'hex')

let matches = PREVIEW_TOKENS.filter((previewToken) => {
  let validationToken = crypto.createHash('sha1')
    .update(spaceId + ':' + previewToken + ':' + timestamp)
    .digest()
  return validationToken.length === signature.length &&
    crypto.timingSafeEqual(validationToken, signature)
})

if (matches.length > 0 && timestamp > Math.floor(Date.now()/1000)-3600) {
  // you're in edit mode.
  this.editMode = true
}

To rotate without downtime:

  1. Create the new preview token in Storyblok. The editor keeps signing with the previous one, so nothing breaks yet.
  2. Add the new token to PREVIEW_TOKENS and deploy. Both old and new are now accepted.
  3. Delete the old preview token in Storyblok. Deleting it is what makes the editor switch.
  4. Optionally remove the old token from PREVIEW_TOKENS on your next deploy.