Web app authentication with QSEoW (web ticket + session cookie)
In one of the previous post we saw how to use web ticket to establish connection with Qlik Sense Engine. And probably for most cases the approach there is enough.
But what if our web app have a requirement to communicate with other Qlik service(s) (in addition to the Engine)? For example, we want to list the reload tasks to which the user have access (this information is provided by Qlik Repository Service REST API)?
The problem with the web ticket is that once it is consumed it can't be used again. And if we use the ticket to open web socket Engine connection then we can't use the same ticket to get the list of the reload tasks. We'll end up with situation where the Engine connection is authenticated and established but upon requesting the list of reload tasks Qlik will response with Unauthenticated
.
To overcome this we can "exchange" the web ticket for a Qlik session. The exchange will be in a form of a session cookie. We'll have the session cookie set in the browser and then our web app can freely open Engine connection (without web ticket) and communicate with other Qlik Sense services. In similar way the Hub is doing it.
Pre-requisites (QMC)
Couple of settings have to be set in the Virtual Proxy
before we can start (if they are not set already):
White list
- add the web app url to the host white list
SameSite attributes
- make sure that theSameSite
attributes are set toNone
(at least)
Code workflow
The code workflow is:
- get web ticket from Qlik (in the same manner as in the previous post)
- use the ticket to retrieve the content of any static Qlik resource via HTTP request. In this example I'm going to use
/resources/autogenerated/qlik-styles.css
but it can be any other static file that requires authentication in order to be viewed/downloaded - since we are using the ticket in HTTP request the response from Qlik will be the content of the requested resource + session cookie
- once session cookie is set (the browser is taking care of this) we can continue and connect to the Engine and "ask" Qlik for different data (in our case get list of reload tasks)
Code
- retrieve the static file content
export const load = async ({ url, params }) => {
// look for "qlikTicket" query parameter
const qlikTicket = url.searchParams.get("qlikTicket");
// if "qlikTicket" is present
// use the ticket to retrieve the content of "qlik-styles.css"
// if all is ok Qlik will send the session cookie
// and the browser will set it
if (qlikTicket) {
const qlikStylesURL = `https://${qsHost}/resources/autogenerated/qlik-styles.css?QlikTicket=${qlikTicket}`;
const qlikStylesResponse = await fetch(qlikStylesURL, {
credentials: "include",
});
}
return true;
};
- variables
const qsHost = "my-sense-instance.com";
const reloadURI = `http://localhost:3000`;
const xrfkey = "1234567890123456";
const qsReloadTasksURL = `https://${qsHost}/qrs/reloadtask?Xrfkey=${xrfkey}`;
let qlikApps = [];
let qlikReloadTasks = [];
- Notification events
// redirect to the login page if the Engine connection response indicates that we have to authenticate
session.on("notification:OnAuthenticationInformation", (data) => {
if (data.loginUri) goto(data.loginUri);
});
// once the Engine connection is established, get the list of the reload tasks (in the logged in user context)
session.on("notification:OnConnected", async () => {
qlikReloadTasks = await fetch(qsReloadTasksURL, {
credentials: "include",
headers: {
"X-Qlik-Xrfkey": `${xrfkey}`,
},
}).then((res) => res.json());
});
- HTML structure
<main>
<div>
<h1>Qlik apps:</h1>
{#each qlikApps as qlikApp}
<div title={qlikApp.qDocId}>{qlikApp.qDocName}</div>
{/each}
</div>
<div>
<h1>Qlik Reload Tasks:</h1>
{#each qlikReloadTasks as reloadTask}
<div title={reloadTask.id}>{reloadTask.name}</div>
{/each}
</div>
</main>
Result
Once the whole process is complete we should see something like:
And if we inspect the network request will see:
- the
qlik-styles.css
request is with attachedQlikTicket
the response is200 OK
and the response headers are containing the session cookie
- we can see the cookie being set in the browser
- both the Engine connection and Repository API request are made without any ticket added to the urls
Complete code
The full code is available at the repo below. Follow the readme there to install and run it.
Hope you liked it!
Stefan