wellcomecollection / wellcomecollection.org

🪟 Wellcome Collection's website and services that support it
https://wellcomecollection.org
MIT License
39 stars 5 forks source link

Use an access token when querying the Prismic API in prod #8309

Closed alexwlchan closed 1 year ago

alexwlchan commented 2 years ago

We got a 500 error from the content app today:

"GET /series/YlgeGBAAACQAieTA HTTP/1.1" 500 115000 "-" "Amazon CloudFront"
ForbiddenError: Access to this Ref requires an access token
    at Client.fetch (/app/node_modules/@prismicio/client/src/client.ts:1563:11)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    at async Client.get (/app/node_modules/@prismicio/client/src/client.ts:489:10)
    at async Object.getByType (/app/content/webapp/.next/server/chunks/576.js:112:13)
    at async getServerSideProps (/app/content/webapp/.next/server/pages/article-series.js:110:25)
    at async Object.renderToHTML (/app/node_modules/next/server/render.tsx:733:13)
    at async doRender (/app/node_modules/next/server/next-server.ts:1669:27)
    at async /app/node_modules/next/server/next-server.ts:1783:21
    at async /app/node_modules/next/server/response-cache.ts:94:25 {
  url: 'https://wellcomecollection.cdn.prismic.io/api/v2/documents/search?q=%5B%5Bat%28my.articles.series.series%2C+%22YlgeGBAAACQAieTA%22%29%5D%5D&q=%5B%5Bnot%28document.tags%2C+%5B%22delist%22%5D%29%5D%5D&q=%5B%5Bany%28document.type%2C+%5B%22articles%22%2C+%22webcomics%22%5D%29%5D%5D&page=1&pageSize=100&fetchLinks=pages.title%2Cpages.promo%2Cevent-series.title%2Cevent-series.promo%2Cbooks.title%2Cbooks.promo%2Cevents.title%2Cevents.promo%2Carticles.title%2Carticles.promo%2Cexhibitions.title%2Cexhibitions.promo%2Cseries.title%2Cseries.promo%2Cwebcomic-series.title%2Cwebcomic-series.promo%2Cwebcomics.title%2Cwebcomics.promo%2Carticle-formats.title%2Carticle-formats.description%2Ceditorial-contributor-roles.title%2Ceditorial-contributor-roles.describedBy%2Cpeople.name%2Cpeople.description%2Cpeople.pronouns%2Cpeople.image%2Cpeople.sameAs%2Corganisations.name%2Corganisations.description%2Corganisations.image%2Corganisations.sameAs%2Cseries.title%2Cseries.promo%2Cseries.schedule%2Cevents.audiences%2Cevents.schedule%2Cevents.interpretations%2Cevents.series%2Cevents.times%2Cevents.locations&orderings=%5Bdocument.first_publication_date+desc%5D&graphQuery=%7B%0Awebcomics+%7B%0A...webcomicsFields%0Aformat+%7B%0A...formatFields%0A%7D%0Abody+%7B%0A...on+editorialImageGallery+%7B%0Anon-repeat+%7B%0Atitle%0A%7D%0Arepeat+%7B%0Aimage%0Acaption%0A%7D%0A%7D%0A%7D%0Aseries+%7B%0Aseries+%7B%0A...seriesFields%0A%7D%0A%7D%0Acontributors+%7B%0A...contributorsFields%0Arole+%7B%0A...roleFields%0A%7D%0Acontributor+%7B%0A...+on+people+%7B%0A...peopleFields%0A%7D%0A%7D%0A%7D%0Apromo+%7B%0A...+on+editorialImage+%7B%0Anon-repeat+%7B%0Acaption%0Aimage%0A%7D%0A%7D%0A%7D%0A%7D%0Aarticles+%7B%0A...articlesFields%0Aformat+%7B%0A...formatFields%0A%7D%0Abody+%7B%0A...on+text+%7B%0Anon-repeat+%7B%0Atext%0A%7D%0A%7D%0A...on+editorialImage+%7B%0Anon-repeat+%7B%0Aimage%0Acaption%0A%7D%0A%7D%0A...on+editorialImageGallery+%7B%0Anon-repeat+%7B%0Atitle%0A%7D%0Arepeat+%7B%0Aimage%0Acaption%0A%7D%0A%7D%0A...on+gifVideo+%7B%0Anon-repeat+%7B%0Acaption%0Atasl%0Avideo%0AplaybackRate%0AautoPlay%0Aloop%0Amute%0AshowControls%0A%7D%0A%7D%0A...on+iframe+%7B%0Anon-repeat+%7B%0AiframeSrc%0ApreviewImage%0A%7D%0A%7D%0A...on+standfirst+%7B%0Anon-repeat+%7B%0Atext%0A%7D%0A%7D%0A...on+quoteV2+%7B%0Anon-repeat+%7B%0Atext%0Acitation%0A%7D%0A%7D%0A...on+embed+%7B%0Anon-repeat+%7B%0Aembed%0Acaption%0A%7D%0A%7D%0A...on+soundcloudEmbed+%7B%0Anon-repeat+%7B%0AiframeSrc%0A%7D%0A%7D%0A...on+vimeoVideoEmbed+%7B%0Anon-repeat+%7B%0Aembed%0A%7D%0A%7D%0A...on+instagramEmbed+%7B%0Anon-repeat+%7B%0Aembed%0A%7D%0A%7D%0A...on+twitterEmbed+%7B%0Anon-repeat+%7B%0Aembed%0A%7D%0A%7D%0A...on+youtubeVideoEmbed+%7B%0Anon-repeat+%7B%0Aembed%0Acaption%0A%7D%0A%7D%0A...on+discussion+%7B%0Anon-repeat+%7B%0Atitle%0Atext%0A%7D%0A%7D%0A...on+tagList+%7B%0Anon-repeat+%7B%0Atitle%0A%7D%0Arepeat+%7B%0Alink%0AlinkText%0A%7D%0A%7D%0A...on+imageList+%7B%0Anon-repeat+%7B%0AlistStyle%0Adescription%0A%7D%0Arepeat+%7B%0Atitle%0Asubtitle%0Aimage%0Acaption%0Adescription%0A%7D%0A%7D%0A%7D%0Acontributors+%7B%0A...contributorsFields%0Arole+%7B%0A...roleFields%0A%7D%0Acontributor+%7B%0A...+on+people+%7B%0A...peopleFields%0A%7D%0A...+on+organisations+%7B%0A...organisationsFields%0A%7D%0A%7D%0A%7D%0Aseries+%7B%0Aseries+%7B%0A...seriesFields%0Aschedule+%7B%0A...scheduleFields%0A%7D%0Acontributors+%7B%0A...contributorsFields%0Arole+%7B%0A...roleFields%0A%7D%0Acontributor+%7B%0A...+on+people+%7B%0A...peopleFields%0A%7D%0A...+on+organisations+%7B%0A...organisationsFields%0A%7D%0A%7D%0A%7D%0Apromo+%7B%0A...+on+editorialImage+%7B%0Anon-repeat+%7B%0Acaption%0Aimage%0A%7D%0A%7D%0A%7D%0A%7D%0A%7D%0AoutroResearchItem+%7B%0A...+on+events+%7B%0A...eventsFields%0A%7D%0A...+on+exhibitions+%7B%0Atitle%0A%7D%0A...+on+books+%7B%0Atitle%0A%7D%0A...+on+articles+%7B%0Atitle%0A%7D%0A...+on+webcomics+%7B%0Atitle%0A%7D%0A...+on+series+%7B%0Atitle%0A%7D%0A...+on+event-series+%7B%0Atitle%0A%7D%0A...+on+pages+%7B%0Atitle%0A%7D%0A%7D%0AoutroReadItem+%7B%0A...+on+events+%7B%0A...eventsFields%0A%7D%0A...+on+exhibitions+%7B%0Atitle%0A%7D%0A...+on+books+%7B%0Atitle%0A%7D%0A...+on+articles+%7B%0Atitle%0A%7D%0A...+on+webcomics+%7B%0Atitle%0A%7D%0A...+on+series+%7B%0Atitle%0A%7D%0A...+on+event-series+%7B%0Atitle%0A%7D%0A...+on+pages+%7B%0Atitle%0A%7D%0A%7D%0AoutroVisitItem+%7B%0A...+on+events+%7B%0A...eventsFields%0A%7D%0A...+on+exhibitions+%7B%0Atitle%0A%7D%0A...+on+books+%7B%0Atitle%0A%7D%0A...+on+articles+%7B%0Atitle%0A%7D%0A...+on+webcomics+%7B%0Atitle%0A%7D%0A...+on+series+%7B%0Atitle%0A%7D%0A...+on+event-series+%7B%0Atitle%0A%7D%0A...+on+pages+%7B%0Atitle%0A%7D%0A%7D%0Apromo+%7B%0A...+on+editorialImage+%7B%0Anon-repeat+%7B%0Acaption%0Aimage%0A%7D%0A%7D%0A%7D%0Aseasons+%7B%0Aseason+%7B%0A...+on+seasons+%7B%0Atitle%0Apromo+%7B%0A...+on+editorialImage+%7B%0Anon-repeat+%7B%0Acaption%0Aimage%0A%7D%0A%7D%0A%7D%0A%7D%0A%7D%0A%7D%0A%7D%0A%7D&ref=YwM3ExAAAPgZGWbW',
  response: {
    type: 'api_security_error',
    message: 'Access to this Ref requires an access token',
    oauth_initiate: 'https://wellcomecollection.prismic.io/auth',
    oauth_token: 'https://wellcomecollection.prismic.io/auth/token'
  }
}

