Offline License Verification Has a Local Trust Limit
Understand how offline license signature verification works in browser extensions, what belongs in the signed payload, and why client-side runtimes have local trust limits.
Table of Contents5 sections

Browser extensions operating offline struggle to validate paid tiers securely without exposing private signing keys. When building such systems, developers must also understand monetization strategies like those discussed in How Chrome Extensions Actually Make Money, ensuring that offline checks complement broader revenue models without introducing brittle dependencies.
Quick solution: Verify a base64url-encoded cryptographic signature inside your extension runtime using the Web Crypto signature verification interface before unlocking pro features:
async function verifyLicense(publicKey, signatureBytes, payloadBytes) {
return await crypto.subtle.verify(
{ name: "ECDSA", hash: { name: "SHA-256" } },
publicKey,
signatureBytes,
payloadBytes
);
}
Prerequisites: The public key must be hardcoded or securely provisioned during extension installation. Caution: Never bundle the private signing key inside the extension package, as anyone can extract it from unpacked extension directories.
Shipping software that functions offline while validating paid tiers requires a delicate balance between user convenience and security. Developers often build extensions that must operate without a constant network connection. An offline license can be verified with a public key while its signing key stays outside the distributed client. A signature establishes authenticity of license data, but it does not make client-side feature gates tamper-proof against a determined user who controls the local runtime.
What Belongs in the Signed Payload
A secure license payload must contain specific metadata to prevent replay attacks, unauthorized tier escalation, and indefinite usage without renewal. Minimizing the scope of what is signed keeps the payload compact and easy to parse inside a restricted extension background worker.
The signed payload should include the licensee identifier, the product or SKU identifier, an issued timestamp, an explicit expiration timestamp, and a unique license key identifier or version number to support future key rotations. According to guidelines outlined in RFC 8725 JWT best current practices, tokens and signed statements must explicitly define their intended audience and validity window to mitigate token substitution attacks.
{
"sub": "user_9f8e7d6c5b4a",
"sku": "pro-tier-annual",
"iat": 1776470400,
"exp": 1808006400,
"kid": "key-2026-01"
}
Including unnecessary fields increases the payload size and expands the surface area for parsing vulnerabilities. Keep the schema flat and strict.
Why Must the Signing Secret Stay Out of Extension Code
Browser extensions are distributed as client-side packages that users can download, unpack, and inspect. Any code or asset included in the extension bundle is fully accessible to the end user. If a developer places the private signing key inside the extension source code to generate licenses locally, users can easily extract that key.
Once a malicious user obtains the private signing key, they can generate valid, cryptographically signed licenses for themselves or others. This completely breaks the monetization model. The signing secret must reside exclusively on a secure server or an isolated build pipeline that only the developer or authorized backend service can access. The extension package must contain only the matching public key used exclusively for read-only verification.
How Revocation and Clock Rollback Affect Offline Operation
Offline verification introduces inherent limitations around revocation status and system time integrity. When an extension has no network connectivity, it cannot query a remote revocation list to check if a specific license key has been revoked due to a refund, chargeback, or security compromise.
To mitigate this, developers use short-lived expiration windows, such as thirty days, forcing the extension to periodically reconnect and download a fresh signed payload. However, this introduces another vulnerability vector: system clock rollback. A user might manually set their operating system clock backward to bypass the expiration check.
| Challenge | When it occurs | Failure mode | Recommended mitigation |
|---|---|---|---|
| Revocation check | Device is offline | Revoked license remains active until next sync | Enforce short expiration windows (7-30 days) |
| Clock rollback | User changes OS time | Expired license appears valid indefinitely | Validate time against last known network timestamp or secure NTP |
| Unknown key ID | After key rotation | Valid signature rejected due to missing public key | Bundle multiple active public keys and check key ID headers |
Handling these edge cases requires accepting that offline trust is bounded. If absolute tamper-proof verification is required, the application must require an online connection.
Verification Checklist
- Confirm that a modified payload string fails cryptographic verification and triggers the fallback free tier.
- Verify that an unknown key identifier is correctly rejected even if the signature format is otherwise valid.
- Test expired and malformed payloads to ensure they throw explicit handling errors rather than crashing the extension runtime.
- Ensure no private signing keys or sensitive server-side environment variables exist within the extension build artifacts.
Continue Exploring
You Might Also Like

Gemini API Backup Key 503 Bounded Failover
Learn how to build a bounded failover chain for Gemini API keys that handles both quota exhaustion and transient server errors without leaking secrets.

Deterministic Formatting Normalization
Learn how to prevent unnecessary LLM generation retries by normalizing minor Markdown formatting variations before schema validation.

Keep Markdown Examples Out of Asset Discovery
Learn how to prevent automated publication asset discovery tools from mistaking literal Markdown image examples inside code blocks for real media dependencies.