Why does HMAC validation keep failing in my embedded app development?

Hi there,

I’m currently testing out a Shopify embedded app development with Sveltekit. I’m constructing my handler file that will run as hooks and in this file, it will run the app installation request validation via the /api/auth/callback route.

However, I’ve noticed that the hmac that I receive in my query parameter is continually different to what I computed.

Here’s a snippet of my code:

case '/api/auth/callback':

            // Obtain values to check from the incoming request
            const incomingState = searchParams.get('state')
            const hmac = searchParams.get('hmac')
            const code = searchParams.get('code')
            searchParams.delete('hmac')
            searchParams.sort()

            // Instantiate values to check the incoming request against
            const generatedNonce = generateNonce(shop)
            const hostNameMatching = /[a-zA-Z0-9][a-zA-Z0-9\-]*\.myshopify\.com[\/]?$/
            const generatedHash = crypto.createHmac('sha256', SHOPIFY_API_SECRET).update(`?${searchParams.toString()}`).digest('hex')

            if (generatedNonce === incomingState && shop!.match(hostNameMatching) != null && hmac === generatedHash) {
                // I can never get here because the hmac is never the same as generatedHash
                const tokenRequestBody = {
                    client_id: SHOPIFY_API_KEY,
                    client_secret: SHOPIFY_API_SECRET,
                    code
                }

                const response = await fetch(`https://${ shop }/admin/oauth/access_token`, {
                    method: 'POST',
                    headers: {
                        'accept': 'application/json'
                    },
                    body: JSON.stringify(tokenRequestBody)
                })
                const { accessToken } = await response.json()

            // the code continues

Is anyone facing the same issue?

Thanks

P.S. I’m aware of the best practice of safe comparing the hmac and the generatedHash. This is just hacked together for now with cleanup to be done once I get it wired up and working.

Hey all, bumping this thread to see if anyone can assist. Also worth noting that the ? in the searchParams.toString() is part of my attempt to troubleshoot as it didn’t work when I used searchParams.toString().

Hi @ksoenandar ,

I’m not a Node.js dev but I figured I’d weigh in. It looks good here as you’re removing the hmac parameter from the message string and encoding it using Hex/Base16, which is correct. I’m not sure what searchParams.toString() is expected to produce but you will need to construct the parameters in a query string format such as:

abc=123&def=456&ghi=789

Note also that the parameters need to be sorted alphabetically.

Maybe try make that change and report back?

I’ve done something similar here in Java: https://github.com/shopstack-projects/shopstack-security-hmac#scenario-2—using-http-query-parameters

Edit: you can see the message format in the Shopify docs here: https://shopify.dev/apps/auth/oauth/getting-started#remove-the-hmac-parameter-from-the-query-string

Hey @AlanGuerin , thanks for the response!

searchParams.toString() is to convert the URLSearchParams object to string in the format required by Shopify (you can see the documentation here). It is super confusing as I’ve triple checked everything but the computed HMAC is always different for me.

Any other idea?

Thanks

Kevin

Hi Kevin - thanks for confirming that.

You mentioned it’s a “Shopify embedded app” (using Shopify App Bridge?), if so maybe check out session tokens. I also found a small library built for SvelteKit which verifies the Shopify token. Maybe take a look at that and see if it can give you any hints or guidance on how to proceed.