Basic and Digest Authentication
Basic and Digest authentication are standardized HTTP authentication methods defined in RFC 7617 and RFC 7616. These methods are well-suited for API access and programmatic HTTP clients.
Overview
Both Basic and Digest authentication use the HTTP Authorization header to transmit credentials. They provide automatic authentication without requiring custom login pages or session management.
Basic Authentication:
- Simple and widely supported
- Credentials encoded in base64 (not encrypted)
- Must be used over HTTPS/TLS
Digest Authentication:
- More secure than Basic
- Uses MD5 hashing of credentials
- Protects against replay attacks
- Can be used over HTTP (though HTTPS still recommended)
When to Use Basic/Digest Authentication
Good Use Cases
- REST APIs - Simple authentication for API endpoints
- Machine-to-Machine - Automated services and scripts
- Command-line tools - curl, wget, and similar utilities
- Embedded clients - IoT devices communicating with server
- Development/Testing - Quick authentication setup
When to Use Form Authentication Instead
- Web applications - Better user experience with HTML login forms
- Browser-based apps - Session management and logout functionality
- Complex authentication - Multi-factor auth, OAuth integration
- User-facing interfaces - More control over login/logout flow
How Basic Authentication Works
- Client requests protected resource without credentials
- Server responds with 401 Unauthorized and WWW-Authenticate: Basic header
- Client re-requests with Authorization header containing base64-encoded credentials
- Server decodes and validates credentials
- Server grants or denies access
Authorization Header Format
Authorization: Basic base64(username:password)Example:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=Where dXNlcm5hbWU6cGFzc3dvcmQ= is base64 encoding of username:password.
Configuring Basic Authentication
Enable Basic Authentication
Configure in web.json5:
{
auth: {
// Define roles with their abilities (2-level structure)
roles: {
public: [],
user: ['view', 'read'],
admin: ['user', 'edit', 'delete'],
},
// Enable Basic authentication
type: "basic",
// Authentication realm (displayed in browser dialog)
realm: "Device Manager",
}
}Route Configuration
Protect routes with role requirements:
web: {
routes: [
// Require admin role
{match: '/api/admin/', role: 'admin'},
// Require at least user role
{match: '/api/user/', role: 'user'},
// Public access
{match: '/api/public/'},
// Catchall - public
{},
],
}Client Usage Examples
curl:
curl -u username:password https://device.local/api/user/statuswget:
wget --user=username --password=password https://device.local/api/user/statusJavaScript (Fetch API):
const credentials = btoa('username:password');
fetch('https://device.local/api/user/status', {
headers: {
'Authorization': 'Basic ' + credentials
}
})
.then(response => response.json())
.then(data => console.log(data));Python (requests):
import requests
from requests.auth import HTTPBasicAuth
response = requests.get(
'https://device.local/api/user/status',
auth=HTTPBasicAuth('username', 'password')
)
print(response.json())How Digest Authentication Works
- Client requests protected resource without credentials
- Server responds with 401 Unauthorized and WWW-Authenticate: Digest header containing:
- realm - Authentication realm
- nonce - Server-generated random value
- qop - Quality of protection (auth, auth-int)
- algorithm - Hash algorithm (MD5, SHA-256)
- Client computes hash response using:
- Username and password
- Server nonce
- HTTP method and URI
- Client nonce (if qop specified)
- Client re-requests with Authorization: Digest header
- Server validates the hash
- Server grants or denies access
Digest Response Calculation
HA1 = MD5(username:realm:password)
HA2 = MD5(method:digestURI)
response = MD5(HA1:nonce:nonceCount:clientNonce:qop:HA2)Configuring Digest Authentication
Enable Digest Authentication
Configure in web.json5:
{
auth: {
// Define roles with their abilities (2-level structure)
roles: {
public: [],
user: ['view', 'read'],
admin: ['user', 'edit', 'delete'],
},
// Enable Digest authentication
type: "digest",
// Authentication realm
realm: "Device Manager",
// Quality of protection (optional)
qop: "auth",
// Hash algorithm (optional, default: MD5)
algorithm: "MD5",
}
}Advanced Digest Configuration
{
auth: {
type: "digest",
realm: "Secure API",
// Quality of protection options
// "auth" - authentication only
// "auth-int" - authentication with integrity protection
qop: "auth",
// Nonce timeout in seconds
nonceTimeout: 300,
// Algorithm: MD5, SHA-256, SHA-512-256
algorithm: "SHA-256",
}
}Client Usage Examples
curl:
curl --digest -u username:password https://device.local/api/user/statusPython (requests):
import requests
from requests.auth import HTTPDigestAuth
response = requests.get(
'https://device.local/api/user/status',
auth=HTTPDigestAuth('username', 'password')
)
print(response.json())JavaScript: (Use a library that supports Digest auth, as native Fetch API doesn't)
Programmatic Authentication
Using webAuthenticate API
For custom authentication logic, use the webAuthenticate API:
static int apiHandler(Web *web)
{
// Authenticate using configured method (Basic or Digest)
if (!webAuthenticate(web)) {
// Authentication failed - response already sent
return 0;
}
// Get authenticated user
cchar *username = webGetUser(web);
cchar *role = webGetRole(web);
// Verify user has required role
if (!webCanUser(web, "admin")) {
webError(web, 403, "Insufficient privileges");
return 0;
}
// Process authenticated request
webWriteResponse(web, 200, "{\"status\": \"ok\"}");
return 0;
}
webAddAction(NULL, "/api/admin/config", apiHandler);Custom Authentication Logic
static int customAuthHandler(Web *web)
{
cchar *authHeader = webGetHeader(web, "Authorization");
if (!authHeader) {
// No credentials provided - request authentication
webSetHeader(web, "WWW-Authenticate", "Basic realm=\"Device Manager\"");
webError(web, 401, "Authentication required");
return 0;
}
// Parse Authorization header
if (strncmp(authHeader, "Basic ", 6) == 0) {
cchar *encoded = authHeader + 6;
cchar *decoded = cryptDecode64(encoded);
// Parse username:password
char *colon = strchr(decoded, ':');
if (colon) {
*colon = '\0';
cchar *username = decoded;
cchar *password = colon + 1;
// Validate credentials
if (validateUser(username, password)) {
// Set authenticated user in web context
webSetUser(web, username);
return 1; // Continue processing
}
}
}
// Authentication failed
webSetHeader(web, "WWW-Authenticate", "Basic realm=\"Device Manager\"");
webError(web, 401, "Invalid credentials");
return 0;
}Security Considerations
Basic Authentication Security
Vulnerabilities:
- Credentials are only base64-encoded, not encrypted
- Susceptible to eavesdropping on unencrypted connections
- Credentials sent with every request (no session)
Mitigations:
- Always use HTTPS/TLS - Essential for Basic auth
- Use strong passwords
- Implement rate limiting
- Monitor for authentication failures
- Consider IP whitelisting for sensitive APIs
Digest Authentication Security
Advantages over Basic:
- Password never sent over network
- Nonce prevents replay attacks
- Server can validate without storing plaintext password
Limitations:
- MD5 is cryptographically weak (use SHA-256 if possible)
- Vulnerable to man-in-the-middle attacks without HTTPS
- No protection against malicious server stealing passwords
Mitigations:
- Use HTTPS/TLS - Still recommended for Digest
- Use SHA-256 algorithm instead of MD5
- Implement nonce timeout
- Use qop="auth-int" for message integrity
- Monitor authentication failures
General Best Practices
- Always use HTTPS/TLS - Even with Digest authentication
- Strong passwords - Enforce password complexity
- Rate limiting - Prevent brute force attacks
- Account lockout - Lock accounts after failed attempts
- Audit logging - Log all authentication events
- IP filtering - Restrict access by IP when possible
- Least privilege - Assign minimal required roles
Comparison: Basic vs. Digest vs. Form
| Feature | Basic | Digest | Form |
|---|---|---|---|
| Security | Low (requires HTTPS) | Medium | High (with HTTPS) |
| Setup Complexity | Very Simple | Simple | Moderate |
| Client Support | Universal | Good | Browsers only |
| User Experience | Browser dialog | Browser dialog | Custom login page |
| API Friendly | Yes | Yes | No |
| Session Management | No | No | Yes |
| Logout Support | No | No | Yes |
| Custom UI | No | No | Yes |
| HTTPS Required | Yes | Recommended | Yes |
Mixing Authentication Methods
You can support multiple authentication methods simultaneously by checking the request type:
static int flexibleAuthHandler(Web *web)
{
cchar *authHeader = webGetHeader(web, "Authorization");
if (authHeader) {
// API client using Basic/Digest
if (!webAuthenticate(web)) {
return 0;
}
} else {
// Browser client - check session
if (!webGetUser(web)) {
webRedirect(web, 302, "/login.html");
return 0;
}
}
// User is authenticated (via API or session)
// Continue processing...
return 1;
}Testing Authentication
Testing with curl
Basic authentication:
# Successful authentication
curl -u admin:password https://device.local/api/user/status
# Wrong credentials
curl -u admin:wrongpass https://device.local/api/user/status
# Verbose output to see headers
curl -v -u admin:password https://device.local/api/user/statusDigest authentication:
# Digest auth
curl --digest -u admin:password https://device.local/api/user/status
# See the challenge-response
curl -v --digest -u admin:password https://device.local/api/user/statusTesting Authentication Failures
Test that authentication is properly enforced:
# No credentials - should return 401
curl https://device.local/api/user/status
# Insufficient role - should return 403
curl -u user:password https://device.local/api/admin/config
# Invalid credentials - should return 401
curl -u admin:wrongpass https://device.local/api/user/statusAPIs
Key APIs for Basic/Digest authentication:
- webAuthenticate - Authenticate using configured method
- webSetAuthType - Set authentication type
- webSetRealm - Set authentication realm
- webGetUser - Get authenticated username
- webGetRole - Get user's role
- webCanUser - Check if user has required role
- cryptDecode64 - Decode base64
- cryptMakePassword - Hash password
Samples
Example implementations:
