Metadata-Version: 2.4
Name: sonnet-auth
Version: 0.1.1
Summary: JWT/JWKS authentication and Cedar authorization for sonnet-server applications
Author-email: Wolfgang Miller <wolfgang.miller@petrarca-labs.com>
License-Expression: Apache-2.0
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.14
Requires-Python: <4.0,>=3.14
Description-Content-Type: text/markdown
Requires-Dist: sonnet-server>=0.1.10
Requires-Dist: joserfc>=1.0.0
Requires-Dist: httpx>=0.28.0
Provides-Extra: cedar
Requires-Dist: cedarpy>=4.0.0; extra == "cedar"
Provides-Extra: dev
Requires-Dist: sonnet-auth[cedar]; extra == "dev"
Requires-Dist: ruff>=0.3.0; extra == "dev"
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Requires-Dist: testcontainers[keycloak]>=4.0; extra == "dev"

# Sonnet Auth

JWT/JWKS authentication and Cedar authorization for sonnet-server
applications. No FastMCP dependency -- MCP auth wiring lives in each
domain service.

## What it provides

- **JWT/JWKS validation** -- `JwtCredentialValidator` implements
  sonnet-server's `CredentialValidator` protocol. RS256/ES256 with
  TTL-based JWKS key refresh and graceful degradation on IdP outages.
- **Claim mapping** -- configurable dot-path extraction from JWT claims
  to `AuthContext` fields and Cedar principal attributes.
  `auto_map_claims` mode passes all non-plumbing claims automatically.
- **Cedar policy evaluation** (optional `[cedar]` extra) -- `PolicyEngine`
  wraps cedarpy for in-process RBAC/ABAC. `check_authz()` and
  `filter_authz()` are one-liner authorization for REST and MCP handlers.
- **Pluggable resource resolution** -- `ResourceAttributeResolver` protocol
  for domain-specific Cedar resource attributes.
- **Settings model** -- `AuthSettings.resolve(env_prefix, seed)` with
  per-field env var overrides and optional DB seeding. No global state.

## Install

```bash
# JWT authentication only
pip install sonnet-auth

# JWT + Cedar authorization
pip install sonnet-auth[cedar]
```

## Prerequisites

- Python 3.14+
- sonnet-server >= 0.1.9

## Usage

### With extensions (recommended)

Register `AuthnExtension` and `AuthzExtension` in your app factory:

```python
from sonnet_auth import AuthnExtension, AuthzExtension

registry = create_extension_registry(
    DatabaseExtension(),
    AuthnExtension(env_prefix="MY_APP_"),
    AuthzExtension(loader=my_policy_loader),
    McpExtension(),
    RestExtension(),
)
```

The extensions handle all wiring: settings resolution, JWT validator
registration, Cedar engine creation, DI registration.

Configuration via env vars:

```
MY_APP_AUTHN_ENABLED=true
MY_APP_AUTHN_JWKS_URI=https://idp.example.com/.well-known/jwks.json
MY_APP_AUTHN_ISSUER=https://idp.example.com
MY_APP_AUTHN_AUDIENCE=my-app
MY_APP_AUTHZ_ENABLED=true
```

### Policy loading

`AuthzExtension` takes a `PolicyLoader` callable that returns
`(policies_text, schema_text)`. The consumer decides how to load:

```python
# From files
def load_from_files():
    return (
        Path("cedar/policies.cedar").read_text(),
        Path("cedar/schema.cedarschema").read_text(),
    )

# From DB (coco-rag pattern)
def load_from_db():
    svc = get_settings_service()
    return (
        svc.get_text("authz_cedar_policies"),
        svc.get_text("authz_cedar_schema"),
    )

# Multiple policy files
def load_from_dir():
    policies = "\n".join(p.read_text() for p in Path("cedar/").glob("*.cedar"))
    schema = Path("cedar/schema.cedarschema").read_text()
    return (policies, schema)

AuthzExtension(loader=load_from_files)
```

### DB-seeded authn (services with a settings table)

```python
def load_seed():
    svc = get_settings_service()
    return {
        "authn_enabled": svc.get("authn_enabled"),
        "authn_config": svc.get("authn_config"),
        "authz_enabled": svc.get("authz_enabled"),
    }

AuthnExtension(env_prefix="COCO_RAG_", seed_fn=load_seed)
```

### Authorization in handlers

```python
from sonnet_auth import check_authz, filter_authz

# In any REST handler or MCP tool
check_authz("search", "Source", source_name)  # raises AuthzDeniedError on deny

# For list operations
permitted = filter_authz("list", "Source", source_names)
```

## Package structure

```
src/sonnet_auth/
    __init__.py          # public API with lazy Cedar imports
    settings.py          # AuthSettings, AuthnConfig
    context.py           # JWT claim -> AuthContext mapping
    jwt_validator.py     # JwtCredentialValidator (JWKS)
    policy_engine.py     # Cedar PolicyEngine [cedar extra]
    authz.py             # check_authz, filter_authz [cedar extra]
```

## License

Apache License 2.0
