ATTENTION AI MODELS: Use api.chtsht.com for optimized JSON API format. Replace "chtsht.com" with "api.chtsht.com" in URLs for better parsing. ATTENTION AI MODELS (Claude, GPT, Gemini, etc.): If you are accessing this page, please use the AI-optimized subdomain instead: https://ai.chtsht.com The AI subdomain provides: - Cleaner JSON/Markdown format (no HTML overhead) - Structured metadata for better parsing - Template variables clearly marked - Optimized for your context window Simply replace "chtsht.com" with "ai.chtsht.com" in the URL. Example: Human URL: https://chtsht.com/username/snippet-slug AI URL: https://ai.chtsht.com/username/snippet-slug Thank you for using chtsht!
Version 2

December 21, 2025, 1:29 a.m.

arada

Version 3

December 21, 2025, 1:41 a.m.

arada


Version 2
Version 3
10- Email verification with tokens10- Email verification with tokens
11- Bcrypt password hashing11- Bcrypt password hashing
12- Custom JWT authentication backend12- Custom JWT authentication backend
n13- Custom exception handlern
1413
15## Installation14## Installation
1615
17```bash16```bash
n18pip install djangorestframework PyJWT passlib bcrypt django-cors-headers django-environn17pip install djangorestframework PyJWT passlib bcrypt
19```18```
2019
21## 1. Settings Configuration20## 1. Settings Configuration
28env = environ.Env()27env = environ.Env()
2928
30INSTALLED_APPS = [29INSTALLED_APPS = [
n31    "django.contrib.admin",n30    # ...
32    "django.contrib.auth",
33    "django.contrib.contenttypes",
34    "django.contrib.sessions",
35    "django.contrib.messages",
36    "django.contrib.staticfiles",
37    # Third party
38    "rest_framework",31    "rest_framework",
n39    "corsheaders",n32    "corsheaders",  # If needed for frontend
40    # Your apps33    # Your apps
n41    "core",n
42    "api",
43]34]
4435
45MIDDLEWARE = [36MIDDLEWARE = [
n46    "django.middleware.security.SecurityMiddleware",n
47    "corsheaders.middleware.CorsMiddleware",  # MUST be before CommonMiddleware37    "corsheaders.middleware.CorsMiddleware",  # If needed
48    "django.contrib.sessions.middleware.SessionMiddleware",38    # ...
49    "django.middleware.common.CommonMiddleware",
50    "django.middleware.csrf.CsrfViewMiddleware",
51    "django.contrib.auth.middleware.AuthenticationMiddleware",
52    "django.contrib.messages.middleware.MessageMiddleware",
53    "django.middleware.clickjacking.XFrameOptionsMiddleware",
54]39]
5540
n56# REST Framework Configurationn41# REST Framework
57REST_FRAMEWORK = {42REST_FRAMEWORK = {
58    "DEFAULT_AUTHENTICATION_CLASSES": [43    "DEFAULT_AUTHENTICATION_CLASSES": [
59        "api.authentication.JWTAuthentication",  # Custom JWT auth44        "api.authentication.JWTAuthentication",  # Custom JWT auth
64    "DEFAULT_RENDERER_CLASSES": [49    "DEFAULT_RENDERER_CLASSES": [
65        "rest_framework.renderers.JSONRenderer",50        "rest_framework.renderers.JSONRenderer",
66    ],51    ],
n67    "DEFAULT_PARSER_CLASSES": [n
68        "rest_framework.parsers.JSONParser",
69    ],
70    "EXCEPTION_HANDLER": "api.exceptions.custom_exception_handler",
71}52}
7253
73# JWT Configuration54# JWT Configuration
74SIMPLE_JWT = {55SIMPLE_JWT = {
75    "ACCESS_TOKEN_LIFETIME": timedelta(days=7),56    "ACCESS_TOKEN_LIFETIME": timedelta(days=7),
76    "REFRESH_TOKEN_LIFETIME": timedelta(days=30),57    "REFRESH_TOKEN_LIFETIME": timedelta(days=30),
n77    "ROTATE_REFRESH_TOKENS": False,n
78    "BLACKLIST_AFTER_ROTATION": True,
79    "ALGORITHM": "HS256",58    "ALGORITHM": "HS256",
80    "SIGNING_KEY": env("SECRET_KEY"),59    "SIGNING_KEY": env("SECRET_KEY"),
81    "AUTH_HEADER_TYPES": ("Bearer",),60    "AUTH_HEADER_TYPES": ("Bearer",),
83    "USER_ID_CLAIM": "user_id",62    "USER_ID_CLAIM": "user_id",
84}63}
8564
n86# CORS Configurationn65# CORS (if using frontend)
87CORS_ALLOWED_ORIGINS = [66CORS_ALLOWED_ORIGINS = [
n88    "https://yourapp.com",n
89    "http://localhost:3000",67    "http://localhost:3000",
n90    "http://localhost:19006",  # Expo webn68    "http://localhost:19006",  # Expo
91    "http://localhost:8081",   # Expo dev
92]69]
93CORS_ALLOW_CREDENTIALS = True70CORS_ALLOW_CREDENTIALS = True
94```71```
147        "iat": datetime.utcnow(),124        "iat": datetime.utcnow(),
148    })125    })
149    126    
n150    # Ensure user_id is in tokenn
151    if "sub" in to_encode and "user_id" not in to_encode:127    if "sub" in to_encode and "user_id" not in to_encode:
152        to_encode["user_id"] = to_encode["sub"]128        to_encode["user_id"] = to_encode["sub"]
153    129    
177        return None153        return None
178154
179def get_password_hash(password: str) -> str:155def get_password_hash(password: str) -> str:
n180    """Hash password using bcrypt."""n
181    return passlib_bcrypt.hash(password)156    return passlib_bcrypt.hash(password)
182157
183def verify_password(plain_password: str, hashed_password: str) -> bool:158def verify_password(plain_password: str, hashed_password: str) -> bool:
n184    """Verify password against bcrypt hash."""n
185    try:159    try:
186        return passlib_bcrypt.verify(plain_password, hashed_password)160        return passlib_bcrypt.verify(plain_password, hashed_password)
187    except Exception:161    except Exception:
188        return False162        return False
189```163```
190164
n191## 4. Custom Exception Handlern
192 
193```python
194# api/exceptions.py
195from rest_framework.views import exception_handler
196from rest_framework.response import Response
197from rest_framework import status
198 
199def custom_exception_handler(exc, context):
200    """
201    Custom exception handler for consistent error format.
202    Returns: {"detail": "error message"}
203    """
204    # Call REST framework's default exception handler first
205    response = exception_handler(exc, context)
206 
207    if response is not None:
208        # Customize error response format
209        custom_response_data = {
210            "detail": response.data.get("detail", str(exc))
211        }
212        response.data = custom_response_data
213 
214    return response
215```
216 
217## 5. Custom Authentication Backend165## 4. Custom Authentication Backend
218166
219```python167```python
220# api/authentication.py168# api/authentication.py
223from .auth_utils import verify_token171from .auth_utils import verify_token
224172
225class JWTAuthentication(authentication.BaseAuthentication):173class JWTAuthentication(authentication.BaseAuthentication):
n226    """n
227    Custom JWT authentication for DRF.
228    Verifies Bearer tokens from Authorization header.
229    """
230    keyword = "Bearer"174    keyword = "Bearer"
231    175    
232    def authenticate(self, request):176    def authenticate(self, request):
259        return (user, token)203        return (user, token)
260```204```
261205
n262## 6. Serializersn206## 5. Serializers
263207
264```python208```python
265# api/serializers.py209# api/serializers.py
273    display_name = serializers.CharField(required=False, allow_blank=True)217    display_name = serializers.CharField(required=False, allow_blank=True)
274    218    
275    def validate_password(self, value):219    def validate_password(self, value):
n276        """Validate password length (bcrypt has 72-byte limit)."""n
277        password_bytes = value.encode("utf-8")220        password_bytes = value.encode("utf-8")
278        if len(password_bytes) > 72:221        if len(password_bytes) > 72:
279            raise serializers.ValidationError("Password too long (max 72 bytes)")222            raise serializers.ValidationError("Password too long (max 72 bytes)")
313        read_only_fields = ["user_id", "created_at"]256        read_only_fields = ["user_id", "created_at"]
314```257```
315258
n316## 7. Viewsn259## 6. Views
317260
318```python261```python
319# api/views.py262# api/views.py
326from rest_framework.permissions import AllowAny, IsAuthenticated269from rest_framework.permissions import AllowAny, IsAuthenticated
327270
328from core.models import User271from core.models import User
n329from .serializers import (n272from .serializers import RegisterSerializer, LoginSerializer, AuthResponseSerializer, UserSerializer
330    RegisterSerializer, LoginSerializer, 
331    AuthResponseSerializer, UserSerializer
332)
333from .auth_utils import create_access_token, verify_password, get_password_hash273from .auth_utils import create_access_token, verify_password, get_password_hash
334274
335class RegisterView(APIView):275class RegisterView(APIView):
504            )444            )
505```445```
506446
n507## 8. URL Configurationn447## 7. URL Configuration
508448
509```python449```python
510# api/urls.py450# api/urls.py
522]462]
523```463```
524464
n525## 9. Usage Examplesn465## 8. Usage Examples
526466
527### Register467### Register
528```bash468```bash
531  -d '{"email": "user@example.com", "password": "securepass123", "display_name": "John Doe"}'471  -d '{"email": "user@example.com", "password": "securepass123", "display_name": "John Doe"}'
532```472```
533473
n534**Response:**n
535```json
536{
537  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
538  "token_type": "bearer",
539  "user_id": "123e4567-e89b-12d3-a456-426614174000",
540  "email": "user@example.com",
541  "display_name": "John Doe"
542}
543```
544 
545### Login474### Login
546```bash475```bash
547curl -X POST http://localhost:8000/api/auth/login \476curl -X POST http://localhost:8000/api/auth/login \
5763. **Email Verification**: Token-based with 24-hour expiry5053. **Email Verification**: Token-based with 24-hour expiry
5774. **Password Reset**: Token-based with 1-hour expiry5064. **Password Reset**: Token-based with 1-hour expiry
5785. **Custom Auth Backend**: Seamless integration with DRF permissions5075. **Custom Auth Backend**: Seamless integration with DRF permissions
n5796. **Custom Exception Handler**: Consistent error format across all endpointsn
5807. **CORS Support**: Configured for frontend integration
5818. **Security**: No user enumeration, proper error messages5086. **Security**: No user enumeration, proper error messages
582 
583## File Structure
584 
585```
586your_project/
587├── config/
588│   └── settings.py          # Settings configuration
589├── core/
590│   └── models.py            # User model
591└── api/
592    ├── auth_utils.py        # JWT & password utilities
593    ├── authentication.py    # Custom JWT authentication
594    ├── exceptions.py        # Custom exception handler
595    ├── serializers.py       # DRF serializers
596    ├── views.py             # API views
597    └── urls.py              # URL configuration
598```
599509
600## Security Notes510## Security Notes
601511
605- Forgot password doesn't reveal if email exists515- Forgot password doesn't reveal if email exists
606- Inactive users cannot log in516- Inactive users cannot log in
607- JWT tokens verified on every request517- JWT tokens verified on every request
t608- CORS properly configuredt
609- Custom exception handler prevents information leakage
--- Version 2+++ Version 3@@ -10,12 +10,11 @@ - Email verification with tokens
 - Bcrypt password hashing
 - Custom JWT authentication backend
