CRUDAuth¶
crudauth.CRUDAuth
¶
CRUDAuth(
*,
session: Callable[..., Any],
user_model: type[Any],
SECRET_KEY: str,
transports: Sequence[Transport] | None = None,
column_map: dict[str, str] | None = None,
identity: IdentityConfig | None = None,
oauth: dict[str, Any] | None = None,
oauth_paths: dict[str, str] | None = None,
oauth_response_mode: Literal[
"redirect", "json"
] = "redirect",
email: Any = None,
channels: list[DeliveryChannel] | None = None,
hooks: AuthHooks | None = None,
redirect_base_url: str | None = None,
algorithm: str = DEFAULT_ALGORITHM,
cookies: CookieConfig | None = None,
register_schema: type[BaseModel] | None = None,
register_extra_fields: set[str] | None = None,
new_user_fields: NewUserFields | None = None,
new_user_defaults: dict[str, Any] | None = None,
rate_limiter: RateLimiterBackend | None = None,
redis_url: str | None = None,
redis_client: Any = None,
rate_limits: dict[str, RateLimit] | None = None,
lockout: LockoutConfig | None = None,
trusted_proxy_hops: int = 0,
sudo: SudoConfig | None = None,
mfa: MfaConfig | None = None,
warn_on_memory_backend: bool = True,
password_policy: PasswordPolicy | None = None,
)
Composition root: configure transports, mount routers, gate routes.
Construct one per auth surface. It owns the user repository, the shared
AuthRuntime, the rate-limiter backend, and the
assembled routers. Session auth is the default; add bearer/oauth/email by
passing transports=, oauth=, email=.
Example
Configure the auth surface.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
Callable[..., Any]
|
FastAPI dependency that yields an |
required |
user_model
|
type[Any]
|
Your SQLAlchemy user model (typically inheriting AuthUserMixin). |
required |
SECRET_KEY
|
str
|
Secret used to sign session/JWT and email tokens. |
required |
transports
|
Sequence[Transport] | None
|
Ordered auth channels to enable; defaults to a single SessionTransport. Order is the first-wins precedence. |
None
|
column_map
|
dict[str, str] | None
|
Maps crudauth logical field names to your model's actual
column names when they differ (e.g. |
None
|
oauth
|
dict[str, Any] | None
|
|
None
|
oauth_paths
|
dict[str, str] | None
|
Optional OAuth router paths: |
None
|
oauth_response_mode
|
Literal['redirect', 'json']
|
|
'redirect'
|
email
|
Any
|
An EmailConfig to enable
verify/reset/change flows over email (the built-in delivery
channel); |
None
|
channels
|
list[DeliveryChannel] | None
|
Additional DeliveryChannels
to route recovery tokens over (SMS, WhatsApp, push, ...). Fired
alongside the email channel if |
None
|
hooks
|
AuthHooks | None
|
Lifecycle callbacks (AuthHooks). |
None
|
redirect_base_url
|
str | None
|
Public base URL used to build OAuth redirect URIs and the post-login redirect default. |
None
|
algorithm
|
str
|
JWT signing algorithm (default |
DEFAULT_ALGORITHM
|
cookies
|
CookieConfig | None
|
App-wide CookieConfig ( |
None
|
register_schema
|
type[BaseModel] | None
|
Custom Pydantic body for |
None
|
register_extra_fields
|
set[str] | None
|
App-defined model columns that |
None
|
new_user_defaults
|
dict[str, Any] | None
|
Constant app columns to set on every new user, on
BOTH |
None
|
new_user_fields
|
NewUserFields | None
|
Callback (sync or async) returning extra columns to
set when crudauth creates a user, for values that must be
derived (and may read the DB) rather than constant - e.g.
|
None
|
rate_limiter
|
RateLimiterBackend | None
|
Backend for lockout/throttles; defaults to an in-process
MemoryRateLimiterBackend. Use
|
None
|
redis_url
|
str | None
|
Redis URL for every server-side store: sessions and CSRF
tokens, the one-time-token and OAuth-state stores, and the default
rate limiter. A part configured directly (a |
None
|
redis_client
|
Any
|
An existing async Redis client to use the same way as
|
None
|
rate_limits
|
dict[str, RateLimit] | None
|
Per-action overrides merged over
:data: |
None
|
lockout
|
LockoutConfig | None
|
LockoutConfig for the
escalating login lockout shared by |
None
|
trusted_proxy_hops
|
int
|
Number of trusted reverse proxies in front of the
app. |
0
|
sudo
|
SudoConfig | None
|
Enable sudo mode (short-lived re-authentication for sensitive
actions) with this SudoConfig. Requires
a session transport - elevation is stamped on the server-side
session. Exposes |
None
|
mfa
|
MfaConfig | None
|
Enable TOTP two-factor authentication with this
MfaConfig. The model needs the MFA
columns ( |
None
|
warn_on_memory_backend
|
bool
|
Log a startup warning when an in-memory
backend is active (the zero-config default). In-memory state is
per-process, so under multiple workers it silently breaks; set
|
True
|
password_policy
|
PasswordPolicy | None
|
The PasswordPolicy every new password must meet on registration, set, change and reset. The default requires at least 8 characters. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
oauth_providers
property
¶
oauth_providers: Mapping[str, AbstractOAuthProvider]
The configured OAuth providers, by the name they were configured under.
Read-only; the provider objects carry what was resolved for them, such as an OIDC provider's discovery document.
emails
property
¶
emails: EmailFlowService | None
The EmailFlowService, or None when no recovery is configured.
Drives the recovery flows (request_recovery_verification, reset_password,
request_email_change, ...) so a hand-written route can trigger them with
the same token mint/verify the built-in endpoints use.
oauth
property
¶
oauth: OAuthAccountService | None
The OAuthAccountService, or None when OAuth isn't configured.
Exposes get_or_create_user (provider-id → verified-email link → create) so
a hand-written OAuth callback can reuse the linking/creation rules.
oauth_router
property
¶
The configured OAuth routes, for apps keeping their own auth routes.
rate_limits
property
¶
rate_limits: dict[str, RateLimit]
The per-action limits in effect: the defaults with rate_limits= applied.
router
property
¶
session_router
property
¶
Only the session transport's routes (/login, /logout).
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If no SessionTransport is configured. |
bearer_router
property
¶
Only the bearer transport's routes (/token, /refresh).
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If no BearerTransport is configured. |
validate_password
async
¶
validate_password(
password: str,
*,
user: Any = None,
source: PasswordSource = "set",
field: str = "password",
) -> None
Check password against password_policy before your own code hashes it.
Raises PasswordPolicyException
(422) at field when it fails. Pass the user it's for so validators
taking a PasswordContext see its username
and email.
resolve_principal
async
¶
resolve_principal(
request: Request, update_activity: bool = False
) -> Principal | None
Resolve the request principal outside FastAPI dependency injection.
This is intended for middleware and other request-level code. It tries
transports in configured order, returns None for anonymous or
invalid credentials, does not enforce CSRF, and does not slide sessions
unless update_activity=True. A later current_user() in the same
request reuses the result, reloading the user through its own session.
It opens its own DB session by calling the session dependency
directly, so FastAPI's dependency_overrides don't apply to it.
authenticate_password
async
¶
authenticate_password(
db: Any,
identifier: str,
password: str,
*,
request: Request,
record_success: bool = True,
) -> Any
Verify a username/email + password with the full login hardening.
The hardened credential check behind /login and /token, exposed so
a hand-written login route gets the same protections (shared escalating
lockout, timing-equalized verification, disabled-account check) instead of
reassembling them. Returns the user row; raises RateLimitException on
lockout and UnauthorizedException on bad credentials. Delegates to
AuthRuntime.authenticate_password.
issue_tokens
¶
Mint a bearer access (+refresh) token pair for a user.
The hardened issuance behind /token, exposed for a hand-written token
endpoint: scopes are clamped to the transport's grantable_scopes
(no self-grant) and both tokens carry the token_version epoch (so a
password reset revokes them). The refresh token is returned under
refresh_token (there's no Response to set a cookie on). Delegates to
BearerTransport.issue_tokens.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If no BearerTransport is configured. |
current_user
¶
current_user(
*,
optional: bool = False,
superuser: bool = False,
verified: bool = False,
scopes: list[str] | None = None,
transport: str | list[str] | None = None,
check: Callable[[Principal], Any] | None = None,
) -> Callable[..., Any]
Build a FastAPI dependency that authenticates and authorizes a request.
Every gate is a keyword: optional, superuser, verified,
scopes, transport (narrow to one/some transports), and check.
Note
check is a predicate (sync or async) run last on the resolved
principal. Returning False denies the request with 403. To deny
with a custom status/message, raise your own exception from inside
check. Returning None (or anything that isn't False)
allows - so both styles work: a boolean predicate
(check=lambda p: p.is_superuser) and a raise-to-deny callback that
simply returns nothing on success.
Note
Transports are tried in order, first credential wins. A transport
returns None when its credential is absent (move to the next),
but RAISES for a present-but-invalid one (e.g. a session cookie that
fails the CSRF header check on a mutation). That hard-fail propagates
even under optional=True - a tampered credential is an attack
signal, not "treat me as anonymous".
Returns:
| Type | Description |
|---|---|
Callable[..., Any]
|
An async dependency yielding the Principal (or |
Callable[..., Any]
|
|
require_sudo
¶
Build a dependency that requires a current sudo elevation.
Authenticates like current_user
(reusing the per-request principal cache) and then demands an unexpired
sudo stamp, raising 403 otherwise. Compose it with current_user gates
on the same route to also enforce identity/role:
Example
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If sudo isn't configured (pass |
rate_limit
¶
rate_limit(
action: str,
limit: RateLimit | RateLimitResolver | None = None,
*,
key: KeyBy | Callable[..., str] = IP,
transport: str | list[str] | None = None,
) -> Callable[..., Any]
Build a FastAPI dependency that throttles an endpoint.
limit is a RateLimit or a sync/async function of (request, principal)
returning one (None for no limit). Without it, action must be a built-in
action, and its rate_limits= override or
:data:~crudauth.ratelimit.DEFAULT_RATE_LIMITS entry applies.
key picks who shares a budget: a KeyBy member, key(request), or
key(request, principal). KeyBy.IP keys an IPv6 client by its /64
(see client_ip_key). transport narrows which credentials identify the
caller, as on current_user. Writes
X-RateLimit-* headers and raises
RateLimitException (429) when the caller exceeds the window.
initialize
async
¶
shutdown
async
¶
Close connections. Call in lifespan teardown.
Every component is closed even when an earlier one fails; the first failure is re-raised once all of them were attempted.