Cannot Use System Token to call APIs from Forge Remote

I have a forge app that can comment on tickets and respond to events and call a remote backend. This all works fine. However while following this documentation

I cannot figure out how to authenticate properly. I can see that the x-forge-oauth-system header is passed to the request but I have tried so many configurations to get this to authenticate but I keep getting a 401 error when calling the Jira APIs from the remote. I had previously used a personal access token with the atlassian python library
https://atlassian-python-api.readthedocs.io/index.html
and I tried using both the session method of authentication as well as just the “token” method but nothing works.

I feel like I’m missing something here

For more context, I tried the python snippet from this url
https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-issue-issueidorkey-comment-post

but modified the headers

# This code sample uses the 'requests' library:
# http://docs.python-requests.org
import requests
from requests.auth import HTTPBasicAuth
import json

url = "https://your-domain.atlassian.net/rest/api/3/issue/{issueIdOrKey}/comment"

    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json",
        "Authorization": f"Bearer {system_token}"
    }

payload = json.dumps( {
  "body": {
    "content": [
      {
        "content": [
          {
            "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.",
            "type": "text"
          }
        ],
        "type": "paragraph"
      }
    ],
    "type": "doc",
    "version": 1
  },
  "visibility": {
    "identifier": "Administrators",
    "type": "role",
    "value": "Administrators"
  }
} )

response = requests.request(
   "POST",
   url,
   data=payload,
   headers=headers,
   auth=auth
)

print(json.dumps(json.loads(response.text), sort_keys=True, indent=4, separators=(",", ": ")))

and it does not work

Hi @joe1 , I can’t definitively say what’s going wrong here with the information provided, are you able to provide me with your appId and a traceId of a request that leads to a 401 (this is value of the X-B3-Traceid response header).

Here are some things to check as well:

  • Did you define the required scope for this API in your app’s manifest.yml
  • OAuth tokens that you receive from Forge are tenant isolated, which means that you can’t use them outside the tenant which the invocation occurred. I would recommend using the baseUrl from the FIT claims (the app.apiBaseUrl claim) instead of constructing it yourself.
1 Like

@BoZhang thank you so much! that was it, I was trying to reconstruct the url based on my jira instance but I was able to pull the apiBaseUrl from the FIT Token. Here is a simplified python version for anyone that comes after me:

from authlib.jose import jwt, JsonWebKey
from atlassian import Jira
import requests
# need to add authlib, requests, and atlassian to your project

def validate_context_token(invocation_token, app_id) -> dict:
    # URL for Atlassian Forge JWKS
    jwks_url = 'https://forge.cdn.prod.atlassian-dev.net/.well-known/jwks.json'

    # Step 1: Retrieve the JWKS
    response = requests.get(jwks_url)
    jwks = JsonWebKey.import_key_set(response.json())

    # Step 2: Verify the JWT
    claims = jwt.decode(invocation_token, key=jwks, claims_options={"aud": {"essential": True, "value": app_id}})

    # Step 3: Validate the claims (e.g., expiration)
    claims.validate(leeway=0)

    return claims

# this is a FastAPI endpoint, but any server framework is fine
@router.post('my-remote-url')
async def my_remote_url(request: Request):
  authorization = request.headers.get("authorization")
  claims = validate_forge_jwt_token(authorization)
  system_token = request.headers.get("x-forge-oauth-system")
  jira_client = Jira(url=claims.get('app').get('apiBaseUrl') , token=system_token, cloud=True)
  jira_client.issue_add_comment("ABC-123", "Hello World!")
1 Like