Skip to content

fix(security): cancelled/downgraded users keep full API access via un-revoked wm_ keys (apiAccess only checked on premium paths) #4611

Description

@koala73

Surfaced during the #3199 shadow-audit: cancelled / downgraded customers retain full programmatic API access via their old wm_ key.

Mechanism (verified in code)

  • validateUserApiKey (server/_shared/user-api-key.ts) validates key existence / not-revoked only — it never checks the owner's subscription/entitlement. A lapsed customer's key still resolves to their userId.
  • The only apiAccess gate for user keys is server/gateway.ts:932 (if (isUserApiKey && needsLegacyProBearerGate …)), scoped to PREMIUM_RPC_PATHS. Tier-gated routes 403 via checkEntitlement. Every other public /api/*/v1/* endpoint has no apiAccess check for user keys → served.

Evidence (Axiom wm_api_usage, 30d)

Two accounts with planKey: free + expired entitlement (apiRateLimit: 0) — keys minted while they were paying API Starter, never revoked on downgrade:

  • 2,633 requests served (200), only 178 blocked (403 tier_403), 1×304.
  • Served the core paid surface: list-market-quotes, list-acled-events, commodity/crypto quotes, list-cyber-threats, list-fire-detections, list-military-flights, macro signals, vessel snapshots, cable-health.

Impact

The API Starter product (keyed, cross-origin, programmatic access — the thing customers pay ~$99/mo for) is not revoked on churn. Cancelled customers keep it free. Revenue-integrity gap, not a data breach (the underlying data is public). Pre-existing (predates #3199). #3199 rate-limit enforcement does NOT close it — an expired key has apiRateLimit:0 → ineligible for the per-account layer → falls to per-IP → still served.

Fix

Enforce active apiAccess on all wm_ user-key requests, not just PREMIUM_RPC_PATHS:

Acceptance criteria

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High priority, fix soonarea: APIBackend API, sidecar, keys

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions