Connect app authentication without user impersonation issue (jwt missing subject error)

I need to do queries to Confluence API in headless scenario (no user impersonation). I’ve found this guide - Security for Connect apps and start implementing it.
So, I have a Connect app with jwt authentication and read scope. It is successfully installed and I have shared key and OAuth client id.
I’m constructing jwt according to this:

{
  "tnt": "https://<my_instance>.atlassian.net/wiki", // looks like this is required
  "nbf": 1660932251,
  "exp": 1660932371,
  "iat": 1660932251,
  "iss": "urn:atlassian:connect:clientid:<connect-addon-client-id>",
  "aud": "https://oauth-2-authorization-server.services.atlassian.com" // also does not work without this
}

I’m trying to exchange this JWT to an access token. Params are

{"grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"},
{"scope", "READ"},
{"assertion", jwt},

I get an error "{\"error\":\"invalid_grant\",\"error_description\":\"Missing subject\"}". If I pass token with empty sub, it is also fails.
I guess when I will add user account id as sub everything would fly, but this does not fulfill my requirements. I’ve looked for some code samples and most of them (like this one) have user built in the jwt.

Added read_as_user scope to addon, passed user account id as sub in jwt, received token successfully.
The original question how to request a token that has no user associated with it still holds.

I had the same question and found this still open question. I re-read the documentation, and while it lists the sub claim as optional in the Understanding JWT for Connect apps article, the Security for Connect apps article says:

Atlassian Connect apps can use two types of authorization when interacting with the Atlassian host product APIs:

  • Authorization via scopes and app users: Scopes are permissions that are defined in the app descriptor. The app has its own app user with permissions controlled by the admin. The set of allowed actions is the intersection of the scopes and the permissions of the app user. This is the normal authorization method, which you should use unless you need to make server-to-server requests on behalf of a user.
  • Authorization with user impersonation: User impersonation allows your integration to access Atlassian APIs on a user’s behalf. You should only use this method when your app needs to make server-to-server requests on behalf of a user.

So it seems to me that even if you’re not using impersonation, you still need a separate user for the app.