What’s already enforced for you
These are decided server-side, on every request so your integration is not just a localif check that someone can patch out:
- The server decides. Every activation and every heartbeat re-applies the same gates — kill switch, license state, blacklist, platform, version, and build hash from one shared module. Activation and heartbeat can never disagree about whether a license is valid.
- Atomic device limits. Concurrent activations cannot overshoot a license’s device cap. Fifty simultaneous requests against a one-seat license grant exactly one.
- Revocation on the next beat. A license is re-read from the database on every heartbeat rather than trusted from a token, so a revoked key stops working within one beat.
- Vague refusals. An unknown key, a key belonging to another product, and a wrong password are all indistinguishable from the outside. Attackers cannot enumerate valid keys from the error responses.
What you should do as a developer
- Do not remove the checks. Keep the refusal handling in place: if
/activateor/validatereturns anything other than success, stop. Don’t turn a failed validation into a warning. - Use HTTPS safely. All traffic is HTTPS already; if you modify the request layer yourself, add certificate pinning so a man-in-the-middle can’t substitute a fake “valid” reply.
- Spread out your checks. Don’t only validate at startup. Use the heartbeat call
POST /api/v1/validateon a timer everyheartbeatSecondsand add your own integrity checks at multiple points in your code, not just one. - Do not hardcode sensitive data. Never embed license keys or your webhook signing secret in source or shipped configs. Keep secrets in environment variables.
- Obfuscate your code, and turn on Build Hash checks. Even basic obfuscation or packing slows an attacker down; anti-debug / anti-inject is recommended where possible. Then register your binary’s SHA-256 under Settings → Build Hashes and enable Check Hash. A patched build then fails with
INVALID_HASHeven if it holds a valid key. - Update your program often. The longer a build sits unchanged, the more time an attacker has to study it. Ship updates, re-register the new build hash, and bump your version so old cracked builds fall below your
minVersiongate and stop activating.
If you’re verifying licenses from your own server (not from an application running on the user’s machine), you don’t need client-side hardening. Servers can’t be patched by end users, so network traffic between your server and Frost Auth doesn’t require anti-tamper protection.
Webhook signature verification — worked example
Frost Auth signs every webhook delivery so your server can prove it came from us. TheX-Frost-Signature header arrives as t=<unix>,v1=<hmac>. You compute an HMAC-SHA256 of ${t}.${rawBody} using your webhook signing secret and compare.
Your signing secret is generated automatically the first time you save a webhook URL under Settings → General → Webhook Deliveries. It looks like this:
whsec_4a59f8cab30447e28d56870004a6, and you get:
v1 value in the header. In your application, compare these using a constant-time function and reject the delivery if they don’t match:
X-Frost-Delivery. Delivery is at least once, never exactly once. If your server accepts a payload and crashes before responding, you will receive it again. Store each delivery ID you’ve processed and skip duplicates.