Skip to main content

Crate umbral_auth

Crate umbral_auth 

Source
Expand description

umbral-auth — the built-in authentication plugin.

The first crate under plugins/ and the proof of the M7 plugin contract: a real built-in expressed through umbral::prelude::Plugin with no special-casing inside umbral-core. Auth is the most common plugin, so getting it right here also pressure-tests the contract for the rest.

§M9 v1 scope

  • AuthUser model: the canonical User model (username, email, password hash, is_active / is_staff / is_superuser, date_joined, last_login).
  • UserModel trait: the minimum surface a custom user model must satisfy so AuthPlugin<U> can swap in any user type. Default impls cover the optional flag methods so a minimal custom user struct only has to implement the load-bearing four.
  • argon2 password hashing via hash_password / verify_password.
  • create_user, authenticate, set_password helpers. authenticate and set_password are generic over any U: UserModel.
  • AuthPlugin registers the user model (which becomes a migration) plus the /auth routes and management commands. The type parameter defaults to AuthUser so existing apps need no changes.
  • [login_required] module: LoginRequired config, LoggedIn<U> extractor, LoginRequiredLayer middleware, and the login_required() / login_required_html() convenience constructors. A login-required gate in two shapes.

§Custom user models

// 1. Declare a custom user struct.
#[derive(Debug, Clone, sqlx::FromRow, Serialize, Deserialize, umbral::orm::Model)]
pub struct TenantUser {
    pub id: i64,
    pub username: String,
    pub password_hash: String,
    pub tenant_id: i64,
    pub is_active: bool,
}

// 2. Implement UserModel (only the four required methods).
impl umbral_auth::UserModel for TenantUser {
    fn id(&self) -> i64               { self.id }
    fn username(&self) -> &str        { &self.username }
    fn password_hash(&self) -> &str   { &self.password_hash }
    fn set_password_hash(&mut self, h: String) { self.password_hash = h; }
}

// 3. Wire the plugin with your type.
App::builder()
    .plugin(AuthPlugin::<TenantUser>::default())
    .build()?

§Deferred (per docs/specs/outlines/auth-and-sessions.md)

  • Permissions, groups, the auth-backend chain.
  • The Auth<U> request extractor + #[login_required] middleware. Needs Plugin::middleware() lifted (M7 deferral).
  • Login / logout / password-reset HTTP flows. Needs the full umbral-sessions session middleware wired end-to-end.
  • Periodic session cleanup via umbral-tasks.

Re-exports§

pub use mailer::AuthMailError;
pub use mailer::AuthMailer;
pub use mailer::ConsoleMailer;
pub use mailer::MailKind;
pub use mailer::OutgoingMail;
pub use password_validation::CommonPasswordValidator;
pub use password_validation::MinLengthValidator;
pub use password_validation::NumericPasswordValidator;
pub use password_validation::PasswordContext;
pub use password_validation::PasswordPolicy;
pub use password_validation::PasswordValidator;
pub use password_validation::UserAttributeSimilarityValidator;
pub use password_validation::validate_password;
pub use bearer_auth::BearerAuthentication;
pub use bearer_auth::parse_bearer_header;
pub use challenge::AuthChallenge;
pub use challenge::change_password;
pub use challenge::reset_password;
pub use challenge::start_email_verification;
pub use challenge::start_password_reset;
pub use challenge::verify_email;
pub use extractors::CurrentIdentity;
pub use extractors::OptionalIdentity;
pub use extractors::RequireAuth;
pub use extractors::RequireStaff;
pub use extractors::resolve_identity;
pub use login_required::LoggedIn;
pub use login_required::LoginRequired;
pub use login_required::LoginRequiredLayer;
pub use login_required::current_session_user_id;
pub use login_required::current_session_user_pk;
pub use login_required::login_required;
pub use login_required::login_required_html;
pub use login_required::resolve_user as current_user_as;
pub use session_user::OptionalUser;
pub use session_user::SessionAuthentication;
pub use session_user::User;
pub use session_user::current_user;
pub use session_user::db_session_var_layer;
pub use session_user::login;
pub use session_user::login_with_request;
pub use session_user::user_context_layer;
pub use throttle::Throttle;
pub use throttle::ThrottleConfig;
pub use throttle::email_action_throttle_check;
pub use throttle::login_throttle_check;
pub use throttle::login_throttle_clear;
pub use throttle::register_throttle_check;
pub use token::AuthToken;
pub use token::PlaintextToken;
pub use token::TOKEN_PREFIX;
pub use token::digest_token;

Modules§

