Basic Authentication

Middleware providing support for Basic Auth.

Introduction to Basic Auth

Basic Auth is a simple HTTP authentication mechanism that transmits username and password by including the Authorization field in the HTTP request header. The format is Authorization: Basic <base64(username:password)>. Although simple, because the credentials are only Base64 encoded and not encrypted, it typically needs to be used with HTTPS to ensure security.

Comparison of Basic Auth Implementations in Common Frameworks

FrameworkLanguageBasic Auth Implementation
SalvoRustImplemented via BasicAuth middleware and custom BasicAuthValidator
ExpressJavaScriptUsing the express-basic-auth middleware package
Spring SecurityJavaImplemented via httpBasic() configuration and UserDetailsService
ASP.NET CoreC#Using .UseAuthentication() and AddAuthentication(AuthenticationSchemes.Basic)
GinGoVia custom middleware or using the gin-contrib/auth package

Use Cases

Basic Auth is suitable for the following scenarios:

  1. Internal APIs and Tools: Management tools and APIs used within a company.
  2. Development and Testing Environments: Quickly implement authentication without needing a complex login system.
  3. Simple API Protection: When a complex user management system is not required.
  4. In Conjunction with Other Security Measures: As part of a multi-layered security architecture.

In Salvo, the Basic Auth middleware can be easily integrated into routes. Custom validation logic can be implemented by implementing the BasicAuthValidator trait, offering great flexibility.

Important Considerations

  • Always use with HTTPS to protect credential transmission.
  • Not suitable for production environments handling sensitive information.
  • Consider using more secure authentication methods like JWT, OAuth, etc., for production environments.
main.rs
Cargo.toml
basic-auth/src/main.rs
use salvo::basic_auth::{BasicAuth, BasicAuthValidator};
use salvo::prelude::*;

// Custom validator implementing BasicAuthValidator trait
struct Validator;
impl BasicAuthValidator for Validator {
// Validate username and password combination
async fn validate(&self, username: &str, password: &str, _depot: &mut Depot) -> bool {
    username == "root" && password == "pwd"
}
}

// Simple handler that returns "Hello" for authenticated requests
#[handler]
async fn hello() -> &'static str {
"Hello"
}

// Create router with basic authentication middleware
fn route() -> Router {
// Initialize basic authentication handler with our validator
let auth_handler = BasicAuth::new(Validator);
// Apply authentication middleware to the router
Router::with_hoop(auth_handler).goal(hello)
}

#[tokio::main]
async fn main() {
// Initialize logging
tracing_subscriber::fmt().init();

// Bind server to port 5800 and start serving
let acceptor = TcpListener::new("0.0.0.0:5800").bind().await;
Server::new(acceptor).serve(route()).await;
}

#[cfg(test)]
mod tests {
use salvo::prelude::*;
use salvo::test::{ResponseExt, TestClient};

#[tokio::test]
async fn test_basic_auth() {
    // Create a service instance from our router for testing purposes
    let service = Service::new(super::route());

    // Test case 1: Verify successful authentication with valid credentials
    let content = TestClient::get("http://0.0.0.0:5800/")
        .basic_auth("root", Some("pwd")) // Use correct username/password
        .send(&service) // Send the request to the service
        .await
        .take_string() // Extract response body as string
        .await
        .unwrap();
    // Verify response contains expected "Hello" message
    assert!(content.contains("Hello"));

    // Test case 2: Verify authentication failure with invalid password
    let content = TestClient::get("http://0.0.0.0:5800/")
        .basic_auth("root", Some("pwd2")) // Use incorrect password
        .send(&service) // Send the request to the service
        .await
        .take_string() // Extract response body as string
        .await
        .unwrap();
    // Verify response contains "Unauthorized" error
    assert!(content.contains("Unauthorized"));
}
}