PyMacaroons
PyMacaroons is a Python implementation of Macaroons, a form of bearer credential similar to cookies but with embedded caveats defining authorization requirements. It is currently at version 0.13.0 and is described as stable with infrequent changes.
Warnings
- gotcha The `verify()` and `deserialize()` methods can raise general exceptions, which might make specific error handling challenging without inspecting the exception message or type.
- deprecated There is an open issue to 'Remove python2 support', indicating that future major versions will likely drop compatibility with Python 2. While 0.13.0 currently supports Python 2 and 3, users should plan to migrate to Python 3.
- gotcha Incorrectly handling or storing signing keys (e.g., hardcoding, using weak keys, or exposing them) compromises the security of your Macaroons. The `key` parameter for `Macaroon` and the `get_key_for_identifier` callback for `Verifier` are critical security points.
- gotcha The `Verifier`'s API, particularly for satisfying caveats, can be ambiguous if not carefully constructed. Misinterpreting how `satisfy_exact`, `satisfy_predicates`, and `satisfy_third_party` interact can lead to incorrect authorization logic.
- deprecated There are open issues regarding 'Remove v1 support' and 'Make v2 the default binary serialization', which suggests that Macaroon V1 serialization might be deprecated or removed in future releases, with V2 becoming standard.
Install
-
pip install pymacaroons
Imports
- Macaroon
from pymacaroons import Macaroon
- Verifier
from pymacaroons import Verifier
Quickstart
from pymacaroons import Macaroon, Verifier
# Keys for signing macaroons are associated with some identifier for later
# verification. This could be stored in a database, key-value store, memory, etc.
keys = {
'key-for-bob': 'asdfasdfas-a-very-secret-signing-key'
}
# Construct a Macaroon. The location and identifier will be visible after
# construction, and identify which service and key to use to verify it.
m = Macaroon(
location='cool-picture-service.example.com',
identifier='key-for-bob',
key=keys['key-for-bob']
)
# Add a caveat for the target service
m.add_first_party_caveat('picture_id = bobs_cool_cat.jpg')
# Serialize for transport in a cookie, url, OAuth token, etc
serialized_macaroon = m.serialize()
print(f"Serialized Macaroon: {serialized_macaroon}")
# --- Verification Process ---
# A Verifier needs a callback to lookup keys and discharge caveats.
def get_key_for_identifier(identifier):
return keys.get(identifier)
def verify_caveat(caveat):
if caveat == 'picture_id = bobs_cool_cat.jpg':
# In a real application, you'd check against your actual data/context
return True
return False
# Deserialize the macaroon on the receiving service
d = Macaroon.deserialize(serialized_macaroon)
# Create a Verifier instance and register the key lookup and caveat verification callbacks
v = Verifier()
v.satisfy_exact('picture_id = bobs_cool_cat.jpg') # Satisfy first-party caveats directly
v.satisfy_third_party(lambda c: True) # Example: satisfy third-party caveats (not used in this example)
try:
# Verify the macaroon
verified = v.verify(d, get_key_for_identifier)
print(f"Macaroon verified: {verified}")
except Exception as e:
print(f"Macaroon verification failed: {e}")