Guiding principles for OAuth impersonation

Introduction

In Qlik Cloud, OAuth impersonation tokens enable web applications to create access tokens on behalf of users within the tenant where the OAuth client is registered.

OAuth impersonation tokens are useful in web applications where you want to access resources and content from a Qlik Cloud tenant, but the tenant uses a different identity provider than your web application. In embedded analytics use cases, impersonation tokens mitigate content blocking by replacing third-party cookies in modern browsers with OAuth tokens to maintain state.

As you prepare to implement OAuth impersonation tokens with Qlik Cloud, it’s important to mind the following guiding principles to ensure a scalable and secure solution.

Guiding principles for OAuth impersonation

Do not expose machine-to-machine client secrets in the frontend

Machine-to-machine OAuth clients in Qlik Cloud require sending the client ID and client secret in the request payload to get a response from a tenant. However, sharing a client secret on the frontend of an application exposes it to anyone with access to your web application. Once a user has acquired the client secret, your tenant can become compromised because they can use it to access information on the tenant that you may not wish to share from any HTTP client interface.

Keep client secret values safe, secure, and out of reach by storing them in the backend of your web application where users may not access them.

Use a backend web application for issuing impersonation tokens

To issue impersonation tokens for embedded analytics to the frontend, it’s recommended to create an endpoint in your web application backend. Your frontend can call this endpoint to receive the token and authorize content rendering based on the user’s access control.

Here’s a code snippet from the OAuth impersonation example using qlik-api.

import { auth as qlikAuth, users as qlikUsers } from "@qlik/api";

const qlikConfig = {
  authType: "oauth2",
  host: "https://tenantName.region.qlikcloud.com",
  clientId: "OAuth impersonation client Id",
  clientSecret: "OAuth impersonation client secret",
};

//set the host configuration to talk to Qlik tenant
qlikAuth.setDefaultHostConfig(qlikConfig);

//access token method the frontend will call
app.post("/access-token", requiresAuth(), async (req, res) => {
  const userId = req.session?.user?.id;
  try {
    //call to Qlik Cloud tenant to obtain an access token
    const accessToken = await qlikAuth.getAccessToken({
      hostConfig: {
        ...qlikConfig,
        userId,
        noCache: true,
      },
    });
    console.log("I got an access token!");
    //access token returned to front end
    res.send(accessToken);
  } catch (err) {
    console.log(err);
    res.status(401).send("No access");
  }
});

Set scopes on impersonation tokens explicitly

Scopes are the first layer of access control for OAuth client access tokens. Impersonation access tokens will inherit the scopes set in the OAuth client configuration for the machine-to-machine impersonation grant type. You can reduce the scopes applied to the access token when making the request. Include the scopes property in the request payload and set its values to the desires scopes for the access token, separated by spaces. Here’s an example using fetch:

const hostConfig = {
  host: "https://tenantName.region.qlikcloud.com",
};

const payload = {
  client_id: "OAuth impersonation client Id",
  client_secret: "OAuth impersonation client secret",
  grant_type: "urn:qlik:oauth:user-impersonation",
  user_lookup: {
    field: "subject",
    value: "SUBJECT_VALUE",
  },
  scope: "user_default",
};

async function getAccessToken(hostConfig, payload) {
  const getToken = await fetch(`${hostConfig.host}/oauth/token`, {
    method: "POST",
    mode: "cors",
    credentials: "include",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(payload),
  });

Use multiple clients for separating management from issuing tokens

For an additional security layer to prevent impersonation tokens with more scopes than desired, consider creating two OAuth clients. One client dedicated to creating impersonation tokens with only the user_default scope. A second client to perform administrative operations on the tenant like user information lookups and other actions.

While this approach adds management overhead to each tenant you handle, it offers benefits by separating the concerns to ensure access tokens are scoped according to their intended use.

Was this page helpful?