Delivery channels¶
Recovery delivery is pluggable: CRUDAuth owns the token (mint, one-time-use, redemption); a
DeliveryChannel owns the medium and the copy. Email is the built-in channel
(EmailConfig); pass channels= to CRUDAuth to add SMS, WhatsApp,
push, fired alongside it best-effort. See the
email flows guide for usage.
crudauth.email.channel.DeliveryChannel
¶
Bases: ABC
A medium crudauth routes a recovery message over.
crudauth fires every configured channel best-effort and swallows failures per channel, so raise freely on failure (it never surfaces and never stops the next channel). Reliability (retry/queue) belongs inside a channel.
Example
class SMSChannel(DeliveryChannel):
async def deliver(self, intent: DeliveryIntent, db) -> None:
if intent.kind != "reset_password" or intent.token is None or db is None:
return
user = await db.get(User, intent.user["id"]) # an app column
if user and user.phone:
await sms.enqueue(to=user.phone, token=intent.token) # hand off
deliver
abstractmethod
async
¶
deliver(
intent: DeliveryIntent, db: AsyncSession | None
) -> None
Route, render, and send intent.
Raise on failure (crudauth swallows per channel). Must not assume email;
read intent.recipient / intent.user.
db is the request-scoped session for the actionable flows (verify /
reset / change), or None for the existing_account notice. Use it
to load an app column you need (e.g.
await db.get(User, intent.user["id"]) for a phone number). It must be
used synchronously and never committed or captured for deferred work:
it is closed when the request ends, so a queued job that kept it would use
a dead session. Read what you need, then enqueue the actual delivery.
crudauth.email.channel.DeliveryIntent
dataclass
¶
DeliveryIntent(
kind: DeliveryKind,
token: str | None,
user: dict[str, Any],
recipient: str,
expires_in: int,
)
A recovery message crudauth needs delivered.
crudauth owns the token and its lifetime; the channel owns the medium and the
copy. Read what you need off recipient / user; do not assume email.
Attributes:
| Name | Type | Description |
|---|---|---|
kind |
DeliveryKind
|
Which message this is ( |
token |
str | None
|
The signed token, or |
user |
dict[str, Any]
|
The logical-contract user dict ( |
recipient |
str
|
The resolved recovery destination (the email today; for an email change, the NEW address). A non-email channel typically ignores this and loads its own destination off the user. |
expires_in |
int
|
Token lifetime in seconds ( |
crudauth.email.channel.EmailChannel
¶
EmailChannel(config: EmailConfig)
Bases: DeliveryChannel
The built-in channel: renders crudauth's recovery copy and calls the EmailSender.
Behaviorally identical to the email delivery crudauth shipped before delivery was pluggable; the subject/body/link building lives here now.