Creating a Simple Shoplazza Node.js OAuth App with HMAC Validation

In this tutorial, we will walk through the creation of a simple Shoplazza OAuth app that retrieves a list of customers. We will also validate the incoming request using HMAC (Hash-based Message Authentication Code) to ensure it's genuinely coming from Shoplazza.

Prerequisites:

  • Node.js installed.
  • Basic knowledge of Express.js.
  • An account on Shoplazza and ngrok (for local development).

Steps:

1. Set Up the Basic Express App:

Install the required npm packages:

npm install express crypto axios

2. Initialize Express and Required Libraries:

const express = require("express");
const crypto = require("crypto");
const axios = require("axios");

const app = express();

3. Define Constants:

Replace CLIENT_ID and CLIENT_SECRET with the values you get from your Shoplazza Developer Platform Dashboard, you will need to create a public app (https://www.shoplazza.dev/reference/create-an-app#public-app) to get these two credentials. BASE_URL should point to your server URL. In this example, it's using ngrok forward the local port 3000 to public for local development (See https://ngrok.com/ for more information), after you set up your ngrok tunnel, please replace https://015d-207-81-205-140.ngrok-free.app to the link ngrok generates for you:

const CLIENT_ID = "<YOUR_CLIENT_ID>";
const CLIENT_SECRET = "<YOUR_CLIENT_SECRET>";
const BASE_URL = "https://015d-207-81-205-140.ngrok-free.app";
const REDIRECT_URI = `${BASE_URL}/auth/shoplazza/callback`;
let access_token = {};

4. HMAC Validation:

The secureCompare function compares two strings securely to prevent timing attacks:

function secureCompare(a, b) {
    return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));
}

The middleware hmacValidatorMiddleWare verifies the HMAC received in the request, you will need to construct a message for HMAC validation (See: https://www.shoplazza.dev/reference/oauth#hmac-validation for details):

function hmacValidatorMiddleWare(req, res, next) {
    const { code, hmac, state, shop } = req.query;
    const map = Object.assign({}, req.query);
    delete map["hmac"];
    const sortedKeys = Object.keys(map).sort();
    const message = sortedKeys.map(key => `${key}=${map[key]}`).join('&');

    const generated_hash = crypto
      .createHmac("sha256", CLIENT_SECRET)
      .update(message)
      .digest("hex");
    if (!secureCompare(generated_hash, hmac)) {
      return res.status(400).send("HMAC validation failed");
    }
    next();
}

5. OAuth Flow:

When the /auth/shoplazza route is accessed, the app will redirect the user to Shoplazza's OAuth page:

app.get("/auth/shoplazza", (req, res) => {
  const scopes = "read_customer";
  const state = crypto.randomBytes(16).toString("hex");

  res.redirect(
    `https://${req.query.shop}/admin/oauth/authorize?client_id=${CLIENT_ID}&scope=${scopes}&redirect_uri=${REDIRECT_URI}&response_type=code&state=${state}`
  );
});

After authorization, Shoplazza will redirect to the /auth/shoplazza/callback route:

app.get("/auth/shoplazza/callback", hmacValidatorMiddleWare, async (req, res) => {
    const { code, hmac, state, shop } = req.query;
  
    if (shop && hmac && code) {
        const { data } = await axios.post(`https://${shop}/admin/oauth/token`, {
            client_id: CLIENT_ID,
            client_secret: CLIENT_SECRET,
            code,
            grant_type: "authorization_code",
            redirect_uri: REDIRECT_URI,
        });
        access_token[shop] = data.access_token;
    
        const result = await axios({
            method: "get",
            url: `https://${shop}/openapi/2022-01/customers`,
            headers: {
               "Access-Token": access_token,
            },
        });
        res.status(200).send(result.data ? result.data : "No customer found");
    } else {
        res.status(400).send("Required parameters missing");
    }
});

Side notes: In this example, this is how to you fill in the URLs in the developer center. And of course, the ngrok link you generated won't be the same as the following example, please do remember to update the ngrok link to yours.

6. Start the Server:

Finally, start the Express server on port 3000:

app.listen(3000, () => console.log("Server is listening on port 3000"));

Conclusion:

You've successfully created a simple Shoplazza OAuth app that uses HMAC validation. This app will redirect users to Shoplazza for authorization and then retrieve a list of customers after successful authentication. Remember to always validate requests to ensure the security of your app.