auth_routes
Built-in /auth HTTP surface — login, logout, me, register.
auth_user
bearer_auth
BearerAuthentication — the bearer-token authenticator.
challenge
Short-lived, single-use, hashed-at-rest secrets for the email-verification and password-reset flows. One table, discriminated by purpose.
extractors
Axum extractors that resolve a request to an umbral::auth::Identity.
form_routes
POST-only form-action auth endpoints — the redirect-style counterpart to the JSON surface in crate::auth_routes.
login_required
A login-required gate for umbral handlers.
mailer
The pluggable email seam. umbral-auth renders bodies via umbral::templates and hands them to whatever AuthMailer the app wired (default: print to stderr). Keeps auth decoupled from any mail crate.
password_validation
Password-strength validation.
session_user
AuthUser-aware session helpers — moved from umbral-sessions so sessions can stay free of any user-model dependency.
throttle
Login / register / email-action rate-limiting — credential-stuffing & brute-force defense.
token
Opaque DB-backed bearer tokens.

Structs§

AuthPlugin
The built-in authentication plugin, generic over the user model.
AuthUser
The canonical authentication user. #[derive(Model)] snake_cases the struct name into the table name auth_user; the M3 derive doesn’t yet accept #[umbral(table = ...)] so the snake_case round-trip is the only way to get a plugin-prefixed table name until the attribute lands.
CreateSuperuserCommand
createsuperuser - interactive superuser creation, dispatched via cargo run -- createsuperuser from any umbral project that registers AuthPlugin.
PasswordAudit
Outcome of reset_unverifiable_passwords.
ResetForeignPasswordsCommand
resetforeignpasswords — the post-port fixer for the password-hash caveat. Neutralizes every stored hash umbral-auth can’t verify (see reset_unverifiable_passwords) and tells the operator those users must reset. Dispatched via cargo run -- resetforeignpasswords.

Enums§

AuthError
Errors the auth helpers can produce. Kept narrow at M9 v1 so the surface is easy to handle in one match arm.

Traits§

UserModel
The minimum surface a user model must expose so AuthPlugin<U> can operate on it generically.

Functions§

authenticate
Verify a username + plaintext password against the user table for user model U. Returns the user on success; returns AuthError::InvalidCredentials for both “no such user” and “wrong password” (the same shape, so a caller can’t enumerate accounts).
create_superuser
Create a superuser - is_staff = true, is_superuser = true, is_active = true. Used by the createsuperuser management command and available directly for tests / seed scripts.
create_user
Create a new active user with the given username, email, and plaintext password. The password is hashed before insert; the plaintext never touches the database. date_joined is set to Utc::now(); last_login is None; is_active = true, is_staff = false, is_superuser = false.
create_user_with_flags
Insert a new user with arbitrary is_staff / is_superuser flags. Used by create_user (flags = false, false) and create_superuser (flags = true, true); exposed publicly so custom seed paths can pick a specific shape (e.g. a staff-but- not-superuser editor account).
hash_password
Hash a plaintext password with argon2’s framework-chosen parameters. Returns the PHC-encoded string ready to store in the password_hash column. The hash is self-describing so future parameter upgrades stay transparent: a verified hash with old parameters can be re-hashed on next login.
hash_password_async
Async wrapper around hash_password that runs the CPU-bound argon2 work on tokio’s blocking pool via spawn_blocking, under the concurrency gate (see above). argon2id with the framework parameters takes ~100ms of CPU; calling it directly from a request handler pins an async worker thread for that whole time, so a login/registration burst starves the runtime and HTTP/1.1 connections hang. Offloading keeps the async workers free to drive other tasks. Async request handlers must use this; the sync hash_password remains for non-async / CLI / test callers.
logout
Log the current request’s user out: destroy the session row, emit a clearing Set-Cookie on resp, and revoke the bearer token the request presented (if any).
normalize_email
Canonicalize an email for storage and lookup: trim then lowercase. Emails are treated case-insensitively (the pragmatic standard — no real deployment relies on a case-sensitive local part), so Dalmas@Gmail.com and dalmas@gmail.com are the same account.
normalize_username
Canonicalize a username for storage and lookup: trim surrounding whitespace, then lowercase. " Dalmasonto ""dalmasonto".
random_password_hash
Argon2 hash of a fresh, random, un-recoverable password.
reset_unverifiable_passwords
Replace every stored password hash that umbral-auth cannot verify with a fresh random_password_hash. This is the fix for the classic port caveat: a database migrated from Django (or any other stack) carries hashes in a foreign format — pbkdf2_sha256$…, argon2$… (Django’s own argon2 framing differs from the PHC string), a bcrypt $2b$… — none of which parse as umbral’s argon2 PHC hash. Left as-is, verify_password errors on them (it can’t even parse the hash), so those users can neither log in nor get a clean rejection.
set_password
Replace a user’s password with a fresh hash of the given plaintext. Writes through to the database using U::TABLE. user.password_hash is updated in place on success so the caller can keep using the same value.
verify_password
Verify a plaintext password against an argon2 PHC-encoded hash. Returns Ok(true) on match, Ok(false) on mismatch, and an error only when the hash itself is malformed. Callers that just want a bool can use .unwrap_or(false).
verify_password_async
Async wrapper around verify_password that runs the CPU-bound argon2 verification on tokio’s blocking pool via spawn_blocking, under the same concurrency gate. See hash_password_async for the starvation rationale. Async request handlers must use this; the sync verify_password remains for non-async / CLI / test callers.