Metadata-Version: 2.4
Name: huudis
Version: 0.2.0
Summary: Official Python SDK for Huudis — email/social/MFA + fine-grained authorization + webhook signature verification.
Project-URL: Homepage, https://huudis.com
Project-URL: Documentation, https://huudis.com/docs
Project-URL: Source, https://github.com/hachimi-cat/saas-huudis/tree/master/sdk/python
Project-URL: Issues, https://github.com/hachimi-cat/saas-huudis/issues
Author-email: Forjio <hello@huudis.com>
License: MIT
Keywords: auth,forjio,huudis,iam,jwt,oidc,openid-connect
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.27.0
Requires-Dist: pyjwt[crypto]>=2.9.0
Description-Content-Type: text/markdown

# huudis (Python)

Official Python SDK for [Huudis](https://huudis.com). Works with FastAPI,
Flask, Django, or anything else that speaks WSGI/ASGI.

## Install

```bash
pip install huudis
```

## Quickstart

Set env vars (or pass them to the client):

```bash
HUUDIS_ISSUER=https://huudis.com
HUUDIS_AUDIENCE=oc_your_client_id
HUUDIS_CLIENT_ID=oc_your_client_id
HUUDIS_CLIENT_SECRET=cs_...   # omit for public clients (PKCE)
```

### Verify an access token

```python
from fastapi import FastAPI, Header, HTTPException
from huudis import verify_access_token, HuudisAuthError

app = FastAPI()

@app.get("/me")
def me(authorization: str = Header(...)):
    try:
        claims = verify_access_token(authorization)
    except HuudisAuthError as e:
        raise HTTPException(401, detail=e.message)
    return {"user_id": claims.sub, "email": claims.email}
```

### OIDC sign-in flow

```python
from huudis import HuudisClient

huudis = HuudisClient()

# Step 1: redirect the user
url = huudis.authorization_url(
    redirect_uri="https://yourapp.com/callback",
    state=session["state"],
    code_challenge=session["pkce_challenge"],
)

# Step 2: exchange the code
tokens = huudis.exchange_code(
    code=request.args["code"],
    redirect_uri="https://yourapp.com/callback",
    code_verifier=session["pkce_verifier"],
)
userinfo = huudis.userinfo(tokens["access_token"])
```

### Authorization check

```python
result = huudis.authz_check(
    access_token=token,
    principal={"type": "user", "id": claims.sub, "accountId": claims.account_id},
    action="plugipay:DeleteInvoice",
    resource="forjio:plugipay::acc_.../invoice/inv_9F8",
)
if not result["allow"]:
    raise HTTPException(403, detail=result.get("reason"))
```

## Types

| Name | Description |
|---|---|
| `verify_access_token(header_or_token, *, issuer=None, audience=None, require_mfa=False)` | Module-level convenience — reads `HUUDIS_ISSUER` / `HUUDIS_AUDIENCE` from env. |
| `HuudisClient(issuer=..., client_id=..., client_secret=..., audience=..., api_base=...)` | Full surface. Use as a context manager (`with HuudisClient() as huudis:`) or call `.close()`. |
| `HuudisClaims` | Dataclass returned by verification — `sub`, `email`, `account_id`, `scope`, `mfa_verified`, etc. |
| `HuudisAuthError` | Raised on any failure; carries `.code` + `.message`. |

JWKS is cached in-process via PyJWT's `PyJWKClient`.

## Docs

- Full docs: <https://huudis.com/docs>
- Source: <https://github.com/hachimi-cat/saas-huudis/tree/master/sdk/python>

## License

MIT
