pub struct AuthPlugin<U: UserModel = AuthUser> {
pub user_model_name: Option<String>,
pub default_routes_prefix: Option<String>,
pub form_routes_prefix: Option<String>,
pub user_in_templates: bool,
/* private fields */
}Expand description
The built-in authentication plugin, generic over the user model.
U defaults to AuthUser so AuthPlugin::default() continues to
work in all existing code unchanged. Apps that need a custom user type
opt in with one line:
.plugin(AuthPlugin::<CustomUser>::default())§user_model_name
An optional informational string surfaced in OpenAPI schemas and the
admin nav. Default None (resolved from U::NAME by the plugin
itself when left empty). Set it explicitly when the type name is
insufficient:
AuthPlugin::<TenantUser>::default().user_model_name("tenant_user")Fields§
§user_model_name: Option<String>Documentation-only: the human-readable name of the active user
model. Consumed by admin / OpenAPI when surfacing the user table.
The actual dispatch is entirely through the type parameter U.
default_routes_prefix: Option<String>When Some, mount the four built-in routes (register / login /
logout / me) under this prefix. None skips them — the user
either doesn’t want them or is rolling their own surface. Only
settable on AuthPlugin<AuthUser> (the handlers FK into
AuthToken → AuthUser); custom user models bring their own.
form_routes_prefix: Option<String>When Some, mount the 7 POST form-action routes (login, logout,
signup, verify-email, resend, password-forgot, password-reset)
under this prefix. Default None — opt in via
AuthPlugin::with_form_routes / AuthPlugin::with_form_routes_at.
Only settable on AuthPlugin<AuthUser>.
user_in_templates: boolWhen true, wrap the app router with user_context_layer so
every template render has user in its global context:
{ is_authenticated, is_staff, username, ... }. Opt-in because
it costs one DB read per request (cookie → session → user); a
REST-only service has nothing to gain from it. Set via
AuthPlugin::with_user_in_templates.
Implementations§
Source§impl<U: UserModel> AuthPlugin<U>
impl<U: UserModel> AuthPlugin<U>
Sourcepub fn user_model_name(self, name: impl Into<String>) -> Self
pub fn user_model_name(self, name: impl Into<String>) -> Self
Override the informational user-model name shown in admin / OpenAPI.
Fluent builder method; the return type is Self so it chains.
Sourcepub fn with_user_in_templates(self) -> Self
pub fn with_user_in_templates(self) -> Self
Mount the user_context_layer middleware globally so every
HTML template gets user in its render context — anonymous
requests see { is_authenticated: false }, authenticated
requests see the full serialized AuthUser merged with
is_authenticated: true. Lets templates write
{% if user.is_staff %} without the consumer having to thread
a user value into every handler’s context manually.
One DB read per request (cookie → session → user row). Off by default because REST-only services have no templates and the cost would be pure overhead. Turn it on for HTML-heavy apps:
AuthPlugin::<AuthUser>::default()
.with_default_routes()
.with_user_in_templates() // ← hereImplemented via Plugin::wrap_router; the wrapper wraps the
merged app router (including every other plugin’s routes), so
admin / REST / playground / your own handlers all see the
populated context with one builder call.
Sourcepub fn password_validators(self, policy: PasswordPolicy) -> Self
pub fn password_validators(self, policy: PasswordPolicy) -> Self
Replace the default password-strength policy with a custom one.
The full PasswordPolicy you pass becomes the active set at boot;
the default validators are NOT merged in. Build the policy
you want from scratch:
use umbral_auth::{AuthPlugin, PasswordPolicy, MinLengthValidator, CommonPasswordValidator};
AuthPlugin::<AuthUser>::default().password_validators(
PasswordPolicy::empty()
.with(Box::new(MinLengthValidator(12)))
.with(Box::new(CommonPasswordValidator)),
)Sourcepub fn min_password_length(self, n: usize) -> Self
pub fn min_password_length(self, n: usize) -> Self
Convenience: keep the four default validators but change the minimum
password length. Equivalent to building a PasswordPolicy with a
MinLengthValidator of n plus the other three defaults.
Sourcepub fn disable_password_validation(self) -> Self
pub fn disable_password_validation(self) -> Self
Explicit opt-OUT: install an empty policy so NO password validation runs. Secure-by-default means an app that genuinely wants to accept any password — a throwaway demo, a migration importing legacy hashes with externally-validated plaintext — has to ask for it by name. Don’t reach for this to silence a failing test; fix the fixture’s password instead.
Sourcepub fn login_throttle(self, max: usize, window: Duration) -> Self
pub fn login_throttle(self, max: usize, window: Duration) -> Self
Tune the login rate limit: max failed-or-not attempts per trailing
window, keyed per IP + username. The default is 5 / 5 min — a budget
that stops credential-stuffing dead while leaving room for a human who
fat-fingers their password a couple of times (a successful login also
clears the counter). Lower it for a high-security surface; raise it for
a shared-NAT office where many users hit login from one IP.
AuthPlugin::<AuthUser>::default().login_throttle(10, Duration::from_secs(300))Sourcepub fn register_throttle(self, max: usize, window: Duration) -> Self
pub fn register_throttle(self, max: usize, window: Duration) -> Self
Tune the register rate limit: max account-creation attempts per
trailing window, keyed per IP. The default is 10 / hour, which brakes
mass automated signups without blocking a legitimate burst from one
office.
Sourcepub fn email_action_throttle(self, max: usize, window: Duration) -> Self
pub fn email_action_throttle(self, max: usize, window: Duration) -> Self
Tune the email-action rate limit: max attempts per trailing window,
keyed per IP + email. Covers verify-email, resend-verification, and
password-forgot. The default is 5 / hour — enough for a user who needs
a couple of resends, but low enough to stop email-bombing / online
code-guessing scripts dead.
Sourcepub fn disable_throttle(self) -> Self
pub fn disable_throttle(self) -> Self
Explicit opt-OUT: turn login, register, and email-action throttling OFF entirely. Secure-by-default means an app that genuinely wants no rate limit — a load test, an internal tool behind its own gateway limiter — has to ask for it by name. Don’t reach for this to silence a throttled test; use a distinct IP/username per attempt or generous budget methods instead.
Sourcepub fn mailer(self, m: impl AuthMailer + 'static) -> Self
pub fn mailer(self, m: impl AuthMailer + 'static) -> Self
Wire the mailer used by the verification + password-reset flows.
Pass a type implementing AuthMailer or an async closure
|mail| async { ... }. Unset → ConsoleMailer (stderr in dev).
AuthPlugin::<AuthUser>::default().mailer(|m: OutgoingMail| async move {
umbral_email::send(&umbral_email::EmailMessage::new(m.subject, vec![m.to])
.html_body(m.html).text_body(m.text)).await
.map(|_| ()).map_err(|e| AuthMailError::Send(e.to_string()))
})Source§impl AuthPlugin<AuthUser>
impl AuthPlugin<AuthUser>
Sourcepub fn with_default_routes(self) -> Self
pub fn with_default_routes(self) -> Self
Mount the built-in /api/auth/{register,login,logout,me,…}
surface. Same handlers that lived in the derive-demo example
app, promoted to the framework so every app gets them with one
line. JSON-only; UNIQUE-violation → 409; login returns both a
Set-Cookie and a bearer token in one response so browsers and
CLI clients share an endpoint.
The prefix resolves at build time: {api_base()}/auth, so it
follows whatever base the REST plugin set (default /api/auth).
Use Self::with_default_routes_at to fix a literal prefix.
Sourcepub fn with_default_routes_at(self, prefix: impl Into<String>) -> Self
pub fn with_default_routes_at(self, prefix: impl Into<String>) -> Self
Same as Self::with_default_routes but the prefix is yours
to pick. Useful when /api/auth collides with an existing
surface or you want versioning (/v1/auth).
Sourcepub fn require_verified_email(self) -> Self
pub fn require_verified_email(self) -> Self
Block login until the user’s email_verified_at column is stamped, and
auto-send a verification code immediately on register. Off by default
— the email_verified_at column is always tracked and the
/verify-email + /resend-verification endpoints are always mounted;
this flag only controls enforcement:
- register: after a successful
create_user, firesstart_email_verificationbest-effort (a mail failure does NOT fail registration; it is logged atwarnlevel). The201response is unchanged. - login: after
authenticatesucceeds and before minting the bearer token / session, checksemail_verified_at IS NULL; returns403 {error: "email_not_verified"}if so.
Available only on AuthPlugin<AuthUser> because enforcement is
implemented inside the built-in handlers (which are AuthUser-only).
Custom user models bring their own routes and their own enforcement.
Requires a working mailer in production — wire
AuthPlugin::mailer alongside this builder, or users won’t receive
the verification code and will be permanently locked out:
AuthPlugin::<AuthUser>::default()
.with_default_routes()
.mailer(my_smtp_mailer)
.require_verified_email()Sourcepub fn with_form_routes(self) -> Self
pub fn with_form_routes(self) -> Self
Mount the 7 POST form-action auth routes (login, logout, signup,
verify-email, resend, password-forgot, password-reset) under the
default /auth prefix.
These are the form-action endpoints that developer-written HTML
forms POST to: <form method="POST" action="/auth/login">. The
framework never ships the pages themselves — the developer writes
those with their own brand and design.
Each handler receives a form-encoded body, runs the same auth logic as the JSON surface (including throttle and enumeration-safe guards), sets a flash message via the session, then returns a 303 redirect.
Use Self::with_form_routes_at to mount under a custom prefix.
Sourcepub fn with_form_routes_at(self, prefix: impl Into<String>) -> Self
pub fn with_form_routes_at(self, prefix: impl Into<String>) -> Self
Same as Self::with_form_routes but you choose the prefix.
AuthPlugin::<AuthUser>::default().with_form_routes_at("/accounts")Trait Implementations§
Source§impl<U: UserModel> Default for AuthPlugin<U>
impl<U: UserModel> Default for AuthPlugin<U>
Source§impl<U: UserModel> Plugin for AuthPlugin<U>
impl<U: UserModel> Plugin for AuthPlugin<U>
Source§fn wrap_router(&self, router: Router) -> Router
fn wrap_router(&self, router: Router) -> Router
Mount user_context_layer on the full merged router when the
user_in_templates flag is on (see
AuthPlugin::with_user_in_templates). The layer reads the
session cookie, hydrates the AuthUser, and pushes a
serde_json representation into umbral::templates::CURRENT_USER
for the duration of the request — every template render
downstream gets user in its global context with no per-handler
plumbing.
Off by default — see the builder method’s docstring for the “why” (one DB read per request, pointless for REST-only apps).
Source§fn on_ready(&self, _ctx: &AppContext) -> Result<(), PluginError>
fn on_ready(&self, _ctx: &AppContext) -> Result<(), PluginError>
Seal the password-strength policy into the ambient OnceLock so the
free-function helpers (create_user, set_password) can read it
without a handle to Self. Mirrors the sessions plugin’s
SLIDING_EXPIRY_ENABLED install.
A None configured policy means “use the secure default” — NOT
“off” — so we install PasswordPolicy::default in that case.
disable_password_validation is the only path that installs an
empty policy. The install is idempotent (first boot wins), matching
the ambient-pool contract.
Source§fn name(&self) -> &'static str
fn name(&self) -> &'static str
migrations/. Plugin names live in the same namespace as
migrate::APP_PLUGIN_NAME ("app"), so user crates must not
pick the name "app".Source§fn models(&self) -> Vec<ModelMeta>
fn models(&self) -> Vec<ModelMeta>
makemigrations. Read moreSource§fn templates_dirs(&self) -> Vec<PathBuf>
fn templates_dirs(&self) -> Vec<PathBuf>
Source§fn commands(&self) -> Vec<Box<dyn PluginCommand>>
fn commands(&self) -> Vec<Box<dyn PluginCommand>>
Source§fn routes(&self) -> Router
fn routes(&self) -> Router
AppBuilder::routes(). Plugins
choose their own path prefixes (spec 02 §“What a plugin can
contribute”: routes are flat, not auto-prefixed).Source§fn route_paths(&self) -> Vec<RouteSpec>
fn route_paths(&self) -> Vec<RouteSpec>
routes used for surfacing route lists outside the request
flow (currently: the dev-mode default 404 page). axum doesn’t
expose its internal route table, so plugins report what they
declare here; the framework treats this as informational only
— not a source of truth for routing. Read moreSource§fn openapi_paths(&self) -> Vec<(String, Value)>
fn openapi_paths(&self) -> Vec<(String, Value)>
Vec<(path, value)> where path is the URL template
(/api/auth/login, /api/foo/{id}) and value is the
matching OpenAPI 3.0 Path Item Object serialised as
a serde_json::Value. Read moreSource§fn dependencies(&self) -> &'static [&'static str]
fn dependencies(&self) -> &'static [&'static str]
App::builder() topological sort uses this; cycles surface as
BuildError::PluginCycle. The default is no dependencies.Source§fn system_checks(&self) -> Vec<SystemCheck>
fn system_checks(&self) -> Vec<SystemCheck>
App::build() alongside the framework’s built-in checks.
Severity::Error blocks boot; Severity::Warning logs and
continues.Source§fn provides_storage(&self) -> bool
fn provides_storage(&self) -> bool
true if this plugin registers a Storage
backend (e.g. StoragePlugin, which calls
crate::storage::set_storage in Plugin::on_ready). Read moreSource§fn database(&self) -> Option<&'static str>
fn database(&self) -> Option<&'static str>
None to use the
"default" pool (the same one umbral::db::pool() returns). Read moreSource§fn template_registrars(
&self,
) -> Vec<Box<dyn Fn(&mut Environment<'static>) + Sync + Send>>
fn template_registrars( &self, ) -> Vec<Box<dyn Fn(&mut Environment<'static>) + Sync + Send>>
Source§fn middleware(&self) -> Vec<Arc<dyn Middleware>>
fn middleware(&self) -> Vec<Arc<dyn Middleware>>
Source§fn static_files(&self) -> Vec<StaticFile>
fn static_files(&self) -> Vec<StaticFile>
Source§fn static_dirs(&self) -> Vec<StaticDir>
fn static_dirs(&self) -> Vec<StaticDir>
Source§fn static_root_dirs(&self) -> Vec<PathBuf>
fn static_root_dirs(&self) -> Vec<PathBuf>
static_url — with
no namespace segment. Read moreSource§fn api_endpoints(&self) -> Vec<ApiEndpoint>
fn api_endpoints(&self) -> Vec<ApiEndpoint>
Auto Trait Implementations§
impl<U = AuthUser> !Freeze for AuthPlugin<U>
impl<U> RefUnwindSafe for AuthPlugin<U>where
U: RefUnwindSafe,
impl<U> Send for AuthPlugin<U>
impl<U> Sync for AuthPlugin<U>
impl<U> Unpin for AuthPlugin<U>
impl<U> UnsafeUnpin for AuthPlugin<U>
impl<U> UnwindSafe for AuthPlugin<U>where
U: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi Quirk value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);