Eliminating PyPI API Tokens with Trusted Publishers
Stop storing long-lived API tokens in your CI/CD secrets. Learn how to use PyPI Trusted Publishers and OIDC to secure your package deployments via GitHub Actions.
26 Jul 2025, 18:28 UTC

The Risk of Long-Lived Secrets
Managing PyPI API tokens in CI/CD pipelines creates a persistent security vulnerability. Traditionally, to automate a release, you generate a token on PyPI and store it as a secret in GitHub Actions or GitLab CI. If that secret is leaked—via a compromised runner, a misconfigured log, or a rogue contributor—the attacker gains permanent write access to your package until the token is manually revoked.
The solution is Trusted Publishers, a feature that replaces static tokens with OpenID Connect (OIDC). Instead of PyPI trusting a password, it trusts the identity of the CI provider itself. When a job runs, the CI provider issues a short-lived identity token that proves the request is coming from a specific repository, branch, or environment.
How OIDC Changes the Authentication Flow
In a token-based flow, the authentication is something you know (the secret). In the Trusted Publisher flow, authentication is where you are (the verified CI environment).
When a GitHub Action triggers a publish event, it requests an OIDC token from GitHub. This token contains "claims"—metadata such as the repository name and the specific workflow trigger. PyPI validates these claims against a pre-registered publisher profile. If the claims match, PyPI grants a temporary upload session. No secrets are ever stored in the repository settings.
Configuring a Trusted Publisher
Setting up Trusted Publishers requires a handshake between the PyPI web interface and your CI configuration. This process cannot be fully automated via CLI for security reasons; the initial trust must be established by a project owner.
1. PyPI Registration
Navigate to your project settings on PyPI and locate the "Trusted Publishers" section. You will need to provide:
- GitHub Repository: The full path (e.g.,
username/repo-name). - Workflow Name: The filename of the YAML workflow (e.g.,
publish.yml). - Environment: The GitHub Environment name, if applicable (e.g.,
pypi-release).
2. Workflow Implementation
Once registered, you can use the pypi-publish action. You must grant the workflow permission to request the OIDC token by adding id-token: write to the job permissions.
# .github/workflows/publish.yml
name: Publish to PyPI
on:
release:
types: [published]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install build tools
run: pip install build
- name: Build package
run: python -m build
- name: Publish package
uses: pypa/gh-action-pypi-publish@release/v1
Verification and Validation
To verify the configuration without risking a production release, deploy to TestPyPI first. TestPyPI supports Trusted Publishers and allows you to confirm that the OIDC handshake is successful before updating your primary project.
Run the workflow and check the GitHub Actions logs. A successful OIDC authentication will show the pypi-publish action identifying the project via the OIDC token rather than requesting a PYPI_API_TOKEN environment variable.
Trade-offs and Limitations
While Trusted Publishers significantly harden the supply chain, there are specific constraints to consider:
- Provider Lock-in: This flow depends on the CI provider supporting OIDC. While GitHub Actions and GitLab CI are supported, smaller or self-hosted CI tools may still require traditional tokens.
- Initial Manual Setup: Because the trust relationship must be established via the PyPI UI, you cannot fully "bootstrap" a new project's publishing pipeline using only code.
- Scope Rigidity: If you change your repository name or move the workflow file, the publisher registration will break, and you must update the settings in the PyPI dashboard.
Moving Forward
If your project currently relies on __token$... secrets stored in GitHub, the most secure path forward is to register a Trusted Publisher and then delete the secret entirely. This removes the credential from your environment and ensures that only verified code from your specific repository can push updates to your users.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.