Metadata-Version: 2.4
Name: crudauth
Version: 0.5.0
Summary: Batteries-included, transport-agnostic authentication for FastAPI.
Keywords: fastapi,auth,authentication,sessions,jwt,oauth,sqlalchemy
Author: Igor Benav
Author-email: Igor Benav <igor@benav.io>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
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: Framework :: FastAPI
Classifier: Typing :: Typed
Requires-Dist: fastapi>=0.100.0
Requires-Dist: sqlalchemy>=2.0.0,<3
Requires-Dist: pydantic>=2.0.0,<3
Requires-Dist: pyjwt>=2.8.0
Requires-Dist: bcrypt>=4.0.0
Requires-Dist: python-multipart>=0.0.7
Requires-Dist: email-validator>=2.0.0
Requires-Dist: httpx>=0.24.0 ; extra == 'all'
Requires-Dist: redis>=5.0.0 ; extra == 'all'
Requires-Dist: user-agents>=2.2.0 ; extra == 'all'
Requires-Dist: httpx>=0.24.0 ; extra == 'oauth'
Requires-Dist: redis>=5.0.0 ; extra == 'redis'
Requires-Dist: user-agents>=2.2.0 ; extra == 'useragent'
Requires-Python: >=3.10
Project-URL: Homepage, https://github.com/benavlabs/crudauth
Project-URL: Documentation, https://benavlabs.github.io/crudauth
Project-URL: Repository, https://github.com/benavlabs/crudauth
Provides-Extra: all
Provides-Extra: oauth
Provides-Extra: redis
Provides-Extra: useragent
Description-Content-Type: text/markdown

<p align="center">
  <a href="https://benavlabs.github.io/crudauth">
    <picture>
      <source media="(prefers-color-scheme: dark)" srcset="docs/assets/crudauth-cover-dark.png">
      <img src="docs/assets/crudauth-cover-light.png" alt="crudauth" width="50%">
    </picture>
  </a>
</p>
<p align="center" markdown=1>
  <i><b>Batteries-included, transport-agnostic authentication for FastAPI.</b></i>
</p>

<p align="center">
<a href="https://pypi.org/project/crudauth/">
  <img src="https://img.shields.io/pypi/v/crudauth?color=%2334D058&label=pypi%20package" alt="PyPi Version"/>
</a>
<a href="https://pypi.org/project/crudauth/">
  <img src="https://img.shields.io/pypi/pyversions/crudauth.svg?color=%2334D058" alt="Supported Python Versions"/>
</a>
<a href="https://github.com/benavlabs/crudauth/blob/main/LICENSE">
  <img src="https://img.shields.io/badge/license-MIT-34D058" alt="License"/>
</a>
<a href="https://benavlabs.github.io/crudauth">
  <img src="https://img.shields.io/badge/docs-benavlabs.github.io%2Fcrudauth-34D058?logo=materialformkdocs&logoColor=white" alt="Documentation"/>
</a>
<a href="https://deepwiki.com/benavlabs/crudauth">
  <img src="https://img.shields.io/badge/DeepWiki-1F2937.svg?logo=book&logoColor=white&labelColor=1F2937&color=34D058" alt="DeepWiki"/>
</a>
</p>

<p align="center">
<a href="https://benavlabs.github.io/crudauth">Docs</a> · <a href="https://deepwiki.com/benavlabs/crudauth">DeepWiki</a> · <a href="https://discord.com/invite/TEmPs22gqB">Discord</a>
</p>

<hr>
<p align="justify">
<b>crudauth</b> gives you one <code>CRUDAuth</code> object that wires cookie sessions, JWT bearer tokens, OAuth, and email flows (verify / reset / change) - with CSRF, escalating login lockout, sudo mode, and multi-device session management - over <b>your own</b> SQLAlchemy <code>User</code> model. App policy lives in hooks, not in forked dependency code. Sessions and bearer both resolve to the same <code>Principal</code>, so narrowing or adding a transport never changes how you authorize a route.
</p>

