This document describes the authentication system in pycontainer-build.
pycontainer-build supports multiple authentication methods for pushing images to container registries. The authentication system is designed to be:
- Flexible: Multiple auth providers with automatic fallback
- Secure: No credentials stored in code, supports environment variables and external tools
- Convenient: Auto-discovers credentials from Docker, Azure CLI, environment
Push Request
│
├─> Check CLI flags (--username, --password)
│ └─> If present: Use Basic Auth or Bearer Token
│
├─> Check Environment Variables
│ ├─> GITHUB_TOKEN (for ghcr.io)
│ ├─> REGISTRY_TOKEN (generic bearer token)
│ └─> REGISTRY_USERNAME + REGISTRY_PASSWORD
│
├─> Check Docker Config (~/.docker/config.json)
│ ├─> Look for registry key
│ ├─> Decode base64 auth
│ └─> Extract username/password
│
├─> Check Azure CLI (for *.azurecr.io)
│ └─> Run: az acr login --expose-token
│
└─> Make Request
├─> If 401 + Www-Authenticate header
│ ├─> Parse OAuth2 challenge
│ ├─> Exchange credentials for bearer token
│ └─> Retry with bearer token
└─> Success
Base interface for authentication providers.
class AuthProvider(ABC):
def get_credentials(self, registry: str) -> Optional[Tuple[str, str]]:
"""Return (username, password/token) or None"""
def get_token(self, registry: str) -> Optional[str]:
"""Return bearer token or None"""Reads credentials from environment variables:
REGISTRY_USERNAME+REGISTRY_PASSWORDGITHUB_TOKEN(for ghcr.io)REGISTRY_TOKEN(generic)
Reads ~/.docker/config.json:
- Parses
authssection - Decodes base64 auth strings
- Handles both encoded and plain credentials
- Tries multiple registry key formats
For Azure Container Registry (*.azurecr.io):
- Runs
az acr login --expose-token - Returns access token from Azure
- Only activates for ACR registries
Chains multiple providers, tries each in sequence.
When the registry returns 401 Unauthorized with Www-Authenticate header:
Www-Authenticate: Bearer realm="https://auth.registry.io/token",
service="registry.io",
scope="repository:user/app:pull,push"
The client:
- Parses the challenge (realm, service, scope)
- Makes token request to
realmwith query params - Includes credentials via
Authorizationheader (Basic or Bearer) - Extracts token from response
- Retries original request with bearer token
- Bearer Token:
Authorization: Bearer <token> - Basic Auth:
Authorization: Basic <base64(username:password)>
Both methods supported, auto-selected based on available credentials.
The push() method:
- Calls
get_auth_for_registry()if no explicit credentials - Passes auth to
RegistryClient - Client handles OAuth2 flow automatically
- Prefers
GITHUB_TOKENenvironment variable - Username can be anything (often just "USERNAME")
- Token must have
write:packagesscope - Supports OAuth2 token exchange
- Uses Docker config by default
- Supports username/password or token
- Token created at https://hub.docker.com/settings/security
- Auto-detects via
az acr login - Supports service principal credentials
- Supports admin username/password
- Token obtained via
--expose-tokenflag
- Falls back to Docker config
- Uses environment variables
- Supports Basic Auth and OAuth2
export GITHUB_TOKEN="ghp_xxxxx"
pycontainer build --tag ghcr.io/user/app:v1 --pushdocker login ghcr.io
pycontainer build --tag ghcr.io/user/app:v1 --pushaz login
pycontainer build --tag myregistry.azurecr.io/app:v1 --pushpycontainer build \
--tag registry.example.com/app:v1 \
--username myuser \
--password mytoken \
--pushexport REGISTRY_USERNAME="myuser"
export REGISTRY_PASSWORD="mypass"
pycontainer build --tag registry.example.com/app:v1 --push- Environment variables: Visible in process list
- Docker config: Stored unencrypted (or via credential helper)
- Azure CLI: Token cached by Azure CLI
- CLI flags: Visible in shell history
Recommendation: Use Docker config with credential helpers or Azure CLI for best security.
- GitHub: Require minimum scope (
write:packages) - Docker Hub: Use access tokens, not account password
- ACR: Use service principals with least privilege
For CI/CD pipelines:
- Use secrets management (GitHub Secrets, Azure Key Vault)
- Set as environment variables in pipeline
- Avoid logging credentials
- Use short-lived tokens when possible
- Mock environment variables
- Create temporary Docker configs
- Test provider chain behavior
- Verify base64 decoding
To test live authentication (manual):
# Test GitHub
export GITHUB_TOKEN="ghp_xxxxx"
pycontainer build --tag ghcr.io/test/myapp:test --push
# Test Docker Hub
docker login
pycontainer build --tag username/myapp:test --push
# Test ACR
az login
pycontainer build --tag myregistry.azurecr.io/myapp:test --push- Check token has correct permissions
- Verify token is for correct registry
- Try
docker loginto verify credentials work
- Registry may not support monolithic uploads
- Check registry version (requires v2 API)
- Run
az acr login --name myregistrymanually first - Check Azure subscription is active
- Verify registry name is correct
- Check
~/.docker/config.jsonexists - Run
docker loginto create it - Verify registry key matches (try variations)
- Credential refresh for long-running operations
- Support for Docker credential helpers
- Token caching to avoid repeated OAuth2 exchanges
- GitHub App JWT authentication
- Workload identity for cloud environments