Generating barcode labels and printing them

Hello, I have actually achieved this using a custom component to generate a PDF and then a Printing API to pass the resulting base64 image. It works in the PWA version of Retool but the mobile version of retool does not have the custom component feature. Ideally i can get an API that can generate a pdf ad-hoc and then pass it though to a printing API or have a service that combines both. Is anyone aware of a single service or a combo that may work for my needs?

I was looking at something like this: PDF Generator API Developer Portal
But i see no way to create JWT easily in retool.

Danm, this is a fun implementation of JWTs! I see where they're coming from, but absolute overkill to sign data for making a PDF... A case of optimising for wanting a specific technology over solving a problem :sweat_smile:.

Sorry, I can't help you here, but I really chuckled over their implementation.

Hey @nickport!

We don't currently support using JS in custom auth flows but we can let you know here when we do! In the meantime, your JWT would have to be generated within the app itself using a JS query.

That being said, Boldtech made an awesome blog post here about generating PDFs within Retool and specifically called out APItemplate.io as a good API for PDF generation - maybe it's worth checking out as well if you haven't already?

Thank you for the information. I was able to find this in the search as well, unfortunately APItemplate.io does not look like a good fit for what i am doing. How would i be able to create the JWT using JS? From my understanding of the documentation i need base64 encode, and something that can convert a string into HMACSHA256.

The constraint here being retool mobile, i believe i need to invoke multiple libraries somehow and i am not sure how you use them in standard JS queries apart from custom components.

Ahh I see, that makes sense. I'm not sure this particular one works but you can try saving a library directly to a JS query as a workaround, then call that query with your string as additional scope.

It's quite a few steps as a workaround :sweat: but we can also let you know here when there's better support for JS libraries in mobile.

Hey thanks for the input i think i was able to progress and build a query but it is stuck trying to execute itself and i am not sure what the problem is:

let header = {
  "alg": "HS256",
  "typ": "JWT"
}

let payload = {
  "iss": "API KEY",
  "sub": "email",
  "exp": moment.now(moment().add(10, 's'))
}
let secret = "secret"
let data = "{"+btoa(header)+"."+btoa(payload)+"."+btoa(secret)+"}"

async function sha256(message) {
  // encode as UTF-8
  const msgBuffer = new TextEncoder('utf-8').encode(message);

  // hash the message
  const hashBuffer = await window.crypto.subtle.digest('SHA-256', msgBuffer);

  // convert ArrayBuffer to Array
  const hashArray = Array.from(new Uint8Array(hashBuffer));

  // convert bytes to hex string
  const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('');
  console.log(hashHex);
  return hashHex;
}

return getJWT.trigger({additionalScope: {str: sha256(data)} })

Can you try await on the sha256 function? Retool doesn't support passing promises as additional scope at the moment.

Hmm, its not putting the additional scope into the query. It looks like the body gets "" sent as the bearer token.

:thinking: are you using return getJWT.trigger({additionalScope: {str: await sha256(data)} })? Would you mind also sharing the code for getJWT and how you're passing that JWT in the body of your request?

Sure:

let header = {
  "alg": "HS256",
  "typ": "JWT"
}

let payload = {
  "iss": "API KEY",
  "sub": "email",
  "exp": moment.now(moment().add(60, 's'))
}
let secret = "Secret"
let data = "{"+btoa(header)+"."+btoa(payload)+"."+btoa(secret)+"}"

async function sha256(message) {
  // encode as UTF-8
  const msgBuffer = new TextEncoder('utf-8').encode(message);

  // hash the message
  const hashBuffer = await window.crypto.subtle.digest('SHA-256', msgBuffer);

  // convert ArrayBuffer to Array
  const hashArray = Array.from(new Uint8Array(hashBuffer));

  // convert bytes to hex string
  const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('');
  console.log(hashHex);
  return hashHex;
}


return getJWT.trigger({additionalScope: {str: await sha256(data)} })

I think i may be misunderstanding the API documentation. The app allows me to create a temp JWT for testing purposes and it looks nothing like the one i make.

I reformated it and this should work if i keep this running and have the thing give me a token every 15 minutes that expires every 15 minutes. The only problem here is if btoa() or sha256() is working as intended.

let header =btoa({
  "alg": "HS256",
  "typ": "JWT"
})

let payload = btoa({
  "iss": "API Key",
  "sub": "email",
  "exp": moment.now(moment().add(15, 'm'))
})
let secret = "secret"
// let data = btoa(header)+"."+btoa(payload)+"."+secret

async function sha256(message) {
  // encode as UTF-8
  const msgBuffer = new TextEncoder('utf-8').encode(message);

  // hash the message
  const hashBuffer = await window.crypto.subtle.digest('SHA-256', msgBuffer);

  // convert ArrayBuffer to Array
  const hashArray = Array.from(new Uint8Array(hashBuffer));

  // convert bytes to hex string
  const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('');
  console.log(hashHex);
  return hashHex;
}


return await sha256(header)+"."+await sha256(payload)+"."+await sha256(secret)



1 Like

Yea to looks like btoa encodes the following string:
image

Have been messing around with this a bunch but haven't had luck yet, will report back here if I find something that definitively works. Otherwise curious to hear what others have to say as well.

Hey @nickport

Unfortunately, still haven't had much luck here. Since the mobile viewer runs in Reactive Native as opposed to your browser there are also a number of JavaScript web APIs that aren't available at the moment, like crypto. You might want to consider either:

  1. Using an external API that you can hit which will generate and deliver a JWT for you to use or
  2. Going with a different PDF generator API that doesn't require JWT auth

Sorry to not have a better solution here :pensive:

For those curious, this seems to work on web:

async function HMAC(key, message){
  const g = str => new Uint8Array([...unescape(encodeURIComponent(str))].map(c => c.charCodeAt(0))),
  k = g(key),
  m = g(message),
  c = await crypto.subtle.importKey('raw', k, { name: 'HMAC', hash: 'SHA-256' },true, ['sign']),
  s = await crypto.subtle.sign('HMAC', c, m);
  return btoa(String.fromCharCode(...new Uint8Array(s)))
}

const toBase64 = (obj) => {
  const str = JSON.stringify(obj);
  return btoa(str);
}

const message = toBase64(header) + "." + toBase64(payload);
const signature = await HMAC(secret, message);


return message + "." + signature;