Skip to content

Index

crudauth crudauth

Batteries-included, transport-agnostic authentication for FastAPI.

PyPi Version Supported Python Versions License DeepWiki


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. Sessions and bearer both resolve to the same Principal, so narrowing or adding a transport never changes how you authorize a route, and app policy lives in hooks instead of forked dependency code.


Status: early 0.2 (alpha) - this is the v1 surface we're converging on. APIs may still shift before 1.0.

Features

  • Transport-agnostic: cookie sessions and JWT bearer tokens behind a single Principal; first credential present wins, and authorization 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, 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 and 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)

Quick Start

1. Install crudauth

pip install crudauth
uv add crudauth

For OAuth, Redis, and device parsing, install the extras: pip install "crudauth[all]".

2. Mount the router

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}

That's it - register a user at POST /register, log in at POST /login, and /dashboard is now gated.

Usage

Protect any route

current_user() is one factory; every authorization rule is a keyword. It returns a Principal carrying user_id, is_superuser, scopes, the authenticating transport, and the resolved user row.

auth.current_user()                          # required, 401 if anonymous
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 are a superset
auth.current_user(transport="bearer")        # narrow to one transport

One identity across transports

Add bearer tokens for your API alongside browser sessions. Both resolve to the same Principal, so your route code never changes - when both credentials are present, the first transport in the list wins.

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
    ],
)

CSRF is a property of the session transport - it appears only where sessions do, never on bearer paths.

Use your existing user table

Already have a users table with different column names? Map the contract instead of renaming your schema:

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

License

MIT

The Benav Labs 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 FastroAI - the complete FastAPI SaaS template

Ship your SaaS faster with FastroAI →