JWT App validation via cURL (PHP) results in 401

I am trying to create an application for Jira Service Desk, however, I keep getting stuck at the following point:
After successfully generating a valid JWT, I try to access the rest URL via cURL, but I keep getting 401, unauthorized:

This is the function where I call the JWT-creature function:

public function generateJWT() {
    $j = new JWTModel();
    $o = new ObjectModel();
    $r = new RestModel();

    $object = $o->getClientByUrl('https://<my-instance-for-demo>.atlassian.net');
    $jwt = $j->generate(10000, $object->key, $object->clientKey, $object->sharedSecret);
    print_r($r->issueData($jwt, 10000));

This is the function that creates the JWT:

public function generate($issue, $key, $clientKey, $sharedSecret) {
    $token = [
        "iss" => $key,
        "iat" => time(),
        "exp" => time() * 3600,
        "sub" => $clientKey,
        "qsh" => hash('sha256', "GET&/rest/api/latest/issue/" . $issue)

    $jwt = JWT::encode($token, $sharedSecret);
    return $jwt;

And eventually the cURL-function:

public function issueData($jwt, $issue) {
    try {
        $curl = new Curl();
        $curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
        $curl->setOpt(CURLOPT_POST, false);
        $curl->setOpt(CURLOPT_HTTPHEADER, ['Authorization: JWT ' . $jwt, 'Content-type: application/json']);
        $curl->post("https://<my-instance-for-demo>.atlassian.net/rest/api/latest/issue/" . $issue);

        return $curl->response;
     } catch (\ErrorException $e) {
        return $e->getMessage();

This is my connect:

  "name": "XXXX XXX",
  "description": "XXX makes your life easier",
  "key": "xxxx-app",
  "baseUrl": "https://xxx.xxxx.nl",
  "vendor": {
    "name": "xxxx",
    "url": "http://xxx.nl"
  "links": {
    "self": "https://xxx.xxx.nl/atlassian-connect.json",
    "homepage": "https://xxx.nl/"
  "authentication": {
    "type": "jwt"
  "lifecycle": {
    "installed": "/installed",
    "uninstalled": "/unstalled"
  "scopes": [
    "READ", "WRITE"
  "apiVersion": 1,
  "modules": {
    "webPanels": [{
      "conditions": [{
        "condition": "user_is_logged_in"
      "key": "xxx-xxx-insert",
      "name": {
        "value": "xxx"
      "url": "/canned?project={servicedesk.serviceDeskId}&issueId={issue.id}&issueKey={issue.key}",
      "location": "atl.jira.view.issue.left.context",
      "layout": {
        "width": "100%",
        "height": "100px"
      "weight": 999

Does anyone sees the point where I made a mistake?

My first suggestion would be to check if the query string hash you generate is accurate. You can do so by using this tool: http://jwt-decoder.herokuapp.com/jwt/decode. The hash for GET&/rest/api/latest/issue/10000& is 3a6df96de7b7c61eee6397e50530d7f4828fc3fcdd7727292e98e1b1016d0ecf.

Once you’ve verified that this works, try making the request in Postman to see if the request itself (with the generated JWT) is valid. This will allow you to identify if the issue is with the request or with your cUrl code.

Hi Willem-Jelle,

Try using $curl->get() instead of $curl->post(). All the other code you wrote is good tho.

1 Like

He Remie,

Thanks for your message! When testing, my JWT and type of connection was good all the time. The only thing I forgot, was changing the curl post to the curl get.

Such mistakes :face_with_thermometer::weary:

:joy: haha, well that is definitely something you can easily overlook