A Basic Authentication middleware designed for the ntex web framework.
- 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 - Optional BCrypt password hashing (requires
bcrypt
feature) - 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 usingzeroize
crate (secure-memory
, enabled by default)
Add the dependency in your Cargo.toml
:
[dependencies]
ntex-basicauth = "^0"
Enable optional features if needed:
[dependencies]
ntex-basicauth = { version = "^0", features = ["bcrypt", "regex"] }
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()
.wrap(auth)
.route("/protected", 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"
}
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();
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();
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();
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();
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())
}
You can use the path_filter!
macro for convenient filter creation:
use ntex_basicauth::path_filter;
let filter = path_filter!(
exact: ["/health", "/metrics"],
prefix: ["/public/"],
suffix: [".css", ".js"]
);
Use built-in common skip paths:
use ntex_basicauth::{BasicAuthBuilder, common_skip_paths};
let auth = BasicAuthBuilder::new()
.user("admin", "secret")
.path_filter(common_skip_paths())
.build()
.unwrap();
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 headerInvalidFormat
- Invalid Authorization header formatInvalidBase64
- Invalid Base64 encodingInvalidCredentials
- Invalid user credentialsValidationFailed
- User validation failed
use ntex_basicauth::{BasicAuthBuilder, CacheConfig};
use std::time::Duration;
let cache_config = CacheConfig::new()
.max_size(1000)
.ttl_minutes(10)
.cleanup_interval_seconds(300);
let auth = BasicAuthBuilder::new()
.user("admin", "secret")
.with_cache(cache_config)
.max_concurrent_validations(100) // Limit concurrent validations
.validation_timeout(Duration::from_secs(30)) // Set validation timeout
.rate_limit_per_ip(10, Duration::from_secs(60)) // 10 requests per minute per IP
.log_usernames_in_production(false) // Don't log usernames in production
.build()
.unwrap();
- 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 is enabled by default (unless disabled via builder/config):
use ntex_basicauth::{BasicAuthBuilder, CacheConfig};
let cache_config = CacheConfig::new()
.max_size(1000)
.ttl_minutes(10)
.cleanup_interval_seconds(300);
let auth = BasicAuthBuilder::new()
.user("admin", "secret")
.with_cache(cache_config)
.build()
.unwrap();
This project is licensed under the MIT License.