← Back to blog

July 10, 2026 · 9 min read

How to Verify Licenses Offline (Signed JWTs + JWKS)

Some software cannot phone home on every launch — an air-gapped install, a CLI, a desktop app on a flaky connection, or a service checking its license millions of times a day. For those, calling a validation endpoint on every check is the wrong shape. The answer is a signed license token the client verifies locally, with no per-check network call. This is how offline verification works with Ed25519-signed JWTs and a JWKS endpoint, what it can honestly promise, and where its limits are. It is the offline half of the signed-JWT vs opaque-key choice.

When you actually need offline verification

Online verification — the client asks the licensing server "is this key valid?" on each check — is simpler and revokes instantly, and it is the right default for most SaaS. You reach for offline verification when a per-check call is impractical or impossible:

  • Air-gapped or on-prem installs that have no route to your server.
  • CLIs and desktop apps that must start instantly and work on a train.
  • High call volume where a network round-trip per check adds cost and latency.
  • Privacy-sensitive deployments that do not want to emit a call on every run.

How offline verification works

The mechanism is a short-lived, cryptographically signed license token plus a public key the client already trusts. Four moving parts:

  1. Issue a signed token. The license is a JWT signed with the vendor’s private Ed25519 key (alg: EdDSA). Its claims carry who it is for, what it entitles, and an expiry (exp). The customer also holds a long-lived refresh secret delivered at purchase.
  2. Publish the public key at a JWKS endpoint. The verifier fetches the JSON Web Key Set from a well-known URL — for ValidonX, GET https://api.validonx.com/.well-known/jwks.json — and caches it (served with a one-hour lifetime). Each key has a kid; the client selects the one that matches the token header, so the signing key can rotate without a client update.
  3. Verify locally on startup (and periodically). No server call: check the signature against the JWKS key, then check exp, aud (audience), and iss (issuer). Pin the algorithm to EdDSA and reject anything else.
  4. Refresh before expiry. While the client is online, it trades the refresh secret at POST /api/v1/license/refresh for a fresh token before the current one expires (ValidonX refreshes within exp − 48h), and rolls the new token onto the same install.

The verify step, in shape (any standard JWT/JOSE library, no vendor SDK required):

// 1. Fetch + cache the public keys (refetch on a timer, not every check)
const jwks = await fetchAndCache('https://api.validonx.com/.well-known/jwks.json')

// 2. Verify the signed license entirely offline
const { payload } = await verifyJwt(licenseToken, jwks, {
  algorithms: ['EdDSA'],          // pin the algorithm — reject "none" and RS/HS
  issuer:   'https://validonx.com',
  audience: 'your-product-aud',
})

// 3. payload.exp is enforced by the verifier; read entitlements from claims
const entitled = payload.limits ?? payload.tier

What offline can honestly promise — and what it cannot

This is the part most "offline licensing" pitches gloss over. Offline verification means no network call on every check — it does not mean "works forever with no connection." The token has a time-to-live: ValidonX tokens live 96 hours. A connected device refreshes silently before expiry; a fully air-gapped device works until its current token’s ≤96h TTL runs out, then needs a fresh token carried to it.

That bound is not a limitation to hide — it is the feature that makes revocation possible without an online kill switch. To revoke an offline license you revoke the refresh secret; the live token then self-expires within its remaining ≤96h and no replacement is issued. The trade is explicit: offline operation costs you up-to-96h revocation latency, where an online opaque key revokes on the very next call. Choose per product, not per dogma.

Security checklist for the verifier

  • Pin the algorithm. Accept only EdDSA; explicitly reject none and any RS/HS variant — algorithm confusion is the classic JWT attack.
  • Verify the signature before reading any claim. An unverified JWT is just untrusted JSON.
  • Check exp, aud, and iss, not just the signature — a validly signed token for another product or a lapsed period must still fail.
  • Fetch and cache the JWKS; never hardcode a key. Hardcoding breaks the day the key rotates and strands your installs.
  • Never trust the client beyond the signature. The token is tamper-evident; anything the app decides on top of it is only as trustworthy as the app.

Offline vs online: which to choose

ConsiderationOffline (signed JWT + JWKS)Online (opaque key + validate)
Network per checkNone (periodic refresh only)One call per check
Works air-gappedYes, within the token TTLNo
Revocation latency≤ token TTL (96h)Immediate (next call)
Best forCLIs, desktop, on-prem, high volumeWeb apps, servers, instant-off needs

Build vs. buy

You can build the issuing side yourself: generate an Ed25519 keypair, sign JWTs, stand up and cache a JWKS endpoint, handle key rotation without breaking installs, and run a refresh endpoint that ties token issuance to subscription state. It is well-trodden but unforgiving — a mishandled key rotation or a missing exp check turns into a licensing outage or a bypass. ValidonX issues the signed tokens, serves and rotates the JWKS, and runs the refresh endpoint, so your side is just "verify this token offline." It is the same build-vs-buy calculus behind licensing as a service.

Frequently asked questions

What is offline license verification?

Offline license verification means your software checks that a license is valid locally — verifying a cryptographic signature and an expiry date — without calling the licensing server on every launch or every check. The client only needs the vendor’s public key and a copy of the signed license token. That is what lets a license work on an air-gapped machine, in a CLI, or under a call volume that would make a per-check network round-trip impractical.

How is offline verification secure if there is no server call?

The license is a JSON Web Token (JWT) signed with the vendor’s private Ed25519 key. The client verifies the signature against the matching public key published at the vendor’s JWKS endpoint, then checks the token’s expiry, audience, and issuer. Because only the vendor holds the private key, the token cannot be forged or altered, and because it carries an expiry it cannot be replayed forever. The client never has to trust anything it cannot cryptographically check.

How long can a license keep working offline?

Only until the token expires. ValidonX license tokens live 96 hours, and a connected client refreshes for a new one before the old expires. So "offline" means "no network call on every check" — not "works forever with no connection at all." A fully air-gapped device keeps working until its current token’s 96-hour TTL runs out, and then needs a fresh token. That bound is deliberate: it is what makes revocation possible without an online kill switch.

How do you revoke a license that verifies offline?

You revoke the refresh secret. The live token then simply self-expires within its remaining ≤96 hours, and no new token is issued to replace it. There is no instant offline kill switch in v1 — the short time-to-live is the revocation mechanism, which is the honest trade-off for letting the license run without a per-check server call. If you need instant "off," an online opaque-key check revokes the moment the next call happens.

Do I have to hardcode the vendor’s public key?

No — and you should not. Fetch the public key from the JWKS endpoint (a standard JSON Web Key Set) and cache it; ValidonX serves it with a one-hour cache lifetime. Select the key whose key id (kid) matches the token header. Fetching rather than hardcoding means the signing key can be rotated without shipping a client update, and it is the standard, forward-compatible way to consume signed tokens.

Need offline-verifiable licenses without building the signing side? Start free — signed tokens, a hosted JWKS endpoint, and refresh, built in, no credit card.

We use essential cookies for authentication and session management. Privacy Policy