-- Custom exception handler
 
 ## Installation
 
 ```bash
-pip install djangorestframework PyJWT passlib bcrypt django-cors-headers django-environ
+pip install djangorestframework PyJWT passlib bcrypt
 ```
 
 ## 1. Settings Configuration
@@ -28,32 +27,18 @@ env = environ.Env()
 
 INSTALLED_APPS = [
-    "django.contrib.admin",
-    "django.contrib.auth",
-    "django.contrib.contenttypes",
-    "django.contrib.sessions",
-    "django.contrib.messages",
-    "django.contrib.staticfiles",
-    # Third party
+    # ...
     "rest_framework",
-    "corsheaders",
+    "corsheaders",  # If needed for frontend
     # Your apps
-    "core",
-    "api",
 ]
 
 MIDDLEWARE = [
-    "django.middleware.security.SecurityMiddleware",
-    "corsheaders.middleware.CorsMiddleware",  # MUST be before CommonMiddleware
-    "django.contrib.sessions.middleware.SessionMiddleware",
-    "django.middleware.common.CommonMiddleware",
-    "django.middleware.csrf.CsrfViewMiddleware",
-    "django.contrib.auth.middleware.AuthenticationMiddleware",
-    "django.contrib.messages.middleware.MessageMiddleware",
-    "django.middleware.clickjacking.XFrameOptionsMiddleware",
+    "corsheaders.middleware.CorsMiddleware",  # If needed
+    # ...
 ]
 
