Skip to main content

No-fuss, pure-Python keyring backend for Azure DevOps Artifacts feeds

Project description

artifacts-keyring-nofuss

CI

Minimal, pure-Python keyring backend for Azure DevOps Artifacts feeds.

Replaces the official artifacts-keyring (which wraps a ~100 MB .NET binary) with a no-fuss, pure-Python implementation — no .NET required.

Install

pip install artifacts-keyring-nofuss

Or for development:

pip install -e .

How it works

When pip, uv, twine, etc. query the keyring for credentials to an Azure DevOps Artifacts feed, this backend:

  1. Discovers the Azure AD tenant by making an unauthenticated request to the feed URL and parsing the WWW-Authenticate header.
  2. Obtains a bearer token using one of the supported auth flows (see below).
  3. For user tokens (Azure CLI): exchanges the bearer token for a narrower VssSessionToken scoped to vso.packaging.
  4. For service principal tokens (managed identity, SP, WIF): returns the Entra bearer token directly as Basic auth credentials.
  5. Returns the credentials to the caller.

Auth flows (priority order)

# Flow How it works
1 Environment variable Reads a bearer token from ARTIFACTS_KEYRING_NOFUSS_TOKEN (or VSS_NUGET_ACCESSTOKEN as fallback). Also supports ARTIFACTS_KEYRING_NOFUSS_TOKEN_FILE pointing to a file, and auto-detects Docker BuildKit secrets at /run/secrets/. Best for CI and Docker builds.
2 Azure CLI Runs az account get-access-token. Most common for local dev.
3 ADO auth helper Calls ~/ado-auth-helper (created by the ado-codespaces-auth VS Code extension). Enables seamless auth in GitHub Codespaces.
4 Workload Identity Exchanges a federated token via AZURE_CLIENT_ID + AZURE_FEDERATED_TOKEN_FILE + AZURE_TENANT_ID. Best for GitHub Actions with azure/login@v2.
5 Azure Identity Uses DefaultAzureCredential from azure-identity. Handles managed identities (system + user-assigned), service principals (secret/cert), workload identity federation, and more.

Configuration

Select a specific flow

By default, providers are tried in the order above. To force a specific one:

# Environment variable
export ARTIFACTS_KEYRING_NOFUSS_PROVIDER=azure_cli  # or: env_var, ado_auth_helper, workload_identity, azure_identity

Or in ~/.config/python_keyring/keyringrc.cfg:

[artifacts_keyring_nofuss]
provider = azure_cli

User-assigned managed identity

Set AZURE_CLIENT_ID to the client ID of the user-assigned managed identity:

export AZURE_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

When unset, system-assigned managed identity is used.

Service principal with secret

Set the standard Azure Identity environment variables:

export AZURE_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export AZURE_TENANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export AZURE_CLIENT_SECRET=your-secret

This requires the azure-identity package (included as a dependency). The service principal must have permissions on the Azure DevOps feed (e.g. Feed Reader).

Bearer token via environment variable

For CI pipelines and Docker builds, pass a pre-minted bearer token:

export ARTIFACTS_KEYRING_NOFUSS_TOKEN=<bearer-token>

For backward compatibility with existing artifacts-keyring CI configs, VSS_NUGET_ACCESSTOKEN is also accepted as a fallback.

Reading tokens from files (_FILE convention)

Set ARTIFACTS_KEYRING_NOFUSS_TOKEN_FILE to a path containing the bearer token. This follows the Docker _FILE convention used by official images (postgres, mysql, etc.):

export ARTIFACTS_KEYRING_NOFUSS_TOKEN_FILE=/run/secrets/my_token

Docker BuildKit secrets (zero config)

When building Docker images with BuildKit, secrets are mounted as files under /run/secrets/ only for the duration of the build step — they are never persisted in image layers.

The env_var provider automatically checks these well-known BuildKit secret paths:

  • /run/secrets/ARTIFACTS_KEYRING_NOFUSS_TOKEN
  • /run/secrets/ado_token

This means you can use BuildKit secrets with no extra env vars inside the container:

# Dockerfile
RUN pip install artifacts-keyring-nofuss

RUN --mount=type=secret,id=ARTIFACTS_KEYRING_NOFUSS_TOKEN \
    PIP_KEYRING_PROVIDER=import pip install \
    --index-url https://__token__@pkgs.dev.azure.com/{org}/_packaging/{feed}/pypi/simple/ \
    my-private-package

Build with:

# Mint a short-lived ADO bearer token and pass it as a BuildKit secret
export ADO_TOKEN=$(az account get-access-token \
  --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv)

DOCKER_BUILDKIT=1 docker buildx build \
  --secret id=ARTIFACTS_KEYRING_NOFUSS_TOKEN,env=ADO_TOKEN \
  -t my-image .

The token is available at /run/secrets/ARTIFACTS_KEYRING_NOFUSS_TOKEN only during that RUN step and is never baked into the image.

Priority order: ARTIFACTS_KEYRING_NOFUSS_TOKEN_FILEARTIFACTS_KEYRING_NOFUSS_TOKENVSS_NUGET_ACCESSTOKEN → BuildKit secret paths.

Workload Identity Federation (GitHub Actions OIDC)

