HMAC function & base64encoding don't play together

I’m running Nakama 3.12 using the postgres docker distribution on a Linux machine. In general, everything’s working great. But as I’ve tried to use the hmacSha256Hash function in the JS runtime, I’ve started encountering problems.

My goal: to generate an HMAC signature in a URL-safe string in a way that can be replicated on the receiving system with Python’s HMAC implementation.

The comments surrounding the implementation suggest the hmacSha256Hash function will return a string. This does not seem to be the case–it delivers an ArrayBuffer–but that’s fine/expected: most other languages return bytes from an HMAC calculation.

The problem is that it’s unclear what to do with these bytes in the runtime. I’ve tried various options:

  • Printing the function’s result directly yields garbage (as expected)
  • Sending them to binaryToString (nk.binaryToString(nk.hmacSha256Hash("some data", "secret")) generates an error about that function expecting a Uint8Array
  • Converting that ArrayBuffer to a Uint8Array (nk.binaryToString(new Uint8Array(nk.hmacSha256Hash("some data", "secret")))) generates the same error “TypeError: expects a Uint8Array object\n\tat*runtimeJavascriptNakamaModule).binaryToString.func1 (native)\n\tat InitModule (index.js:21:57(23))\n\tat native\n”
  • nk.base64Encode(nk.hmacSha256Hash("some data", "secret")) doesn’t generate an error, but produces obviously bad output: %!(EXTRA string=77-977-977-9ADPvv73vv71W77-977-977-9Se-_ve-_ve-_ve-_vSgx77-977-9E–_vUoS77-904Pvv707Nu-_ve-_vQ==)
  • nk.base64EncodeUrl(nk.hmacSha256Hash("some data", "secret")) fails, complaining that it will only take a string, not an ArrayBuffer (seems odd for these functions to have different signatures, no?)

I haven’t been able to find example code showing how to generate an HMAC and its hex digest in the JS runtime. Apologies for what’s doubtless a basic question. Although I used to write a lot of Node, I’m now badly out of date, and never spent much time with TypeScript at all.

Looking at this more, I’m not sure anything is coming back from hmacSha256Hash at all. The returned ArrayBuffer has its byteLength set to null. And any UIntXArray I create from it has length 0. Feels like the hmac isn’t getting calculated, but I don’t see a method I need to invoke or any hint that this is an async process (wouldn’t expect it to be).

Hey @sbma44 I’m going to take this back to the team and let you know tomorrow.

1 Like

Hello @sbma44, there’s a few changes that will be part of the Nakama 3.13 release that should solve your issues, with those changes the hmacSha256Hash function will return an ArrayBuffer, and you’ll be able to use the base64Encode function (which will accept both an ArrayBuffer or a string) to get an URL safe string out.

We don’t have an exact date for the release yet but it shouldn’t be too far away, alternatively you can build Nakama from the master branch to start using these changes right now.

Hope this helps.

That’s great news! Thanks a ton

Hi all,
I was having the same issues with hmacSha256Hash and found this helpful post.

I have just upgraded to nakama:3.13.1 and retested with this TS code finding a couple of errors.

hash = nk.hmacSha256Hash("hellosworlds", "145709ygbx");"Hash: ", hash);

// Logging this shows it is a ArrayBuffer
//"msg":"Hash: %!(EXTRA goja.ArrayBuffer={0xc0005b62d0})"

// Which works fine with base64Encode
b64String = nk.base64Encode(hash);

// This errors: TypeError: expects data to be UTF-8 encoded
hmacString = nk.binaryToString(hash);

//Also errors: TypeError: expects a ArrayBuffer object 
hmacString = nk.binaryToString(new Uint8Array(hash));

Is that how binaryToString should be called?
As the docs say it returns a string its really not clear what I need to do with the ArrayBuffer. I’d be happy to help improve the docs if I can because I can’t be the only one :wink:


Hello @toby, the result of nk.base64Encode is a string, it’s not clear to me why you’d need to use nk.binaryToString?

I wanted the HMAC hash as a string and not the base64 encoded version of that string. Unless I am missing something fundamental about how hmac works (I just assumed it was hash func like md5 but had a key)

hash = nk.hmacSha256Hash(“hellosworlds”, “145709ygbx”)
hmacHash = binaryToString(hash)

The built in nk.binaryToString function expects the data to be encoded in UTF-8, the output of nk.hmacSha256Hash is binary data which has no character encoding, to represent it as a string you should use the nk.base64Encode function, which encodes arbitrary binary data into base64.

Do you have a specific need to represent it into some format other than base64?