<hr>

## Features

- **Transport-agnostic**: cookie sessions and JWT bearer tokens behind a single `Principal`; first credential present wins, and authorization code never depends on *which* transport authenticated.
- **Your model, your schema**: works over your existing SQLAlchemy `User` via a logical-field `column_map` - no forced renames, no second user table.
- **Secure by default**: synchronizer-token CSRF, escalating per-IP/per-user login lockout, bcrypt with SHA-256 pre-hash (no 72-byte truncation), timing-equalized login, and trusted-proxy IP resolution.
- **OAuth**: Google, GitHub, or a custom provider - with the `state` bound to the initiating browser to block login CSRF.
- **Email flows**: verify / reset / change - you implement the `EmailSender` port (render your own HTML from `context.link`), the package mints and verifies the signed, single-use tokens.
- **Sudo mode**: short-lived re-authentication to gate sensitive actions, stamped on the session and cleared on logout.
- **Multi-device sessions**: list, revoke one, or "sign out everywhere", with a configurable per-user session cap.
- **App policy in hooks**: `AuthHooks` for welcome email, trial grant, audit logging - fired uniformly across every auth path.
- **Pluggable backends**: in-memory for dev, Redis for production - for sessions, CSRF, lockout counters, and one-time tokens.
- **Fully typed & async**: ships `py.typed`, built on SQLAlchemy 2.0 and Pydantic v2.

## Requirements

- **Python** 3.10+
- **FastAPI**, **SQLAlchemy 2.0+**, **Pydantic v2** (installed as dependencies)

## Install

```bash
pip install crudauth            # core (session + bearer)
pip install "crudauth[all]"     # + httpx (oauth), redis, user-agents
```

Or with uv:

```bash
uv add crudauth
```

## AI agents (library skill)

