{"library":"mohawk","title":"Mohawk: Hawk HTTP Authorization","description":"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.","language":"python","status":"active","last_verified":"Fri May 15","install":{"commands":["pip install mohawk"],"cli":{"name":"mohawk","version":"sh: 1: mohawk: not found"}},"imports":["from mohawk import Sender","from mohawk import Receiver"],"auth":{"required":false,"env_vars":[]},"quickstart":{"code":"import os\nfrom mohawk import Sender, Receiver\nfrom mohawk.exc import HawkAuthenticateError\n\n# --- Shared Credentials (typically stored securely) ---\ncredentials = {\n    'id': os.environ.get('HAWK_ID', 'some-id'),\n    'key': os.environ.get('HAWK_KEY', 'a super secret key'),\n    'algorithm': 'sha256'\n}\n\nurl = 'http://example.com/resource'\nmethod = 'POST'\ncontent = b'this is some test content'\ncontent_type = 'text/plain'\n\n# --- Sender (Client-side) ---\ndef make_hawk_request(credentials, url, method, content, content_type):\n    sender = Sender(\n        credentials,\n        url,\n        method,\n        content=content,\n        content_type=content_type\n    )\n    \n    headers = {\n        'Authorization': sender.request_header,\n        'Content-Type': content_type\n    }\n    print(f\"\\nSender generated Authorization header: {sender.request_header}\")\n    # In a real application, you would send this via requests.post(url, headers=headers, data=content)\n    return headers, content\n\n# --- Receiver (Server-side) ---\ndef receive_hawk_request(credentials, request_headers, request_content, url, method):\n    try:\n        # The `lookup_credentials` and `seen_nonce` are application-specific callbacks\n        # For this example, we'll use simple in-memory functions.\n        def lookup_credentials(sender_id):\n            if sender_id == credentials['id']:\n                return credentials\n            return None\n\n        # In a real app, this would check a database/cache for replay attacks\n        processed_nonces = set()\n        def seen_nonce(sender_id, nonce, timestamp):\n            # A simple, insecure example. DO NOT USE IN PRODUCTION.\n            # Real implementation needs a persistent, shared, and atomic store.\n            key = f\"{sender_id}:{nonce}:{timestamp}\"\n            if key in processed_nonces:\n                return True\n            processed_nonces.add(key)\n            return False\n\n        receiver = Receiver(\n            lookup_credentials,          # Callback to retrieve credentials\n            request_headers['Authorization'], # Incoming Authorization header\n            url,                         # Request URL\n            method,                      # Request method\n            content=request_content,     # Request body\n            content_type=request_headers['Content-Type'], # Request Content-Type\n            seen_nonce=seen_nonce,       # Callback to check for replay attacks\n        )\n        print(\"\\nReceiver: Hawk authentication successful!\")\n        print(f\"Sender ID: {receiver.credentials['id']}\")\n        print(f\"Ext data: {receiver.ext}\")\n\n        # Optionally, the receiver can sign its response\n        response_content = b'response from server'\n        response_content_type = 'text/plain'\n        receiver.respond(\n            content=response_content,\n            content_type=response_content_type\n        )\n        print(f\"Receiver generated Server-Authorization header: {receiver.response_header}\")\n        return True\n    except HawkAuthenticateError as e:\n        print(f\"\\nReceiver: Hawk authentication failed: {e}\")\n        return False\n\n# --- Simulate a request-response cycle ---\nrequest_headers, request_body = make_hawk_request(credentials, url, method, content, content_type)\n\n# Simulate server receiving and processing the request\nsuccess = receive_hawk_request(credentials, request_headers, request_body, url, method)\n\nif success:\n    print(\"End-to-end Hawk flow demonstrated successfully.\")\nelse:\n    print(\"Hawk flow failed.\")\n","lang":"python","description":"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.","tag":null,"tag_description":null,"last_tested":null,"results":[]},"compatibility":{"tag":null,"tag_description":null,"last_tested":"2026-05-15","installed_version":"1.1.0","pypi_latest":"1.1.0","is_stale":false,"summary":{"python_range":"3.10–3.9","success_rate":100,"avg_install_s":1.5,"avg_import_s":0.08,"wheel_type":"wheel"},"results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"mohawk","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":null,"import_time_s":0.06,"mem_mb":2.8,"disk_size":"18.1M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"mohawk","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":1.5,"import_time_s":0.04,"mem_mb":2.8,"disk_size":"19M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"mohawk","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":null,"import_time_s":0.11,"mem_mb":3.2,"disk_size":"19.9M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"mohawk","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":1.6,"import_time_s":0.09,"mem_mb":3.2,"disk_size":"20M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"mohawk","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":null,"import_time_s":0.1,"mem_mb":3,"disk_size":"11.8M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"mohawk","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":1.4,"import_time_s":0.11,"mem_mb":3,"disk_size":"12M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"mohawk","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":null,"import_time_s":0.11,"mem_mb":3.9,"disk_size":"11.5M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"mohawk","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":1.4,"import_time_s":0.11,"mem_mb":3.9,"disk_size":"12M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"mohawk","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":null,"import_time_s":0.04,"mem_mb":2.4,"disk_size":"17.5M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"mohawk","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":1.7,"import_time_s":0.04,"mem_mb":2.4,"disk_size":"18M"}]}}