TypeError: client.generateSecuredApiKey is not a function

I’ve tried generate secured API key in algoliasearch 4, both not working on:

  • nodejs environment, and also
  • single-html using CDN (frontend)

the client initialized without .generateSecuredApiKey method. I’ve try console log(client) soon after initializing.

<script src="https://cdn.jsdelivr.net/npm/algoliasearch@4.3.0/dist/algoliasearch.umd.min.js"></script>
<script>
const client = algoliasearch('GD48H95KY2','searchkeyabc123');
console.log(client);

   // <- client.initIndex() and client.search() works fine if i try it here

const publicKey = client.generateSecuredApiKey('searchkeyabc123', {
  filters: '_tag:user42'
});
console.log(publicKey);  // <- code does not reach here due to error
</script>

and this is the output of the client from Firefox
image

is it an expected behaviour? (does the function exposed into browser anyway?)
can anyone point me any hint to make it work? Thanks in advance

Hi @billwill.onggo!

Indeed this is an expected behaviour. There are two reasons for that:

  1. You shouldn’t generate secured API keys from your front end. If you do, users can modify the code and remove restrictions, which can expose hidden, sensitive data.

  2. JS do not support natively the creation of HMAC sha256 . A polyfill for that in the browser would be massive. Therefore, it’s only available for builds that target node.

1 Like

Is it possible to generate the key in JS by manually creating the HMAC sha256 key via this:

var hash = CryptoJS.HmacSHA256(“message”, “secret”);
var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
if yes could you please tell me the value of the placeholder

While it’s possible, if a key has been created frontend, that means the user has access to the original key, and will be able to search with that one. What is your use case that requires a key to be created frontend?

Here’s our use case (friends with OP):

We’re trying to generate secured keys using Cloudflare Workers. It’s a serverless execution environment that lets you write backend code and host it on Cloudflare.

The problem is, Cloudflare Workers don’t currently support node environments. Instead, they use a Webworker type implementation that has limited functionality. For common cryptographic tasks, they offer a Web Crypto API that lets you do things like sign requests.

What is the best way to generate secured keys in such a environment?

1 Like

Ah, that’s something we didn’t consider. I’m having a look at what makes the most sense to implement in an environment like that

1 Like

The source code of generateSecuredApiKey can be found here: https://github.com/algolia/algoliasearch-client-javascript/blob/master/packages/client-search/src/methods/client/generateSecuredApiKey.ts

I haven’t used window.crypto myself yet, so I’m a little lost what the equivalent methods are. I’ll keep you updated when I have got the time to compare the methods

1 Like

Thank you for the quick reply! It didn’t occur to us to look through the repo.

We’ll try making a workaround using the Web Crypto API I mentioned earlier and will post our results here. It’ll only work on Cloudflare Workers but may benefit someone with that specific use case.

It might be worth looking into a proper frontend equivalent though. Something like the polyfill @chloe.liban mentioned. Not a priority, but for completion’s sake. :sparkles:

1 Like

the polyfills I all saw were very big, and would therefore not fit an otherwise small client so well; but if you find a way with crypto.subtle, that would be a good option

1 Like

Whooo! :partying_face:

We managed to create a simple workaround using CryptoJS and the function source code you shared. This approach does not require Node or the Algolia SDK. We don’t even need to expose our Admin API key! (the SDK requires that)


Here’s what we did:

1. Serialize key parameters using this bit of serializer code from the algolia repo (here)

    const serializedParams = Object.keys(parameters)
              .map(key => encode( '%s=%s', key, JSON.stringify(parameters[key]) ))
              .join('&');

2. Create a HMAC from our Search-Only API key.

    const securedKeyHMAC = crypto.algo.HMAC.create( crypto.algo.SHA256, apiKey );                        

3. Add the serialized key parameters to the HMAC and finalize.

    const securedKeyObject = securedKeyHMAC.update(serializedParams).finalize();

4. Convert the result WordArray object into a hex string.

    const securedKeyHex = crypto.enc.Hex.stringify(securedKeyObject);                        

5. Encode the secured Key and serialized key parameters into a Base64 string.

    const securedKey = Buffer.from(securedKeyHex + serializedParams)
              .toString('base64');

And that’s it! We can now use this secured key client-side.

I wasn’t able to use the Web Crypto API I talked about earlier but this approach should work for most non-Node environments. CryptoJS is a little heavy (about 430kB) but I think we can significantly reduce that by removing stuff we don’t need.

Thank you for all the help! :heart:

1 Like

Nice to read, hopefully this will help the next person trying to make a secured Api Key in a cloudflare worker as well. In the mean time I have found this StackOverflow ticket which seems to solve a very similar issue: https://stackoverflow.com/questions/47329132/how-to-get-hmac-with-crypto-web-api (thanks @jonas.badalic)

Note that with the SDK you don’t have to use your Admin API key, a you could create a secured Api Key without giving credentials: algoliasearch('','').generateSecuredApiKey("searchKey", {...})

1 Like

Sorry… I thought we needed to initialize the SDK with our Admin API key. My bad!

1 Like