Configuration
Documentation / Configuration / Password configuration

Password configuration

PasswordConfig defines the hashing and verification strategy for the entire application. It is initialized once at startup via password_init().

Initialization in `main.rs`

use runique::prelude::{password_init, PasswordConfig, Manual};

#[tokio::main]
async fn main() {
    // Argon2 automatic mode (recommended default)
    password_init(PasswordConfig::auto());

    RuniqueApp::builder(config)
        // ...
        .run()
        .await;
}

If password_init() is not called, Argon2 is used by default.


Available modes

Auto — Automatic hashing (recommended)

Runique detects whether a value is already hashed and only hashes it once. The algorithm is configurable.

// Argon2 by default
password_init(PasswordConfig::auto());

// Choose the algorithm
password_init(PasswordConfig::auto_with(Manual::Bcrypt));
password_init(PasswordConfig::auto_with(Manual::Scrypt));

Supported algorithms: Manual::Argon2, Manual::Bcrypt, Manual::Scrypt.

Manual — Explicit hashing

Hashing is not applied automatically in finalize(). The developer calls hash() manually.

password_init(PasswordConfig::manual(Manual::Argon2));

Use this when you need precise control over when and how the password is hashed.

Built-in reset and admin: the built-in reset route (with_password_reset) hashes the new password itself with the app's configuration, whatever the mode (Auto, Manual or Custom). In Delegated mode, it refuses the reset: no local password is written, and the link stays usable. An account created from the admin gets a random password, hashed the same way, then a link to choose its own.

In your own forms, however, finalize() only hashes in Auto mode: in Manual or Custom, call hash() before writing to the database.

Delegated — External authentication (OAuth / SSO)

No local password is managed by Runique: verify() always answers false, hash() fails, and the built-in reset is refused.

This mode doesn't provide the external authentication itself yet. The providers below are only names: Runique implements today neither the OAuth flow (redirect, code exchange, profile reading), nor LDAP, nor SAML. Delegated is there to switch local passwords off cleanly when your app handles sign-in by other means (an OAuth library, an authentication proxy), then opens the session with login(&request.session, &user, None, false) once the user is identified. Full provider integration is a planned evolution.

use runique::prelude::External;

password_init(PasswordConfig::Delegated(External::GoogleOAuth));
password_init(PasswordConfig::Delegated(External::Microsoft));
password_init(PasswordConfig::Delegated(External::Ldap("ldap://...".to_string())));

Available providers: GoogleOAuth, Microsoft, Apple, Ldap(url), Saml(url), Custom { name, authorize_url, token_url }.

Custom — Custom handler

Implement the PasswordHandler trait to plug in your own hashing/verification logic.

use runique::prelude::{PasswordHandler, PasswordConfig};

#[derive(Clone)] // required: PasswordHandler is cloned with the config
struct MyHasher;

impl PasswordHandler for MyHasher {
    fn name(&self) -> &str { "my_hasher" }
    fn create_field(&self, name: &str) -> Box<dyn runique::forms::FormField> {
        Box::new(runique::forms::fields::text::TextField::password(name))
    }
    fn validate_input(&self, input: &str) -> Result<(), String> {
        if input.len() < 8 { Err("Too short".into()) } else { Ok(()) }
    }
    fn transform(&self, input: &str) -> Result<String, String> {
        my_algo::hash(input).map_err(|e| e.to_string())
    }
    fn verify(&self, input: &str, stored: &str) -> bool {
        // The comparison must run in constant time (the algorithm's own
        // verify function, or `ct_eq`), never with `==`.
        my_algo::verify(input, stored)
    }
}

password_init(PasswordConfig::custom(MyHasher));

Usage in code

use runique::prelude::{hash, verify};

// Hash a password (uses the global config)
let hashed = hash("my_password")?;

// Verify a password against a stored hash
let ok = verify("my_password", &user.password);

These functions automatically use the PasswordConfig initialized at startup.


In forms

TextField::password() fields are automatically hashed in finalize() in Auto mode. In Manual or Custom mode, no automatic hashing occurs: call hash() yourself. A field marked .no_hash() is never hashed (a sign-in form compares the password as typed, with authenticate_user).

In Auto mode, a password hashed with another algorithm than the configured one is moved to it on the first successful sign-in.

See → Field types — TextField


← Builder