Metadata-Version: 2.4
Name: qavren-auth
Version: 0.1.0
Summary: Thin FastAPI auth dependency for Qavren (Keycloak) realms
Project-URL: Repository, https://github.com/stevenfackley/qavren-auth
License: MIT
Requires-Python: >=3.11
Requires-Dist: fastapi>=0.110
Requires-Dist: pyjwt[crypto]>=2.13.0
Description-Content-Type: text/markdown

# qavren-auth (Python)

Thin FastAPI auth dependency for Qavren (Keycloak) realms. Validates RS256
bearer tokens against a realm's JWKS endpoint. No custom cryptography; PyJWT
does the verification. Fails closed (401) on every error path.

## Install

```bash
pip install qavren-auth
```

## Use

```python
from fastapi import Depends, FastAPI
from qavren_auth import require_user, require_roles, User

app = FastAPI()

@app.get("/me")
def me(user: User = Depends(require_user("squarelog"))):
    return {"sub": user.sub, "email": user.email, "roles": sorted(user.roles)}

@app.get("/admin")
def admin(user: User = Depends(require_roles("squarelog", "admin"))):
    return {"sub": user.sub}
```

## Convention (shared by all Qavren SDKs)

- **Base URL** comes from `QAVREN_AUTH_URL` (default `https://auth.qavren.com`);
  override per-dependency with `base_url=`.
- **Issuer** is `{base}/realms/{realm}`; **JWKS** is
  `{issuer}/protocol/openid-connect/certs`.
- **Claims consumed:** `sub`, `email`, `realm_access.roles`. Nothing else.
- **Audience is NOT validated** — Keycloak defaults `aud` to `account`. Trust
  rests on RS256 signature (realm JWKS) + issuer + expiry.
- **Fail closed:** a missing/garbage/expired/wrong-issuer token, or an
  unreachable JWKS with no valid cached keys, returns **401** — never 500,
  never pass-through. Missing a required role returns **403**.

## Realm isolation

Each app gets its own realm, so a token minted for `squarelog` is signed by
squarelog's key and carries `iss=.../realms/squarelog`. A `require_user("recharacter")`
dependency fetches recharacter's JWKS and validates a different issuer, so the
squarelog token is rejected. This is the product guarantee, covered by
`test_token_from_another_realm_rejected`.

## Not yet published

This package is not on PyPI yet. Consume it from a local checkout
(`pip install -e sdks/python`) until the publishing pipeline lands.
