Sanic JWT
Sanic-JWT provides a JWT (JSON Web Token) authentication flow for the Sanic web framework. It simplifies the process of user authentication, token generation, and securing endpoints. The library is currently at version 1.8.0, with releases focused on compatibility with newer Sanic and PyJWT versions, and feature enhancements.
Common errors
-
AttributeError: 'Request' object has no attribute 'query_args'
cause Your custom authentication logic or an older version of sanic-jwt is attempting to access `request.query_args`, which has been removed in newer Sanic versions.fixUpdate your code to use `request.args` instead of `request.query_args`. Ensure your `sanic-jwt` version is 1.3.1 or newer. -
jwt.exceptions.InvalidSignatureError: Signature verification failed
cause The JWT signature does not match the expected signature, likely due to a different secret key being used for signing than for verification, or the token being tampered with.fixVerify that `app.config.SANIC_JWT_SECRET` in your Sanic application exactly matches the secret key used to sign the JWT. Check for typos or environment variable mismatches. -
jwt.exceptions.InvalidKeyError: The specified key is not valid for the current algorithm
cause The secret key provided for JWT operations is not valid for the configured algorithm (e.g., using a simple string with an RSA algorithm, or incorrect key format).fixEnsure your `SANIC_JWT_SECRET` is correctly formatted for the chosen algorithm. For symmetric algorithms (like HS256), a strong string or byte string is usually sufficient. For asymmetric (RSA, ECDSA), ensure you're providing correct private/public key pairs. -
TypeError: 'NoneType' object has no attribute 'get' (or similar errors related to configuration)
cause The `SANIC_JWT_SECRET` or other critical configuration settings for `sanic-jwt` are not properly defined or are `None`, leading to runtime errors when they are accessed.fixEnsure `app.config.SANIC_JWT_SECRET` (and any other necessary `SANIC_JWT_` settings) are explicitly set to a non-`None` value before `SanicJWT.setup()` is called.
Warnings
- breaking Upgrading `sanic-jwt` to v1.6.0 or newer introduces PyJWT v2 support. If your application or custom JWT handling code directly interacts with PyJWT, you may encounter breaking changes related to API updates, especially regarding `jwt.encode()` parameters (e.g., `algorithm='none'` deprecation) and bytes vs string handling.
- breaking Versions of `sanic-jwt` prior to v1.7.0 may not be fully compatible with Sanic versions 21.3 and newer. This can lead to unexpected errors or authentication failures due to internal API changes in Sanic.
- deprecated The `request.query_args` attribute was deprecated and eventually removed from Sanic. Using it in your `authenticate` function or other custom logic will cause `AttributeError` on newer Sanic versions. `sanic-jwt` itself adjusted to use `request.args` in v1.3.1.
- gotcha Since `sanic-jwt` v1.2.0, requests with invalid tokens (e.g., malformed, expired, invalid signature) will return an HTTP 401 Unauthorized status code instead of 403 Forbidden. This might require adjustments to client-side error handling logic.
Install
-
pip install sanic-jwt
Imports
- SanicJWT
from sanic_jwt import SanicJWT
- protected
from sanic_jwt.decorators import protected
from sanic_jwt import protected
- Configuration
from sanic_jwt import Configuration
Quickstart
from sanic import Sanic, response
from sanic_jwt import SanicJWT, protected
import os
app = Sanic("my_jwt_app")
# Set a secret key for JWT signing. Crucial for security.
app.config.SANIC_JWT_SECRET = os.environ.get("SANIC_JWT_SECRET", "your-super-secret-key-that-no-one-knows")
# Define an asynchronous authentication function.
# This function handles both token verification (payload present) and user login (payload None).
async def authenticate(request, payload):
if payload: # Token verification for protected routes
# In a real app, you'd fetch user data from a DB based on payload (e.g., user_id)
user_id = payload.get("user_id")
if user_id:
return {"user_id": user_id, "username": payload.get("username", "user")} # Return user info for ctx
return False
else: # User login attempt for the /auth endpoint
# Expect username/password in request.json
username = request.json.get("username")
password = request.json.get("password")
if username == "test" and password == "test": # Dummy check
return {"user_id": 1, "username": "testuser"} # Return user info to be included in JWT payload
return False # Authentication failed
# Initialize Sanic-JWT with the app and your custom authentication function.
SanicJWT.setup(app, authenticate=authenticate)
@app.route("/protected")
@protected()
async def protected_route(request):
# Access user data via request.ctx.user after successful authentication
username = request.ctx.user.get('username', 'authenticated user')
return response.json({"message": f"Hello, {username}! This is a protected route."})
@app.get("/public")
async def public_route(request):
return response.json({"message": "This is a public route, accessible without a token."})
if __name__ == "__main__":
# To run:
# 1. Start the app: python your_script_name.py
# 2. Login (obtain token): curl -X POST -H "Content-Type: application/json" -d '{"username":"test","password":"test"}' http://localhost:8000/auth
# 3. Access protected route with token: curl -H "Authorization: Bearer <your_token_here>" http://localhost:8000/protected
app.run(host="0.0.0.0", port=8000, debug=True)