Skip to content
man glossary/appsecret-proof

glossary define appsecret-proof

Name

App Secret Proof (appsecret_proof)

A Graph API parameter holding an HMAC-SHA256 of the access token keyed with the app secret, so a stolen token cannot be used without the secret.

Synopsis

appsecret_proof, App secret proof, Require App Secret

Section

Security & identity

Level

expert

▍

Reviewed

Recompute the proof the way the Graph API does

3 / 3

Incoming request

As signed

POST /webhook HTTP/1.1

appsecret_proof: sha256=383ad4f1e7cb705e4d9fc09e2361fc7ce13c7c51cab11dd09ec518fa9e420171

60 bytes, exactly as the sender signed them.

Your server verifies

  1. Read the raw body and the headerUse the exact bytes received — 60 bytes, before any JSON parsing.
  2. Recompute the HMACHMAC-SHA256(secret, raw body)
  3. Compare the two digestsUse a constant-time comparison.
Your app secret (the HMAC key)demo-app-secret

appsecret_proof (received) vs HMAC-SHA256(secret, raw body) (computed)

Receivedsha256=383ad4f1e7cb705e4d9fc09e2361fc7ce13c7c51cab11dd09ec518fa9e420171
Computedsha256=383ad4f1e7cb705e4d9fc09e2361fc7ce13c7c51cab11dd09ec518fa9e420171
Match — signature validThe body is byte-for-byte what the sender signed. Process the event.
The token plays the request body and appsecret_proof the header (sent as bare hex, without the sha256= prefix shown). Change one character, or use the wrong app secret, and the recomputed HMAC no longer matches.

Match — signature valid. The body is byte-for-byte what the sender signed. Process the event.

Description

What is appsecret_proof?

App secret proof (appsecret_proof) is an extra parameter you add to Graph API requests to show that the caller knows the app secret, not just the access token. Its value is the HMAC-SHA256 of the access token, keyed with the app secret and written as lowercase hex. Meta recomputes the same HMAC from the token it received and your app's secret, and rejects the call if the two do not match.

The reason is that an access token is a portable bearer credential. A token copied from a log file, a misconfigured client or a stolen laptop works from any machine. With proofs enforced, the token alone is no longer enough: an attacker also needs the app secret, which never leaves your servers. For WhatsApp Cloud API integrations that run on a long-lived system user access token, this is a cheap second lock on the most valuable credential you hold.

Sending a proof is optional until you switch on Require App Secret under App settings → Advanced → Security in the App Dashboard. From then on, calls without a valid proof fail, which turns the proof from a nice-to-have into a control. It is the outbound counterpart of X-Hub-Signature-256, the HMAC Meta uses in the other direction to sign the webhooks it sends you; both are built on HMAC and keyed with the same secret.

Key takeaways

  • appsecret_proof is the hex HMAC-SHA256 of an access token, keyed with your app secret, sent as a parameter on Graph API calls.
  • It ties a token to your app's secret, so a token leaked from logs or a client is not enough on its own.
  • Turning on Require App Secret makes the proof mandatory; server calls without a valid proof then fail.
  • Recompute the proof whenever the token or the app secret changes: it is a digest of exactly that token.
  • It protects calls you make to Meta; X-Hub-Signature-256 is the matching check on webhooks Meta sends you.

Usage

Appsecret_proof in code

import crypto from "node:crypto";
const token = process.env.WA_SYSTEM_USER_TOKEN; // both values come from your secret store
const proof = crypto
.createHmac("sha256", process.env.META_APP_SECRET) // key: the app secret
.update(token) // message: the exact token
.digest("hex");
const url = new URL("https://graph.facebook.com/v26.0/{PHONE_NUMBER_ID}/messages");
url.searchParams.set("appsecret_proof", proof);
const res = await fetch(url, {
method: "POST",
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
body: JSON.stringify({
messaging_product: "whatsapp",
to: "919812345678",
type: "template",
template: { name: "order_shipped", language: { code: "en" } },
}),
});

Compute the proof in the same helper that attaches the token, so the two can never drift apart.

Mechanics

