Unauthorized; scope does not match on confluence pages api request

I am developing a function to get all pages for a user via api.

 let url: string | null | undefined = '/wiki/api/v2/pages';
  
    while (typeof url === 'string') {
      let pagesFromApi = await api.asUser().requestConfluence(route`${url}`, {
        headers: {
          'Accept': 'application/json'
        }
      });
  
      const res = await pagesFromApi.json();
  console.log(res, 'this is res');

When i call this function it returns 25 documents.
HOWEVER: when i add the ?limit=25 param to the url I get a 401 unauthorized error.

Here is my manifest.yml

modules:
  confluence:spacePage:
    - key: chatbox-hello-world
      resource: main
      resolver:
        function: resolver
      title: chatbox
      route: hello-world
  function:
    - key: resolver
      handler: index.handler
resources:
  - key: main
    path: static/chatbox/build
app:
  id: <my id>
permissions:
  scopes:
    - 'read:confluence-space.summary'
    - 'read:confluence-props'
    - 'read:page:confluence'
    - 'read:confluence-content.all'
    - 'read:confluence-content.summary'
    - 'search:confluence'
    - 'read:confluence-content.permission'
    - 'read:confluence-user'

Please help:)

Hi @shawn1 ,

Because route is a tagged template function, it will treat constant strings differently from ${} expressions.

If you call

route`/wiki/api/v2/pages?limit=25`

it should work, while

let url = '/wiki/api/v2/pages?limit=25';
route`${url}`

won’t. route will encode the results of all ${} expressions for safety, by calling encodeURIComponent internally. This will encode your ? character, so the API won’t be called properly.

This is still incorrect behaviour, and I’ll pass it on to the Forge team, but in the meantime , the workaround is to not pass any ${} expressions to route that you don’t want URI-encoded.
Update: this behaviour is officially documented here: https://developer.atlassian.com/platform/forge/runtime-reference/product-fetch-api/#route

If you want to save the route as a variable, save the result of route, don’t save the string then pass it to route as an expression.

Thanks,
Michael

4 Likes