pyramid_jwt2
JWT authentication security policy for Pyramid 2.0+.
This package provides a modern, type-safe JWT authentication implementation for Pyramid web framework, an alternative to the existing pyramid_jwt for users using Pyramid 2.0's unified security policy.
Features
- ✅ Pyramid 2.0+ unified security policy (no deprecated auth/authz split)
- ✅ JWT token creation and validation using PyJWT
- ✅ ACL-based permissions support
- ✅ Custom validation callbacks
- ✅ Bearer token authentication
- ✅ Fully typed (PEP 561 compatible)
Installation
pip install pyramid-jwt2
Usage
Basic Configuration
from pyramid.config import Configurator
from pyramid_jwt2 import set_jwt_authentication_policy
def load_user(userid, request):
"""Load user from database."""
return request.db.query(User).filter(User.id == userid).first()
def main(global_config, **settings):
config = Configurator(settings=settings)
# Configure JWT authentication
set_jwt_authentication_policy(
config,
secret=settings['jwt.secret'],
user_loader=load_user, # Required: load user from DB
auth_type='Bearer', # Authorization: Bearer <token>
)
return config.make_wsgi_app()
Creating Tokens
from pyramid.view import view_config
@view_config(route_name='login', request_method='POST', renderer='json')
def login(request):
# Validate credentials...
user_id = "12345"
# Create JWT token
token = request.create_jwt_token(
user_id,
expiration=timedelta(hours=48)
)
return {'token': token}
Protected Views
from pyramid.view import view_config
@view_config(
route_name='protected',
request_method='GET',
renderer='json',
permission='authenticated'
)
def protected_view(request):
user_id = request.authenticated_userid
return {'user_id': user_id}
Custom Token Validation
Implement custom token validation (e.g., invalidate token if it was issued before the last user logout, invalidate token if it was issued before password changed, etc.):
def load_user(userid: str, request: Request) -> User | None:
"""Load user from database."""
return request.db.query(User).filter(User.id == userid).first()
def validate_token(user: User, request: Request) -> bool:
"""
Validate token - receives user object from user_loader.
Return True if token is valid, False if invalid.
"""
if user is None:
return False # User not found
# Check if token was issued before user logged out
if user.logged_out:
issued_at = datetime.fromtimestamp(request.jwt_claims['iat'])
if issued_at < user.logged_out:
return False # Token invalidated by logout
return True # Valid token
# Configure with user_loader and validation
set_jwt_authentication_policy(
config,
secret=settings['jwt.secret'],
user_loader=load_user,
custom_token_validation=validate_token,
)
ACL Permissions
Use with Pyramid's ACL system:
from pyramid.authorization import Allow, Authenticated
class RootFactory:
def __init__(self, request):
self.request = request
@property
def __acl__(self):
if self.request.identity:
# Grant permissions using Authenticated principal
return [
(Allow, Authenticated, 'view'),
(Allow, Authenticated, 'edit'),
]
return []
config.set_root_factory(RootFactory)
For user-specific permissions, the userid is also available as a principal:
# Allow document owner to delete
class DocumentContext:
def __init__(self, document_id, request):
self.document = get_document(document_id)
@property
def __acl__(self):
return [
(Allow, Authenticated, 'view'),
(Allow, self.document.owner_id, 'delete'), # Only owner can delete
]
Additional Principals (Role-Based Access)
Add custom principals like roles for cleaner ACLs:
def load_user(userid, request):
return request.db.query(User).filter(User.id == userid).first()
def add_role_principals(request, user: User) -> set[str]:
"""Add role-based principals."""
principals = set()
if user and user.role:
principals.add(f"role:{user.role}") # e.g., "role:admin"
return principals
# Configure with principals callback
set_jwt_authentication_policy(
config,
secret=settings['jwt.secret'],
user_loader=load_user,
additional_principals=add_role_principals,
)
# Now use clean role-based ACLs
class RootFactory:
@property
def __acl__(self):
return [
(Allow, 'role:admin', ALL_PERMISSIONS),
(Allow, 'role:editor', 'edit'),
(Allow, Authenticated, 'view'),
]
Configuration Options
set_jwt_authentication_policy(config, secret, user_loader, **options)
secret(str, required): Secret key for signing/verifying JWTsuser_loader(callable, required): Function(userid, request) -> user object | None- Loads user from database once and caches it
- Return
Noneif user doesn't exist (auth fails)
algorithm(str, optional): JWT algorithm (default:"HS512")auth_type(str, optional): Authorization header type (default:"Bearer")custom_token_validation(callable, optional): Function(user, request) -> bool- Return
Trueif token is valid,Falseif invalid - Receives user object from user_loader
- Use for logout timestamps, banned users, etc.
- Return
additional_principals(callable, optional): Function(request, user) -> set[str]- Returns additional principals to add (e.g.,
{"role:admin"}) - Enables clean role-based ACLs
- Returns additional principals to add (e.g.,
API Reference
Request Methods
After configuration, these methods are available on the request object:
request.create_jwt_token(userid, expiration=None, **claims): Create a JWT tokenrequest.jwt_claims_from_token(token): Decode and validate an arbitrary token, returning its claims. Raisesjwt.InvalidTokenErrorif the token is invalid or expiredrequest.authenticated_userid: Get the authenticated user IDrequest.jwt_claims: Access decoded JWT claims. Only populated afterrequest.identityhas been resolved and the token decoded successfullyrequest.identity: Get identity dict with:userid: User ID from tokenclaims: Decoded JWT claimsuser: User object from user_loader (cached, no redundant DB queries)
Stateless Authentication
JWT is stateless, meaning authentication doesn't use cookies or sessions. Therefore:
remember()raisesNotImplementedError- usecreate_jwt_token()insteadforget()raisesNotImplementedError- handle logout viacustom_token_validation
Requirements
- Python 3.11+
- Pyramid 2.0+
- PyJWT 2.0+
Development
This project uses uv and ruff.
uv sync --dev # install dependencies into .venv/
make check # ruff lint + format check
make unit # run tests with coverage
make tests # check + unit (run before every commit)
make build # build sdist + wheel into dist/
See RELEASE.md for the release process.
License
MIT
Metadata
Release files for pyramid-jwt2 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pyramid_jwt2-1.0.1.tar.gz | 40.9 kB | With provenance | Details for pyramid_jwt2-1.0.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pyramid_jwt2-1.0.1-py3-none-any.whl | Python 3 | none | any | Details for pyramid_jwt2-1.0.1-py3-none-any.whl |
Total release size: 48.4 kB
Release files / pyramid_jwt2-1.0.1.tar.gz
| Download URL | pyramid_jwt2-1.0.1.tar.gz |
|---|---|
| Size | 40.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
057cbf9643b66461f5f98cdcc729c748c389856aac5ea4d1caa1429c4a88917c
|
|
BLAKE2b-256 checksum How to use checksums |
c594e56888c88592979ef1b1589b88d08fef996bd2ced170f202afdb2bd1d156
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 16, 2026.
Transparency logRelease files / pyramid_jwt2-1.0.1-py3-none-any.whl
| Download URL | pyramid_jwt2-1.0.1-py3-none-any.whl |
|---|---|
| Size | 7.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
307d26e1b9aca27879ead9d8cef7c90728c42cb02f1ab4797972224b236a466c
|
|
BLAKE2b-256 checksum How to use checksums |
7e304811511c2fbd65cdc448201990a66693c2195841b0fab8f7e7af976da440
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 16, 2026.
Transparency logSecurity for pyramid-jwt2 1.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for pyramid_jwt2-1.0.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 16, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.