withProvider not substituting remote name when using external auth in Forge app

I want to send the contents of a Confluence page to one of my APIs with the authorization done with Slack. I have the following resolver code:

resolver.define('summarise', async (req) => {
 const pageId = req.payload.pageId;
 const confluenceAPIResult = await api.asUser().requestConfluence(route`/wiki/api/v2/pages/${pageId}?body-format=atlas_doc_format`);

 const json = await confluenceAPIResult.json();
 const body = JSON.parse(json.body.atlas_doc_format.value);

 const atlasBotAPI = api.asUser().withProvider('slack', 'atlasbot-api');
 if(!await atlasBotAPI.hasCredentials()) {
   await atlasBotAPI.requestCredentials();
 }
 const accounts = await atlasBotAPI.listAccounts();
 console.log(`accounts: ${inspect(accounts, false, null, true)}`);
 const account = await atlasBotAPI.getAccount();
 console.log(`account: ${inspect(account, false, null, true)}`);
 const options = {
   method: 'POST',
   body: JSON.stringify(body)
 };
 try {
   const atlasBotAPIesult = await atlasBotAPI.fetch(route`/0_0_1/atlas-webhook`, options);
   console.log(`slackResult: ${inspect(atlasBotAPIesult, false, null)}`);
 }
 catch(error) {
   console.error(error);
   console.log(`error: ${util.inspect(error, false, null, true)}`);
 }
});

My manifest.yml looks like this:

modules:
  confluence:contentBylineItem:
    - key: atlasbot-content-byline-item
      resource: main
      resolver:
        function: resolver
      render: native
      title: atlasbot
  function:
    - key: resolver
      handler: index.handler
      providers:
        auth:
          - slack

resources:
  - key: main
    path: src/frontend/index.tsx

app:
  runtime:
    name: nodejs18.x
  id: ari:cloud:ecosystem::app/my-id

permissions:
  scopes:
    - read:page:confluence
  external:
    fetch:
      backend:
        - remote: atlasbot-api
        - remote: slack-com

providers:
  auth:
    - key: slack
      name: Slack
      scopes:
        - 'openid'
        - 'email'
        - 'profile'
      type: oauth2
      clientId: 'my-slack-app-client-id'
      remotes:
        - slack-com
      bearerMethod: authorization-header
      actions:
        authorization:
          remote: slack-com
          path: /openid/connect/authorize
        exchange:
          remote: slack-com
          path: /api/openid.connect.token
        retrieveProfile:
          remote: slack-com
          path: /api/openid.connect.userInfo
          resolvers:
            id: sub
            displayName: name
            avatarUrl: picture

remotes:
  - key: atlasbot-api
    baseUrl: https://atlasbot.example.com
  - key: slack-com
    baseUrl: https://slack.com


(I’ve obfuscated values here including the baseUrl for atlasbot-api).

The Slack OAuth authentication works fine and I get sensible values back for the account.
The fetch throws a 400 error with PROXY_ERR: Forge platform failed to process runtime HTTP request - 400 - INVALID_REMOTE.

I’ve stepped through the debugger and the error is because it’s trying to POST to https://atlasbot-api/?path=%2F0_0_1%2Fatlas-webhook rather than https://atlasbot.example.com/0_0_1/atlas-webhook that I would expect. The function
fetchRemote at line 35 of @Forge/api/out/api/fetch.js seems to be just using the name of the remote rather than looking up the remote’s baseUrl.

I’ve looked at the example on Bitbucket and I can’t see what I am doing differently to that.

Please help!

Hi, @AndySturrock . I took a quick look at your code and one thing that looked different from the example (and how I’ve used withProvider) is this line:

   const atlasBotAPIesult = await atlasBotAPI.fetch(route`/0_0_1/atlas-webhook`, options);

It looks like fetch expects a string and not a route-wrapped object.

Thanks @RinoJose1 . Unfortunately that doesn’t make any difference. I tried the route-wrapper out of desperation! According to the Typescript definitions fetch can take a string or a Route:
(property) ExternalAuthAccountMethods.fetch: (url: string | Route, init?: RequestInit) => Promise<APIResponse>

Ah, got it. Makes sense.

Another thing I do when debugging Forge apps is to start with a working example and then gradually revise it to match my app. So far, that’s always worked :slight_smile:

I have pretty much done that. If I copy/paste the Google example into the same codebase that seems to work. That’s why I’m tearing my hair out trying to work out what I’ve done wrong! Thanks for your suggestions anyway.

Hi there @AndySturrock, in your manifest file, can you add atlasbot-api under the remotes property of your auth provider?

i.e.:

providers:
  auth:
    - key: slack
      name: Slack
      scopes:
        - 'openid'
        - 'email'
        - 'profile'
      type: oauth2
      clientId: 'my-slack-app-client-id'
      remotes:
        - slack-com
        - atlasbot-api # <---- added here
...
2 Likes

Yep that has fixed it! Thanks so much for your help :pray:.

The bit in the Google example I should have seen is this:

providers:
  auth:
    - key: google
      name: Google
      scopes:
        - 'profile'
        - 'https://www.googleapis.com/auth/userinfo.email'
        - 'https://www.googleapis.com/auth/photoslibrary.readonly'
      type: oauth2
      clientId: CHANGEME
      remotes:
        - google-apis
        - google-photos <---- HERE!!!