How do I authenticate when building a Connect App in Python?

Hi @ibuchanan,

Can I continue to clarify this for my benefit?

I am building a “Connect App” to be listed in the marketplace and am really struggling with the authentication part.

I have successfully implemented the handshake when the user installs the app and got the tenant info block like this:

{
	"key": "pm.fixed.jira",
	"clientKey": "63f4dcdd-...-9aaca8fa7938",
	"publicKey": "/EHs/c4HGmbzJ...DwoKJfhtDlkuxgbijzQIDAQAB",
	"sharedSecret": "ATCOlyRuiwfowODUCfi8_u8jLRSF63adgz...ESCB1yLxhAueNsttuIlFgD-KTaQAEC6A563",
	"serverVersion": "100207",
	"pluginsVersion": "1001.0.0-SNAPSHOT",
	"baseUrl": "https://fixedpm.atlassian.net",
	"productType": "jira",
	"description": "Atlassian JIRA at https://kdinn.atlassian.net ",
	"eventType": "installed"
}

Now I want my (Python) backend to be able to query the user’s Jira objects like projects or issues. When in standalone mode I have implemented OAuth and call the JIRA API, e.g. to get a list of the client’s projects:

https://fixedpm.atlassian.net/rest/api/3/project

What do I do to authorise this API call as the client who has installed my app?

I have tried generating a JWT auth token but that fails and I read that it isn’t available for API calls. I figure maybe I should use user impersonation but apparently this is strongly discouraged

So how does one access the Jira REST API as the client in a Connect App?

JWT is the right way. The issue you linked is about Jira Server, not Jira Cloud.

What’s difficult is that Atlassian Connect’s JWT uses custom claims; specifically, the QSH (query string hash). Our docs tend to assume you will use one of the Connect frameworks in JavaScript or Java and don’t do a great job specifying the whole auth flow. Working in Python, I suggest my Connect JWT overview and using Bitbucket’s spec for QSH.

Thanks a lot Ian, your video was very informative.

I have worked out what I was doing wrong (at last!). As you say, when your backend is Python, the Connect App/JWT documentation is a little less helpful. For anyone else encountering the same problem I will set out the solution:

I am using the Python library at Bitbucket which is working OK, as long as you know that:

  1. The ‘clientKey’ it wants is the ‘key’ you supplied in atlassian-connect.json, not the ‘clientKey’ field in the tenant info!
  2. You also want to ensure that your parameters are supplied as part of the canonical_uri
  3. Don’t feed the tenant_info block to the encode_token call as **tenant_info

Once I made that fix it works for URLs with or without parameters, and get and post methods.

So to call something like “https://myjira.atlassian.net/rest/api/3/search?jql=project=TST

The code looks like:

req = Request(url, method = method)

token = atlassian_jwt.encode_token(
    method,         # E.g. 'GET'
    url = canonical_uri,    # E.g. '/rest/api/3/search?jql=project=TST'
    clientKey = 'pm.fixed.jira',    # 'key' in atlassian-connect.json, not tenant_info['clientKey'] !
    sharedSecret = tenant_info['sharedSecret'],     # Shared secret from install callback
)
auth = f"JWT {token}"

req.add_header('Content-Type', 'application/json')
req.add_header('Authorization', auth)
result = urlopen(req)

I hope this saves some felow Python people some time!

1 Like