Problem
npm platform claims currently use token verification via registry.npmjs.org/-/whoami. This is vulnerable to the same attack vector as the LiteLLM supply-chain compromise (March 24, 2026): an attacker who steals an npm token from CI can verify as the maintainer and claim their namespace on auths.
The LiteLLM attacker stole PyPI credentials from a compromised Trivy GitHub Action in CI/CD. If the same pattern targets npm tokens (which are routinely stored as CI secrets), the current npm claim flow would let the attacker claim namespaces under the real maintainer's identity.
Current npm flow (vulnerable)
- User runs
auths id claim npm
- CLI prompts for npm access token
- CLI calls
HttpNpmAuthProvider::verify_token() → GET registry.npmjs.org/-/whoami with Bearer auth
- Server receives the token, independently verifies via npm whoami, stores the claim
- Attack: stolen npm token → attacker verifies as maintainer → claims namespace
PyPI flow (secure, already implemented)
- User runs
auths id claim pypi
- CLI prompts for PyPI username (no token)
- CLI creates a signed platform claim with the device signing key (in platform keychain, not CI)
- Server verifies the Ed25519 signature only — trusts the self-reported username
- At namespace claim time, the PyPI verifier checks the public
pypi.org/pypi/{package}/json API for maintainer status
- Attack blocked: stolen PyPI token → attacker can't produce a valid signed claim (needs the device key from macOS Keychain)
Proposed change
Switch npm to the same model as PyPI:
CLI (crates/auths-cli/src/commands/id/claim.rs)
- Remove token prompt from
ClaimPlatform::Npm handler
- Prompt for npm username instead
- Remove
HttpNpmAuthProvider usage
SDK (crates/auths-sdk/src/workflows/platform.rs)
- Remove
npm_token parameter from claim_npm_identity()
- Change proof URL format from
npm-token:{token}:{claim} to npm-claim:{claim}
Infra (crates/auths-infra-http/src/npm_auth.rs)
- Delete
HttpNpmAuthProvider (dead code after this change)
- Remove from
lib.rs exports
Server (auths-cloud/crates/auths-registry-server/src/services/proof_verification.rs)
- Replace
verify_npm_token_proof() with verify_npm_claim_proof() (signature-only, no whoami call)
- Update proof dispatch in
routes/identity.rs to match npm-claim: prefix
Server (auths-cloud/crates/auths-registry-server/src/routes/identity.rs)
- Update proof URL prefix check from
npm-token: to npm-claim:
- Update
proof_type DB value from "npm-token-proof" to "npm-claim-proof"
Security model after change
| Layer |
What it checks |
Can stolen token bypass? |
| Platform claim |
Ed25519 signature from device key |
No — key is in platform keychain |
| Namespace claim |
Public npm registry maintainers API |
No — can't fake the public API |
The npm namespace verifier (npm_verifier.rs) already checks registry.npmjs.org/{package} for maintainers — no changes needed there.
Why this is more secure
The LiteLLM attack chain was: compromised CI tool → stolen PyPI token → malicious publish. The same chain with npm tokens is: compromised CI tool → stolen npm token → auths id claim npm with stolen token → claim namespace.
With the signed-claim model, the attacker needs the device signing key (not stored in CI, lives in macOS Keychain / Linux Secret Service) to produce a valid platform claim. Stealing an npm token from CI gives them nothing.
Files to modify
| File |
Change |
crates/auths-cli/src/commands/id/claim.rs |
Prompt for username, remove token prompt |
crates/auths-sdk/src/workflows/platform.rs |
Remove npm_token param, change proof format |
crates/auths-infra-http/src/npm_auth.rs |
Delete file |
crates/auths-infra-http/src/lib.rs |
Remove HttpNpmAuthProvider export |
auths-cloud/.../services/proof_verification.rs |
Replace verify_npm_token_proof with signature-only verifier |
auths-cloud/.../routes/identity.rs |
Update prefix check and proof_type |
Problem
npm platform claims currently use token verification via
registry.npmjs.org/-/whoami. This is vulnerable to the same attack vector as the LiteLLM supply-chain compromise (March 24, 2026): an attacker who steals an npm token from CI can verify as the maintainer and claim their namespace on auths.The LiteLLM attacker stole PyPI credentials from a compromised Trivy GitHub Action in CI/CD. If the same pattern targets npm tokens (which are routinely stored as CI secrets), the current npm claim flow would let the attacker claim namespaces under the real maintainer's identity.
Current npm flow (vulnerable)
auths id claim npmHttpNpmAuthProvider::verify_token()→GET registry.npmjs.org/-/whoamiwith Bearer authPyPI flow (secure, already implemented)
auths id claim pypipypi.org/pypi/{package}/jsonAPI for maintainer statusProposed change
Switch npm to the same model as PyPI:
CLI (
crates/auths-cli/src/commands/id/claim.rs)ClaimPlatform::NpmhandlerHttpNpmAuthProviderusageSDK (
crates/auths-sdk/src/workflows/platform.rs)npm_tokenparameter fromclaim_npm_identity()npm-token:{token}:{claim}tonpm-claim:{claim}Infra (
crates/auths-infra-http/src/npm_auth.rs)HttpNpmAuthProvider(dead code after this change)lib.rsexportsServer (
auths-cloud/crates/auths-registry-server/src/services/proof_verification.rs)verify_npm_token_proof()withverify_npm_claim_proof()(signature-only, no whoami call)routes/identity.rsto matchnpm-claim:prefixServer (
auths-cloud/crates/auths-registry-server/src/routes/identity.rs)npm-token:tonpm-claim:proof_typeDB value from"npm-token-proof"to"npm-claim-proof"Security model after change
The npm namespace verifier (
npm_verifier.rs) already checksregistry.npmjs.org/{package}for maintainers — no changes needed there.Why this is more secure
The LiteLLM attack chain was: compromised CI tool → stolen PyPI token → malicious publish. The same chain with npm tokens is: compromised CI tool → stolen npm token →
auths id claim npmwith stolen token → claim namespace.With the signed-claim model, the attacker needs the device signing key (not stored in CI, lives in macOS Keychain / Linux Secret Service) to produce a valid platform claim. Stealing an npm token from CI gives them nothing.
Files to modify
crates/auths-cli/src/commands/id/claim.rscrates/auths-sdk/src/workflows/platform.rsnpm_tokenparam, change proof formatcrates/auths-infra-http/src/npm_auth.rscrates/auths-infra-http/src/lib.rsHttpNpmAuthProviderexportauths-cloud/.../services/proof_verification.rsverify_npm_token_proofwith signature-only verifierauths-cloud/.../routes/identity.rs