How does appsecret_proof work?

  1. step 1 — Take the exact access token

    Use the token string you are about to send, byte for byte: no Bearer prefix, no surrounding quotes, no trailing newline.

  2. step 2 — HMAC it with the app secret

    Compute HMAC-SHA256 with the app secret as the key and the token as the message, then encode the 32-byte result as 64 lowercase hex characters.

  3. step 3 — Send it with the call

    Add appsecret_proof=<hex> as a query or form parameter on the Graph API request, alongside the token in the Authorization header or the access_token parameter.

  4. step 4 — Meta recomputes and compares

    Graph computes the same HMAC from the token it received and your app's secret. A match lets the call through; a mismatch returns an error and nothing is executed.

  5. step 5 — Enforce it

    Once every server call sends a proof, turn on Require App Secret. Calls without one then fail, so a token used anywhere else is useless without the secret.

Properties

Appsecret_proof: key facts and limits

Parameter
appsecret_proof (query or form field)
Value
hex HMAC-SHA256, key = app secret, message = token
Length
64 lowercase hexadecimal characters
Enforcement
App settings → Advanced → Security → Require App Secret
Changes when
The token or the app secret changes
Inbound counterpart
X-Hub-Signature-256 on webhooks

Caveats

Common mistakes with appsecret_proof

  • Hashing the wrong string

    Including Bearer , a trailing newline (the classic echo mistake) or a different token from the one actually sent gives a proof that looks right and never matches. Hash the exact token, with printf '%s' in shell scripts.

  • Swapping key and message

    The app secret is the HMAC key and the token is the message. Reversing them, or using plain SHA-256 with no key, produces a digest Graph will never accept.

  • Reusing a proof after a token change

    A proof belongs to one token. When a token is refreshed or replaced, or the app secret is reset, recompute it, or calls start failing once enforcement is on.

  • Enabling Require App Secret too early

    Any legacy script or client-side call without a proof breaks immediately. Inventory every caller, add proofs everywhere, then switch the setting on.

Notes

Best practices for appsecret_proof

  • Treat an "Invalid appsecret_proof" error as a configuration alarm: it usually means the token and secret come from different environments.
  • Keep separate Meta apps for staging and production so each environment has its own secret and its own proofs.
  • Log whether a proof was attached, never the proof or the token itself.
  • Rotate long-lived system user tokens on a schedule even with proofs on: the two controls stack.

Why it matters

Why does appsecret_proof matter?

System user tokens for WhatsApp often never expire and carry the whatsapp_business_messaging and whatsapp_business_management permissions, so a single leak can mean messages sent in your name and templates changed without your knowledge. An appsecret_proof costs one HMAC per request and turns a leaked token from an immediate incident into a much harder attack that also requires your app secret.

Example

Example: A stolen laptop that was not an emergency

A Bengaluru direct-to-consumer brand once let its support tooling print full request URLs, tokens included, into a shared log viewer. After an internal audit the team fixed the logging and enabled proofs: every Graph API call from its order-notification service now carries appsecret_proof, and Require App Secret is switched on. Months later a contractor's laptop holding old log exports is stolen. Any token in those exports would still need the app secret, which lives only in the production vault, so the response is a planned token rotation the next morning rather than a midnight incident.

Questions

App Secret Proof (appsecret_proof) FAQs

How do I generate appsecret_proof?
Compute HMAC-SHA256 of the access token using the app secret as the key, and send the hex digest. In Node: crypto.createHmac("sha256", appSecret).update(token).digest("hex"); in PHP: hash_hmac('sha256', $token, $appSecret).
Is appsecret_proof required for the WhatsApp Cloud API?
Only if you turn on Require App Secret for your app. Without that setting the proof is optional, though a proof you do send must be valid; with it, server calls lacking a valid proof fail.
What does "Invalid appsecret_proof provided in the API argument" mean?
The proof Meta computed does not match the one you sent. Usually the token and the secret come from different apps or environments, the secret was reset, or the token was hashed with extra characters such as a newline.
What is the difference between appsecret_proof and X-Hub-Signature-256?
appsecret_proof protects calls you make to Meta; X-Hub-Signature-256 protects webhooks Meta sends to you. Both are HMAC-SHA256 keyed with the app secret, computed over the token and the raw body respectively.

See InfiQ pricing

Put App Secret Proof (appsecret_proof) to work

InfiQ is an official Meta Business Partner. Start a 7-day free trial — no card needed — and see this running on your own number.