Registry / auth-security / mohawk

mohawk

JSON →
library1.1.0pypypi✓ verified 22d ago

Mohawk is an alternate Python implementation of the Hawk HTTP authorization scheme. Hawk allows two parties to securely communicate with each other using messages signed by a shared key. It is based on HTTP MAC access authentication (which was derived from parts of OAuth 1.0). The library's API was designed to be intuitive, less prone to security problems, and more Pythonic compared to other implementations. The current version is 1.1.0, with the last major release in late 2019, suggesting a stable, mature library.

pip install mohawk
INSTALL
IMPORT
SIG · MOHAWK
M
mohawk
auth-securitypythonv1.1.0
Install
1.5s avg
Import
82ms
Disk
16MB
Pass rate
10/ 10
Env Coverage10 / 10
glibc
3.93.13
musl
3.93.13
Install & Compatibility
Where this runs
tested against v1.1.0 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
musl
py 3.103.95 runs
installs and imports cleanly · install 0.0s · import 0.086s · 18.1MB
glibc
py 3.103.95 runs
installs and imports cleanly · install 1.5s · import 0.078s · 19MB
16MB installed
● package 16MB
Code
Verified usage

Verified import paths — ran on the pinned version, not inferred.

Sender
from mohawk import Sender
Receiver
from mohawk import Receiver

This quickstart demonstrates a basic Hawk authentication flow using `mohawk.Sender` to generate an authenticated request and `mohawk.Receiver` to verify it. It simulates the HTTP request and response headers and body. Note that `lookup_credentials` and `seen_nonce` callbacks are simplified for demonstration; a production application would integrate these with a secure credential store and a persistent, atomic nonce-checking mechanism to prevent replay attacks.

