Skip to content

lollipopkit/ntex-basicauth

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ntex-basicauth

A Basic Authentication middleware for the ntex web framework.

Crates.io Documentation License: MIT Rust

Features

  • Cache - Built-in authentication result cache to reduce validation overhead
  • Flexible Configuration - Supports multiple user validation methods
  • Path Filtering - Supports skipping authentication for specific paths
  • BCrypt Support - BCrypt password hashing (enabled by default, disable with default-features = false)
  • JSON Response - Optional JSON error response (requires json feature)
  • Custom Validator - Support for custom user validation logic
  • Regex Paths - Regular expression path matching (requires regex feature)
  • Safety - Timing-safe password comparison (timing-safe, enabled by default). Automatic password memory cleanup using zeroize crate (secure-memory, enabled by default)

Installation

Add the dependency in your Cargo.toml:

[dependencies]
ntex-basicauth = "0"

Enable optional features if needed:

[dependencies]
ntex-basicauth = { version = "0", features = ["bcrypt", "regex"] }

Quick Start

Basic Usage

use ntex::web;
use ntex_basicauth::BasicAuthBuilder;
use std::collections::HashMap;

#[ntex::main]
async fn main() -> std::io::Result<()> {

    web::HttpServer::new(move || {
        let mut users = HashMap::new();
        users.insert("admin".to_string(), "secret".to_string());
        users.insert("user".to_string(), "password".to_string());

        let auth = BasicAuthBuilder::new()
            .users(users)
            .realm("My Application")
            .build()
            .expect("Failed to configure authentication");

        web::App::new()
            .route(
                web::scope("/protected")
                    .wrap(auth)
                    .route("/", web::get().to(protected_handler)),
            )
            .route("/public", web::get().to(public_handler))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

async fn protected_handler() -> &'static str {
    "This is protected content!"
}

async fn public_handler() -> &'static str {
    "This is public content"
}

Using the Builder Pattern

use ntex_basicauth::{BasicAuthBuilder, PathFilter};
use std::collections::HashMap;

let mut users = HashMap::new();
users.insert("admin".to_string(), "secret".to_string());

let filter = PathFilter::new()
    .skip_prefix("/public/")
    .skip_exact("/health")
    .skip_suffix(".css");

let auth = BasicAuthBuilder::new()
    .users(users)
    .realm("My Application")
    .path_filter(filter)
    .log_failures(true)
    .max_header_size(4096)
    .build()
    .unwrap();

Regex Path Filtering

Enable the regex feature in Cargo.toml:

[dependencies]
ntex-basicauth = { version = "0", features = ["regex"] }
use ntex_basicauth::{BasicAuthBuilder, PathFilter};

let filter = PathFilter::new()
    .skip_regex(r"^/assets/.*\.(js|css|png|jpg)$").unwrap();

let auth = BasicAuthBuilder::new()
    .user("admin", "secret")
    .path_filter(filter)
    .build()
    .unwrap();

BCrypt Password Support

Enable the bcrypt feature in Cargo.toml:

[dependencies]
ntex-basicauth = { version = "0", features = ["bcrypt"] }
use ntex_basicauth::{BasicAuthBuilder, BcryptUserValidator};
use std::sync::Arc;

let mut validator = BcryptUserValidator::new();
validator.add_user_with_password("admin".to_string(), "secret").unwrap();

let auth = BasicAuthBuilder::new()
    .validator(Arc::new(validator))
    .realm("My Application")
    .build()
    .unwrap();

Custom Validator

use ntex_basicauth::{UserValidator, Credentials, AuthResult, BasicAuthBuilder};
use std::sync::Arc;
use std::future::Future;
use std::pin::Pin;

struct DatabaseValidator;

impl UserValidator for DatabaseValidator {
    fn validate<'a>(
        &'a self,
        credentials: &'a Credentials,
    ) -> Pin<Box<dyn Future<Output = AuthResult<bool>> + Send + 'a>> {
        Box::pin(async move {
            // Replace with your DB logic
            Ok(credentials.username == "admin" && credentials.password == "secret")
        })
    }
}

let auth = BasicAuthBuilder::new()
    .validator(Arc::new(DatabaseValidator))
    .realm("Custom Realm")
    .build()
    .unwrap();

Getting User Information

Get authenticated user information in the request handler:

use ntex::web;
use ntex_basicauth::{extract_credentials, get_username, is_user};

async fn handler(req: web::HttpRequest) -> web::Result<String> {
    if let Some(credentials) = extract_credentials(&req) {
        return Ok(format!("User: {}", credentials.username));
    }

    if let Some(username) = get_username(&req) {
        return Ok(format!("Welcome, {}!", username));
    }

    if is_user(&req, "admin") {
        return Ok("Admin access granted".to_string());
    }

    Ok("Unknown user".to_string())
}

PathFilter Builder Pattern

You can use the PathFilter builder for convenient filter creation:

use ntex_basicauth::PathFilter;

let filter = PathFilter::new()
    .skip_exact("/health")
    .skip_exact("/metrics")
    .skip_prefix("/public/")
    .skip_suffix(".css")
    .skip_suffix(".js");

Common Skip Paths

Use built-in common skip paths for typical web applications (health checks, static assets, etc.):

use ntex_basicauth::{BasicAuthBuilder, PathFilter};

// Create a filter with common web paths
let common_filter = PathFilter::new()
    .skip_exact("/health")
    .skip_exact("/metrics")
    .skip_exact("/favicon.ico")
    .skip_prefix("/static/")
    .skip_prefix("/assets/")
    .skip_suffix(".css")
    .skip_suffix(".js")
    .skip_suffix(".png")
    .skip_suffix(".jpg")
    .skip_suffix(".ico");

let auth = BasicAuthBuilder::new()
    .user("admin", "secret")
    .path_filter(common_filter)
    .build()
    .unwrap();

Error Handling

When authentication fails, the middleware returns HTTP 401 status code and corresponding error information:

{
    "code": 401,
    "message": "Authentication required",
    "error": "Invalid credentials"
}

Error types include:

  • MissingHeader - Missing Authorization header
  • InvalidFormat - Invalid Authorization header format
  • InvalidBase64 - Invalid Base64 encoding
  • InvalidCredentials - Invalid user credentials
  • ValidationFailed - User validation failed

Configuration

Advanced Security Configuration

use ntex_basicauth::{BasicAuthBuilder, CacheConfig, PathFilter};
use std::time::Duration;

// Production-ready configuration with security hardening
let cache_config = CacheConfig::new()
    .max_size(1000)
    .ttl_minutes(10)
    .cleanup_interval_seconds(300);

// Common paths to skip authentication
let skip_paths = PathFilter::new()
    .skip_exact("/health")
    .skip_exact("/metrics")
    .skip_prefix("/static/")
    .skip_suffix(".css")
    .skip_suffix(".js");

let auth = BasicAuthBuilder::new()
    .users_from_file("users.txt")           // Load users from file
    .realm("Production API")
    .with_cache(cache_config)
    .max_concurrent_validations(100)        // Prevent resource exhaustion
    .validation_timeout(Duration::from_secs(30))
    .rate_limit_per_ip(10, Duration::from_secs(60))  // 10 req/min per IP
    .log_usernames_in_production(false)     // Security: no username logging
    .path_filter(skip_paths)                // Skip health checks, assets
    .build()
    .unwrap();

Security Best Practices

  • Memory Security: The secure-memory feature (enabled by default) automatically clears password data from memory after use
  • Cache Security: Cache keys are SHA256-hashed with application-specific salt to prevent rainbow table attacks
  • Production Logging: Set log_usernames_in_production(false) to prevent username leakage in production logs
  • DoS Protection: Configure rate limiting and concurrent validation limits to prevent resource exhaustion

Cache Configuration

Cache is enabled by default (unless disabled via builder/config):

use ntex_basicauth::{BasicAuthBuilder, CacheConfig};

// High-traffic configuration
let cache_config = CacheConfig::new()
    .max_size(10000)                    // Large cache for busy servers
    .ttl_minutes(5)                     // Short TTL for security
    .cleanup_interval_seconds(60)       // Frequent cleanup
    .enable_stats(true);                // Monitor cache performance

let auth = BasicAuthBuilder::new()
    .user("admin", "secret")
    .with_cache(cache_config)
    .build()
    .unwrap();

License

Licensed under the MIT License. See LICENSE for details.

About

A Basic Authorization middleware for ntex web framework.

Topics

Resources

Stars

Watchers

Forks