crudauth ships a [library skill](https://library-skills.io) embedded in the package, so AI coding
agents follow its actual conventions and gotchas (account shapes, gates, recovery, custom email
bodies, production wiring) in sync with the version you installed. After adding crudauth, install it
into your project:

```bash
uvx library-skills            # scans deps, links bundled skills (re-run to keep them in sync on upgrade)
uvx library-skills --claude   # for Claude Code (.claude/skills)
```

## Quickstart

Sessions are the default - no `transports=` needed. You get cookie auth, CSRF,
login lockout, secure cookies, and `/login` `/logout` `/register` `/me`.

```python
from fastapi import FastAPI, Depends
from crudauth import CRUDAuth, Principal
from myapp.db import get_session
from myapp.models import User

auth = CRUDAuth(session=get_session, user_model=User, SECRET_KEY="change-me")

app = FastAPI()
app.include_router(auth.router)

@app.get("/dashboard")
async def dashboard(me: Principal = Depends(auth.current_user())):
    return {"hello": me.user.username}
```

## The user model

Inherit the mixin and get every column the package needs; your own columns
coexist freely.

```python
from sqlalchemy.orm import Mapped, mapped_column
from crudauth.models import AuthUserMixin
from myapp.db import Base

class User(Base, AuthUserMixin):
    __tablename__ = "users"
    full_name: Mapped[str | None] = mapped_column(default=None)
```

Existing table with different names? Map the contract, don't rename your schema:

```python
auth = CRUDAuth(
    session=get_session, user_model=LegacyAccount, SECRET_KEY=...,
    column_map={"id": "account_id", "email": "email_address", "hashed_password": "pw_hash"},
)
```

## Protecting routes - one factory, every case a kwarg

```python
auth.current_user()                              # required, 401 if anon
auth.current_user(optional=True)                 # None instead of raising
auth.current_user(superuser=True)                # 403 unless is_superuser
auth.current_user(verified=True)                 # 403 unless email_verified
auth.current_user(scopes=["reports:read"])       # 403 unless scopes ⊇ required
auth.current_user(transport="bearer")            # narrow to one transport
auth.current_user(superuser=True, check=my_predicate)  # extra per-route check
```

The resolved `Principal` carries `user_id`, `is_superuser`, `scopes`,
`transport` (which transport authed the request), and `user` (your resolved
row).

## Multiple transports, one identity

```python
from crudauth import CRUDAuth, SessionTransport, BearerTransport

auth = CRUDAuth(
    session=get_session, user_model=User, SECRET_KEY=...,
    transports=[
        SessionTransport(backend="redis", redis_url=..., csrf=True),  # browsers
        BearerTransport(access_ttl=900, refresh="cookie"),            # apps/scripts
    ],
)
```

When both credentials are present, the **first transport in the list wins**.
CSRF is a property of the session transport - it appears only where sessions do,
never on bearer/api-key paths.

## Storage & lifespan

Server-side backends open connections on startup - call `initialize()` /
`shutdown()` in your lifespan:

```python
@asynccontextmanager
async def lifespan(app: FastAPI):
    await auth.initialize()
    yield
    await auth.shutdown()
```

## OAuth, email, hooks, sudo

See the usage cookbook for OAuth (Google / GitHub / custom providers), email
flows (implement the `EmailSender` port; the package mints/verifies the signed
tokens), lifecycle hooks (`AuthHooks` - welcome email, trial grant, audit log),
sudo mode (`sudo=SudoConfig()` + `auth.require_sudo()`), and dropping to
primitives.

## Architecture

crudauth is ports-and-adapters with feature slices and a single composition
root (`CRUDAuth`). The layering and the import-direction rules live in the
[Architecture docs](https://benavlabs.github.io/crudauth/architecture/) - read them before
adding a transport, OAuth provider, or storage backend; each is meant to be a drop-in
file, not a cross-cutting edit.

## License

[`MIT`](LICENSE)

## Contact

Benav Labs – [benav.io](https://benav.io), [Discord](https://discord.com/invite/TEmPs22gqB)

## The Benav Labs FastAPI family

crudauth is part of a family of composable FastAPI building blocks - use whichever you need:

- **[FastCRUD](https://github.com/benavlabs/fastcrud)** - powerful CRUD methods and automatic endpoint creation for your SQLAlchemy models.
- **[CRUDAdmin](https://github.com/benavlabs/crudadmin)** - a modern, secure admin interface generated straight from your models.
- **[Fastro (FastAPI-boilerplate)](https://github.com/benavlabs/FastAPI-boilerplate)** - a batteries-included FastAPI starter: auth, CRUD, jobs, caching, and rate-limits.
- **[FastroAI](https://fastro.ai)** - the complete FastAPI SaaS template: payments, entitlements, email, a frontend, and AI agents.

## Build a full SaaS on FastAPI

crudauth handles authentication in **[FastroAI](https://fastro.ai)** - the complete FastAPI SaaS template: auth, Stripe payments (subscriptions, credits, discounts), entitlements, transactional email, an Astro frontend, and PydanticAI agents, wired together and production-ready.

<p align="center">
  <a href="https://fastro.ai">
    <picture>
      <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/benavlabs/FastAPI-boilerplate/main/docs/assets/fastroai-card-dark.png">
      <img src="https://raw.githubusercontent.com/benavlabs/FastAPI-boilerplate/main/docs/assets/fastroai-card-light.png" alt="FastroAI - the complete FastAPI SaaS template: auth, Stripe payments, entitlements, email, frontend and AI" width="100%">
    </picture>
  </a>
</p>

<p align="center"><b><a href="https://fastro.ai">Ship your SaaS faster with FastroAI →</a></b></p>

<hr>
<a href="https://benav.io">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/benavlabs/FastAPI-boilerplate/main/docs/assets/benav-labs-banner-dark.png">
    <img src="https://raw.githubusercontent.com/benavlabs/FastAPI-boilerplate/main/docs/assets/benav-labs-banner-light.png" alt="Benav Labs - benav.io" width="100%"/>
  </picture>
</a>
