Account Locking

Account locking automatically locks user accounts after multiple failed login attempts, protecting against brute-force attacks.

Enabling Account Locking

Add the account-lock feature to your Cargo.toml:

[dependencies]
actix-security = { version = "0.2", features = ["account-lock"] }

Basic Usage

use actix_security::http::security::{
    AccountLockManager, LockConfig, check_login
};
use std::time::Duration;

// Create lock manager
let lock_manager = AccountLockManager::new(
    LockConfig::new()
        .max_attempts(5)
        .lockout_duration(Duration::from_secs(15 * 60))
);

// In your login handler:
async fn login(
    form: web::Form<LoginForm>,
    lock_manager: web::Data<AccountLockManager>,
) -> impl Responder {
    let username = &form.username;

    // Check if account is locked
    let result = check_login(&lock_manager, username).await;
    if !result.is_allowed() {
        return HttpResponse::Forbidden().body("Account locked");
    }

    // Attempt authentication
    if authenticate(username, &form.password) {
        lock_manager.record_success(username).await;
        HttpResponse::Ok().body("Logged in")
    } else {
        lock_manager.record_failure(username).await;
        let remaining = lock_manager.get_remaining_attempts(username).await;
        HttpResponse::Unauthorized()
            .body(format!("{} attempts remaining", remaining))
    }
}

Configuration Options

LockConfig::new()
    // Maximum failed attempts before lock
    .max_attempts(5)

    // How long to lock the account
    .lockout_duration(Duration::from_secs(15 * 60))

    // Reset counter on successful login
    .reset_on_success(true)

    // Progressive lockout (doubles duration each time)
    .progressive_lockout(true)

Preset Configurations

// Strict: 3 attempts, 30 minute lockout
LockConfig::strict()

// Lenient: 10 attempts, 5 minute lockout
LockConfig::lenient()

Lock Status

use actix_security::http::security::LockStatus;

let status = lock_manager.get_lock_status(&username).await;

match status {
    LockStatus::Unlocked => { /* Account is accessible */ }
    LockStatus::TemporarilyLocked { until, reason } => {
        // Locked until specified time
    }
    LockStatus::PermanentlyLocked { reason } => {
        // Requires admin intervention
    }
}

IP Address Tracking

Track which IP addresses have attempted to access an account:

// Record failure with IP
lock_manager
    .record_failure_with_ip(&username, Some(&ip_address))
    .await;

// Get account statistics
let stats = lock_manager.get_account_stats(&username).await;
println!("Failed attempts: {}", stats.failed_attempts);
println!("Associated IPs: {:?}", stats.associated_ips);

Manual Lock/Unlock

// Manually unlock an account
lock_manager.unlock(&username).await;

// Permanently lock an account
lock_manager
    .lock_permanently(&username, "Suspicious activity detected")
    .await;

Check Result

The check_login function returns detailed information:

let result = check_login(&lock_manager, &username).await;

match result {
    LoginCheckResult::Allowed { remaining_attempts } => {
        println!("{} attempts remaining", remaining_attempts);
    }
    LoginCheckResult::Blocked { message, unlock_time } => {
        println!("Blocked: {}", message);
        if let Some(time) = unlock_time {
            println!("Unlocks at: {:?}", time);
        }
    }
}

Spring Security Comparison

Spring SecurityActix Security
LockedExceptionLockStatus::TemporarilyLocked
AccountStatusUserDetailsCheckercheck_login()
JdbcUserDetailsManager.lockUser()lock_permanently()