Skip to main content

Add authentication to your Python app

Open in Cursor

This guide walks you through authenticating a Python backend service or AI agent with LumoAuth using the lumoauth Python SDK. You'll set up client credentials authentication, use the Ask API for authorization, and optionally set up delegation chains.


Before you start​

You need:

  • A LumoAuth account with an organization (sign up)
  • An Agent or OAuth application registered in your organization with Client ID and Client Secret
  • Python 3.9+

From your organization portal, note your Organization ID, Client ID, and Client Secret. To register an OAuth application for this, open Applications → New application and choose Machine to machine; the client secret is shown once, on the page you land on after Create application.

The Quickstarts page (under Applications & developer tools) has a Python tab that shows the organization ID and client ID pre-filled, ready to copy.

The Quickstarts page with the Python tab selected, showing pre-filled environment variables

The tab's first step lists only OAuth clients of the kind these snippets need; if none exists, Create one for this framework opens the create form with the right type preselected and brings you back. Its last step, Check it worked, shows the first sign-in that completes through the selected client.


Install the SDK​

pip install lumoauth

For cryptographic agent identity (AAuth), install with the extra:

pip install 'lumoauth[aauth]'

Authenticate your agent​

The SDK uses the OAuth 2.0 client credentials flow for server-to-server authentication.

agent.py
from lumoauth import LumoAuthAgent

agent = LumoAuthAgent(
base_url="https://app.lumoauth.dev",
org_id="acme-corp",
client_id="your-client-id",
client_secret="your-client-secret",
)

# Authenticate and get an access token
agent.authenticate(scopes=["read:users", "write:documents"])
print(f"Authenticated. Scopes: {agent.token_scopes}")

You can also use environment variables instead of passing credentials directly:

.env
LUMOAUTH_URL=https://app.lumoauth.dev
LUMOAUTH_ORG_ID=acme-corp
AGENT_CLIENT_ID=your-client-id
AGENT_CLIENT_SECRET=your-client-secret
agent = LumoAuthAgent() # reads from environment
agent.authenticate()

Use the Ask API​

The Ask API provides lightweight preflight authorization checks, designed for LLM tool-calling workflows where you need a quick allow/deny decision before executing an action.

ask_api.py
from lumoauth import LumoAuthAgent

agent = LumoAuthAgent()
agent.authenticate()

# Simple boolean check
if agent.is_allowed("read_document", context={"document_id": "doc-123"}):
print("Access granted - reading document")
else:
print("Access denied")

# Detailed response with audit trail
result = agent.ask("delete_user", context={"user_id": "u-456"})
print(result)
# {
# 'allowed': False,
# 'action': 'delete_user',
# 'reason': 'insufficient_permissions',
# 'audit_id': 'aud_abc123',
# 'context': {'user_id': 'u-456'}
# }

Check agent identity and capabilities​

capabilities.py
from lumoauth import LumoAuthAgent

agent = LumoAuthAgent()
agent.authenticate()

# Get full agent identity
info = agent.get_agent_info()
print(f"Agent: {info['agent_id']}")
print(f"Capabilities: {info['capabilities']}")

# Check for specific capability
if agent.has_capability("web_search"):
print("Agent can perform web searches")

# Monitor budget
budget = agent.get_budget_status()
print(f"Budget policy: {budget}")

if agent.is_budget_exhausted():
print("Daily token budget reached - pausing operations")

Act on behalf of users with delegation chains​

Use token exchange (swapping the user's consent for a token that records both the agent and the user it acts for) to perform actions on behalf of a user. This is useful for AI agents that need user-scoped permissions.

delegation.py
from lumoauth import LumoAuthAgent, DelegationChain

agent = LumoAuthAgent()
agent.authenticate()

chain = DelegationChain(agent, redirect_uri="https://myapp.com/consent")

# Step 1: Generate a consent URL for the user
consent_url = chain.get_consent_url(
"session-1",
scopes=["profile", "documents"],
)
print(f"Redirect user to: {consent_url}")

# Step 2: After user grants consent, handle the callback
chain.handle_consent_callback("session-1", authorization_code="CODE_FROM_CALLBACK")

# Step 3: Exchange for a delegated token
delegated_token = chain.exchange("session-1", scopes=["read:documents"])

# Step 4: Make API calls on behalf of the user
response = chain.request("session-1", "GET", "/api/v1/documents")
print(response.json())

# Clean up when done
chain.revoke("session-1")

Integrate with MCP servers​

Exchange your agent token for an MCP server-scoped token:

mcp.py
from lumoauth import LumoAuthAgent

agent = LumoAuthAgent()
agent.authenticate()

mcp_token = agent.get_mcp_token("my-mcp-server")
# Use mcp_token to authenticate with the MCP server

Use in a FastAPI app​

Install the SDK with the FastAPI extra (this also pulls in fastapi):

pip install 'lumoauth[fastapi]'
app.py
from fastapi import FastAPI, Depends, HTTPException
from lumoauth import LumoAuthAgent

app = FastAPI()
agent = LumoAuthAgent()
agent.authenticate()


def check_permission(action: str):
def dependency():
agent.ensure_authenticated()
if not agent.is_allowed(action):
raise HTTPException(status_code=403, detail="Forbidden")
return Depends(dependency)


@app.get("/api/documents", dependencies=[check_permission("read_documents")])
async def list_documents():
return {"documents": []}


@app.delete("/api/documents/{doc_id}", dependencies=[check_permission("delete_documents")])
async def delete_document(doc_id: str):
return {"deleted": doc_id}

What's next?​

Standards used on this page​

Plain nameStandard
Token exchange (delegation)RFC 8693

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