Bitbucket Cloud API: Unable to authenticate against Bitbucket using JWT token from NodeJS 8

I’m building a simple bitbucket cloud app that is supposed to react to webhooks, one of reactions is to post a comment to a commit or pull-request.

So far I came up with following atlassian-connect.json

  "modules": {
    "webhooks": [
        "event": "*",
        "url": "https://***"
  "key": "antons-devil-shadow",
  "name": "Anton's Devil Shadow",
  "description": "An app that validates pull request and commit syntax",
  "apiVersion": 2,
  "vendor": {
    "name": "Anton Boritskiy"
  "links": {
    "self": "https://***"
  "lifecycle": {
    "installed": "https://***",
    "uninstalled": "https://***"
  "baseUrl": "https://***",
  "authentication": {
    "type": "jwt"
  "enableLicensing": false,
  "scopes": [
  "contexts": ["account"]

the installation endpoint was successfully called when I installed the app to my profile:
I created a single private repo by hands (i.e via borwser and my user account) to test the whole thing

and then I wanted to get authentication running, so I pulled out nodejs 8, installed few packages:

  "dependencies": {
    "atlassian-jwt": "^1.0.2",
    "axios": "^0.18.0",
    "moment": "^2.14.1"

and I’m trying to run the following code

var JWT    = require('atlassian-jwt');
var moment = require('moment');
var axios  = require('axios');

const now    = moment().utc();
const apiUrl = '';
const req    = JWT.fromMethodAndUrl('GET', apiUrl);

const tokenData = {
    "iss": 'antons-devil-shadow',
    "iat": now.unix(),
    "exp": now.add(3, 'minutes').unix(),
    "qsh": JWT.createQueryStringHash(req),
    "sub": "557058:6dcdb6c9-da5c-4b08-9929-a81b560cf71b"

// here I'm using the value which I have received in the /installed callback, field "sharedSecret"
const secret = '***';

const token = JWT.encode(tokenData, secret);

const decodedToken = JWT.decode(token, secret);

axios.defaults.headers.common['Authorization'] = 'JWT ' + token;

axios.get(apiUrl, {
    params: {}
    .then(function (response) {
    .catch(function (error) {

so far I’m only getting 401 from Bitbucket. But I don’t see anymore where is the mistake.
Could you please point me in the right direction, it feels like I’m missing something obvious.

Hi @anton.boritskiy,

For reference, please see this sample app that I created.

JWT OAuth reference code:

let unverifiedClaims = '';
try {
    unverifiedClaims = jwt.decode(req.query.jwt, '', true); // decode without verification;
} catch (e) {

let issuer = unverifiedClaims.iss;          // connection id from /installed
console.log(`Connection ID from /installed lifecycle: ${issuer}`);

const now = moment().utc();
const tokenData = {
    "iss": addon.key,
    "iat": now.unix(),                      // the time the token is generated
    "exp": now.add(3, 'minutes').unix(),    // token expiry time (recommend 3 minutes after issuing)
    "sub": issuer                           // connection id from /installed

const secret = process.env.my_app_share_secret; // app sharedSecred from /installed

const token = jwt.encode(tokenData, secret, 'HS256');

And you should be able to get accessToken to be used for REST calls.

Anne Calantog

Hi @acalantog,

thanks for the reference that really helped me, I can now authenticate.

From the docs here
and especially here

it wasn’t clear what to put in the sub field, the doc example: “connection:479” doesn’t really help.
So I tried to use account_id, user_uuid and just my username, all turned out wrong - I should have used the clientKey which is provided during the “installed” callback call, it looks like “ari:cloud:bitbucket::app/{…”
Maybe worth updating the doc with the link to the reference app and maybe a better explanation of the sub claim.

This topic can be closed I think, thank you!

Hi @anton.boritskiy,

I marked it as “Solution”. We are continuously improving our docs and we appreciate your feedback. Please click on the “Give docs feedback” and a ticket number would be given to you and someone from Atlassian will triage it.

Anne Calantog