Registry / auth-security / py-vapid

py-vapid

JSON →
library1.9.4pypypi✓ verified 26d ago

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.

pip install py-vapid
INSTALL
IMPORT
SIG · PY-VAPID
P
py-vapid
auth-securitypythonv1.9.4
Install
2.4s avg
Import
96ms
Disk
33MB
Pass rate
10/ 10
Env Coverage10 / 10
glibc
3.93.13
musl
3.93.13
Install & Compatibility
Where this runs
tested against v1.9.4 · 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.098s · 35.1MB
glibc
py 3.103.95 runs
installs and imports cleanly · install 2.4s · import 0.094s · 36MB
33MB installed
● package 33MB
Code
Verified usage

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

Vapid01
from py_vapid import Vapid01

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.

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.
Debug
Known issues
breakingVersion 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.
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`).
affects: < 1.2.0
gotchaThe `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.
fix
Always prepend `mailto:` to the email address when setting the `sub` claim. Example: `{'sub': 'mailto:developer@example.com'}`.
affects: All
gotchaThe `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.
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.
affects: All
gotchaWhen 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.
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.
affects: All
Errors
Common errors & fixes
AttributeError: module 'py_vapid' has no attribute 'sign'
This error occurs when attempting to call instance methods like `sign` or `generate_keys` directly on the `py_vapid` module itself, rather than on an instantiated `Vapid` (or `Vapid01`) object.
fix
You need to create an instance of the `Vapid` class first and then call the method on that instance. For example, `from py_vapid import Vapid; vapid_obj = Vapid(); headers = vapid_obj.sign(claims)` or `vapid_obj.generate_keys()`.
binascii.Error: Incorrect padding
This error typically arises when attempting to load a VAPID private key using `from_raw` or `from_der` if the provided key string is not correctly base64 URL-safe encoded or has improper padding, often encountered when keys are copied from sources that don't format them for `py-vapid`'s expected input.
fix
Ensure the private key string is a properly base64 URL-safe encoded value without incorrect padding. If the key is in PEM format, use `Vapid.from_pem()` instead. If it's a raw uncompressed private key, ensure it's correctly base64url-encoded for `from_raw()`.
"Missing 'aud' from claims"
This indicates that the 'aud' (audience) field, which is a required claim for VAPID, is missing or improperly formatted in the claims dictionary provided for signing.
fix
Include a valid 'aud' field in your VAPID claims dictionary. The 'aud' value should be the scheme and host of the push service endpoint, e.g., `{'sub': 'mailto:user@example.com', 'aud': 'https://push.example.com', 'exp': 123456789}`.
ModuleNotFoundError: No module named 'py_vapid'
This error indicates that the `py-vapid` library is not installed in the Python environment being used, or there's an issue with the Python path.
fix
Install the library using pip: `pip install py-vapid`. Ensure you are running this command and your Python script in the same environment.
Upgrade
Version history
1.9.4latest on PyPI · released Jan 5, 2026
Audit
Dependencies
cryptographyrequiredUsed for cryptographic operations, including key generation and JWT signing.
Agent activity
24 hits · last 30 days
node
20
OpenAI (training)
1
Resources
py-vapid — pip install py-vapid · libregistry