External Game
Introduction
The External Game integration enables you to embed your own games into Qualifio campaigns, while Qualifio manages the data capture and campaign flow. This solution is designed for agencies and developers who want to deliver custom interactive experiences within the Qualifio platform.
In summary:
- The "Game" step can appear before or after the form step (if any).
- All other steps (look & feel, campaign structure) are still managed and configured in Qualifio.
- On desktop, the game is shown in a campaign layout (minisite or iframe).
- On mobile, it takes over the full screen if the smart URL is used.
Prerequisites
Before you begin, ensure you have:
- Familiarity with JWT (JSON Web Tokens)
- Understanding of public/private key cryptography (RS512, PKCS8)
- Experience with the browser postMessage API and iframe communication
- Access to a private/public key pair (PKCS8 format)
- Your public key ready to share securely with Qualifio
Integration Flow Overview
The integration between your external game and Qualifio relies on a secure token exchange using two JSON Web Tokens (JWTs):
- JWT1: Sent from Qualifio (the Player) to your game. It is signed with Qualifio's private key and should be validated with their public key. JWT1 contains data such as the game ID and is passed as a URL query parameter named
token
. - JWT2: Sent from your game back to the Player. It is signed with your private key and validated by Qualifio using your public key. JWT2 contains the game result (score, time spent, etc.) and is sent via
postMessage()
.
The typical flow is as follows:
- Qualifio sends JWT1 to your game:
- When your game loads, Qualifio provides a signed JWT (JWT1) as a URL parameter.
- Your game validates JWT1:
- Your game verifies JWT1 using Qualifio's public key to ensure authenticity and integrity.
- User plays the game:
- The game logic runs as usual.
- Your game sends JWT2 to Qualifio:
- After the game ends, your game creates and signs a new JWT (JWT2) with the results, and sends it to the Player using
postMessage
.
- After the game ends, your game creates and signs a new JWT (JWT2) with the results, and sends it to the Player using
- Qualifio validates JWT2 and saves results:
- Qualifio validates JWT2 using your public key and saves the results (score, time, etc.).
Note: All payloads and token structures are described in detail in the sections below.
Integration Steps
Follow these steps to integrate your external game with Qualifio:
Step 1: Receive and Validate JWT1
When Qualifio loads your game in an iframe, it appends a token
parameter (JWT1) to the game URL:
<iframe src="https://your-game-url?token=<JWT1>"></iframe>
- Parse the
token
parameter from the URL. - Validate JWT1 using Qualifio’s public key (see below).
- JWT1 contains configuration details such as timestamps, campaign language, orientation, and the URL to post results.
{
"iat": 1601889938869,
"exp": 1601894738869,
"iss": "com.qualifio.game.integration",
"refresh": {},
"config_location": "assets/noel.json",
"config_override": {
"lang": "en",
"landscape": 0,
"postURL": "...",
"resetURL": "..."
}
}
Property | Type | Description |
---|---|---|
iat | integer | Issued at (Unix timestamp - 20 minutes). |
exp | integer | Expiration time (Unix timestamp + 60 minutes). |
iss | string | Issuer. Always com.qualifio.game.integration . |
refresh | object | Info for token refresh (not currently used). |
config_location | string | URL for a configuration file (for games with variants). |
config_override | object | Campaign/game config overrides (see below). |
└ lang | string | Campaign language code (e.g., en ). |
└ landscape | integer | 0 = portrait only, 1 = supports both landscape and portrait. |
└ postURL | string | URL to which the game should send results (JWT2). |
└ resetURL | string | URL to restart the campaign. |
Notes:
- The time shift (see
iat
andexp
properties) prevents possible clock drifts.- Always validate JWT1 before starting the game logic.
Qualifio public key (RS512, PKCS8):
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvZSuTqskEn+p7bauY561
NvOVlPq623fB21J67FrUirWlYbBf/OhNWdS1ckV0AP8fgGvkavtya1kIef+6V1d2
hFS1E+T6vy19deEOBrL4h6qW1fP003F74ISnjfqzNd4cpX3ES8zI7nv0b0sw9EPF
fNICqBhgwBQ/ziz6eguYI5CHf/JJfInobWg9OvTQm7147TurAC8JOQDrZij7gEJJ
jj8e4j2HFKS700X+8Kaeubo1pjRUoyomjASg6HcjXgexKYRX3+diD4dj5ms+8aOd
st2J9vE/haRwe33Ax+BxRgqx+n2gwVeFRe666T8nsSnnuEp8TrRUeXbIfiMku1nD
xi+h1jr99kxsZ4R1+qABUMX0O9idDGAXn/vTBKDBIHHrXeRhnFg10CcagdjnImCx
QI0C/8J/fGoCJbXLg3y0wF74iBuNEW/4jmU9yUnQwG6W3iTMS+RwGr44w2RCeD5p
dgjc+XEy4kHJRRJCnsKwnon7bz0JSCbuEEi3AuFj3G0rnEFSRHmPhbhxoYUYT6cA
40FKMVtkAGtBKMr6h5HpTLmdtzsuf1V/sqcCP+7/xzJ0TSbNG/Oa04G331kZPZAS
i97ITuVjC3/ccLwU5uWpWELxehKxDSYPKR7gwSe0LsGJZaiETq4nLQ4dIHLC7BAk
5FNxm9C7KjIs97LUg5M2ztECAwEAAQ==
-----END PUBLIC KEY-----
Step 2: Send Game Results with JWT2
After the game ends:
- Create a new JWT (JWT2) containing:
- The original JWT1
- A
result
array (e.g.,[ ["score", 17], ["time", 36000] ]
) - New
iat
andexp
timestamps - Your issuer string (
iss
)
- Sign JWT2 with your private key (RS512, PKCS8).
- Send JWT2 to the Player using
parent.postMessage
:
{
"jwt": "<JWT1>",
"result": [
["score", 17],
["time", 36000]
],
"iat": 1601891574,
"exp": 1601891694,
"iss": "com.yourCompanyName.games.gameName"
}
Property | Type | Description |
---|---|---|
jwt | string | The original JWT1 received from Qualifio. |
result | array | Array of key-value pairs for game results (e.g., score, time in ms). |
iat | integer | Issued at (Unix timestamp - 20 minutes). |
exp | integer | Expiration time (Unix timestamp + 60 minutes). |
iss | string | Issuer string (e.g., your company and game name). |
Notes:
- The time shift (see
iat
andexp
properties) prevents possible clock drifts.- Only
score
andtime
are currently supported as result keys.
parent.postMessage({ type: "gameOver", payload: JSON.stringify({ jwt: <JWT2> }), url }, "*");
Property | Type | Description |
---|---|---|
type | string | The message type, always set to gameOver for this event. |
payload | string | A JSON string containing the JWT2 with the game results. |
url | string | The postURL sent by the Player in the JWT1, used for validation. |
Step 3: Player Validates Results
- The Player receives JWT2 via
postMessage
and validates it using your public key. - If valid, the Player saves the score and time in campaign statistics and continues the campaign.
- If invalid, the campaign resets to the game step and the result is not saved.
if (event.data && event.data.type === "gameOver") {
// Validate JWT2, save data, continue campaign
}
Code Example
Below is a complete example demonstrating how to read JWT1, validate it, and send JWT2 with the game results using the jsrsasign library. This example is for educational purposes—never expose your private key in production code.
Example: Reading and Sending Signed JWTs
<html>
<head>
<title>HTML game</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsrsasign/10.0.2/jsrsasign-all-min.js"></script>
<style>
body { font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif; font-size: 1em; }
</style>
</head>
<body>
<h3>Minigame</h3>
<p>This page demonstrates how to read JWT1 and return score and time in a signed JWT2.</p>
<script>
// Get JWT1 from URL
const urlParams = new URLSearchParams(window.location.search);
const jwt1 = urlParams.get('token');
// Qualifio Public Key (for demo only)
var publicKey1 = `-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----`;
// Your Private Key (for demo only)
var privateKey2 = `-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----`;
// Validate JWT1
var pubKey1 = KEYUTIL.getKey(publicKey1);
var isValid = KJUR.jws.JWS.verify(jwt1, pubKey1, ['RS512']);
if (!isValid) {
alert('Invalid JWT1!');
throw new Error('JWT1 validation failed');
}
// Parse JWT1 payload
var jwt1Payload = KJUR.jws.JWS.parse(jwt1).payloadObj;
var postURL = jwt1Payload.config_override.postURL;
// Prepare JWT2
var jwt2Payload = {
jwt: jwt1,
result: [
["score", 17],
["time", 26000]
],
iat: Math.floor(Date.now() / 1000) - (20*60),
exp: Math.floor(Date.now() / 1000) + (60*60),
iss: "com.yourCompanyName.games.gameName"
};
var jwt2 = KJUR.jws.JWS.sign(null, {alg: "RS512"}, JSON.stringify(jwt2Payload), privateKey2);
// Send JWT2 to Player
function postResult() {
parent.postMessage({ type: "gameOver", payload: JSON.stringify({ jwt: jwt2 }), url: postURL }, "*");
}
</script>
<button onclick="postResult()">Send Result</button>
</body>
</html>
Recommendations
To ensure the best experience for participants and maintain compatibility with Qualifio, please follow these guidelines:
- Portability: Develop your games using JavaScript and/or HTML5 for maximum compatibility across browsers and devices.
- Performance: Optimize all game assets (images, sounds, etc.) for fast loading. Use vector graphics where possible and implement a preloader if needed.
- Usability: Design your game to work seamlessly on both desktop and mobile devices. Ensure that touch interactions are mapped to mouse and/or keyboard controls as appropriate.
- Responsiveness: Your game must adapt to different screen sizes and work within an iframe, which may be displayed in a reserved area or fullscreen.
- Display Mode: If your game only supports portrait or landscape mode, inform Qualifio so that an orientation prompt can be displayed to users.
Best Practices
- Do not collect or store personally identifiable information (PII) within your game.
- Do not set cookies from your game.
- Never expose private or public keys in your client-side source code.
Dynamic Variables
You can display the user's score and time on the campaign exit screen using these placeholders:
{score}
: The score returned by JWT2{time}
: The time returned by JWT2 (in milliseconds){timeInSeconds}
: The time in seconds ({time}
divided by 1000)
Security Notes
Warning:
- Never expose your private key in any client-side or public code repository. Only use your private key on a secure backend or in a secure build process.
- Share your public key with Qualifio securely (e.g., via encrypted email or a secure portal). This is required for Qualifio to validate your JWT2 tokens.
- Always validate JWT1 using Qualifio's public key before starting the game logic.
Error Handling & Troubleshooting
- If JWT1 validation fails, do not start the game. Display an error or fallback message to the user.
- If JWT2 is not accepted by the Player (e.g., signature error, expired token), the campaign will reset and the result will not be saved.
- Common issues:
- Clock drift: When generating JWT2, set the
iat
(issued at) property to the current Unix timestamp minus 20 minutes, and theexp
(expiration) property to the current Unix timestamp plus 60 minutes. This helps prevent issues caused by differences in system clocks. - Keys format: Both your private key (for signing JWT2) and the Qualifio public key (for validating JWT1) must be in PKCS8 format and use the RS512 algorithm. Using the wrong format or algorithm will cause token validation to fail.
- Iframe communication: Your game will run inside an iframe. Always use
parent.postMessage
to send the JWT2 result back to the Qualifio Player, and ensure you include all required properties (type
,payload
, andurl
).
- Clock drift: When generating JWT2, set the
Supported Result Keys
Currently, only the following keys are supported in the result
array of JWT2:
score
: The user's score (integer)time
: Time spent in the game (milliseconds)
If you need to support additional result keys, please contact Qualifio support.
FAQ
Q: What happens if the JWT2 is invalid or expired? A: The Player will not save the result and the campaign will reset to the game step.
Q: How do I test my JWT2 and public key? A: Use the Token Checker to verify your JWT2 and public key before sharing with Qualifio.
Q: Can I use other algorithms or key formats? A: No, only RS512 with PKCS8 keys is supported at this time.
How to Get Started
To quickly set up and test your external game integration with Qualifio, follow these steps:
Download and Host the HTML Kit
- Download the starter HTML kit:
- Unzip and host the HTML kit on your own server or development environment.
Share Your Game URL
- Provide Qualifio with the URL where your hosted HTML kit (or game) is accessible.
- Qualifio will configure a campaign to use your game URL as the external game step.
Develop and Integrate Your Game
- Replace the demo game in the kit with your own game logic and assets.
- Ensure your game:
- Reads and validates JWT1 from the URL.
- Sends JWT2 with the required result fields (
score
,time
) using postMessage. - Uses your own private/public key pair for JWT2 (PKCS8, RS512).
Test JWT2 and Public Key
- If you change the keys for signing JWT2, test your JWT2 and public key using the Token Checker.
- Once validated, securely share your public key with Qualifio.
Go Live
- After successful testing and configuration, your campaign with the external game is ready to go live.
For more details, see the integration steps and code examples above.