Metadata-Version: 2.4
Name: tynari-auth-sdk
Version: 0.1.1
Summary: JWT verification SDK for Tynari microservices — JWKS fetching, caching, RBAC, and FastAPI middleware
Project-URL: Homepage, https://github.com/tynari/tynari-auth-sdk
Project-URL: Issues, https://github.com/tynari/tynari-auth-sdk/issues
Author-email: Tynari <tynari.dev01@gmail.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Requires-Python: >=3.10
Requires-Dist: httpx>=0.24
Requires-Dist: pydantic>=2.0
Requires-Dist: python-jose[cryptography]>=3.3
Requires-Dist: starlette>=0.27
Provides-Extra: dev
Requires-Dist: fastapi>=0.100; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Description-Content-Type: text/markdown

# tynari-auth-sdk

JWT verification SDK for Tynari microservices. Handles JWKS fetching, key caching, automatic key rotation, JWT verification, and role-based access control.

## Installation

```bash
pip install tynari-auth-sdk
```

## Quick Start

```python
from fastapi import FastAPI, Depends
from auth_sdk import AuthMiddleware, get_current_user, require_roles, TokenUser

app = FastAPI()

# Add authentication middleware
app.add_middleware(
    AuthMiddleware,
    jwks_url="https://auth.tynari.com/.well-known/jwks.json",
    exclude_paths={"/health", "/docs", "/openapi.json"},
)

# Protected endpoint — any authenticated user
@app.get("/me")
async def me(user: TokenUser = Depends(get_current_user)):
    return {"user_id": user.sub, "role": user.role}

# Role-restricted endpoint
@app.get("/artists-only", dependencies=[Depends(require_roles(["artist"]))])
async def artists_only():
    return {"message": "Welcome, artist!"}
```

## Configuration

### AuthMiddleware

| Parameter | Type | Default | Description |
|---|---|---|---|
| `jwks_url` | `str` | *required* | URL to the JWKS endpoint |
| `cache_ttl` | `float` | `3600.0` | Key cache TTL in seconds |
| `exclude_paths` | `set[str]` | `set()` | Paths to skip authentication |
| `auto_error` | `bool` | `True` | If `False`, sets `request.state.user = None` instead of returning 401 |

### TokenUser

The verified user object attached to `request.state.user`:

| Field | Type | Description |
|---|---|---|
| `sub` | `str` | User ID (UUID string) |
| `role` | `str` | User role (e.g. `"artist"`, `"seller"`) |
| `scope` | `str` | Token scope (always `"access"`) |
| `exp` | `int` | Expiration timestamp |

## How It Works

1. **Middleware** intercepts each request and extracts the `Authorization: Bearer <token>` header
2. **JWKS client** fetches the RSA public key from your auth service (cached for 1 hour by default)
3. **Token verification** validates the RS256 signature, checks expiration, and enforces `scope == "access"`
4. **User attachment** puts the decoded `TokenUser` on `request.state.user`
5. **RBAC** — use `require_roles(["role1", "role2"])` as a FastAPI dependency to restrict access

Key rotation is handled automatically: if a token has an unknown `kid`, the SDK re-fetches the JWKS.

## Publishing

```bash
pip install build twine
python -m build
twine upload dist/*
```
