Simple VAPID Header Generation Library
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.
Warnings
- breaking 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.
- gotcha 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.
- gotcha 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.
- gotcha 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.
Install
-
pip install py-vapid
Imports
- Vapid01
from py_vapid import Vapid01
Quickstart
import os
import time
from py_vapid import Vapid01
# --- 1. Generate VAPID keys (if you don't have them) ---
# In a real application, you would generate these once and store them securely.
# For this example, we'll generate them in memory.
vapid_instance = Vapid01()
vapid_instance.generate_keys()
private_key_pem = vapid_instance.private_key.private_bytes(
encoding=os.environ.get('VAPID_ENCODING', 'PEM').encode('utf-8'),
format=os.environ.get('VAPID_FORMAT', 'PKCS8').encode('utf-8'),
encryption_algorithm=os.environ.get('VAPID_ENCRYPTION_ALG', 'NoEncryption').encode('utf-8')
).decode('utf-8')
public_key_pem = vapid_instance.public_key.public_bytes(
encoding=os.environ.get('VAPID_ENCODING', 'PEM').encode('utf-8'),
format=os.environ.get('VAPID_FORMAT', 'SubjectPublicKeyInfo').encode('utf-8')
).decode('utf-8')
print("Generated Private Key (PEM format):\n", private_key_pem)
print("Generated Public Key (PEM format):\n", public_key_pem)
# --- 2. Load keys and sign claims ---
# In a real app, you'd load private_key_pem from a secure store.
vapid = Vapid01.from_pem(private_key_pem.encode('utf-8'))
# Define claims required for VAPID
# 'sub' is the email address of the sender, must be prefixed with 'mailto:'
# 'aud' is the audience (the push service endpoint's origin)
# 'exp' is the expiration time (Unix timestamp), max 24 hours. If omitted, py-vapid sets it to 24 hours.
claims = {
"sub": os.environ.get('VAPID_CONTACT', 'mailto:webpush@example.com'),
"aud": os.environ.get('VAPID_AUDIENCE', 'https://fcm.googleapis.com'), # Example push service audience
"exp": int(time.time()) + 12 * 60 * 60 # Expires in 12 hours
}
# Sign the claims to generate VAPID headers
vapid_headers = vapid.sign(claims)
print("\nGenerated VAPID Headers:")
for header, value in vapid_headers.items():
print(f" {header}: {value}")
# The 'Authorization' header contains the JWT, and 'Crypto-Key' contains the public key.
# These headers are then sent with your Web Push request.