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://***.execute-api.eu-central-1.amazonaws.com/live/webhook"
      }
    ]
  },
  "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://***.execute-api.eu-central-1.amazonaws.com/live/"
  },
  "lifecycle": {
    "installed": "https://***.execute-api.eu-central-1.amazonaws.com/live/installed",
    "uninstalled": "https://***.execute-api.eu-central-1.amazonaws.com/live/uninstalled"
  },
  "baseUrl": "https://***.execute-api.eu-central-1.amazonaws.com/live/",
  "authentication": {
    "type": "jwt"
  },
  "enableLicensing": false,
  "scopes": [
    "repository",
    "repository:write",
    "repository:admin",
    "pullrequest",
    "pullrequest:write",
    "webhook"
  ],
  "contexts": ["account"]
}

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

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 = 'https://api.bitbucket.org/2.0/repositories/aboritskiy/test';
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);
console.log(token);

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

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

axios.get(apiUrl, {
    params: {}
})
    .then(function (response) {
        console.log(response.data);
    })
    .catch(function (error) {
        console.log(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) {
    console.log(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.

Cheers,
Anne Calantog

Hi @acalantog,

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

From the docs here
https://developer.atlassian.com/cloud/bitbucket/authentication-for-apps/
and especially here
https://developer.atlassian.com/cloud/bitbucket/understanding-jwt-for-apps/#claims

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.

Cheers,
Anne Calantog