One of the content editors pushed "Publish" in Prismic at the exact same time as this request going in, so I think that was the issue – we allow unauthenticated access to the "master"/latest ref, but require authentication for everything else. The app fetched a ref that had just become outdated.

This should only occur very occasionally (once in the last fortnight, according to app logs), so it's not urgent to fix the 500 error – but it might open a broader conversation about how we do Prismic auth.

I'll put my thoughts in a comment, so they're separate from the problem statement.

alexwlchan commented 2 years ago

I think there are two things we could do here, which are separate:

  1. Start using access tokens for Prismic in the content web app
  2. Change the visibility level of the Prismic API
  3. Allow putting private information in Prismic

1. Start using access tokens for Prismic in the content web app

I think we already have some code to do this, in the tool that manages custom types.

This feels moderately fiddly to get right; something like:

  1. Configure an access token in Prismic
  2. Store it somewhere the content app can get to
  3. Pass it into the content app as config
  4. Pass it from the config into the code that queries Prismic
  5. Actually use it in all the places we query Prismic

If we do, we should introduce it progressively, not all in one go.

This would fix the particular issue we saw today (but that's low priority). It would also open the door to…

2. Change the visibility level of the Prismic API

Prismic has three levels of authentication:

I don't think we should use "Open API", because that would allow people to preview forward refs – and in particular, articles we haven't published yet.

If we were using access tokens for all our apps, we could switch to the Private API.

I don't know why we use the Public API; if I had to guess it's for ease of integration? It was one less thing to set up when we initially built the content web app, and we've never changed it. There may be a good reason to keep it, but I'm not aware of anybody but us ever using it.

If we made the API private, that would open the door to…

3. Allow putting private information in Prismic

This is something we have to be careful about right now, because anything in Prismic is available through the public API.

There have been discussion about adding production notes to Prismic in the past (e.g. #4329), but I think we need the API to be private before we can do that safely.


This all feels like a moderate amount of work to get right (several days at a minimum) and it doesn't feel especially urgent, but I think we need to consider it if we're going to lean more on Prismic.

alexwlchan commented 1 year ago

I'm going to rescope this to step 1 – using a token to access Prismic – and leave the other two items as open questions for the future.

If we enforced access tokens for Prismic, it makes the local dev workflow a bit more fiddly, because you need to get a token before anything works.

alexwlchan commented 1 year ago

Closing as done, with 🤞 this sorts out our errors.