Last active
June 7, 2024 03:39
-
-
Save kbagher/904e3ccf8fe6a196bdd219a12f8e5fcd to your computer and use it in GitHub Desktop.
This Flask code authenticates API requests using HMAC SHA256 signatures. The API headers include an apiKey and signature. The api_key_required decorator function checks the API key and signature for specified endpoints. An example POST endpoint is included.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This code implements API key and signature authentication for | |
Flask API endpoints using HMAC SHA256 signatures. | |
The API headers require two parameters, `apiKey` and `signature`, | |
which are used to validate the request sender's identity and ensure | |
the integrity of the data being transmitted. | |
The `api_key_required` decorator function checks whether the requested | |
endpoint is in the `ENDPOINTS` list and performs the API key and | |
signature authentication check only for those endpoints. | |
The signature is calculated using the provided hashing key (the `SECRET_KEY`) | |
and the value, which is defined as the endpoint path and total parameters | |
(query string concatenated with the request body). | |
This code also includes an example POST endpoint (`/endpoint1`) that | |
demonstrates how to include the request body in the signature | |
calculation and retrieve it using Flask's `request.get_json()` method. | |
""" | |
from flask import Flask, request, jsonify | |
import hashlib | |
import hmac | |
import urllib.parse | |
app = Flask(__name__) | |
# Define the API key and secret key | |
API_KEY = "your_api_key" | |
SECRET_KEY = "your_secret_key" | |
# Define the endpoints that require API key and signature | |
ENDPOINTS = ['/endpoint1', '/endpoint2', '/endpoint3'] | |
# Define the decorator function to check API key and signature | |
def api_key_required(func): | |
def wrapper(*args, **kwargs): | |
# Check if the API key is valid | |
if request.headers.get('apiKey') != API_KEY: | |
return jsonify({'error': 'Invalid API key'}) | |
# Check if the signature is valid | |
signature = request.headers.get('signature') | |
if not signature: | |
return jsonify({'error': 'Missing signature'}) | |
# Generate the expected signature | |
endpoint = request.path | |
total_params = urllib.parse.unquote(request.query_string.decode()) | |
if request.method == 'POST': | |
total_params += request.get_data().decode() | |
value = endpoint + '?' + total_params if total_params else endpoint | |
expected_signature = hmac.new(SECRET_KEY.encode(), value.encode(), hashlib.sha256).hexdigest() | |
# Compare the signatures | |
if not hmac.compare_digest(signature, expected_signature): | |
return jsonify({'error': 'Invalid signature'}) | |
# Call the original function | |
return func(*args, **kwargs) | |
return wrapper | |
# Define the API endpoints | |
@app.route('/endpoint1', methods=['POST']) | |
@api_key_required | |
def endpoint1(): | |
data = request.get_json() | |
return jsonify({'message': 'Hello from endpoint1!', 'data': data}) | |
@app.route('/endpoint2') | |
@api_key_required | |
def endpoint2(): | |
return jsonify({'message': 'Hello from endpoint2!'}) | |
@app.route('/endpoint3') | |
@api_key_required | |
def endpoint3(): | |
return jsonify({'message': 'Hello from endpoint3!'}) | |
if __name__ == '__main__': | |
app.run() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Flask==2.1.0 | |
hashlib | |
hmac | |
urllib3==1.26.6 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello, This code snippet help a lot! Thank you.