Skip to main content

AAuth Quickstart — Python SDK

Authenticate a Python agent using AAuth (the agent identity protocol: an agent proves who it is by signing each request with its own private key instead of sending a shared secret) with the lumoauth SDK. The agent's identity is an Ed25519 key pair registered with LumoAuth; request_authorization trades a resource token for a short-lived access token; signed_request turns every outgoing HTTP call into a signed HTTP request (a per-request, replay-proof proof that the caller holds the key).

Prerequisites
  • A LumoAuth organization (agent identity is available on every plan, nothing to switch on)
  • Python 3.9+

Complete Steps 1–3 first: generate a key pair, register the agent, and register a resource.

Install​

pip install 'lumoauth[aauth]'

Quick Start​

from lumoauth import AAuthClient

# Generate a key pair (one-time) - or load an existing private key
private_pem, jwks = AAuthClient.generate_keypair()
# Register the **jwks** with LumoAuth in the portal, then:

client = AAuthClient(
agent_identifier="https://my-agent.example.com",
private_key_pem=private_pem, # or open("agent-key.pem").read()
org_id="acme-corp",
)

# You need two tokens first (see the AAuth Quickstart):
# agent_token — your `agent+jwt`, bound to this key pair via its cnf.jwk;
# it is presented in the Agent-Auth header.
# resource_token — obtained from the resource server you intend to call.
agent_token = "..."
resource_token = "..."

# Request authorisation (direct flow - no user interaction)
tokens = client.request_authorization(
resource_token=resource_token,
scope="read write",
agent_token=agent_token,
redirect_uri="https://my-agent.example.com/oauth/callback", # only used when consent is needed
)

if tokens.get("authorization_required"):
# User consent needed - redirect the user
print("Redirect user to:", tokens["auth_url"])
# After the user approves, exchange the code (see "Handling user consent" below)
else:
# On success the response body carries the agent access token under
# the key "auth_token" (not "access_token"). Use it as the bearer.
resp = client.signed_request(
"GET",
"https://api.example.com/v1/data",
auth_token=tokens["auth_token"],
)
print(resp.json())

Key points​

StepWhat happens
AAuthClient.generate_keypair()Generates an Ed25519 key pair; register the JWKS in the portal
request_authorization(...)Sends a signed AAuth request; may return authorization_required for user-consent flows
client.signed_request(...)Sends a signed HTTP request: adds the Signature and Signature-Input headers plus Authorization: Bearer <auth_token>

When authorization_required is True, redirect the user to auth_url. After the user approves, the browser is redirected back to your redirect_uri with a code parameter. Exchange it with the same redirect URI the code was delivered to (it must match one registered for the agent):

# Exchange the authorization code for tokens
tokens = client.exchange_code(
received_code,
"https://my-agent.example.com/oauth/callback",
agent_token=agent_token,
)
resp = client.signed_request("GET", "https://api.example.com/v1/data",
auth_token=tokens["auth_token"])

Next steps​

Standards used on this page​

Plain nameStandard
Signed HTTP requests (HTTP message signatures)RFC 9421

See the standards reference for what each one covers and how LumoAuth uses it.