Metadata-Version: 2.4
Name: mgf-auth
Version: 0.1.0
Summary: ISP-split auth verifier seams for mgf-common consumers — a generic SessionVerifier + WebhookVerifier, a Clerk adapter (session-JWT + Svix-webhook), typed 401 errors, and mocks. Sibling of mgf-common under the mgf.* namespace.
Author: Bassam Alsanie, mgf-auth contributors
License: MIT
License-File: LICENSE
Keywords: auth,clerk,jwt,session,svix,webhook
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: mgf-common<0.42,>=0.41
Provides-Extra: clerk
Requires-Dist: pyjwt>=2.8; extra == 'clerk'
Provides-Extra: dev
Requires-Dist: cryptography>=42.0; extra == 'dev'
Requires-Dist: import-linter>=2.0; extra == 'dev'
Requires-Dist: mgf-test-supervisor<0.2,>=0.1.2; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pyjwt>=2.8; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest-timeout>=2.3; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: test
Requires-Dist: cryptography>=42.0; extra == 'test'
Requires-Dist: pyjwt>=2.8; extra == 'test'
Description-Content-Type: text/markdown

# mgf-auth

Two **ISP-split auth verifier seams** for `mgf-common` consumers — a
generic `SessionVerifier` + `WebhookVerifier`, a **Clerk** adapter
(session-JWT verification + Svix-webhook verification), typed 401 errors,
and test mocks. A sibling of `mgf-common` under the `mgf.*` namespace.

Extracted from PlasmaMapper after its core re-design ISP-split the auth
provider (ADR-016). A consumer keeps its own role-typed principal —
`SessionVerifier` is **generic over the principal type**; identity
resolution (Clerk ids → your principal) is your `principal_resolver`.

## Install

```bash
pip install "mgf-auth[clerk]"   # [clerk] adds pyjwt for the session adapter
```

## Use

```python
from mgf.auth.clerk_session import ClerkSessionVerifier, ClerkClaims
from mgf.auth.clerk_webhook import ClerkWebhookVerifier

async def resolve(claims: ClerkClaims) -> MyPrincipal:
    return MyPrincipal(user_id=uuid5(NS, claims.sub), role=map_role(claims.org_role), ...)

session_verifier: ClerkSessionVerifier[MyPrincipal] = ClerkSessionVerifier(
    jwt_public_key=PEM, issuer="https://clerk.acme.example", principal_resolver=resolve,
)
principal = await session_verifier.verify_session(bearer_token)   # -> MyPrincipal

webhook_verifier = ClerkWebhookVerifier(webhook_secret=b"whsec_...")
event = await webhook_verifier.verify_webhook(headers, raw_body)   # -> WebhookEvent
```

Tests use `MockSessionVerifier[MyPrincipal]()` (`add_session`) and
`MockWebhookVerifier()` (`configure_webhook_secret` + the `sign()`
helper) — no Clerk, no network.

## What's in it

| Name | What |
|---|---|
| `SessionVerifier` / `WebhookVerifier` | the two `@runtime_checkable` seams (session is generic over `P`) |
| `WebhookEvent` | the verified-webhook DTO (`event_type`, `event_id`, `payload`, `received_at`) |
| `InvalidSessionError` / `InvalidWebhookSignatureError` | typed **401** errors (subclass `mgf.common` `HttpUnauthorizedError`) |
| `mgf.auth.clerk_session.ClerkSessionVerifier` / `ClerkClaims` | PyJWT session verification (`[clerk]` extra) |
| `mgf.auth.clerk_webhook.ClerkWebhookVerifier` | Svix-Signature HMAC webhook verification (stdlib) |
| `MockSessionVerifier` / `MockWebhookVerifier` | in-process test doubles |

## Design

- **Generic over the principal** (`SessionVerifier[P]`): the JWT
  mechanics live here; the consumer's `principal_resolver` produces its
  own principal shape — no imposed role/identity model.
- **Vendor SDK behind an extra**: importing `mgf.auth` pulls no `pyjwt`
  (import-linter-enforced); the Clerk session adapter is the only place
  that needs it.
- **Webhook side is stdlib-only** (HMAC-SHA256 over `<id>.<ts>.<body>`).
- Session revocation (checking a `revoked_sessions` table) is a
  **consumer concern** — it's DB-coupled and app-specific, so it stays
  in the consumer, not here.
