Has anyone encountered this problem? Error: Signature verification failed for input: <token>

Hello developer community,

I’m in the process of updating my Atlassian Connect Express (ACE) app to comply with the latest security requirements, specifically by enabling signed-install.

I have implemented a new /installed lifecycle endpoint to handle the RS256-signed JWT from Atlassian. The installation process itself seems to complete successfully (my app returns a 204 No Content response to Jira).

However, after the installation, any subsequent request from Jira to my app (for example, when loading a dashboard gadget) fails with a 401 Unauthorized error. My server log shows the following error:
Unauthorized: Unable to decode JWT token: Error: Signature verification failed for input: <here is a token> with method sha256

This leads me to believe that while my custom /installed endpoint is correctly validating the installation token, there might be an issue with how the sharedSecret is being persisted, which then causes the standard ACE middleware (addon.authenticate()) to fail during subsequent JWT validation.

Here are the relevant sections from my atlassian-connect.json descriptor:

{ "authentication": { "type": "jwt" }, "lifecycle": { "installed": "/installed", "uninstalled": "/uninstalled" }, "apiMigrations": { "gdpr": true, "context-qsh": true, "signed-install": true } }

And here is a simplified version of my /installed route handler:

    app.post(‘/installed’, async (req, res) => {
    try {
     // 1. I extract the RS256 JWT from the request
    const token = extractJwt(req);
     // 2. I correctly verify it against Atlassian's public key
    const claims = await verifyLifecycleJwtRS256(token, { ... });
    const clientKey = claims.iss || req.body.clientKey;

    // 3. I use the ACE settings manager to store the installation payload
    await addon.settings.set('clientInfo', req.body, clientKey);

    // 4. I return a success status
    return res.sendStatus(204);

    } catch (e) {
    console.error('Installation failed:', e);
    return res.status(401).send('Unauthorized');
}});

I use “atlassian-connect-express”: “^3.4.0”

My question is: Is this the correct approach for handling the signed-install process? Is using addon.settings.set() the right way to ensure that the standard ACE authentication middleware can find the sharedSecret for later requests?

Any guidance or insight would be greatly appreciated. Thank you!