When using azure/login@v2 in GitHub Actions, the action automatically sets AZURE_CLIENT_ID, AZURE_TENANT_ID, and AZURE_FEDERATED_TOKEN_FILE. The workload identity provider detects these and exchanges the federated token for a bearer — no extra configuration needed.

GitHub Codespaces

Add the artifacts-helper devcontainer feature to your .devcontainer/devcontainer.json:

{
  "features": {
    "ghcr.io/microsoft/codespace-features/artifacts-helper:3": {}
  }
}

This installs the ado-codespaces-auth VS Code extension, which creates ~/ado-auth-helper. The ado_auth_helper provider calls it automatically — no az login needed. Sign in via the "Click to authenticate" prompt in the VS Code status bar on first use.

Usage with pip

pip install --index-url https://pkgs.dev.azure.com/{org}/_packaging/{feed}/pypi/simple/ my-package

The keyring backend is automatically discovered by pip. No extra flags needed.

Usage with uv

  1. Install keyring with this backend:
pip install keyring artifacts-keyring-nofuss
# or
uv tool install keyring --with artifacts-keyring-nofuss
  1. Configure uv to use keyring for authentication. Either add it to your project config:
# pyproject.toml or uv.toml
[tool.uv]
keyring-provider = "subprocess"

Or set the environment variable:

export UV_KEYRING_PROVIDER=subprocess
  1. Use uv as normal with your private feed. A username in the URL (e.g. __token__@) is required to trigger keyring lookup:
uv pip install my-package --index-url https://__token__@pkgs.dev.azure.com/{org}/_packaging/{feed}/pypi/simple/

This also works with legacy subdomain-prefixed feed URLs:

uv pip install my-package --index-url https://__token__@myorg.pkgs.visualstudio.com/_packaging/{feed}/pypi/simple/

For pyproject.toml index configuration:

[[tool.uv.index]]
url = "https://__token__@pkgs.dev.azure.com/{org}/_packaging/{feed}/pypi/simple/"
name = "my-feed"

Supported feed URLs

Any URL whose host matches one of (including subdomain-prefixed variants):

  • pkgs.dev.azure.com (e.g. https://pkgs.dev.azure.com/myorg/…)
  • pkgs.visualstudio.com (e.g. https://myorg.pkgs.visualstudio.com/…)
  • pkgs.codedev.ms
  • pkgs.vsts.me

URLs with userinfo (e.g. https://__token__@host/…) and bare hostnames without a scheme are also handled correctly.

Troubleshooting

Enable verbose debug output to see the full authentication flow:

ARTIFACTS_KEYRING_NOFUSS_DEBUG=1 pip install --index-url https://pkgs.dev.azure.com/{org}/_packaging/{feed}/pypi/simple/ my-package

This prints the provider chain, token exchange steps, and any errors to stderr.

Security model

This package handles authentication tokens. Key security properties:

  • Endpoint validation: Discovery responses are validated against allowlists. The authorization_uri and VSTS authority must point to known hosts over HTTPS, with no non-default ports, userinfo, or deep paths. The authority must be a clean origin (https://host or https://host/). This prevents bearer token exfiltration via DNS hijacking or rogue proxy responses.
  • Short-lived tokens: Bearer tokens are not persisted to disk. In-memory caching has a 50-minute TTL (tokens typically live 60–75 minutes).
  • Narrow scope: User tokens (Azure CLI) are exchanged for session tokens scoped to vso.packaging (read-only). Service principal tokens (MI/SP/WIF) are returned directly — scope is determined by the identity's Azure DevOps permissions.
  • No CWD config: Provider configuration is read only from ~/.config/ or environment variables, never from the working directory.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

artifacts_keyring_nofuss-1.2.1.tar.gz (31.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

artifacts_keyring_nofuss-1.2.1-py3-none-any.whl (20.4 kB view details)

Uploaded Python 3

File details

Details for the file artifacts_keyring_nofuss-1.2.1.tar.gz.

File metadata

  • Download URL: artifacts_keyring_nofuss-1.2.1.tar.gz
  • Upload date:
  • Size: 31.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for artifacts_keyring_nofuss-1.2.1.tar.gz
Algorithm Hash digest
SHA256 33b46b1c625a85469222a6647b5b2c6ae4ebcd9d446f29142e2f113037d40ebe
MD5 8670122d8b75675aaeb6680a9ef84638
BLAKE2b-256 d5bbd64a1eecad1f2400862041c5ff2d2f283db3862a5899c98fe83ab7fb6438

See more details on using hashes here.

Provenance

The following attestation bundles were made for artifacts_keyring_nofuss-1.2.1.tar.gz:

Publisher: release.yml on microsoft/artifacts-keyring-nofuss

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file artifacts_keyring_nofuss-1.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for artifacts_keyring_nofuss-1.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9fc1792f35ded30a0dacd3bc8d5ad99361b99b5b6593363f0df7af9d30704708
MD5 6566c4535b0517e12f20c3801d7be422
BLAKE2b-256 99fe3558e00d07c74c16ca4e18ad827c79255c2a219912ce7143f9db4d24cbe0

See more details on using hashes here.

Provenance

The following attestation bundles were made for artifacts_keyring_nofuss-1.2.1-py3-none-any.whl:

Publisher: release.yml on microsoft/artifacts-keyring-nofuss

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page