prismicio / prismic-client

The official JavaScript + TypeScript client library for Prismic
https://prismic.io/docs/technical-reference/prismicio-client
Apache License 2.0
168 stars 60 forks source link

Preview mode Samesite cookie attribute #184

Closed wishiz closed 3 years ago

wishiz commented 3 years ago

Hi guys, our team has recently experienced an issue with cookies samesite attribute when previewing Prismic documents. We hope you could help us. Please let us know if we should re-address the question.

Reproduction

Log into Prismic repository dashboard > access document > click Preview

What is expected

Open the app preview

What is actually happening

The following error appears: image

Versions

Additional Details

We preview the app on localhost and via AWS staging > issue is reproduced on both.

nuxt.config.js

import Prismic from '@prismicio/client'

const PRISMIC_ENDPOINT = '...'
const PRISMIC_TOKEN = '...'
const LOCALES_CONFIG = {
  'en-gb': {
    name: 'English',
    code: 'en',
    iso: 'en-GB'
    // file: 'en.js'
  },
  'fr-fr': {
    name: 'Français',
    code: 'fr',
    iso: 'fr-FR'
    // file: 'fr.js'
  },
  'de-de': {
    name: 'Deutsch',
    code: 'de',
    iso: 'de-DE'
    // file: 'de.js'
  },
  'pt-pt': {
    name: 'Português',
    code: 'pt',
    iso: 'pt-PT'
    // file: 'pt.js'
  },
  'es-es': {
    name: 'Español',
    code: 'es',
    iso: 'es-ES'
    // file: 'es.js'
  }
}

