Skip to main content

Batteries-included, transport-agnostic authentication for FastAPI.

Project description

crudauth

Batteries-included, transport-agnostic authentication for FastAPI.

PyPi Version Supported Python Versions License Documentation DeepWiki

Docs · DeepWiki · Discord


crudauth gives you one CRUDAuth 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 your own SQLAlchemy User model. App policy lives in hooks, not in forked dependency code. Sessions and bearer both resolve to the same Principal, so narrowing or adding a transport never changes how you authorize a route.


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

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

Or with uv:

uv add crudauth

AI agents (library skill)

crudauth ships a library skill 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:

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.

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.

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:

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

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

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:

@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 - 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

Contact

Benav Labs – benav.io, Discord

The Benav Labs FastAPI family

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

  • FastCRUD - powerful CRUD methods and automatic endpoint creation for your SQLAlchemy models.
  • CRUDAdmin - a modern, secure admin interface generated straight from your models.
  • Fastro (FastAPI-boilerplate) - a batteries-included FastAPI starter: auth, CRUD, jobs, caching, and rate-limits.
  • FastroAI - the complete FastAPI SaaS template: payments, entitlements, email, a frontend, and AI agents.

Build a full SaaS on FastAPI

crudauth handles authentication in FastroAI - 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.

FastroAI - the complete FastAPI SaaS template: auth, Stripe payments, entitlements, email, frontend and AI

Ship your SaaS faster with FastroAI →


Benav Labs - benav.io

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

crudauth-0.5.0.tar.gz (100.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

crudauth-0.5.0-py3-none-any.whl (136.7 kB view details)

Uploaded Python 3

File details

Details for the file crudauth-0.5.0.tar.gz.

File metadata

  • Download URL: crudauth-0.5.0.tar.gz
  • Upload date:
  • Size: 100.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for crudauth-0.5.0.tar.gz
Algorithm Hash digest
SHA256 c7272acf1cb7996636b4d14232f9810cdef90a2c0d1ac4b1cbe9d4873928575e
MD5 211f6452df03715d75bb2df8799cb6b1
BLAKE2b-256 e00d5fcc88411cfd07b37be3ca9ceb22b62598421aa19f2f7a8f7030aff22f33

See more details on using hashes here.

File details

Details for the file crudauth-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: crudauth-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 136.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for crudauth-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 608db95f241a39a9f3b1493e9c2f2652846ed9d4ff27c3dc633bb28cba647733
MD5 ffaeef9c73eb7102f988337239ba8922
BLAKE2b-256 35badaa6b2b9bebdf08e3d366312c63e8562432b6332ccd543adbdb33b7ca82e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page