Metadata-Version: 2.4
Name: auth-agents
Version: 0.4.3
Summary: Verify AI agent identities with Agent Auth. DID-based authentication using Ed25519 and Verifiable Credentials.
Project-URL: Homepage, https://usevigil.dev
Project-URL: Documentation, https://usevigil.dev/docs/
Project-URL: Repository, https://github.com/AgenthAgent/auth-agents-sdk-python
Author: Zeliang Cheng
License-Expression: MIT
License-File: LICENSE
Keywords: agent-auth,ai-agent,auth-agents,authentication,did,ed25519,identity,usevigil,verifiable-credential
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.8
Requires-Dist: cryptography>=41.0.0
Requires-Dist: httpx>=0.24.0
Description-Content-Type: text/markdown

# auth-agents

Verify AI agent identities with [Agent Auth](https://usevigil.dev). DID-based authentication using Ed25519 and Verifiable Credentials.

## Install

```bash
pip install auth-agents
```

## Quick Start — Verify an Agent Credential

```python
from auth_agents import AuthAgents

client = AuthAgents()

result = client.verify("eyJhbGciOiJFZERTQSJ9...")

if result["valid"]:
    print(result["did"])          # did:key:z6Mk...
    print(result["agent_name"])   # Claude
    print(result["agent_model"])  # claude-opus-4-6
    print(result["key_origin"])   # "server_generated" or "client_provided"
```

## Authentication Flows

### Server-Generated Keys (zero setup)

The server generates an Ed25519 keypair and returns the private key once.
Store it securely — the server never retains it.

```python
from auth_agents import AuthAgents

client = AuthAgents()

# 1. Register — server generates keypair and returns private key
identity = client.register(
    agent_name="MyAgent",
    agent_model="claude-opus-4-6",
    agent_provider="Anthropic",
    agent_purpose="Automated data processing",
)

did             = identity["did"]
credential      = identity["credential"]
private_key_jwk = identity["private_key_jwk"]   # save this securely
# identity["key_origin"] == "server_generated"

# 2. Request a challenge nonce
challenge_resp = client.challenge(did)
challenge_id   = challenge_resp["challenge_id"]
nonce          = challenge_resp["nonce"]

# 3. Sign the nonce with the stored private key
signature = AuthAgents.sign_challenge(private_key_jwk, nonce)

# 4. Authenticate
auth_result = client.authenticate(
    challenge_id=challenge_id,
    did=did,
    signature=signature,
)

if auth_result["valid"]:
    print("Authenticated!", auth_result["session_token"])
```

### Headless / Bring-Your-Own-Key (BYOK)

Generate your own keypair locally, register with your public key, and sign
challenges with your private key. The server never sees your private key.

```python
from auth_agents import AuthAgents

client = AuthAgents()

# 1. Generate a local Ed25519 keypair
key_pair        = AuthAgents.generate_key_pair()
public_key_jwk  = key_pair["public_key_jwk"]
private_key_jwk = key_pair["private_key_jwk"]   # keep secret

# 2. Register with your own public key
identity = client.register(
    agent_name="MyAgent",
    agent_model="claude-opus-4-6",
    agent_provider="Anthropic",
    agent_purpose="Automated data processing",
    public_key_jwk=public_key_jwk,
)

did        = identity["did"]
credential = identity["credential"]
# identity["key_origin"] == "client_provided"

# 3. Request a challenge nonce
challenge_resp = client.challenge(did)
challenge_id   = challenge_resp["challenge_id"]
nonce          = challenge_resp["nonce"]

# 4. Sign the nonce locally — nonce is signed as UTF-8 text
signature = AuthAgents.sign_challenge(private_key_jwk, nonce)

# 5. Authenticate
auth_result = client.authenticate(
    challenge_id=challenge_id,
    did=did,
    signature=signature,
)

if auth_result["valid"]:
    print("Authenticated!", auth_result["session_token"])
```

## API Reference

### `AuthAgents(base_url=...)`

Client class. Defaults to `https://auth.usevigil.dev`. The SDK enforces HTTPS for all API communication. HTTP is only allowed for `localhost` during development.

### `AuthAgents.generate_key_pair()` — static

Generate a fresh Ed25519 keypair. Returns:

```python
{
    "public_key_jwk":  {"kty": "OKP", "crv": "Ed25519", "x": "<base64url>"},
    "private_key_jwk": {"kty": "OKP", "crv": "Ed25519", "x": "<base64url>", "d": "<base64url>"},
}
```

### `AuthAgents.sign_challenge(private_key_jwk, nonce)` — static

Sign a challenge nonce with an Ed25519 private key JWK. Returns a
base64url-encoded signature string (no padding).

### `client.register(...)`

Register a new agent identity. Pass `public_key_jwk` for BYOK; omit it for
server-generated keys. Optional inputs:
- `metadata` — dict of string key/value pairs

Returns a dict including:

| Field | Description |
|---|---|
| `did` | Agent DID (`did:key:z6Mk...`) |
| `credential` | VC-JWT string |
| `key_fingerprint` | Short fingerprint of the public key |
| `key_origin` | `"server_generated"` or `"client_provided"` |
| `private_key_jwk` | Private key JWK (server-generated flow only) |

Registration always returns a credential with the server default lifetime (24 hours).
To customize the credential lifetime, use `credential_expires_in` on `client.challenge(...)`.

### `client.challenge(did, site_id=None, credential_expires_in=None)`

Request an authentication challenge nonce. Optional `site_id` scopes the
challenge/session in site-provisioned deployments. Optional `credential_expires_in`
sets the credential lifetime in seconds, controlled by the website developer
(`0` means non-expiring). Returns `challenge_id`, `nonce`, and `expires_in`.

### `client.authenticate(challenge_id, did, signature)`

Submit a signed challenge. Returns `valid`, `session_token`, `credential`,
`agent` object, and `expires_in`. The credential uses the lifetime chosen in the
preceding `client.challenge(...)` call.

### `client.verify(credential)`

Verify a VC-JWT credential.
- Valid credential: returns `{"valid": True, ...}`
- Invalid credential (HTTP 401): returns `{"valid": False, "error": "...", "message": "..."}` without raising

Returns a dict including:

| Field | Description |
|---|---|
| `valid` | `True` if the credential is valid |
| `did` | Agent DID |
| `agent_name` | Agent display name |
| `agent_model` | Model identifier (e.g. `claude-opus-4-6`) |
| `agent_provider` | Provider name (e.g. `Anthropic`) |
| `agent_purpose` | What the agent intends to do |
| `key_fingerprint` | Short fingerprint of the public key |
| `key_origin` | `"server_generated"` or `"client_provided"` |
| `issued_at` | ISO 8601 issuance timestamp |
| `expires_at` | ISO 8601 expiration timestamp (or `None`) |

### `verify(credential)` — module-level shorthand

Equivalent to `AuthAgents().verify(credential)`.

## Documentation

Full API reference at [usevigil.dev/docs](https://usevigil.dev/docs/)
