Skip to content

Integrating with Python Django API

This guide explains how to implement JWT authentication in your Django REST Framework API using AuthAction’s JWKS (JSON Web Key Set) endpoint.

Example Repository: For a complete working example, check out our example repository.

Before you begin, ensure you have:

  1. Python 3.11+: Download from python.org
  2. AuthAction Account: You’ll need your AuthAction tenant domain and API identifier
Terminal window
pip install django djangorestframework python-jose[cryptography] httpx python-dotenv

Create a .env file in your project root:

Terminal window
AUTHACTION_DOMAIN=your-authaction-tenant-domain
AUTHACTION_AUDIENCE=your-authaction-api-identifier
DJANGO_SECRET_KEY=your-django-secret-key

Create api/authentication.py:

import os
import httpx
from jose import JWTError, jwt
from jose.exceptions import ExpiredSignatureError
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
AUTHACTION_DOMAIN = os.getenv("AUTHACTION_DOMAIN")
AUTHACTION_AUDIENCE = os.getenv("AUTHACTION_AUDIENCE")
_jwks_cache: dict | None = None
def _get_jwks() -> dict:
global _jwks_cache
if _jwks_cache is None:
jwks_uri = f"https://{AUTHACTION_DOMAIN}/.well-known/jwks.json"
response = httpx.get(jwks_uri)
response.raise_for_status()
_jwks_cache = response.json()
return _jwks_cache
def _find_rsa_key(token: str) -> dict:
jwks = _get_jwks()
unverified_header = jwt.get_unverified_header(token)
kid = unverified_header.get("kid")
for key in jwks.get("keys", []):
if key.get("kid") == kid:
return {"kty": key["kty"], "kid": key["kid"],
"use": key["use"], "n": key["n"], "e": key["e"]}
# Key not found — could be a rotation; bust cache and retry once
global _jwks_cache
_jwks_cache = None
jwks = _get_jwks()
for key in jwks.get("keys", []):
if key.get("kid") == kid:
return {"kty": key["kty"], "kid": key["kid"],
"use": key["use"], "n": key["n"], "e": key["e"]}
raise AuthenticationFailed("Unable to find matching public key")
def verify_token(token: str) -> dict:
try:
rsa_key = _find_rsa_key(token)
return jwt.decode(
token, rsa_key,
algorithms=["RS256"],
audience=AUTHACTION_AUDIENCE,
issuer=f"https://{AUTHACTION_DOMAIN}",
)
except ExpiredSignatureError:
raise AuthenticationFailed("Token has expired")
except JWTError as exc:
raise AuthenticationFailed(str(exc))
class AuthenticatedToken:
def __init__(self, payload: dict):
self.payload = payload
self.is_authenticated = True
@property
def sub(self) -> str:
return self.payload.get("sub", "")
class JWTAuthentication(BaseAuthentication):
def authenticate(self, request):
auth_header = request.headers.get("Authorization", "")
if not auth_header.startswith("Bearer "):
return None
token = auth_header.split(" ", 1)[1].strip()
if not token:
raise AuthenticationFailed("Empty token")
payload = verify_token(token)
return AuthenticatedToken(payload), token
def authenticate_header(self, request):
return "Bearer"

In settings.py, set JWTAuthentication as the default so all views are protected unless explicitly overridden:

REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"api.authentication.JWTAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
}
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response
from api.authentication import JWTAuthentication
@api_view(["GET"])
@authentication_classes([])
@permission_classes([AllowAny])
def public_view(request: Request) -> Response:
return Response({"message": "This is a public message!"})
@api_view(["GET"])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAuthenticated])
def protected_view(request: Request) -> Response:
return Response({
"message": "This is a protected message!",
"sub": request.user.sub,
})
  1. Obtain an Access Token:
Terminal window
curl --request POST \
--url https://your-authaction-tenant-domain/oauth2/m2m/token \
--header 'content-type: application/json' \
--data '{
"client_id": "your-authaction-m2m-app-clientid",
"client_secret": "your-authaction-m2m-app-client-secret",
"audience": "your-authaction-api-identifier",
"grant_type": "client_credentials"
}'
  1. Call the Public Endpoint:
Terminal window
curl http://localhost:8000/public
  1. Call the Protected Endpoint:
Terminal window
curl --request GET \
--url http://localhost:8000/protected \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
  • Verify AUTHACTION_DOMAIN and AUTHACTION_AUDIENCE match your AuthAction dashboard exactly
  • Ensure the token is signed with the RS256 algorithm
  • Verify your application can reach https://your-authaction-tenant-domain/.well-known/jwks.json
  • Ensure the Authorization: Bearer <token> header is present and the token is not expired
  • Confirm the token’s audience matches your API identifier