> ## Documentation Index
> Fetch the complete documentation index at: https://docs.authforge.cc/llms.txt
> Use this file to discover all available pages before exploring further.

# Security Best Practices

> Harden your AuthForge integration against tampering, reverse engineering, and abuse.

AuthForge provides the infrastructure for license verification, but how you integrate it affects how resistant your application is to cracking and abuse. Follow these practices to maximize protection.

## Protect your App Secret

The App Secret is the most sensitive value in your integration. It's used to authenticate `/auth/validate` requests. If an attacker obtains it, they can impersonate your app and consume licenses/credits. Response signatures are verified with your app's public key, but protecting the App Secret still matters because it gates every new login.

<Steps>
  <Step title="Never hardcode in plain text">
    Don't embed the secret as a string literal in your source code. Use environment variables, encrypted config files, or a secrets manager.

    ```python theme={null}
    # Bad
    client = AuthForgeClient(app_secret="550e8400-e29b-41d4-a716-446655440000")

    # Good
    client = AuthForgeClient(app_secret=os.environ["AUTHFORGE_SECRET"])
    ```
  </Step>

  <Step title="Never log the secret">
    Ensure your logging framework doesn't accidentally capture it. Redact secrets from error reports and crash dumps.
  </Step>

  <Step title="Rotate if compromised">
    If you suspect the secret has been leaked, rotate it immediately in the dashboard (**App Settings → Rotate Secret**). All running clients will fail on the next heartbeat, prompting a restart with the new secret.
  </Step>

  <Step title="Don't commit to source control">
    Add your config file or `.env` to `.gitignore`. Use CI/CD secrets for deployment.
  </Step>
</Steps>

## Authenticate early

Call `login()` as early as possible in your application's startup. Don't let the app run meaningful code paths before authentication succeeds.

```python theme={null}
# Bad; app runs unprotected for several seconds
initialize_app()
load_plugins()
setup_ui()
# ... much later
client.login(key)

# Good; nothing runs before auth
client = AuthForgeClient(...)
if not client.login(key):
    exit(1)
initialize_app()
```

## Minimize error details

Don't expose internal error information to the user. Detailed error messages help attackers understand your verification logic.

```python theme={null}
# Bad; reveals implementation details
print(f"Signature verification failed: expected {expected}, got {actual}")
print(f"Nonce mismatch: sent {sent_nonce}, received {recv_nonce}")

# Good; generic message
print("Authentication failed. Please try again or contact support.")
```

Log the detailed error internally for debugging, but show only generic messages in the UI.

## Use SERVER heartbeat mode for high-value software

LOCAL heartbeat mode can be bypassed by:

* Freezing or rolling back the system clock
* Patching the SDK to skip the local signature check

SERVER mode requires a valid signed response from the AuthForge API on every heartbeat. For software where piracy has significant business impact, use SERVER mode.

## Don't trust the client

Your binary can be decompiled, patched, and modified. Design with this assumption:

* **Don't gate features with a simple boolean.** Instead of `if (licensed) { runProFeature() }`, read [license variables](/features/variables#license-variables) and check specific capabilities.
* **Don't store the license status in a predictable location.** Avoid a single `isLicensed = true` field that can be patched.
* **Spread auth checks.** Don't check the license in a single function; verify state at multiple points in your application.

## Obfuscation (defense in depth)

While not a substitute for proper license verification, code obfuscation raises the difficulty of cracking:

| Language | Tools                                       |
| -------- | ------------------------------------------- |
| Python   | PyArmor, Cython compilation, Nuitka         |
| C#       | ConfuserEx, .NET Reactor, Eazfuscator       |
| C++      | LLVM obfuscation passes, Themida, VMProtect |

## Network security

* **Always use HTTPS.** The SDK communicates with `https://auth.authforge.cc` by default. Never override the base URL to use HTTP.
* **Pin the certificate** if your platform supports it, to prevent MITM with a trusted CA compromise.
* **Validate the nonce on `/auth/validate`.** The SDK does this automatically; the nonce in the response must match the one you sent. Heartbeats don't use nonce replay detection; they rely on the signed short-lived session token instead.

## Self-ban safely

If your anti-tamper checks detect runtime manipulation, call `/auth/selfban` to lock out future auth attempts.

* **Prefer post-session self-ban for revoke.** Revoke by key only when you have a valid `sessionToken` from a successful login.
* **Never revoke by key pre-session.** Before activation, a `licenseKey` value can be attacker-controlled. Revoking at that stage can ban random or other customers' keys.
* **Use pre-session for containment only.** Pre-session self-ban should be limited to `blacklistHwid` and/or `blacklistIp`.
* **Keep nonce hygiene.** Pre-session self-ban uses nonce replay protection; always send a fresh nonce.
* **Treat self-ban as high risk action.** Add local anti-tamper confidence thresholds so noisy detections do not mass-ban legitimate users.

<Warning>
  The API enforces this rule: pre-session self-ban requests with `revokeLicense: true` are rejected with `revoke_requires_session`.
</Warning>

## Rate limiting

`/auth/validate` is rate-limited in addition to API Gateway throttling:

* **Per IP address:** up to **30** `/auth/validate` requests per minute.
* **Per license key:** up to **5** `/auth/validate` requests per minute (in addition to the per-IP limit).

`/auth/heartbeat` is **not** IP rate-limited at the application layer; a flood of heartbeats only burns the victim app's credits (which the server already handles via `no_credits` / `app_burn_cap_reached`), so the usual "denial of service" shape flips into "denial of wallet" for the attacker. API Gateway burst/sustained throttling still applies.

Official SDKs apply retry logic automatically when `/auth/validate` returns `rate_limited`. If you are building a **custom integration**, treat `rate_limited` as a transient error: implement **exponential backoff** (with jitter) and avoid tight retry loops so you stay within the limits above.

## HWID and IP security

Use [blacklists and whitelists](/features/security) to control access:

* **Blacklist known-bad HWIDs**: If you discover a cracked copy, blacklist the HWID to prevent re-authentication.
* **Use IP whitelists for enterprise**: Restrict authentication to known office IP ranges.
* **Monitor for anomalies**: If a license key is being validated from many different HWIDs (more than the slot count allows, via resets), investigate abuse.

## API key security

For your Developer API keys:

* **Don't expose API keys in client-side code.** API calls should only be made from your server.
* **Use separate keys for different environments** (development, staging, production).
* **Audit key usage**: If a key is compromised, delete it and create a new one. Keys are scoped to your account, so a compromised key gives access to all your apps' license management.

## Webhook security

* **Always verify the signature** on incoming webhook requests using the `X-AuthForge-Signature` header.
* **Use HTTPS** for your webhook endpoint.
* **Don't expose your webhook secret** in client code or logs.
* **Validate the event type**: only process events you expect.

See [Webhooks: Signature Verification](/features/webhooks#signature-verification) for implementation examples.

## Incident response

If you discover a cracked version of your software:

1. **Identify the HWID** from your auth logs (if available via webhooks or the dashboard).
2. **Blacklist the HWID** to block future authentication from that machine.
3. **Rotate your App Secret** if you suspect it was extracted from the binary.
4. **Update your binary** with stronger obfuscation and push an update.
5. **Review your integration** against this checklist.
