I’ve fully implemented the install flow for Connect apps and I am now trying to make some API requests to my test Jira instance. Unfortunately, no matter what I do, I always run into 401 - Unauthorized
as the response with no further context whatsoever:
Postman screenshot of the HTML formatted error
Initially I have used the atlassian-jwt
library utilities to help generate the appropriate JWT. Since that did not work, I’ve thus far also tried to do every step manually but I keep getting the same result.
Here is my (TS) code. In this example I’m using the /rest/api/3/project/recent
endpoint but I’ve tried many others as well. I just wanted to pick a simple GET endpoint with no arguments to make the implementation as simple as possible.
const connection = await JiraConnectionService.get.by({ clientKey })
assert(connection)
const req = fromMethodAndUrl('GET', '/rest/api/3/project/recent')
const tokenData = {
// tried this in both the `jira:{clientKey}` format as well as just `{clientKey}`
iss: connection.clientKey, // 1000% sure this is correct
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 3600,
qsh: createQueryStringHash(req),
sub: '60f00ad8f749c400680daf47', // tried with and without this
}
const token = encodeSymmetric(tokenData, connection.sharedSecret) // also 1000% sure the shared secret is correct
console.log('Generated JWT token!')
console.log(token)
const res = await Axios.get('https://my-instance.atlassian.net/rest/api/3/project/recent', {
headers: {
Authorization: `JWT ${token}`,
Accept: 'application/json',
},
}).catch(err => {
console.log('Error!')
console.error(err)
})
I’ve also placed a console.log
in the createCononicalRequest
function of the atlassian-jwt
library to validate what the library is hashing and this results in: GET&/rest/api/3/project/recent&
. If I sha-256 this it comes out to:
4953376a4a620a337fab595409def4f695270777a9536fded98a4790b1ad3832
Which corresponds to what I see encoded in the generated JWT (output from jwt-cli
):
➜ ~ jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJiM2UwZjY1MC1hODZjLTNmN2UtYjFkYi00MzY0NTJmODVkZjciLCJpYXQiOjE2Mzg0NjAwODgsImV4cCI6MTYzODQ2MzY4OCwicXNoIjoiNDk1MzM3NmE0YTYyMGEzMzdmYWI1OTU0MDlkZWY0ZjY5NTI3MDc3N2E5NTM2ZmRlZDk4YTQ3OTBiMWFkMzgzMiIsInN1YiI6IjYwZjAwYWQ4Zjc0OWM0MDA2ODBkYWY0NyJ9.kIehfv_fCdgu-ztmtf0t0tjW2jaS1xqu71a7wxb_pBQ
To verify on jwt.io:
https://jwt.io/#id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJiM2UwZjY1MC1hODZjLTNmN2UtYjFkYi00MzY0NTJmODVkZjciLCJpYXQiOjE2Mzg0NjAwODgsImV4cCI6MTYzODQ2MzY4OCwicXNoIjoiNDk1MzM3NmE0YTYyMGEzMzdmYWI1OTU0MDlkZWY0ZjY5NTI3MDc3N2E5NTM2ZmRlZDk4YTQ3OTBiMWFkMzgzMiIsInN1YiI6IjYwZjAwYWQ4Zjc0OWM0MDA2ODBkYWY0NyJ9.kIehfv_fCdgu-ztmtf0t0tjW2jaS1xqu71a7wxb_pBQ
✻ Header
{
"typ": "JWT",
"alg": "HS256"
}
✻ Payload
{
"iss": "b3e0f650-a86c-3f7e-b1db-436452f85df7",
"iat": 1638460088,
"exp": 1638463688,
"qsh": "4953376a4a620a337fab595409def4f695270777a9536fded98a4790b1ad3832",
"sub": "60f00ad8f749c400680daf47"
}
Issued At: 1638460088 12/2/2021, 4:48:08 PM
Expiration Time: 1638463688 12/2/2021, 5:48:08 PM
✻ Signature kIehfv_fCdgu-ztmtf0t0tjW2jaS1xqu71a7wxb_pBQ
It seems to me that I’m doing everything right… Am I overseeing something?
PS, my full atlassian-connect.json
:
{
"name": "Redacted name",
"description": "Redacted name Jira Integration",
"key": "com.redacted-name.jira-integration",
"baseUrl": "https://56cc-62-163-29-19.ngrok.io",
"vendor": {
"name": "Redacted name,
"url": "https://redacted.com"
},
"authentication": {
"type": "jwt"
},
"lifecycle": {
"installed": "/integrations/jira/installed"
},
"scopes": [
"admin"
],
"apiVersion": 1,
"apiMigrations": {
"context-qsh": true,
"signed-install": true
},
"modules": {
"generalPages": [
{
"url": "/helloworld.html",
"key": "hello-world",
"location": "system.top.navigation.bar",
"name": {
"value": "Greeting"
}
}
]
}
}