entitlement-verify

Source: entitlement-verify.js:9

Pure entitlement verification — plain Node, no Electron. src/auth.js owns the network calls, the safeStorage cache, and the IPC surface; this module owns the Ed25519 signature check and the cached-payload validity rules (expiry + clock-rollback), so the security-critical decisions run anywhere Node runs.


Instance Methods

publicKeyFromB64(rawKeyB64: string): crypto.KeyObject

Build a Node KeyObject from the server's raw 32-byte Ed25519 public key.

Parameters

  • rawKeyB64 (string) — Raw 32-byte public key, base64-encoded.

Returns

  • crypto.KeyObject — Public key usable with crypto.verify.

canonicalJson(value: *): string

Serialize a value EXACTLY like the server's canonical_json(): keys sorted, no whitespace, raw unicode. Otherwise valid signatures won't verify.

Parameters

  • value (*) — Any JSON-serializable value.

Returns

  • string — Canonical JSON string.

verifyEntitlement( response: Object, verifyKey: crypto.KeyObject, ): boolean

Verify the Ed25519 signature on an entitlement response. The digest algorithm passed to crypto.verify is null because Ed25519 has no separate digest step.

Parameters

  • response (Object) — Entitlement response — signed payload plus base64 signature.
  • verifyKey (crypto.KeyObject) — Public key to verify against.

Returns

  • boolean — True when the signature verifies; false for a missing payload or signature.

checkCachedEntitlement( cached: Object, verifyKey: crypto.KeyObject, now: number, ): Object

Validity rules for a cached entitlement blob: the signature must still verify, the clock must not have been rolled back past the tolerance (to fake freshness), and valid_until must not have passed. An unparseable valid_until (Date.parse → NaN) fails closed: every comparison against NaN is false, which would otherwise read as "never expires", so a missing/garbled timestamp means the cache is not trustworthy.

Parameters

  • cached (Object) — Cached blob — the signed response plus the wall clock last seen when it was stored.
  • verifyKey (crypto.KeyObject) — Public key to verify against.
  • now (number) — Current wall clock, milliseconds since epoch.

Returns

  • Object — The payload when the cache is trustworthy, null otherwise.

nextRefreshDelayMs( payload: Object, now: number, opts?: Object, ): number

When the entitlement should be re-fetched. Normally the 24h cadence (capMs), but if the payload itself says the subscription state will change sooner — a trial ending, a paid/canceled period lapsing — refresh just after that boundary (graceMs past it, so the server's read-time state machine has definitely flipped). Boundaries already in the past are ignored: the current payload was built after them, so they're old news. floorMs stops a just-passed boundary from hot-looping the scheduler.

Parameters

  • payload (Object) — Entitlement payload; trial_ends_at and current_period_end (ISO timestamps) are the boundaries considered.
  • now (number) — Current wall clock, milliseconds since epoch.
  • opts (Object, optional)
    • opts.capMs (number, optional, default: 86400000) — Maximum delay — the normal 24h cadence.
    • opts.floorMs (number, optional, default: 30000) — Minimum delay.
    • opts.graceMs (number, optional, default: 60000) — Delay added past a boundary before refreshing.

Returns

  • number — Delay until the next refresh, in milliseconds.

Other

CLOCK_ROLLBACK_TOLERANCE_MS: number

How far backwards the wall clock may move, in milliseconds, before the cached entitlement is distrusted (crude clock-rollback defense — design §6: "not bulletproof").

SPKI_PREFIX: Buffer

Standard 12-byte SPKI header for Ed25519. Node wants a DER/SPKI key while the server hands out a raw 32-byte key; prefixing with this bridges the two.