Hi Hs160227,
Thank you for contacting us.
The application key followed by the HMAC key, concatenated (applicationKey + hmacKey), with HMAC-SHA512.
The signed message is:
The output is lowercase hex.
That is exactly what your /api/hmac does.
iink-ts 4.1.0 has no hmac option in configuration.server. The library ignores it, so it never calls your Worker. It signs the challenge itself with hmacKey, which is 'placeholder' here. The server rejects that signature, hence WRONG_CREDENTIALS.
Please note that we will add a configuration.server.sign in our next iinkTS major version (5.x) : It is a function that receives the message and the application key and returns the signature, so the key will remain on your server.
Best regards,
Gwenaëlle
Hs160227
Hi MyScript team,
I'm building a web app using iink-ts (v4.1.0) and hosting it on Cloudflare Pages. For security reasons, I do not want to expose the hmacKey in the frontend. Therefore, I am trying to use the custom hmac function provided in the server configuration to calculate the HMAC signature on my own Cloudflare Worker backend ( /api/hmac ).
However, when the client tries to connect, it fails with the error: Application credentials are invalid. Please check or regenerate your application key and hmackey.
Here is my frontend setup:
canvas = await iink.Canvas.load(editorEl, "INTERACTIVE_INK", {
configuration: {
server: {
applicationKey: 'MY_APP_KEY',
hmacKey: 'placeholder', // to bypass the internal validation
hmac: async (challenge) => {
const response = await fetch('/api/hmac', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ challenge })
});
const data = await response.json();
return data.hmac;
}
}
}
});
Here is my Cloudflare Worker backend (/api/hmac):
export async function onRequestPost(context) {
const APP_KEY = context.env.MYSCRIPT_APP_KEY;
const HMAC_KEY = context.env.MYSCRIPT_HMAC_KEY;
const { challenge } = await context.request.json();
const encoder = new TextEncoder();
// Attempt 1: Using APP_KEY + HMAC_KEY as the key
const combinedKey = APP_KEY + HMAC_KEY;
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(combinedKey),
{ name: 'HMAC', hash: 'SHA-512' },
false,
['sign']
);
const signature = await crypto.subtle.sign(
'HMAC',
key,
encoder.encode(challenge)
);
// Converting to Hex
const hex = Array.from(new Uint8Array(signature))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
return new Response(JSON.stringify({ hmac: hex }), {
headers: { 'Content-Type': 'application/json' },
});
}
I have tried different variations (using just HMAC_KEY as the key, using Base64 instead of Hex, etc.), but all of them return WRONG_CREDENTIALS.
Could you please clarify the exact algorithm used by iink-ts internally for computeHmac?
Should the secret key be the applicationKey, the hmacKey, or a concatenation of both?
What is the exact string being signed? Is it just the challenge?
Should the final output returned to iink-ts be Hex encoded or Base64 encoded?