Trouble accessing app properties from the lifecycle install event in ACE

I’m looking into copying some app properties from Confluence to my app’s store when the app is upgraded.

When I attempt to access Confluence app properties from my ACE app on lifecycle install, the response from Confluence is a 401. with the message in the HTML response:

The request has not been applied because it lacks valid authentication credentials for the target resource.

Here’s the example code…

  const MY_PROPERTY_NAME = 'app-configuration';
  ...
  app.post("/installed", [ addon.authenticateInstall() ], (req, res, next) => {
    getAppPropertyFromConfluence(MY_PROPERTY_NAME, addon.httpClient(req))
      .then((data) => {
        // check and save to the app's store
        ...
      })
      .catch(() => {
        console.error("appProperty not found ", propertyName );
        ...
      });
      next();
  });

  ...
  
  const getAppPropertyFromConfluence = (propertyName, httpClient) => {
    const url = `/rest/atlassian-connect/latest/addons/${addon.config.appKey()}/properties/${propertyName}`;
    return new Promise((resolve, reject) =>
      httpClient.get(
        {
          url,
          json: true,
        },
        (err, httpResponse, body) => {
          if (err || (httpResponse && httpResponse.statusCode >= 400)) {
            console.error("httpResponse", JSON.stringify(httpResponse, null, 2)); 
            // **** FAILURE HERE - httpResponse.statusCode is 401
            reject(err);
            return;
          }
          resolve(body);
          return;
        }
      )
    );
  };

If I access the same app property elsewhere, it works just fine, with Confluence returning a 200 status code:

  app.get("/rest/app-configuration", [ addon.checkValidToken() ], (req, res) => {
    getAppPropertyFromConfluence(
      MY_PROPERTY_NAME,
      addon.httpClient(req)
    );
    ...
  });

The main difference is in the second parameter for app.get(..).

Looking at the authorization headers and the JWT token for both requests, they both contain a very similar JWT payload, but the /installed route gives a 401, whereas the other gives a 200 status code:

{
  "iss": "my.app.key",
  "iat": 1632823349, // issued at timestamp
  "exp": 1632823529, // expiration timestamp
  "qsh": "the same *qsh* string",
  "aud": [
    "the same *aud* string"
  ]
}
  • Why does this failing with a 401 when communicating with Confluence on lifecycle install?
  • Is there a way around this?

@david when your app receives the installation lifecycle callback, the installation process has not yet completed. If your app returns an error, the installation will be rolled back. Your app is granted access to the tenant’s data later on in the process.

The enabled lifecycle callback (a regular webhook) should be fired once your app has obtained API access to the tenant.

3 Likes

Of course, so the answer is to add a webhook in atlassian-connect.json e.g.

    "webhooks": [
      {
        "event": "connect_addon_enabled",
        "url": "/rest/webhooks/connect_addon_enabled"
      }
      ...
    ]

and then catch that in routes/index.js:

  app.post("/rest/webhooks/connect_addon_enabled", addon.authenticate(), (req, res) => {
    getAppPropertyFromConfluence(
      MY_PROPERTY_NAME,
      addon.httpClient(req)
    );
    ...
  }

which works nicely.

Thanks @epehrson.

1 Like