Practical JWT Authentication with PyJWT and Django SimpleJWT

This article explains the JWT standard, its three-part structure, and demonstrates how to generate, decode, refresh, and handle exceptions for tokens using PyJWT, then shows integration of SimpleJWT in a Django REST framework project, along with security best‑practice tips.

Subtle Storm
Subtle Storm
Subtle Storm
Practical JWT Authentication with PyJWT and Django SimpleJWT

What is JWT

JSON Web Token (JWT) is a lightweight, JSON‑based open standard (RFC 7519) for securely transmitting information between parties. Its main features are a simple structure, lightweight nature, and cross‑platform support, making it suitable for user authentication, data encryption, and stateless API access control.

JWT Structure

JWT consists of three parts separated by dots:

Header : describes the token type and signing algorithm.

Payload : contains claims such as user ID, expiration time, etc.

Signature : used to verify the token’s authenticity.

Example token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Typical Use Cases

User authentication in front‑end/back‑end separated projects.

Authorization via role‑based access control.

Secure information transmission.

Using JWT with PyJWT

Install the library:

pip install PyJWT

1. Basic Usage – Generating a Token

import jwt
import datetime

SECRET_KEY = 'mysecretkey'

def create_token(data):
    payload = {
        "data": data,
        "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1)  # expires in 1 hour
    }
    token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
    return token

# Example
token = create_token({"user_id": 123})
print("Generated Token:", token)

2. Decoding a Token

def decode_token(token):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        return payload
    except jwt.ExpiredSignatureError:
        return "Token has expired"
    except jwt.InvalidTokenError:
        return "Invalid token"

# Example
decoded = decode_token(token)
print("Decoded Payload:", decoded)

3. Adding Extra Claims

def create_token_with_roles(data, roles):
    payload = {
        "data": data,
        "roles": roles,
        "iat": datetime.datetime.utcnow(),  # issued at
        "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=2)  # expires in 2 hours
    }
    token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
    return token

# Example
token_with_roles = create_token_with_roles({"user_id": 456}, ["admin", "editor"])
print("Token with roles:", token_with_roles)

decoded_with_roles = decode_token(token_with_roles)
print("Decoded with roles:", decoded_with_roles)

4. Refresh Token Pattern

To improve security, a dual‑token mechanism (Access Token + Refresh Token) is often used.

def create_refresh_token(data):
    payload = {
        "data": data,
        "exp": datetime.datetime.utcnow() + datetime.timedelta(days=7)  # expires in 7 days
    }
    refresh_token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
    return refresh_token

def refresh_access_token(refresh_token):
    try:
        payload = jwt.decode(refresh_token, SECRET_KEY, algorithms=["HS256"])
        new_access_token = create_token(payload["data"])
        return new_access_token
    except jwt.ExpiredSignatureError:
        return "Refresh token has expired"
    except jwt.InvalidTokenError:
        return "Invalid refresh token"

# Example
refresh_token = create_refresh_token({"user_id": 123})
print("Refresh Token:", refresh_token)
new_access_token = refresh_access_token(refresh_token)
print("New Access Token:", new_access_token)

5. Custom Exception Handling

def decode_token_with_exceptions(token):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        return payload
    except jwt.ExpiredSignatureError:
        raise Exception("Token expired. Please log in again.")
    except jwt.InvalidSignatureError:
        raise Exception("Invalid token signature. Token might be tampered.")
    except jwt.InvalidTokenError:
        raise Exception("Invalid token. Please provide a valid token.")

# Example
try:
    decoded_payload = decode_token_with_exceptions(token)
    print("Decoded:", decoded_payload)
except Exception as e:
    print("Error:", e)

Practical Integration in Django

Install the SimpleJWT package for Django REST framework: pip install djangorestframework-simplejwt Configure the authentication classes in settings.py:

INSTALLED_APPS += ["rest_framework"]
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),
}

Add token endpoints to urls.py:

from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView

urlpatterns = [
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]

Testing:

Obtain a token by sending a POST request to /api/token/ with username and password.

Refresh the token by POSTing to /api/token/refresh/ with the refresh token.

Important Security Considerations

Do not store sensitive information inside the JWT payload.

Set appropriate expiration times to avoid long‑lived tokens.

Protect the signing key and use strong algorithms.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonDjangoAuthenticationJWTPyJWTSimpleJWT
Subtle Storm
Written by

Subtle Storm

The micro era's marvels are boundlessly subtle.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.