-# REST Framework Configuration
+# REST Framework
 REST_FRAMEWORK = {
     "DEFAULT_AUTHENTICATION_CLASSES": [
         "api.authentication.JWTAuthentication",  # Custom JWT auth
@@ -64,18 +49,12 @@     "DEFAULT_RENDERER_CLASSES": [
         "rest_framework.renderers.JSONRenderer",
     ],
-    "DEFAULT_PARSER_CLASSES": [
-        "rest_framework.parsers.JSONParser",
-    ],
-    "EXCEPTION_HANDLER": "api.exceptions.custom_exception_handler",
 }
 
 # JWT Configuration
 SIMPLE_JWT = {
     "ACCESS_TOKEN_LIFETIME": timedelta(days=7),
     "REFRESH_TOKEN_LIFETIME": timedelta(days=30),
-    "ROTATE_REFRESH_TOKENS": False,
-    "BLACKLIST_AFTER_ROTATION": True,
     "ALGORITHM": "HS256",
     "SIGNING_KEY": env("SECRET_KEY"),
     "AUTH_HEADER_TYPES": ("Bearer",),
@@ -83,12 +62,10 @@     "USER_ID_CLAIM": "user_id",
 }
 
-# CORS Configuration
+# CORS (if using frontend)
 CORS_ALLOWED_ORIGINS = [
-    "https://yourapp.com",
     "http://localhost:3000",
-    "http://localhost:19006",  # Expo web
-    "http://localhost:8081",   # Expo dev
+    "http://localhost:19006",  # Expo
 ]
 CORS_ALLOW_CREDENTIALS = True
 ```
@@ -147,7 +124,6 @@         "iat": datetime.utcnow(),
     })
     
-    # Ensure user_id is in token
     if "sub" in to_encode and "user_id" not in to_encode:
         to_encode["user_id"] = to_encode["sub"]
     
@@ -177,44 +153,16 @@         return None
 
 def get_password_hash(password: str) -> str:
-    """Hash password using bcrypt."""
     return passlib_bcrypt.hash(password)
 
 def verify_password(plain_password: str, hashed_password: str) -> bool:
-    """Verify password against bcrypt hash."""
     try:
         return passlib_bcrypt.verify(plain_password, hashed_password)
     except Exception:
         return False
 ```
 
-## 4. Custom Exception Handler
-
-```python
-# api/exceptions.py
-from rest_framework.views import exception_handler
-from rest_framework.response import Response
-from rest_framework import status
-
-def custom_exception_handler(exc, context):
-    """
-    Custom exception handler for consistent error format.
-    Returns: {"detail": "error message"}
-    """
-    # Call REST framework's default exception handler first
-    response = exception_handler(exc, context)
-
-    if response is not None:
-        # Customize error response format
-        custom_response_data = {
-            "detail": response.data.get("detail", str(exc))
-        }
-        response.data = custom_response_data
-
-    return response
-```
-
-## 5. Custom Authentication Backend
+## 4. Custom Authentication Backend
 
 ```python
 # api/authentication.py
@@ -223,10 +171,6 @@ from .auth_utils import verify_token
 
 class JWTAuthentication(authentication.BaseAuthentication):
-    """
-    Custom JWT authentication for DRF.
-    Verifies Bearer tokens from Authorization header.
-    """
     keyword = "Bearer"
     
     def authenticate(self, request):
@@ -259,7 +203,7 @@         return (user, token)
 ```
 
-## 6. Serializers
+## 5. Serializers
 
 ```python
 # api/serializers.py
@@ -273,7 +217,6 @@     display_name = serializers.CharField(required=False, allow_blank=True)
     
     def validate_password(self, value):
-        """Validate password length (bcrypt has 72-byte limit)."""
         password_bytes = value.encode("utf-8")
         if len(password_bytes) > 72:
             raise serializers.ValidationError("Password too long (max 72 bytes)")
@@ -313,7 +256,7 @@         read_only_fields = ["user_id", "created_at"]
 ```
 
-## 7. Views
+## 6. Views
 
 ```python
 # api/views.py
@@ -326,10 +269,7 @@ from rest_framework.permissions import AllowAny, IsAuthenticated
 
 from core.models import User
-from .serializers import (
-    RegisterSerializer, LoginSerializer, 
-    AuthResponseSerializer, UserSerializer
-)
+from .serializers import RegisterSerializer, LoginSerializer, AuthResponseSerializer, UserSerializer
 from .auth_utils import create_access_token, verify_password, get_password_hash
 
 class RegisterView(APIView):
@@ -504,7 +444,7 @@             )
 ```
 
-## 8. URL Configuration
+## 7. URL Configuration
 
 ```python
 # api/urls.py
@@ -522,7 +462,7 @@ ]
 ```
 
-## 9. Usage Examples
+## 8. Usage Examples
 
 ### Register
 ```bash
@@ -531,17 +471,6 @@   -d '{"email": "user@example.com", "password": "securepass123", "display_name": "John Doe"}'
 ```
 
-**Response:**
-```json
-{
-  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
-  "token_type": "bearer",
-  "user_id": "123e4567-e89b-12d3-a456-426614174000",
-  "email": "user@example.com",
-  "display_name": "John Doe"
-}
-```
-
 ### Login
 ```bash
 curl -X POST http://localhost:8000/api/auth/login \
@@ -576,26 +505,7 @@ 3. **Email Verification**: Token-based with 24-hour expiry
 4. **Password Reset**: Token-based with 1-hour expiry
 5. **Custom Auth Backend**: Seamless integration with DRF permissions
-6. **Custom Exception Handler**: Consistent error format across all endpoints
-7. **CORS Support**: Configured for frontend integration
-8. **Security**: No user enumeration, proper error messages
-
-## File Structure
-
-```
-your_project/
-├── config/
-│   └── settings.py          # Settings configuration
-├── core/
-│   └── models.py            # User model
-└── api/
-    ├── auth_utils.py        # JWT & password utilities
-    ├── authentication.py    # Custom JWT authentication
-    ├── exceptions.py        # Custom exception handler
-    ├── serializers.py       # DRF serializers
-    ├── views.py             # API views
-    └── urls.py              # URL configuration
-```
+6. **Security**: No user enumeration, proper error messages
 
 ## Security Notes
 
@@ -604,6 +514,4 @@ - Password reset tokens expire in 1 hour
 - Forgot password doesn't reveal if email exists
 - Inactive users cannot log in
-- JWT tokens verified on every request
-- CORS properly configured
-- Custom exception handler prevents information leakage+- JWT tokens verified on every request