Skip to main content

Why HMAC verification matters

Before performing any operation (such as exchanging an authorization code for an access token), your application must verify that the request really comes from Genuka. When Genuka sends the installation callback to your application, the request includes the following parameters:
company_id, code, timestamp, hmac, redirect_to
The hmac parameter is a cryptographic signature generated by Genuka. Your application must recompute this signature using your GENUKA_CLIENT_SECRET and compare it to the one received. If they don’t match, you must reject the request — otherwise, your app could be impersonated.
⚠️ Important: If HMAC verification is not implemented or fails, your application will never be validated or published in the Genuka App Store.

Example: Validating HMAC

Below are robust examples to validate HMAC in different programming languages. Each example recreates the signed string exactly, computes the HMAC using your client secret and compares both values using a constant-time comparison.
import crypto from "crypto";

const MAX_AGE_SECONDS = 300; // 5 minutes

export function validateHmac({
  hmac: receivedHmac,
  timestamp,
  companyId,
}) {
  if (!receivedHmac || !timestamp || !companyId) return false;

  // 1. Check timestamp to prevent replay attacks
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - parseInt(timestamp)) > MAX_AGE_SECONDS) {
    return false;
  }

  // 2. Recreate the exact string used to generate the HMAC on Genuka's side
  const stringToHash = `company_id=${companyId}&timestamp=${timestamp}`;

  // 3. Compute the HMAC using your GENUKA_CLIENT_SECRET
  const secret = process.env.GENUKA_CLIENT_SECRET || "";
  const computedHmac = crypto
    .createHmac("sha256", secret)
    .update(stringToHash)
    .digest("hex");

  // 4. Compare in constant time to avoid timing attacks
  try {
    return crypto.timingSafeEqual(
      Buffer.from(computedHmac, "hex"),
      Buffer.from(receivedHmac, "hex")
    );
  } catch (err) {
    return false;
  }
}
All examples include replay attack prevention by validating the timestamp (requests older than 5 minutes are rejected).

Example verification flow

1

Receive callback from Genuka

Genuka sends an installation callback to your app with the following parameters:
/callback?company_id=123&timestamp=1731456700&hmac=abcd1234&code=xyz
2

Validate HMAC before processing

Extract parameters and validate the HMAC before any other processing:
// Express / Next.js example
app.get('/callback', async (req, res) => {
  const { company_id, timestamp, hmac, code } = req.query;

  const isValid = validateHmac({
    hmac: String(hmac || ''),
    timestamp: String(timestamp || ''),
    companyId: String(company_id || ''),
  });

  if (!isValid) {
    return res.status(401).json({ error: 'Invalid HMAC. Request not from Genuka.' });
  }

  // HMAC valid — proceed to exchange the code for an access token
  res.status(200).send('HMAC validated — proceeding with token exchange');
});
3

Exchange code for access token

Once HMAC is validated, proceed to exchange the code for an access token. See the Authentication guide for details.

Best Practices

  • Always verify the HMAC before any other processing (e.g., before exchanging the code for a token).
  • Never log or expose your GENUKA_CLIENT_SECRET.
  • ✅ Use constant-time comparison (e.g., crypto.timingSafeEqual) to avoid timing attacks.
  • ✅ Immediately reject any request with an invalid HMAC and return an appropriate HTTP status (401 or 403).
  • ✅ Optionally, validate the timestamp to prevent replay attacks (for example, reject requests older than 5 minutes).
  • ✅ Record secure logs for failed verifications (without logging secrets or HMAC values) to help debugging.

Summary

StepDescription
1Receive callback parameters (company_id, timestamp, hmac, code)
2Recreate stringToHash = "company_id={company_id}&timestamp={timestamp}"
3Generate HMAC using GENUKA_CLIENT_SECRET
4Compare with received hmac using constant-time comparison
5Reject if invalid, proceed if valid
🔒 Implementing HMAC verification is mandatory to protect your integration and validate your app for the Genuka App Store.