Skip to content

Document the platform claim trust model across GitHub, npm, and PyPI #111

@bordumb

Description

@bordumb

Context

We have three platform claim flows with different verification mechanisms. The security properties are equivalent at the system level (namespace claims are always independently verified), but the platform claims themselves have different strength levels. This should be documented so users and security reviewers understand the trust model.

Trust model

Platform claim strength (identity linking)

Platform Verification method What it proves Weakness
GitHub OAuth device flow + Gist proof You approved in your browser AND published to your account Attacker needs your browser session
npm Token verification via /-/whoami You possess a valid npm token for this account Attacker with stolen npm token can claim (being migrated to signed-claim model per #107)
PyPI Self-reported username + DID-signed claim You possess the device signing key Attacker with device key can self-report any username

Namespace claim strength (package ownership)

Platform Verification method What it proves Can stolen credential bypass?
GitHub (cargo) crates.io owners API GitHub username is listed as a crate owner No — public API can't be faked
npm npm registry maintainers list Username appears in package maintainers No — public API can't be faked
PyPI PyPI JSON API (pypi.org/pypi/{pkg}/json) Username appears as Owner/Maintainer in roles, or GitHub owner matches No — public API can't be faked

System-level security (platform claim + namespace claim combined)

Attack scenario GitHub npm (after #107) PyPI
Attacker steals CI token (e.g., LiteLLM attack) Can't claim — needs OAuth browser approval Can't claim — needs device signing key Can't claim — needs device signing key
Attacker steals device signing key Can fake platform claim, but OAuth blocks them at claim time Can fake platform claim + self-report any username, but namespace check blocks them Can fake platform claim + self-report any username, but namespace check blocks them
Attacker steals device key + claims namespace Blocked by OAuth (can't complete platform claim) Blocked by public API maintainer check Blocked by public API maintainer check
Attacker compromises the ecosystem registry itself (e.g., adds themselves as maintainer) Namespace claim succeeds — but this is a different attack class (registry compromise, not credential theft) Same Same

Key insight

The platform claim is the weaker link. PyPI's platform claim is the weakest (self-reported username), and GitHub's is the strongest (OAuth + published proof). But the platform claim alone grants nothing — it only enables namespace claims, which are always independently verified against public APIs.

The security property that matters: a stolen CI token cannot produce a valid namespace claim. This is true for all three platforms because:

  1. The platform claim requires a device signing key (stored in macOS Keychain / Linux Secret Service, not in CI)
  2. The namespace claim checks a public API that the attacker can't manipulate

Why PyPI doesn't use token verification

PyPI's /danger-api/echo endpoint (the equivalent of npm's /-/whoami) is an unstable v0 API that returns 403 for standard API tokens in production. Rather than depend on an unreliable endpoint, we use the signed-claim model where the real verification happens at namespace claim time via the public PyPI JSON API.

This is actually more secure than token verification because a stolen PyPI token (the LiteLLM attack vector) cannot be used to claim a platform identity. The device signing key is required, and it lives in the platform keychain — not in CI secrets.

Where to document this

  • docs/security/trust-model.md in the auths repo — full writeup with the tables above
  • CLAUDE.md — brief summary of the trust model for AI assistants working on the codebase
  • PlatformContext docstring in auths-core/src/ports/namespace.rs — already partially documented, needs the trust model distinction
  • Hub "How it works" page — user-facing explanation of why auths protects against supply-chain attacks
  • CLI --help text for auths id claim pypi — note that ownership is verified at namespace claim time

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions