Json Web Token - JWT
To access a integration in iHub using a JWT the user must setup a JWT Trigger.
This is done by toggle the JWT option on under webhook triggers.
JSON Web Tokens consist of three parts separated by dots (.
), which are:
Therefore, a iHub JWT looks like the following.
xxxxx.yyyyy.zzzzz
The JWT token is then exchanged to a bearer which will be the short lived token that authenticate and triggers the integration flow.
RSA 256 Key Pair
First generate a RSA Key pair and download the the key, once downloaded the key can not be recovered if lost. Only option if the key is lost is to generate a new key.
The downloaded file will contain the information needed to exchange the JWT to a Bearer token.
Sign the JWT token
To sign the JWT token use the header and payload.
Header
{
"alg": "RS256",
"typ": "JWT"
}
Payload
{
"iss": "client_id_from_atlassian",
"sub": "flow_id",
"aud": "https://ihubprod.rixter.net/prod/incoming/token",
"iat": "currentTimeSeconds",
"exp": "plus currentTimeSeconds 1200",
}
Output from the signed will be xxxxx.yyyyy.zzzzz
Exchange the JWT to Bearer
To exchange the JWT to a short lived bearer token,
POST to https://ihubprod.rixter.net/incoming/token
Body:
{
"grant_type":"urn:ihub:jwt:bearer",
"assertion":"{{JWT}}"
}
Replace the {{JWT}} with the signed token from above step.
This URN structure indicates:
urn: Namespace denoting a Uniform Resource Name
ihub: Identifies the IHub system
jwt: Specifies a JSON Web Token (JWT)
bearer: Indicates the token type is bearer
Response
If valid it will return a body like below
{
access_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI0ZjAxOWQ0My00OTAwLTNhOTgtYWEwNi00NjE0M2Y0MGMwNjEiLCJzdWIiOiIzNGNkMzNlNC1iNmFhLTQ4MzctODc1Yi03ZWQ4MWFhMzk2ZDYiLCJleHAiOjE3MTkzMTk0MDMsImlhdCI6MTcxOTMxOTA4MX0.jljJZJMcYD4PapgraNXoZZWUYOR3mPcTgpd_CUeeqCU',
token_type: 'Bearer',
expires_in: 1719319403
}
Use the Bearer token
To trigger the flow send the request to https://ihubprod.rixter.net/incoming/webhook/jwt with the http header Authorization: Bearer {{access_token}}
In this call you will include any data that the integration will process.
Example code for trigging flow
Python
pip install PyJWT requests cryptography
The private key is stored in a file called privateKey.txt in this example
Python
import jwt
import requests
import time
import os
from pathlib import Path
import unittest
class TestIncomingJWTIntegration(unittest.TestCase):
def setUp(self):
self.timeout = 10 # seconds
self.maxDiff = None
def test_should_return_a_valid_bearer(self):
headers = {
"alg": "RS256",
"typ": "JWT"
}
current_time = int(time.time())
payload = {
'iss': 'copy from trigger page',
'sub': 'copy from trigger page',
'aud': 'https://ihubprod.rixter.net/prod/incoming/token',
'iat': current_time,
'exp': current_time + 1200
}
private_key_path = Path(__file__).parent / 'privateKey.txt'
with open(private_key_path, 'r') as file:
private_key = file.read()
token = jwt.encode(payload, private_key, algorithm='RS256', headers=headers)
# print('Generated JWT:', token)
data = {
"grant_type": "urn:ihub:jwt:bearer",
"assertion": token
}
access_token = None
# test generate jwt
try:
response = requests.post(payload['aud'], json=data, headers={'Content-Type': 'application/json'})
response.raise_for_status()
access_token = response.json().get('access_token')
except requests.exceptions.RequestException as error:
print('Error:', error.response.json() if error.response else str(error))
self.assertIsNotNone(access_token)
# test trigger flow
incoming_data = {
"issueKey": "SERVICENOW-1",
"description": "HEJ",
"summary": "Ticket from test case"
}
try:
response = requests.post(
"https://ihubprod.rixter.net/prod/incoming/webhook/jwt",
json=incoming_data,
headers={
'Content-Type': 'application/json',
'Authorization': f'Bearer {access_token}'
}
)
response.raise_for_status()
print(response.json())
except requests.exceptions.RequestException as error:
print('Error:', error.response.json() if error.response else str(error))
if __name__ == '__main__':
unittest.main()
Nodejs
The private key is stored in a file called privateKey.txt in this example
Click here to expand...
var jwt = require('jsonwebtoken');
const axios = require('axios');
import { expect } from "chai";
const fs = require('fs');
const path = require('path');
describe('Incoming JWT integration test', function() { // Use function() to access this.timeout
this.timeout(10000); // Increase timeout to 10 seconds
it('should return a valid bearer', async () => {
const headers = {
"alg": "RS256",
"typ": "JWT"
};
const currentTime = Math.floor(Date.now() / 1000);
const payload = {
iss: 'copy from trigger page',
sub: 'copy from trigger page',
aud: 'https://ihubprod.rixter.net/prod/incoming/token',
iat: currentTime,
exp: (currentTime + 1200)
};
const privateKeyPath = path.join(__dirname, 'privateKey.txt');
const privateKey = fs.readFileSync(privateKeyPath, 'utf8');
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256', header: headers });
//console.log('Generated JWT:', token);
const data = {
"grant_type": "urn:ihub:jwt:bearer",
"assertion": token
};
let access_token;
//test generate jwt
try {
const response = await axios.post(payload.aud, data, {
headers: {
'Content-Type': 'application/json'
}
});
access_token = response.data.access_token;
} catch (error) {
console.error('Error:', error.response ? error.response.data : error.message);
}
expect(access_token).not.to.be.equal(undefined);
//test trigger flow
const incomingData = {
"issueKey": "SERVICENOW-1",
"description": "HEJ",
"summary":"Ticket from test case"
};
try {
const response = await axios.post("https://ihubprod.rixter.net/prod/incoming/webhook/jwt", incomingData, {
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer '+access_token
}
});
console.log(response.data)
} catch (error) {
console.error('Error:', error.response ? error.response.data : error.message);
}
});
});