APITokenAudit — Non-OAuth API Key Inventory
88 tokens across 7 types (PATs, service accounts, customer-issued, partner, signed-request, cron, legacy). Last-used age, scope, rotation cadence, IP allow-list, abuse signals. Surfaces 5 orphan tokens, 6 over-privileged (admin: *), 2 known-leaked-not-rotated, 1 former-employee token still active.
What it is
The companion to ScopeCreep (which audits OAuth grants). 88 non-OAuth keys on one screen, each with its last-used age, scope, rotation cadence, IP allow-list, and abuse signals.
What’s in it
- 88 tokens across 7 types:
- PAT — personal access tokens (CI bots, security scanners, release bots)
- service-account — workload identity for payment workers, KYC pipelines, analytics ETL
- customer-issued — keys held by customers (over-privileged legacy
api:*patterns) - partner — Stripe webhook verifier, Segment write-only, Fivetran ETL
- signed-request — HMAC keys for outbound webhooks
- cron-token — internal scheduler tokens
- legacy — pre-2023 admin console, refusing-to-update mobile clients
- Per-token signals:
- Last-used age vs idle threshold
- Rotation cadence vs policy
- Scope (flagged if includes
*oradmin) - IP allow-list status (none / recommended / enforced)
- Known-leaked status (and whether rotated)
- Abuse pattern (unusual-rate, foreign-geo)
- Worst-offender findings surfaced:
AK04 / AK26 / AK27— orphan tokens withadmin:*and no ownerAK31— leaked partner key from 2024-06 breach, NOT rotated, ACTIVE usage from PL geoAK32— former-employee PAT leaked in OSS commit, hitting API at unusual rateAK29 / AK30— known-leaked tokens that WERE properly rotated (see SecretRotation SR53/SR54)
Why this shape
SOC2 CC6.1 + NIST SP 800-63B + OWASP API1:2023 + API2:2023 all measure the same operational fact: do you know every API credential, who owns it, when it was last used, and is it overdue for rotation. The hardest audit finding is the orphan — a still-active admin-scope token with no owner. APITokenAudit prototypes the inventory that surfaces it.
How it ships
Single HTML file, ~24KB. Zero dependencies. 88 tokens × 7 types × signal classifier in 260 lines of vanilla JavaScript.