401 error while using appToken fetched using await addon.getForgeAppToken(installationId);

My connect app is used as remote in forge.
I am using below code in connect app

let { appToken, apiBaseUrl } = await addon.getForgeAppToken(installationId);

as mentioned on Using Forge Remote in Connect frameworks

However after some time it has started giving 401 Error.

const cql = `space in ('${spaceKeys.join("','")}') and type=page and id = ${pageId}`;
const searchUrl = `${apiBaseUrl}/wiki/rest/api/content/search?cql=${encodeURIComponent(cql)}&expand=...;
const headers = {
	Accept: 'application/json',
	Authorization: `Bearer ${appToken}`
};

const apiRes = await fetch(searchUrl, { headers });

api.Res.status is 401

However this was working for some time. what can be wrong?

if appToken is expired, how do i fetch new?

Thanks
Shiv

Hey Shiv,

You’re using addon.getForgeAppToken(installationId) correctly, but the 401 Unauthorized you’re getting is almost certainly because the token has expired. Forge app tokens are short-lived JWTs and must be generated fresh for every request. They are not meant to be cached or reused.


What’s going wrong In your code:

let { appToken, apiBaseUrl } = await addon.getForgeAppToken(installationId);

// Later in the code...
const headers = {
  Accept: 'application/json',
  Authorization: `Bearer ${appToken}`
};

const apiRes = await fetch(searchUrl, { headers });

To avoid 401 Unauthorized errors, make sure you’re calling getForgeAppToken inside the function that executes the API request, so a fresh token is generated every time.

async function fetchConfluenceContent({ installationId, spaceKeys, pageId }) {
  // Generate a fresh token right before the request
  const { appToken, apiBaseUrl } = await addon.getForgeAppToken(installationId);

  const cql = `space in ('${spaceKeys.join("','")}') and type=page and id = ${pageId}`;
  const searchUrl = `${apiBaseUrl}/wiki/rest/api/content/search?cql=${encodeURIComponent(cql)}&expand=...`;

  const headers = {
    Accept: 'application/json',
    Authorization: `Bearer ${appToken}`
  };

  const apiRes = await fetch(searchUrl, { headers });

  if (apiRes.status === 401) {
    console.error('Token expired or invalid. Make sure you generate a fresh app token for every request.');
  }

  return apiRes;
}

Then call this function for example like:

await fetchConfluenceContent({ installationId, spaceKeys, pageId });

Hi @AlexCevicelow How shortlived this token is?

let { appToken, apiBaseUrl } = await addon.getForgeAppToken(installationId);

// Later in the code...
const headers = {
  Accept: 'application/json',
  Authorization: `Bearer ${appToken}`
};

const apiRes = await fetch(searchUrl, { headers });

These are done in same function, and there is not more than 1-2 sec gap between getting a token and using it!

Hi @shiv

The token will last for a few hours, I’m not sure if the time is specified anywhere but it’s certainly longer than a few seconds.

It could be to do with how your app is structured. Where is this code being called? Is it part of a Forge interaction?

getForgeAppToken in ACE accesses a cache which stores the last app token the app has “seen” for a given installation ID, and the cache gets refreshed any time the remote recieves something from Forge.

It’s possible that if you’re doing this as part of some background task for example that doesn’t involve any Forge invocation, the token in the cache has actually expired. For situations like this, you can try using a scheduled trigger to keep the token up to date.