export default {
  mode: 'universal',
  target: 'static',
  head: {
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
  },
  css: ['swiper/css/swiper.css'],
  plugins: [
    '~/plugins/meta',
    '~plugins/common-sections-animations',
    '~/plugins/i18n',
    '~plugins/hubspot-form',
    { src: '~plugins/head-scripts-resolver', mode: 'server' },
    { src: '~plugins/vue-awesome-swiper', mode: 'client' },
    { src: '~plugins/preview', mode: 'client' },
    { src: '~/plugins/prismicLink', mode: 'client' }
  ],
  components: true,
  buildModules: [
    '@nuxt/typescript-build',
    '@nuxtjs/stylelint-module',
    '@nuxtjs/tailwindcss',
    '@aceforth/nuxt-optimized-images'
  ],
  modules: [
    '@nuxtjs/axios',
    '@nuxtjs/prismic',    [
      'nuxt-i18n',
      {
        locales: Object.values(LOCALES_CONFIG),
        seo: true,
        defaultLocale: 'en',
        lazy: true,
        vueI18n: {
          fallbackLocale: 'en'
        }
      }
    ]
  ],
  /*
  axios: {},
  prismic: {
    endpoint: PRISMIC_ENDPOINT,
    preview: '/preview',
    apiOptions: {
      accessToken: PRISMIC_TOKEN
    },
    linkResolver: '@/plugins/link-resolver',
    htmlSerializer: '@/plugins/html-serializer'
  },

  optimizedImages: {
    inlineImageLimit: 1000,
    imagesName: ({ isDev }) =>
      isDev ? '[path][name][hash:optimized].[ext]' : 'img/[contenthash:7].[ext]',
    responsiveImagesName: ({ isDev }) =>
      isDev ? '[path][name]--[width][hash:optimized].[ext]' : 'img/[contenthash:7]-[width].[ext]',
    handleImages: ['jpeg', 'png', 'svg', 'webp', 'gif'],
    optimizeImages: true,
    optimizeImagesInDev: false,
    defaultImageLoader: 'img-loader',
    mozjpeg: {
      quality: 75
    },
    optipng: {
      optimizationLevel: 3
    },
    pngquant: false,
    giflossy: {
      interlaced: true,
      optimizationLevel: 3
    },
    svgo: {
      plugins: [
        {
          removeDimensions: true
        },
        {
          cleanupNumericValues: true
        }
      ]
    },
    webp: {
      preset: 'default',
      quality: 75
    }
  },
  router: {
    prefetchLinks: true
  },
  build: {
    transpile: ['gsap'],
    extend(config, _ctx) {
      config.resolve.alias.vue = 'vue/dist/vue.common'
    }
  },
  generate: {
    cache: false,
    routes() {
      return Prismic.api(PRISMIC_ENDPOINT, {
        accessToken: PRISMIC_TOKEN
      })
        .then((api) => {
          const client = api.query(Prismic.Predicates.at('document.type', 'client_story'), {
            lang: '*'
          })
          const category = api.query(Prismic.Predicates.at('document.type', 'category_page'), {
            lang: '*'
          })
          const career = api.query(Prismic.Predicates.at('document.type', 'career_story'), {
            lang: '*'
          })
          const discovery = api.query(Prismic.Predicates.at('document.type', 'discovery_page'), {
            lang: '*'
          })

          return Promise.all([client, category, career, discovery])
        })
        .then((responses) => {
          const clientRoutes = responses[0].results.map((doc) => {
            if (doc.lang === 'en-gb') {
              return `/client/${doc.uid}`
            } else return `/${LOCALES_CONFIG[doc.lang].code}/client/${doc.uid}`
          })

          const categoryRoutes = responses[1].results.map((doc) => {
            if (doc.lang === 'en-gb') {
              return `/category/${doc.uid}`
            } else return `/${LOCALES_CONFIG[doc.lang].code}/category/${doc.uid}`
          })

          const careerRoutes = responses[2].results.map((doc) => {
            if (doc.lang === 'en-gb') {
              return `/career/${doc.uid}`
            } else return `/${LOCALES_CONFIG[doc.lang].code}/career/${doc.uid}`
          })

          const discoveryRoutes = responses[3].results.map((doc) => {
            if (doc.lang === 'en-gb') {
              return `/discovery/${doc.uid}`
            } else return `/${LOCALES_CONFIG[doc.lang].code}/discovery/${doc.uid}`
          })

          return [...clientRoutes, ...categoryRoutes, ...careerRoutes, ...discoveryRoutes]
        })
    }
  }
}
~/plugins/preview.ts

export default async function ({ query, enablePreview }) {
  if (query.preview) {
    console.log('query.preview', query.preview)
    await enablePreview()
  }
}

If you need more information, just let us know. We assume it could be hotfixed by adjusting browsers cookie settings, but we're not up to use it as a long-term decision.

Related: https://github.com/nuxt-community/prismic-module/issues/144

lihbr commented 3 years ago

Hey @wishiz, thanks for opening an issue!

It looks like the cookie affected in your screenshot is coming from Mixpanel, an analytics service we're using at Prismic.

  1. Can you confirm or not if previews are not working for you, beyond this cookie error?
  2. If previews are not working, can you provide us a full-page screenshot with the error shown in context?

Thanks!

wishiz commented 3 years ago

Hello @lihbr , sorry for misunderstanding. The preview page is loaded, but the published changes are not applied until we rebuild the project. We tried using forked nuxt-community/prismic-module and updating @prismicio/client from 4.0.0 to 5.1.0 within it, then changes started to be applied when project built in dev mode and previewing locally, but still no luck when built with nuxt generate

lihbr commented 3 years ago

No worries!

The preview page is loaded, but the published changes are not applied until we rebuild the project.

Do you mean saved changes? If published, indeed publishing changes require a website rebuild to be taken into account with a statically generated website.

We tried using forked nuxt-community/prismic-module and updating @prismicio/client from 4.0.0 to 5.1.0 within it

Updating should have no effect for you here, version 4.0.0 is the same as 5.0.0, version 5.1.0 only bringing a new feature to deal with the new tag API.

Basically, the Prismic preview feature allows you to preview unpublished, saved changes by using your website (Nuxt) SPA capabilities. The preview feature cannot preview published changes and those won't appear unless you rebuild your static website. This can be done automatically using the webhook feature ☺️

wishiz commented 3 years ago

Thank you for clearing this out :)
Still, unpublished changes are not applied as well, with the same cookie error. What we want to achieve is to have the ability to preview changes of documents on a statically generated website.

lihbr commented 3 years ago

Ok great!

Looking at your Nuxt configuration everything looks alright:

  1. You shouldn't need to add a preview plugin manually as this gets handled by the Prismic module on its own.
  2. Can you confirm that you do not have a page for ~/pages/preview.vue? Having a said page would conflict with the module preview resolving (if you want to customize the preview resolving page you still can)
  3. Can you double-check your preview configuration inside Prismic's website settings? It should mention your domain without any path and the link resolver should mention /preview as configured in your Nuxt config file.
  4. Can you make sure you are checking on Google Chrome exclusively (I saw you mentioned Safari above also), Safari isn't supported by Prismic preview correctly due to its restrictive cookie policy.
  5. Can you check if the toolbar script is loaded effectively on your website? It should appear before the end of your body tag.
wishiz commented 3 years ago
  1. Got you, thanks 👍🏿
  2. Confirm, no preview page within pages
  3. Here it is: Screenshot from 2021-07-09 14-54-43
  4. Got you, we would like to fix it for Chrome at least
  5. Here it is: Screenshot from 2021-07-09 15-03-45
lihbr commented 3 years ago

Awesome, thanks!

Everything looks alright for me here. I'm starting to run a bit out of ideas but we'll find out why previews aren't working for you, no worries ☺️

  1. Perhaps you can try upgrading Nuxt to version 2.15+, I know the team performed some stability improvements since 2.14 on it
  2. Can you show me how do you get data from Prismic on one of your pages?
wishiz commented 3 years ago

Thank you for looking into the matter :)

  1. Might be an option. We can't do it right now, because of team-client processes, but will give it a try.
  2. 
    ~/pages/index.vue

~/plugins/i18n.ts

declare module 'vue/types/vue' { interface Vue { $loadTranslation(docType: string, options: any): any } }

const getI18n = ({ $prismic, app }, inject) => { const getCurrentLocale = (customLocale?: string) => { const locale = customLocale || app.i18n.locale const currentLocale = app.i18n.locales.filter((lang) => lang.code === locale)[0] const lang = currentLocale.iso.toLowerCase()

return lang

}

const loadTranslation = async (uid: string, options = {}, id: string = 'document.type') => { const predicates = [$prismic.predicates.at(id, uid)] const lang = getCurrentLocale() const fallbackLang = getCurrentLocale(app.i18n.fallbackLocale)

const defaultOptions = { lang }

// fething data with current lang
const currentLangData = await $prismic.api.query(predicates, { ...defaultOptions, ...options })

if (currentLangData.results.length) {
  return currentLangData
}

// fething data with fallback lang
const fallbackLangData = await $prismic.api.query(predicates, { lang: fallbackLang })

return fallbackLangData

}

inject('getCurrentLocale', getCurrentLocale) inject('loadTranslation', loadTranslation) }

export default getI18n

lihbr commented 3 years ago

Ok I think I got it!

Can you try removing the if statement on your asyncData method? (if (process.server || isDev) {)

I think it is preventing your website from refreshing the data once statically generated because it needs to run the asyncData method on the client side in order to get preview data.

Let me know if that's unclear but that'd be explaining why it's working in the development server and not on production also :)

wishiz commented 3 years ago

Thank you for the answer and sorry for the delay!

Makes total sense and it indeed fixed the issue. I appreciate your support :)

lihbr commented 3 years ago

You're welcome! Feel free to reach out to us again in case of any trouble, either on GitHub or our community forum ☺️