import os from mohawk import Sender, Receiver from mohawk.exc import HawkAuthenticateError # --- Shared Credentials (typically stored securely) --- credentials = { 'id': os.environ.get('HAWK_ID', 'some-id'), 'key': os.environ.get('HAWK_KEY', 'a super secret key'), 'algorithm': 'sha256' } url = 'http://example.com/resource' method = 'POST' content = b'this is some test content' content_type = 'text/plain' # --- Sender (Client-side) --- def make_hawk_request(credentials, url, method, content, content_type): sender = Sender( credentials, url, method, content=content, content_type=content_type ) headers = { 'Authorization': sender.request_header, 'Content-Type': content_type } print(f"\nSender generated Authorization header: {sender.request_header}") # In a real application, you would send this via requests.post(url, headers=headers, data=content) return headers, content # --- Receiver (Server-side) --- def receive_hawk_request(credentials, request_headers, request_content, url, method): try: # The `lookup_credentials` and `seen_nonce` are application-specific callbacks # For this example, we'll use simple in-memory functions. def lookup_credentials(sender_id): if sender_id == credentials['id']: return credentials return None # In a real app, this would check a database/cache for replay attacks processed_nonces = set() def seen_nonce(sender_id, nonce, timestamp): # A simple, insecure example. DO NOT USE IN PRODUCTION. # Real implementation needs a persistent, shared, and atomic store. key = f"{sender_id}:{nonce}:{timestamp}" if key in processed_nonces: return True processed_nonces.add(key) return False receiver = Receiver( lookup_credentials, # Callback to retrieve credentials request_headers['Authorization'], # Incoming Authorization header url, # Request URL method, # Request method content=request_content, # Request body content_type=request_headers['Content-Type'], # Request Content-Type seen_nonce=seen_nonce, # Callback to check for replay attacks ) print("\nReceiver: Hawk authentication successful!") print(f"Sender ID: {receiver.credentials['id']}") print(f"Ext data: {receiver.ext}") # Optionally, the receiver can sign its response response_content = b'response from server' response_content_type = 'text/plain' receiver.respond( content=response_content, content_type=response_content_type ) print(f"Receiver generated Server-Authorization header: {receiver.response_header}") return True except HawkAuthenticateError as e: print(f"\nReceiver: Hawk authentication failed: {e}") return False # --- Simulate a request-response cycle --- request_headers, request_body = make_hawk_request(credentials, url, method, content, content_type) # Simulate server receiving and processing the request success = receive_hawk_request(credentials, request_headers, request_body, url, method) if success: print("End-to-end Hawk flow demonstrated successfully.") else: print("Hawk flow failed.")
mohawk --version
Debug
Known issues
breakingIn version 1.0.0, escape characters (like backslash) in Hawk header values are no longer permitted. Clients relying on this behavior might break.
fix
Ensure that Hawk header values are correctly encoded and do not contain disallowed escape characters. Review the Hawk specification for valid header content.
affects: >=1.0.0
breakingAs of version 1.0.0, failing to provide `content` and `content_type` arguments to `mohawk.Receiver` or `mohawk.Sender.accept_response()` without explicitly setting `accept_untrusted_content=True` will now raise `mohawk.exc.MissingContent` instead of `ValueError`.
fix
Always provide `content` and `content_type` when there is content, or explicitly set `accept_untrusted_content=True` if content hashing is intentionally skipped. Handle `mohawk.exc.MissingContent` if relevant.
affects: >=1.0.0
breakingIn version 0.3.0, the signature for the `seen_nonce()` callback changed from `(nonce, timestamp)` to `(sender_id, nonce, timestamp)`.
fix
Update your `seen_nonce` callback function to accept `sender_id` as the first argument, e.g., `def seen_nonce(sender_id, nonce, timestamp):`.
affects: >=0.3.0
gotchaMohawk does not provide a default implementation for checking nonces, which is critical for preventing replay attacks. Your application *must* implement and provide a `seen_nonce` callback.
fix
Implement a `seen_nonce(sender_id, nonce, timestamp)` callable that checks a persistent, atomic store (e.g., database, Redis) to determine if a nonce for a given sender and timestamp has already been processed. Return `True` if seen, `False` otherwise.
affects: All versions
gotchaAccurate clock synchronization between sender and receiver servers is crucial. Timestamp discrepancies can lead to `mohawk.exc.TokenExpired` exceptions.
fix
Ensure all servers involved in Hawk communication have their clocks synchronized using a reliable service (e.g., NTP, TLSdate). Hawk provides mechanisms for senders to adjust timestamps, but proper server clock sync is foundational.
affects: All versions
gotchaBy default, Mohawk enforces content hashing. If you explicitly skip content hashing (e.g., by setting `always_hash_content=False` or `accept_untrusted_content=True`), your application could be susceptible to content tampering if not handled with extreme care.
fix
Only disable content hashing if you fully understand the security implications and have alternative integrity checks in place. For most use cases, content hashing should remain enabled to prevent tampering.
affects: All versions
Errors
Common errors & fixes
mohawk.exc.TokenExpired: token with UTC timestamp...has expired...
This error indicates that the timestamp in the Hawk authorization header of an incoming request has expired, often due to a significant clock difference between the client and the server, or a delayed request.
fix
Ensure that the server and client clocks are synchronized (e.g., using NTP or TLSdate). The receiver should respond with a `WWW-Authenticate` header including the server's current timestamp and MAC, allowing compliant clients to adjust their clocks and retry the request.
mohawk.exc.MacMismatch
The Message Authentication Code (MAC) calculated by the receiver does not match the MAC provided in the Hawk authorization header, meaning the request has either been tampered with or was incorrectly signed by the sender.
fix
Double-check that both the sender and receiver are using the exact same credentials (ID, key, algorithm) and that all components of the request (method, URL, headers, content, content-type) are correctly included in the MAC calculation. Setting `mohawk` logging to DEBUG can provide more detailed information for debugging.
mohawk.exc.AlreadyProcessed
This exception is raised when the nonce (a unique identifier for a request) has already been encountered by the receiver within the valid timestamp window, indicating a potential replay attack or an accidental duplicate request.
fix
Implement a robust `seen_nonce` callback function that persistently stores and checks nonces to prevent replay attacks. This function should return `True` if the `(sender_id, nonce, timestamp)` combination has been seen recently.
ModuleNotFoundError: No module named 'mohawk'
The `mohawk` library is not installed in the Python environment where the code is being executed, or the environment is not correctly configured.
fix
Install the `mohawk` library using pip: `pip install mohawk`.
Upgrade
Version history
1.1.0latest on PyPI · released Oct 28, 2019
Audit
Dependencies
sixrequiredCompatibility layer for Python 2 and 3, noted as a requirement in older documentation for Python 2.7+ or 3.4+. While often implicitly handled, it's a foundational dependency.
Agent activity
15 hits · last 30 days
node
12
OpenAI (training)
1
Resources
mohawk — pip install mohawk · libregistry