AAuth protocol implementation for Python
Project description
aauth
Python implementation of the AAuth protocol — an authorization protocol for agent-to-resource access built on HTTP Message Signatures (RFC 9421) and JWT-based proof-of-possession tokens.
See the full AAuth demo walkthrough for a live exploration of the protocol flows.
Packages
This repo contains two installable packages with distinct responsibilities:
| Package | pip install | import as | Responsibility |
|---|---|---|---|
aauth |
pip install aauth |
import aauth |
Full AAuth protocol: tokens, headers, metadata, agent/resource roles |
aauth-signing |
pip install aauth-signing |
from aauth_signing import ... |
HTTP Message Signatures (RFC 9421) + Signature-Key header — standalone, no AAuth dependency |
aauth-signing is the low-level signing layer. It can be used independently if you only need RFC 9421 HTTP Message Signatures with the Signature-Key header extension (hwk, jwks_uri, jwt, jkt-jwt schemes). It has no dependency on the rest of AAuth.
aauth is the full protocol implementation. It depends on aauth-signing (pulled in automatically) and re-exports its signing functions, so you never need to import both — just import aauth and everything is available.
Installation
# Install everything (recommended)
pip install aauth
# Install only the signing layer (no tokens, headers, or agent/resource APIs)
pip install aauth-signing
For development:
pip install -e ".[dev]"
Quick Start
import aauth
# Generate an Ed25519 key pair
private_key, public_key = aauth.generate_ed25519_keypair()
# Sign a request (pseudonymous — public key embedded in header)
signed_headers = aauth.sign_request(
method="GET",
target_uri="https://resource.example/api/data",
headers={},
body=None,
private_key=private_key,
sig_scheme="hwk"
)
# Sign with agent identity (JWKS-backed)
signed_headers = aauth.sign_request(
method="POST",
target_uri="https://resource.example/api/data",
headers={"Content-Type": "application/json"},
body=b'{"key": "value"}',
private_key=private_key,
sig_scheme="jwks_uri",
id="https://agent.example",
kid="key-1",
dwk="aauth-agent.json"
)
# Sign with an auth token
signed_headers = aauth.sign_request(
method="GET",
target_uri="https://resource.example/api/data",
headers={},
body=None,
private_key=private_key,
sig_scheme="jwt",
jwt=auth_token
)
Library Reference
Key Management
import aauth
private_key, public_key = aauth.generate_ed25519_keypair()
jwk = aauth.public_key_to_jwk(public_key, kid="key-1")
thumbprint = aauth.calculate_jwk_thumbprint(jwk)
Signature Verification
is_valid = aauth.verify_signature(
method=request.method,
target_uri=str(request.url),
headers=dict(request.headers),
body=request_body,
signature_input_header=request.headers.get("Signature-Input"),
signature_header=request.headers.get("Signature"),
signature_key_header=request.headers.get("Signature-Key"),
jwks_fetcher=my_jwks_fetcher # required for jwks_uri/jwt schemes
)
What the signature covers by default: @method, @authority, @path, signature-key (plus @query when a query string is present). This is enough to bind the signature to the specific endpoint and prevent replay across methods or hosts.
Body signing is opt-in and usually not needed. Resources can require content-digest and/or content-type coverage via additional_signature_components, but this adds significant complexity — bodies must be fully buffered before signing/verifying, content-encoding negotiation interferes, and most agent-to-resource interactions are already protected by TLS. Covering the request line and key identity is sufficient for the vast majority of use cases.
Token Creation
# Resource token (resource → auth server)
resource_token = aauth.create_resource_token(
iss="https://resource.example",
aud="https://auth.example",
agent="https://agent.example",
agent_jkt=agent_thumbprint,
scope="data.read data.write",
private_key=resource_private_key,
kid="resource-key-1"
)
# Auth token (auth server → agent)
auth_token = aauth.create_auth_token(
iss="https://auth.example",
aud="https://resource.example",
agent="https://agent.example",
cnf_jwk=agent_jwk,
act={"sub": "https://agent.example"},
scope="data.read",
private_key=auth_private_key,
kid="auth-key-1"
)
# Parse token claims (no verification)
claims = aauth.parse_token_claims(token)
AAuth Header Parsing
# Parse an AAuth challenge from a resource's 401 response
challenge = aauth.parse_agent_auth_header(
'requirement=auth-token; resource-token="..."; auth-server="https://auth.example"'
)
# Build an AAuth challenge
challenge_header = aauth.build_agent_auth_challenge(
require_signature=True,
require_identity=True,
require_auth_token=True,
resource_token=resource_token,
auth_server="https://auth.example"
)
High-Level Agent and Resource APIs
# Agent-side request signer
signer = aauth.AgentRequestSigner(
private_key=private_key,
agent_id="https://agent.example",
agent_token=agent_token
)
signed_headers = signer.sign_request(
method="GET",
target_uri="https://resource.example/api/data",
headers={},
body=None,
sig_scheme="jwt"
)
# Resource-side request verifier
verifier = aauth.RequestVerifier(
canonical_authorities=["resource.example:443"],
jwks_fetcher=my_jwks_fetcher
)
result = verifier.verify_request(
method="GET",
target_uri="https://resource.example/api/data",
headers=request_headers,
body=request_body,
require_identity=True,
require_auth_token=True
)
if result["valid"]:
print(f"Agent: {result['agent_id']}, Scopes: {result['scopes']}")
Package Structure
aauth-signing/ ← standalone pip package (aauth_signing)
└── aauth_signing/
├── signer.py # sign_request — builds Signature-Input/Signature/Signature-Key
├── verifier.py # verify_signature — validates RFC 9421 signatures
├── signature_key.py# Signature-Key header (hwk/jwks_uri/jwt/jkt-jwt schemes)
├── signature_base.py
├── signature_input.py
├── signature.py
├── algorithms.py
└── keys/ # JWK helpers used by the signing layer
aauth/ ← main pip package (aauth), depends on aauth-signing
├── signing/ # Thin shims — re-exports aauth_signing.{signer,verifier,...}
├── keys/ # Key management and JWK operations
├── tokens/ # JWT token creation and validation (resource, auth, agent tokens)
├── headers/ # AAuth header parsing and building (AAuth-Requirement, etc.)
├── metadata/ # Metadata discovery (.well-known endpoints)
├── http/ # Deferred response helpers (202 + polling)
├── agent/ # Agent role: signing, challenge handling, token exchange, polling
└── resource/ # Resource role: challenge building, request verification
Testing
pytest tests/ -v
Protocol
- Spec: github.com/dickhardt/AAuth
- Demo: blog.christianposta.com/aauth-full-demo
- Site: aauth.dev
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aauth-0.3.5.tar.gz.
File metadata
- Download URL: aauth-0.3.5.tar.gz
- Upload date:
- Size: 48.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da955ab49cf771144b575e82665f060c684ed262a74af108ad648c560686b044
|
|
| MD5 |
530bc19638ca01ac51a3454855556e81
|
|
| BLAKE2b-256 |
0e30381ecde766cf726eb4c82be1cd0cd34e8c44600332dc2c0cb02525c7aaef
|
File details
Details for the file aauth-0.3.5-py3-none-any.whl.
File metadata
- Download URL: aauth-0.3.5-py3-none-any.whl
- Upload date:
- Size: 51.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8a0e313377bbf090640063c53aa488cf2959d0ef9be11377400d69ef8f97eff7
|
|
| MD5 |
099adbf422e87e45b6f9ede5020bf46a
|
|
| BLAKE2b-256 |
0eac9ca18701703d98f3c19c14a1ea1d8007d701ffafe7f8969351de45254045
|