sanic-jwt
JWT authentication, refresh tokens, and scope-based permissions for the Sanic async web framework.
Repository Health
Technical Analysis
Sanic JWT is an authentication add-on for the Sanic web framework that wires up JSON Web Token login, verification, and refresh endpoints without requiring the developer to hand-roll the plumbing. A single Initialize(app, authenticate=...) call registers a Blueprint of auth endpoints, wraps configuration in a dedicated Configuration class, and exposes protected and scoped decorators for guarding views and Class-Based Views alike.
Beyond the defaults, it is built to be overridden: authentication logic, refresh-token storage, scope resolution, and payload extension are all pluggable hook points via subclassing Authentication, so teams can swap in their own user store or database lookups instead of being boxed into a specific ORM or auth backend.
What You Get
- An
Initializeclass that registers authenticate/verify/refresh/retrieve_user endpoints as a Sanic Blueprint with one call protectedandscopeddecorators to guard both function-based views and Class-Based Views- A subclassable
Authenticationclass for plugging in custom user lookup, refresh-token storage, and scope resolution - Cookie-based and split-cookie token delivery alongside standard Authorization-header tokens
- Configurable claims (
iss,aud,nbf) and support for HS256 as well as RSA/EC algorithms via the cryptography extra - A
ClaimAPI for registering custom, application-specific JWT claims
Common Use Cases
- Adding login/logout and token-refresh endpoints to a Sanic API without writing JWT boilerplate by hand
- Gating specific routes or entire Blueprints behind authentication with the
protecteddecorator - Implementing role- or permission-based access control on endpoints using
scoped - Issuing tokens via HttpOnly cookies for browser clients instead of exposing them to JavaScript
- Extending the JWT payload with custom claims (tenant ID, permissions, etc.) for downstream services to consume
Under The Hood
Architecture
The library is organized around an Initialize class (sanic_jwt/initialization.py) that walks a table of _EndpointMapping named tuples to register AuthenticateEndpoint, RetrieveUserEndpoint, VerifyEndpoint, and RefreshEndpoint as a Sanic Blueprint, and a parallel table of _Handler entries that map optional hooks (store_refresh_token, add_scopes_to_payload, retrieve_user_secret, etc.) to specific exceptions when a developer forgets to implement one. Cross-cutting behavior — checking a request for a valid token before a view runs — is implemented in decorators.py via an instant_config context manager that stashes per-request configuration overrides into a module-level cache (cache.py) for the duration of the call and clears it afterward, which lets protected/scoped wrap both plain async view functions and HTTPMethodView subclasses with the same code path.
Tech Stack
Python 3.7+ targeting the Sanic async framework, with PyJWT (pyjwt>=2.1.0,<3.0.0) doing the actual token encode/decode and an optional cryptography dependency for RSA/EC-signed tokens. The test suite runs on pytest with pytest-asyncio and sanic_testing, freezegun for time-dependent token-expiry tests, and tox drives a matrix across Python 3.7-3.9 plus a dedicated check environment for flake8, black, isort, and mypy; CI is GitHub Actions (python-package.yml) with a separate python-publish.yml for PyPI releases.
Code Quality
The repository ships an extensive test suite — 48 test files under tests/ covering endpoints, decorators, cookies, custom claims, blueprint composition, and cryptography-backed algorithms — alongside .coveragerc for coverage tracking and a check tox environment enforcing flake8, black (79-char lines), isort, and mypy. Error paths are explicit: unimplemented optional hooks raise dedicated exception classes (e.g. RefreshTokenNotImplemented) rather than failing silently. Development activity has slowed substantially — the health scan flags the project as inactive with its last commit in October 2024 — so quality here reflects a project that was well-tested and disciplined during active development rather than one seeing ongoing maintenance.
API Design
Getting started is a three-step story the README leads with directly: install, Initialize(app, authenticate=my_authenticate), then hit /auth. Extension points are consistent — override methods on Authentication for anything non-default (refresh storage, scopes, extra payload claims) rather than juggling scattered callback parameters. The one design wrinkle is the request-scoped cache module used to thread per-call configuration through decorators; it is cleared immediately after each call and is not itself a source of cross-request bugs, but it is a less conventional pattern than passing context explicitly, and newcomers extending the library have to understand it to follow the control flow.