Cacheable iframes - could not find authentication data on request

We’re attempting to switch one of our Dynamic Content Macros to Cacheable app iframes.

(FYI - the app uses atlassian-connect-express@3.2.0, which should support cacheable iframes)

As a first step, we successfully removed all context params previous passed in the app’s URL (e.g. pageId, pageVersion, macroId), and replaced them with a client-side AP.context.getContext() call to fetch these params.

Next, we replaced the URL for all.js to https://connect-cdn.atl-paas.net/all.js, rather than loading from relative to the tenant base URL.

At this point (before enabling the all-important cacheable: true in our app’s descriptor), we tested the above changes to confirm that the app continues to function as normal, which it does.

As expected, the iframe URL at this point still includes all of the context params (xdm_e etc.), including the jwt token used to authenticate the request.

After adding "cacheable": true into the app descriptor, the iframe URL no longer contains any context params (as expected).

However, our app now fails to load, with the message “Unauthorized: Could not find authentication data on request”.

Looking through the ACE source, this message appears to come from the extractJwtFromRequest() function, when it fails to find a JWT token in either the query, request body, or authorization request header.

Obviously we’ve missing something, but we can’t see anything in the Cacheable app iframes guide that we’ve missed.

Any help would be greatly appreciated.

2 Likes

I think I’ve figured out the problem.

In atlassian-connect-express, routes are secured with JWT by including addon.authenticate() middleware as part of the route:

// If JWT is invalid, the addon.authenticate() middleware will respond with 401 Unauthorized
app.get('/my-thing', addon.authenticate(), (req, res) => {
  ...
  res.render('my-thing');
});

For cacheable iframes, this middleware needs to be removed, so the route effectively becomes public (unprotected):

app.get('/my-thing', (req, res) => {
  ...
  res.render('my-thing');
});

Naturally this means that the response for the route should not contain any sensitive or private information, since anyone can now call this route, without needing a valid JWT.

Depending on your use case, it may be possible for someone to circumvent licensing (e.g. by manually inserting an iframe macro on their page with the URL of your app; since there is no longer any JWT check on the server. You may need to evaluate whether this is an issue for your add-on (in our case it wasn’t).

Just further to this, it is possible to see if the license is active from the context, e.g.

AP.context.getContext(context => {
  if (context.license && context.license.active) {
    // Active trial | Active subscription | Cancelled subscription
  } else {
    // Unlicensed | Inactive trial | Inactive subscription
  }
});

Cancelled subscription remains active license until the end of the billing period, at which point it reverts to Unlicensed

1 Like

Unfortunately for local development (when atlassian-connect-express starts an ngrok tunnel and automatically registers the app with a dev Confluence Cloud instance), the app is Unlicensed.

This means that in the context returned from AP.context.getContext(), no license property is present; and the app fails the license check.

Is there a way to tell atlassian-connect-express when registering the app to automatically mark it as licensed in development?

Alternatively I could output the value of app.get('env') in my rendered markup; and have an exception in the client-side license check for when running in development:

<div id="my-app" data-env="development"/>
const env = AJS.$("#my-app").data("env");

AP.context.getContext(context => {
  if (context.license && context.license.active || env === "development") {
    // All OK...
  }
});

But I can’t be the first person to have hit this problem? Surely there’s a better way than this?

You can create a private listing on the marketplace (go to https://marketplace.atlassian.com/manage/apps/{appid}/private-listings) and with this listing’s token you can set the license on your Atlassian cloud dev system to whatever state you want to.
You do that in the UPM:
30 37

Thanks, I’m using access tokens in my staging/support Cloud instances.

However there is no option to enter an access token for an addon automatically registered by ACE in development? The field just isn’t there in UPM?

Interesting, I am not using ACE. Does it have a different app (add-on) id?

Ah, silly me…I see the problem.

I didn’t have “Enable Private Listings” (in UPM settings) turned on in my dev cloud instance. I only had “Enable development mode” turned on.

With the setting now enabled, I get the Access token field showing; and I’ve been able to apply a token and change the status to Active.

All good now. Thanks @christoffer, for nudging me towards the solution with your earlier suggestion.

Thanks for coming back - I was spinning this in my head “How does the instance know it’s a dev version?”, now I can rest in peace

@scottohara and @christoffer, thank you so much for sharing your solutions.

I have been pulling out my hair over this and it seems that this might be exactly what I was looking for!

Cheers,

Dirk de Klerk