Metadata-Version: 2.5
Name: cauthy
Version: 0.5.0
Summary: Cauthy for Python backends: verify access tokens and webhooks, check permissions, and manage a system's organizations.
Project-URL: Documentation, https://cauthy.docs.vennet.dev/guide/sdk-python
Author: Vennet
License-Expression: Apache-2.0
Keywords: auth,authentication,authorization,cauthy,jwks,jwt,oauth
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Django
Classifier: Framework :: FastAPI
Classifier: Framework :: Flask
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: pyjwt[crypto]>=2.8
Provides-Extra: django
Requires-Dist: django>=4.2; extra == 'django'
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.110; extra == 'fastapi'
Provides-Extra: flask
Requires-Dist: flask>=3.0; extra == 'flask'
Description-Content-Type: text/markdown

# cauthy

Cauthy for Python backends: verify the access tokens and webhooks Cauthy signs,
check what a person may do in an organization, and let the application manage
its own organizations.

```bash
pip install cauthy                 # or "cauthy[fastapi]", "cauthy[django]", "cauthy[flask]"
```

```python
import cauthy

who = cauthy.verify_token(request.headers)        # 401 on CauthyTokenError
orgs = cauthy.organizations_of(who)               # their organizations, with permissions
cauthy.can(orgs[0], "invoicing", "write")         # a lookup: no call to Cauthy

event = cauthy.verify_webhook(headers, raw_body, url="https://app.example.com/hooks/cauthy")

system = cauthy.System()                          # the application acting on its own
org = system.organizations.create(name="Acme", external_id="acme")
system.members.add(org.id, email="ana@acme.com", role="admin")
```

FastAPI:

```python
from fastapi import Depends
from cauthy import Identity, SessionOrganization
from cauthy.fastapi import require_permission, require_session

@app.get("/me")
async def me(who: Identity = Depends(require_session)): ...

@app.post("/orgs/{organization_id}/invoices")
async def create(org: SessionOrganization = Depends(require_permission("invoicing:write"))): ...
```

## Conditions: asking Cauthy to decide

A permission with a condition (`resource.attrs.amount <= 1000000`) is not in an
organization's `permissions`: it arrives in `conditional`, and `can` answers
`False` for it. Ask Cauthy, giving what the condition reads:

```python
# With the person's own token
d = cauthy.check(who, org, "invoices", "approve", attributes={"amount": 500})
if not d:                                         # a Decision is truthy when allowed
    raise Forbidden(d.reason)

# As the application, for any member of its organizations
d = system.check(
    "acme-crm",
    organization_id=org.id,
    user_id=user_id,
    resource="invoices",
    action="approve",
    resource_id="inv_1",
    owner_id=owner_id,
    attributes={"amount": 500, "branch": "north"},
)
d.allowed, d.reason, d.rule                       # rule: what decided, or None
```

The resources a system declares, with the attributes its conditions may read:

```python
from cauthy import CatalogResource

system.catalog.set("acme-crm", [
    CatalogResource("invoices", actions=("read", "approve"), attributes={"amount": "number", "branch": "string"}),
])
system.catalog.get("acme-crm")
```

Configuration comes from `CAUTHY_ISSUER`, `CAUTHY_CLIENT_ID`, `CAUTHY_CLIENT_SECRET`
(or `CAUTHY_SYSTEM_KEY` and `CAUTHY_SYSTEM_KEY_ID`) and `CAUTHY_SYSTEM`, or from
`cauthy.Settings`. Python 3.10 or later; every call has an async twin.

`system.members.add` only adds: a person who already belongs keeps their role and the call
raises `CauthyMemberExistsError` (409 `member_exists`, a `CauthyApiError`). Change a role with
`system.members.set_role`, or pass `only_new=False` to set the role of whoever is already there.

The invitations `system.members.add` and `system.invite` send open Cauthy's own page,
or the application's when it names one: `CAUTHY_ACCEPT_INVITE_URL`
(`https://app.example.com/accept-invite`, where cauthy-auth's `<AcceptInvite />` is).

The full guide: https://cauthy.docs.vennet.dev/en/guide/sdk-python

## Developing

```bash
uv sync
uv run pytest
uv run ruff check src tests && uv run mypy
```
