{"id":3223,"library":"py-vapid","title":"Simple VAPID Header Generation Library","description":"py-vapid is a Python library designed for generating VAPID (Voluntary Application Server Identification) headers, essential for authenticating Web Push notifications. It allows for the creation of VAPID key sets and the signing of JWT claims to identify the push service sender. As of its current version 1.9.4, the library is actively maintained with regular updates.","status":"active","version":"1.9.4","language":"en","source_language":"en","source_url":"https://github.com/mozilla-services/vapid","tags":["web push","vapid","security","jwt","cryptography"],"install":[{"cmd":"pip install py-vapid","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Used for cryptographic operations, including key generation and JWT signing.","package":"cryptography","optional":false}],"imports":[{"symbol":"Vapid01","correct":"from py_vapid import Vapid01"}],"quickstart":{"code":"import os\nimport time\nfrom py_vapid import Vapid01\n\n# --- 1. Generate VAPID keys (if you don't have them) ---\n# In a real application, you would generate these once and store them securely.\n# For this example, we'll generate them in memory.\n\nvapid_instance = Vapid01()\nvapid_instance.generate_keys()\n\nprivate_key_pem = vapid_instance.private_key.private_bytes(\n    encoding=os.environ.get('VAPID_ENCODING', 'PEM').encode('utf-8'),\n    format=os.environ.get('VAPID_FORMAT', 'PKCS8').encode('utf-8'),\n    encryption_algorithm=os.environ.get('VAPID_ENCRYPTION_ALG', 'NoEncryption').encode('utf-8')\n).decode('utf-8')\n\npublic_key_pem = vapid_instance.public_key.public_bytes(\n    encoding=os.environ.get('VAPID_ENCODING', 'PEM').encode('utf-8'),\n    format=os.environ.get('VAPID_FORMAT', 'SubjectPublicKeyInfo').encode('utf-8')\n).decode('utf-8')\n\nprint(\"Generated Private Key (PEM format):\\n\", private_key_pem)\nprint(\"Generated Public Key (PEM format):\\n\", public_key_pem)\n\n# --- 2. Load keys and sign claims ---\n# In a real app, you'd load private_key_pem from a secure store.\n\nvapid = Vapid01.from_pem(private_key_pem.encode('utf-8'))\n\n# Define claims required for VAPID\n# 'sub' is the email address of the sender, must be prefixed with 'mailto:'\n# 'aud' is the audience (the push service endpoint's origin)\n# 'exp' is the expiration time (Unix timestamp), max 24 hours. If omitted, py-vapid sets it to 24 hours.\nclaims = {\n    \"sub\": os.environ.get('VAPID_CONTACT', 'mailto:webpush@example.com'),\n    \"aud\": os.environ.get('VAPID_AUDIENCE', 'https://fcm.googleapis.com'), # Example push service audience\n    \"exp\": int(time.time()) + 12 * 60 * 60 # Expires in 12 hours\n}\n\n# Sign the claims to generate VAPID headers\nvapid_headers = vapid.sign(claims)\n\nprint(\"\\nGenerated VAPID Headers:\")\nfor header, value in vapid_headers.items():\n    print(f\"  {header}: {value}\")\n\n# The 'Authorization' header contains the JWT, and 'Crypto-Key' contains the public key.\n# These headers are then sent with your Web Push request.","lang":"python","description":"This quickstart demonstrates how to generate a VAPID key pair using `Vapid01`, and then use the private key to sign a set of VAPID claims, producing the necessary `Authorization` and `Crypto-Key` HTTP headers for Web Push notifications. Keys are generated in memory for demonstration, but in a production environment, they should be securely stored and loaded. Essential claims like `sub` (sender email) and `aud` (push service audience) are highlighted."},"warnings":[{"fix":"Ensure that your VAPID claims dictionary always includes an 'aud' key with the appropriate push service endpoint origin (e.g., `https://fcm.googleapis.com` or `https://updates.push.services.mozilla.com`).","message":"Version 1.2.0 introduced a breaking change requiring the `aud` (audience) parameter in VAPID declarations. Failing to provide this will result in errors for older codebases.","severity":"breaking","affected_versions":"< 1.2.0"},{"fix":"Always prepend `mailto:` to the email address when setting the `sub` claim. Example: `{'sub': 'mailto:developer@example.com'}`.","message":"The `sub` claim (sender contact information) must be a `mailto:` URI, e.g., `mailto:your@email.com`. Providing just an email address without the `mailto:` prefix will result in an invalid VAPID header.","severity":"gotcha","affected_versions":"All"},{"fix":"Explicitly set the `exp` claim to a Unix timestamp within 24 hours from the current time, preferably shorter, for enhanced security. For example: `int(time.time()) + 12 * 60 * 60` for 12 hours.","message":"The `exp` (expiration timestamp) claim has a maximum validity period of 24 hours. While the library will auto-generate one for 24 hours if omitted, shorter expiration times (e.g., 12 hours or less) are recommended to mitigate replay attacks and should be explicitly managed for better security.","severity":"gotcha","affected_versions":"All"},{"fix":"Utilize the built-in `Vapid01.from_pem()`, `Vapid01.from_der()`, or `Vapid01.from_raw()` methods for loading keys from various formats, as they handle the underlying cryptographic library's requirements. When encoding/decoding, ensure consistent handling of base64 padding or use utility functions that specifically handle URL-safe encoding without padding.","message":"When manually handling VAPID keys, especially converting between raw bytes and base64-URL-safe encoded strings, be aware of padding. Base64-URL-safe encoding typically removes `=` padding, which might need to be re-added or handled appropriately if you are decoding a string that expects padding.","severity":"gotcha","affected_versions":"All"}],"env_vars":null,"last_verified":"2026-04-11T00:00:00.000Z","next_check":"2026-07-10T00:00:00.000Z"}