๐ Overview
This tutorial covers the implementation of Basic Authentication in Flask-based APIs. Basic Authentication is one of the simplest authentication mechanisms where credentials are transmitted as Base64-encoded strings in the HTTP Authorization header. Understanding this method is crucial for API security testing and development.
๐ ๏ธ Prerequisites and Installation
Python Version Check
Before installing the required packages, verify your Python version:
Installing Required Packages
Install Flask framework (if not already installed):
Install Flask-HTTPAuth extension for authentication:
pip for Python 2.x or pip3 for Python 3.x systems.
๐ป Code Implementation
Complete Python Code
from flask import Flask, jsonify, make_response
from flask_httpauth import HTTPBasicAuth
# Initialize Flask application
app = Flask(__name__)
# Initialize HTTP Basic Authentication
auth = HTTPBasicAuth()
# Define user credentials (username: password)
users = {
"user1": "password1",
"user2": "password2"
}
# Password verification method
@auth.verify_password
def verify_password(username, password):
"""
Verifies if the provided username exists and
if the password matches the stored credentials
"""
if username in users:
if users[username] == password:
return username
return None
# Protected API endpoint
@app.route('/api', methods=['GET'])
@auth.login_required
def protected_api():
"""
This endpoint requires authentication.
Returns JSON data only if credentials are valid.
"""
return jsonify({'data': 'This is protected data', 'status': 'success'})
# Error handler for authentication failures
@app.errorhandler(401)
def unauthorized(error):
"""
Custom error response for 401 Unauthorized errors
"""
return make_response(
jsonify({'message': 'Authentication required'}),
401,
{'WWW-Authenticate': 'Basic realm="Login Required"'}
)
# Run the application
if __name__ == '__main__':
app.run(port=5000, debug=True)
๐ Code Breakdown and Explanation
1. Import Statements
from flask import Flask, jsonify, make_response
from flask_httpauth import HTTPBasicAuth
Flask: The main web framework for building the API.
jsonify: Converts Python dictionaries to JSON responses.
make_response: Creates custom HTTP responses with specific status codes and
headers.
HTTPBasicAuth: Provides Basic Authentication functionality.
2. Application Initialization
app = Flask(__name__)
auth = HTTPBasicAuth()
app = Flask(__name__): Creates a Flask application instance. The __name__
variable helps Flask determine the root path of the application.
auth = HTTPBasicAuth(): Initializes the HTTP Basic Authentication object that will
handle authentication logic.
3. User Credentials Storage
users = {
"user1": "password1",
"user2": "password2"
}
This dictionary stores username-password pairs. In production environments, credentials should be stored securely using hashing algorithms (like bcrypt) and never in plain text.
4. Password Verification Method
@auth.verify_password
def verify_password(username, password):
if username in users:
if users[username] == password:
return username
return None
The @auth.verify_password decorator marks this function as the password verification
callback. The function:
- Checks if the username exists in the users dictionary
- Verifies if the provided password matches the stored password
- Returns the username if authentication succeeds
- Returns None if authentication fails
5. Protected API Endpoint
@app.route('/api', methods=['GET'])
@auth.login_required
def protected_api():
return jsonify({'data': 'This is protected data', 'status': 'success'})
@app.route('/api'): Defines the URL endpoint for the API.
@auth.login_required: Decorator that enforces authentication. This triggers the
verify_password method before allowing access to the endpoint.
jsonify(): Returns a JSON response with the protected data.
6. Error Handler
@app.errorhandler(401)
def unauthorized(error):
return make_response(
jsonify({'message': 'Authentication required'}),
401,
{'WWW-Authenticate': 'Basic realm="Login Required"'}
)
This custom error handler intercepts 401 (Unauthorized) errors and returns a standardized JSON response
with appropriate headers, including the WWW-Authenticate header that instructs the client
to use Basic authentication.
๐ Authentication Flow
Basic Authentication Process
โ Failure: Return 401 error
๐ Running the Application
Start the Flask Server
The application will start on port 5000. You should see output similar to:
* Running on http://127.0.0.1:5000/
* Debug mode: on
app.run() call if needed.
๐งช Testing the API
Using cURL
Test without authentication (should fail with 401):
Test with valid credentials (user1:password1):
Test with invalid credentials:
Using Python Requests Library
import requests
from requests.auth import HTTPBasicAuth
# Without authentication
response = requests.get('http://127.0.0.1:5000/api')
print(response.status_code) # Should return 401
# With valid authentication
response = requests.get(
'http://127.0.0.1:5000/api',
auth=HTTPBasicAuth('user1', 'password1')
)
print(response.json()) # Should return protected data
Using Browser
Navigate to http://127.0.0.1:5000/api in your browser. You should see a login prompt
requesting username and password.
๐ Understanding Basic Authentication Headers
How Basic Auth Works
Basic Authentication encodes credentials in the HTTP Authorization header using Base64 encoding:
| Step | Process | Example |
|---|---|---|
| 1 | Combine username and password | user1:password1 |
| 2 | Encode with Base64 | dXNlcjE6cGFzc3dvcmQx |
| 3 | Add "Basic" prefix | Basic dXNlcjE6cGFzc3dvcmQx |
| 4 | Send in Authorization header | Authorization: Basic dXNlcjE6cGFzc3dvcmQx |
Manual Base64 Encoding Example
import base64
credentials = "user1:password1"
encoded = base64.b64encode(credentials.encode()).decode()
print(f"Authorization: Basic {encoded}")
# Output: Authorization: Basic dXNlcjE6cGFzc3dvcmQx
๐ก๏ธ Security Considerations
Critical Security Issues with Basic Authentication
- No Encryption: Credentials are only Base64-encoded, NOT encrypted. Anyone intercepting the request can easily decode them.
- Always Use HTTPS: Basic Auth should NEVER be used over HTTP in production. Always use HTTPS/TLS to encrypt the entire communication.
- Credential Storage: Never store passwords in plain text. Use proper hashing algorithms like bcrypt, Argon2, or PBKDF2.
- Session Management: Basic Auth requires credentials with every request. Consider using token-based authentication (JWT, OAuth) for better security.
- Brute Force Protection: Implement rate limiting and account lockout mechanisms to prevent brute force attacks.
๐ Response Examples
Successful Authentication Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": "This is protected data",
"status": "success"
}
Failed Authentication Response
HTTP/1.1 401 UNAUTHORIZED
Content-Type: application/json
WWW-Authenticate: Basic realm="Login Required"
{
"message": "Authentication required"
}
๐ง Advanced Modifications
Adding Password Hashing
from werkzeug.security import generate_password_hash, check_password_hash
# Store hashed passwords
users = {
"user1": generate_password_hash("password1"),
"user2": generate_password_hash("password2")
}
@auth.verify_password
def verify_password(username, password):
if username in users:
if check_password_hash(users[username], password):
return username
return None
Custom Port Configuration
To run on a different port (e.g., port 8080):
Modify the code:
if __name__ == '__main__':
app.run(port=8080, debug=True)
๐ Key Takeaways
- Basic Authentication is simple but has significant security limitations
- Flask-HTTPAuth simplifies authentication implementation in Flask applications
- The
@auth.login_requireddecorator protects endpoints from unauthorized access - The
verify_passwordmethod is the core of the authentication logic - Always use HTTPS in production when implementing Basic Authentication
- Consider more secure alternatives like OAuth 2.0 or JWT for production APIs
- Proper error handling (401 responses) helps clients understand authentication requirements
๐ฏ Common Use Cases
| Scenario | Appropriate | Reason |
|---|---|---|
| Internal APIs | โ Yes (with HTTPS) | Simple authentication for trusted environments |
| Public APIs | โ No | Requires more robust authentication mechanisms |
| Development/Testing | โ Yes | Quick setup for testing authentication flows |
| Production (over HTTP) | โ Never | Credentials sent in clear text |
| Production (over HTTPS) | โ ๏ธ Maybe | Consider OAuth 2.0 or JWT for better security |
๐ Troubleshooting
Common Issues and Solutions
Issue: "ModuleNotFoundError: No module named 'flask'"
Issue: "ModuleNotFoundError: No module named 'flask_httpauth'"
Issue: Port already in use
Change the port in your code or kill the process using the port:
Issue: 401 error even with correct credentials
Check that:
- Username and password match exactly (case-sensitive)
- No extra spaces in credentials
- Base64 encoding is correct