SimpleJWT adapter¶
SimpleJWTAuthentication is an async-aware adapter for
djangorestframework-simplejwt.
It reuses simplejwt's token validation and adds an aauthenticate
hook that resolves the user via the async ORM, so simplejwt-based
projects plug cleanly into restflow's AsyncAPIView dispatch loop
without rewriting their issuance pipeline.
When to use the adapter¶
The adapter exists for two scenarios.
- An existing project standardised on simplejwt, with token issuance, claim customisation, blacklisted tokens, and tooling already in place. The adapter avoids rewriting that code while bringing async-aware authentication to restflow async views.
- A project that needs a feature simplejwt has and the built-in does not, such as sliding tokens, the OutstandingToken model, or simplejwt-specific token rotation hooks.
For a new project that does not have those constraints, the built-in JWTAuthentication is the simpler choice. It is async-native, has fewer moving parts, and ships its own pre-built views.
Installation¶
The adapter lives behind the simplejwt extra so projects that do
not need it are not forced to install simplejwt.
Add rest_framework_simplejwt to INSTALLED_APPS if simplejwt's own
apps are required (for example, when using the token_blacklist
app).
INSTALLED_APPS = [
"rest_framework",
"rest_framework_simplejwt",
"restflow.caching",
"restflow.authentication",
]
The adapter raises ImportError at import time when simplejwt is not
installed. The error message names the install command so the failure
mode is obvious.
Difference from the built-in¶
| Concern | Built-in JWTAuthentication | SimpleJWTAuthentication |
|---|---|---|
| Settings block | RESTFLOW_SETTINGS["JWT"] | SIMPLE_JWT |
| Token classes | restflow's AccessToken, RefreshToken | simplejwt's AccessToken, RefreshToken, SlidingToken |
| Sliding tokens | Not supported | Supported |
| OutstandingToken model | Not present | Available via simplejwt's token_blacklist app |
| Pre-built views | TokenObtainView, TokenRefreshView, TokenBlacklistView | simplejwt's TokenObtainPairView, TokenRefreshView, TokenVerifyView, etc. |
| Async user lookup | Native | Added by the adapter |
The two implementations are not interchangeable at the token level. A token issued by simplejwt cannot be verified by the built-in verifier, and vice versa. Pick one for the lifetime of an unmigrated client population.
Usage¶
The class lives at
restflow.authentication.simplejwt.SimpleJWTAuthentication and is
not re-exported at the package root. Import it from the submodule.
Wire it into a view exactly like any other authenticator.
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from restflow.authentication.simplejwt import SimpleJWTAuthentication
from restflow.views import AsyncAPIView
class ProfileView(AsyncAPIView):
authentication_classes = [SimpleJWTAuthentication]
permission_classes = [IsAuthenticated]
async def get(self, request):
return Response({"id": request.user.id})
For project-wide configuration, set it as the default authenticator.
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"restflow.authentication.simplejwt.SimpleJWTAuthentication",
],
}
Settings¶
The adapter reads simplejwt's own settings under SIMPLE_JWT. The
restflow RESTFLOW_SETTINGS["JWT"] block is ignored when the
adapter is in use.
# settings.py
from datetime import timedelta
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=15),
"REFRESH_TOKEN_LIFETIME": timedelta(days=7),
"ALGORITHM": "HS256",
"SIGNING_KEY": "...",
"USER_ID_CLAIM": "user_id",
"USER_ID_FIELD": "id",
"AUTH_HEADER_TYPES": ("Bearer",),
"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,
}
The full list of settings is documented in the
simplejwt project.
Every key under SIMPLE_JWT works exactly as it does under stock
simplejwt; the adapter only changes the user-lookup path.
Async user lookup¶
Stock simplejwt resolves the user with the synchronous ORM, which forces a thread hop on async views. The adapter swaps that for a native async lookup.
async def aget_user(self, validated_token):
user_model = get_user_model()
user_id = validated_token[_simplejwt_settings.USER_ID_CLAIM]
user = await user_model.objects.aget(
**{_simplejwt_settings.USER_ID_FIELD: user_id},
)
if not user.is_active:
raise AuthenticationFailed("User is inactive.")
return user
The lookup uses simplejwt's USER_ID_FIELD and USER_ID_CLAIM, so a
custom field configuration on simplejwt is honoured automatically.
Mixed deployments¶
The adapter works alongside other authenticators. A common pattern: JWT for API consumers, Session for the browsable API, both on the same view.
class ProfileView(AsyncAPIView):
authentication_classes = [
SimpleJWTAuthentication,
SessionAuthentication,
]
permission_classes = [IsAuthenticated]
A request with a Bearer header takes the JWT path. A browser request with a session cookie takes the session path. Order matters; the list runs top to bottom and the first non-None tuple wins.
Token issuance and refresh¶
For token issuance and refresh, mount simplejwt's own views.
# urls.py
from django.urls import path
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
TokenVerifyView,
)
urlpatterns = [
path("api/token/", TokenObtainPairView.as_view()),
path("api/token/refresh/", TokenRefreshView.as_view()),
path("api/token/verify/", TokenVerifyView.as_view()),
]
For an async-native obtain or refresh, switch to the built-in views
shipped under restflow.authentication. That requires committing to
the built-in token format, which is a breaking change for clients
holding simplejwt-issued tokens.
Sliding tokens and other simplejwt features¶
The adapter inherits from simplejwt's JWTAuthentication, which
supports sliding tokens through TOKEN_TYPE_CLAIM and the related
settings. To use sliding tokens:
- Configure
SIMPLE_JWT["AUTH_TOKEN_CLASSES"]to includerest_framework_simplejwt.tokens.SlidingToken. - Mount
TokenObtainSlidingViewandTokenRefreshSlidingViewon the URL conf. - The adapter validates sliding tokens through the same
get_validated_tokencall.
The built-in restflow JWT sliding tokens is not implemented yet.