Skip to main content

Python SDK for authgent — IETF agent-identity stack: identity-chaining, transaction-tokens, RFC 8693 delegation, DPoP, MCP

Project description

authgent — Python SDK

The open-source identity SDK for AI agents. Token verification, delegation chain validation, DPoP sender-constrained tokens, and middleware for FastAPI and Flask.

PyPI Python 3.11+ License

Install

pip install authgent

# With FastAPI middleware
pip install authgent[fastapi]

# With Flask middleware
pip install authgent[flask]

Quick Start

Verify a Token

from authgent import verify_token

identity = await verify_token(
    token="eyJ...",
    issuer="http://localhost:8000",
)

print(identity.subject)           # "client:agnt_xxx"
print(identity.scopes)            # ["search:execute"]
print(identity.delegation_chain)  # DelegationChain(depth=0)

Validate Delegation Chains

from authgent import verify_token, verify_delegation_chain

identity = await verify_token(token=token, issuer="http://localhost:8000")

# Enforce: max 3 hops, must originate from a human
verify_delegation_chain(
    identity.delegation_chain,
    max_depth=3,
    require_human_root=True,
    allowed_actors=["client:agnt_trusted_orchestrator"],
)

Server API Client

from authgent import AgentAuthClient

client = AgentAuthClient("http://localhost:8000")

# Register an agent
agent = await client.register_agent(
    name="search-bot",
    scopes=["search:execute"],
)

# Get a token
token = await client.get_token(
    client_id=agent.client_id,
    client_secret=agent.client_secret,
    scope="search:execute",
)

# Delegate to another agent (token exchange)
delegated = await client.exchange_token(
    subject_token=token.access_token,
    audience="https://downstream-api.example.com",
    scopes=["read"],
    client_id=downstream_agent.client_id,
    client_secret=downstream_agent.client_secret,
)

# Introspect a token
info = await client.introspect_token(delegated.access_token)
print(info["active"])   # True
print(info["act"])      # {"sub": "client:agnt_search", "act": {"sub": "user:123"}}

# Revoke a token
await client.revoke_token(delegated.access_token)

DPoP (Sender-Constrained Tokens)

from authgent import DPoPClient

# Create an ephemeral key pair
dpop = DPoPClient.create()
print(dpop.jkt)  # JWK thumbprint for cnf binding

# Generate proof for a request
proof = dpop.create_proof(
    method="POST",
    url="https://api.example.com/data",
    access_token="eyJ...",
)
# Send as DPoP header alongside the access token

# Verify an incoming DPoP proof
from authgent import verify_dpop_proof

result = verify_dpop_proof(
    proof_jwt=request.headers["DPoP"],
    access_token=token,
    http_method="POST",
    http_uri="https://api.example.com/data",
)
print(result["jkt"])  # JWK thumbprint — must match token's cnf.jkt

Middleware

FastAPI

from fastapi import FastAPI, Depends, Request
from authgent.middleware.fastapi import AgentAuthMiddleware, get_agent_identity

app = FastAPI()
app.add_middleware(AgentAuthMiddleware, issuer="http://localhost:8000")

@app.post("/tools/search")
async def search(request: Request):
    identity = get_agent_identity(request)
    print(f"Agent: {identity.subject}")
    print(f"Scopes: {identity.scopes}")
    print(f"Delegation depth: {identity.delegation_chain.depth}")
    return {"results": [...]}

Flask

from flask import Flask, g
from authgent.middleware.flask import agent_auth_required

app = Flask(__name__)

@app.route("/tools/search", methods=["POST"])
@agent_auth_required(issuer="http://localhost:8000", scopes=["search:execute"])
def search():
    identity = g.agent_identity
    return {"agent": identity.subject}

MCP Scope Challenge

from authgent.middleware.scope_challenge import ScopeChallengeHandler

handler = ScopeChallengeHandler(
    server_url="http://localhost:8000",
    client_id="agnt_xxx",
    client_secret="sec_xxx",
)

