Create signed tokens for JWT authorization

Overview

In this tutorial you are going to learn how to configure Qlik Sense SaaS to use JSON web tokens (JWT) for authorizing users to your tenant.

  • Create a public / private key pair for signing JWTs
  • Configure JWT identity provider
  • JWT format for Qlik Sense authorization
  • Example JWT signing code

Note Only a user with the Tenant Admin role may access the management console settings and add roles to users. If you aren't the Tenant Admin of your Qlik Sense SaaS tenant, please contact your Tenant Admin and direct them to this tutorial.

Create a public / private key pair for signing JWTs

Open a terminal window and use openssl to generate public and private key. Store the private key someplace safe and don't share it. The public key is used in the configuration section.

openssl genrsa -out privatekey.pem 4096
openssl req -new -x509 -key privatekey.pem -out publickey.cer -days 1825

Note If you are using Windows, you need to download and install openssl. You can obtain a copy of openssl from here.

Configure JWT identity provider

Inside the management console, click the Identity provider menu item on the left side of the screen.

a screenshot of the Qlik Sense SaaS management console
with the Identity provider menu item selected

Click the Create new button to begin the configuration. When the configuration panel appears, click the Type dropdown control and choose JWT from the list.

a screenshot of the dropdown list of available
identity management configurations.

The remaining configuration dialogs appear after making the selection from the dropdown. There are four input boxes to fill in:

  1. Description
  2. Certificate (required) - the placeholder for the public key created earlier.
  3. Issuer (required) - the value input here identifies the application or service that issues the JWT.
  4. Key ID (required) - the value input here informs the application which signing key to use.

Note If an Issuer or Key ID aren't supplied during configuration, values automatically generate upon creation. These claims need to be supplied in the JWT for Qlik to trust the JWT and authorize users.

Add the public key to the configuration

Open the file containing the public key in your favorite text editor.

a screenshot of the public key certificate in a text
editor.

Remove any carriage returns or line feeds existing in the text. In Visual Studio Code, removing the characters can be done using a regular expression find and replace on \n. Once the characters are removed, the text should appear in the editor as a single line with no spaces.

Copy the text and paste it into the certificate input field in the configuration.

a screenshot of the public key certificate input into
the identity provider configuration.

Note A public key certificate with trailing spaces, carriage returns, or line feeds triggers an error upon saving the configuration.

Input Issuer and Key ID values

As stated previously, adding an Issuer or Key ID is optional. However, they're required attributes (claims) to include in the JWT sent to Qlik. Both values are strings so entering any information here is suitable as long as a matching value exists in the JWT.

When no Issuer or Key ID values are supplied, upon completing the configuration the management console presents a window with generated values.

a screenshot of the autogenerated issuer and
key id for JWT authorization in Qlik Sense SaaS.

Alternatively, entering Issuer and Key ID values surface the input values in the confirmation screen.

a screenshot of the populated issuer and
key id for JWT authorization in Qlik Sense SaaS.

Complete the configuration

Press the Create button to save the JWT authorization configuration. The confirmation screen appears to copy the Issuer and Key ID values to the clipboard.

JWT format for Qlik Sense authorization

The JWT is made of three pieces:

  1. The payload.
  2. The signing options.
  3. The private key for signing the token.

The payload

The required claims for a Qlik JWT payload are the following:

  • sub - The main identifier (aka subject) of the user.
  • subType - The type of identifier the sub represents. In this case, user is the only applicable value.
  • name - The friendly name to apply to the user.
  • email - The email address of the user.
  • email_verified - A claim indicating to Qlik that the JWT source has verified the email address belongs to the subject.

It's possible to send additional claims in the JWT. Today, only the optional claim groups is read.

Here is a sample JWT payload including the groups attribute:

{
   "sub": "SomeSampleSeedValue", //e.g. 0hEhiPyhMBdtOCv2UZKoLo4G24p-7R6eeGdZUQHF0-c
   "subType": "user",
   "name": "Hardcore Harry",
   "email": "harry@example.com",
   "email_verified": true,
   "groups": ["Administrators", "Sales", "Marketing"]
}

The signing options

The required properties for the signing options of the JWT are the following:

  • keyid - This is a value created or supplied previously with identity provider configuration. Can be a random string.
  • algorithm - The encryption algorithm to protect the JWT with the private key.
  • issuer - This is a value created or supplied previously with identity provider configuration. Can be a random string.
  • expiresIn - The lifespan of the resulting JWT.
  • audience - A required value instructing the Qlik platform how to treat the received JWT.

Here is a sample JWT signing options:

{
   "keyid": "my-custom-jwt",
   "algorithm": "RS256",
   "issuer": "https://my-custom-jwt",
   "expiresIn": "6h",
   "audience": "qlik.api/login/jwt-session"
}

Combining the payload, the signing options, and the private key using a library like jsonwebtoken in javascript produces a token.

Example JWT signing code

The following code is an example express application that creates JWTs.

const express = require('express');

const app = express();
const port = process.env.PORT || 8080;
const fs = require('fs');
const https = require('https');
const jsonWebToken = require('jsonwebtoken');

app.use(express.static(__dirname));

// This is the private key to encrypt the JWT
const jwtEncryptionKey = fs.readFileSync('./certs/privatekey.pem');

// Edit these to reflect your solution
const hostname = '<example.us.qlikcloud.com>';

// kid and issuer have to match with the IDP config and the audience has to
// be qlik.api/jwt-login-session
const signingOptions = {
  keyid: 'my-custom-jwt',
  algorithm: 'RS256',
  issuer: 'https://my-custom-jwt',
  expiresIn: '6h',
  audience: 'qlik.api/login/jwt-session',
};

// These are the claims that will be accepted and mapped anything else will
// be ignored. sub, subtype and name are mandatory. Realm is optional in the
// sub field.
const payload = {
  sub: '<User Subject>',
  subType: 'user',
  name: 'Hardcore Harry',
  email: 'harry@example.com',
  email_verified: true,
  groups: ['Adminstrators', 'Sales', 'Marketing'],
};

// Create the token
const token = jsonWebToken.sign(payload, jwtEncryptionKey, signingOptions);
console.log(token);

// Sample request logic for getting a cookie back from Qlik Sense
function authRequest(signedToken) {
  return new Promise(function (resolve) {
    let cookie;
    const options = {
      hostname,
      port: 443,
      path: '/login/jwt-session',
      method: 'POST',
      headers: {
        Authorization: `Bearer ${signedToken}`,
      },
      rejectUnauthorized: false,
    };

    const request = https.request(options, function (response) {
      cookie = response.headers['set-cookie'];
      console.log(cookie);
      response.on('data', function () {
        resolve(cookie);
      });
      resolve(response.headers['set-cookie']);
    });
    request.on('error', function (error) {
      console.log(error);
    });

    request.end();
  });
}

// Sample login endpoint to call the authentication request function
app.get('/login', (request, response) => {
  return authRequest(token).then(function (res) {
    response.send(res);
    response.end();
  });
});

// Start the express server
app.listen(port, function () {
  console.log(`App is running on port ${port}`);
});

Was this page helpful?