# Automatically detect 403 scope challenges and trigger step-up
result = await handler.handle_scope_challenge(
    response=http_response,
    action="access_pii",
    scope="data:pii:read",
)

Adapters

MCP Server

from authgent.adapters.mcp import MCPAuthProvider

auth = MCPAuthProvider(server_url="http://localhost:8000")
identity = await auth.verify(token)

# Discovery URLs for MCP clients
auth.metadata_url  # http://localhost:8000/.well-known/oauth-authorization-server
auth.jwks_url      # http://localhost:8000/.well-known/jwks.json

Protected Resource Metadata (RFC 9728)

from authgent.adapters.protected_resource import ProtectedResourceMetadata

metadata = ProtectedResourceMetadata(
    resource="https://mcp-server.example.com",
    authorization_servers=["http://localhost:8000"],
    scopes_supported=["tools:execute", "db:read"],
)

# Serve at /.well-known/oauth-protected-resource
@app.get("/.well-known/oauth-protected-resource")
async def resource_metadata():
    return metadata.to_dict()

Error Handling

All SDK errors extend AuthgentError:

from authgent import (
    verify_token,
    AuthgentError,
    InvalidTokenError,
    DelegationError,
    DPoPError,
)

try:
    identity = await verify_token(token=token, issuer=issuer)
except InvalidTokenError as e:
    # Token expired, wrong issuer, bad signature
    print(f"Invalid token: {e}")
except DelegationError as e:
    # Chain too deep, unauthorized actor, scope escalation
    print(f"Delegation violation: {e}")
except DPoPError as e:
    # Proof mismatch, expired, wrong binding
    print(f"DPoP error: {e}")
except AuthgentError as e:
    # Any other SDK error
    print(f"Auth error: {e}")

API Reference

Core Functions

Function Description
verify_token(token, issuer) Verify JWT against issuer's JWKS, return AgentIdentity
verify_delegation_chain(chain, ...) Enforce depth, actors, human root policies
verify_dpop_proof(proof_jwt, ...) Verify DPoP proof-of-possession
DPoPClient.create() Create ephemeral DPoP proof generator
AgentAuthClient(url) Full server API client

Models

Class Fields
AgentIdentity subject, scopes, claims, delegation_chain
DelegationChain depth, actors, human_root
TokenClaims sub, scope, iss, exp, iat, jti, act, cnf

Middleware

Import Framework
authgent.middleware.fastapi FastAPI (ASGI)
authgent.middleware.flask Flask (WSGI)
authgent.middleware.scope_challenge MCP scope challenge handler

Adapters

Import Purpose
authgent.adapters.mcp MCP server auth provider
authgent.adapters.protected_resource RFC 9728 metadata
authgent.adapters.langchain LangChain tool auth

License

Apache 2.0

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

authgent-0.2.1.tar.gz (23.3 kB view details)

Uploaded Source

Built Distribution

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

authgent-0.2.1-py3-none-any.whl (25.9 kB view details)

Uploaded Python 3

File details

Details for the file authgent-0.2.1.tar.gz.

File metadata

  • Download URL: authgent-0.2.1.tar.gz
  • Upload date:
  • Size: 23.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for authgent-0.2.1.tar.gz
Algorithm Hash digest
SHA256 f86bd3e74fce2e81336fb2faec74452a10b1076cd15cfbb4585321c89d53e4bc
MD5 51d965d91e5ab0983c5d9d54070fdb69
BLAKE2b-256 aba0f2f444334ff1f2d242cc6367b7f90bff88d4c29ea40759c9cc61a17d5c23

See more details on using hashes here.

File details

Details for the file authgent-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: authgent-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 25.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for authgent-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 97911f4be2c7c0f7874d83f2be20fcf834769998d25b0a26296446b20089933c
MD5 cce5be8067cd858bb916310bcd557a79
BLAKE2b-256 15aa25f4049c2a425d9fb3d4e99640993bbc4f13f0c5e07126e829c918e2688c

See more details on using hashes here.

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