Accounts App - OTP Authentication & User Management
The Accounts app provides a comprehensive user authentication and management system with OTP (One-Time Password) authentication, registration source tracking, and advanced profile management.
Overview
The Accounts app includes:
- 🔐 OTP Authentication - Email and SMS-based one-time passwords
- 👥 Custom User Model - Extended user model with email as primary identifier
- 📊 Registration Tracking - Track user acquisition sources and analytics
- 🎨 Profile Management - Avatar support and user preferences
- 🔧 Manager-based Logic - Performance-optimized computed properties
- 📱 API Integration - Complete REST API with DRF and Spectacular
Architecture
Core Models
# User Management
CustomUser # Extended AbstractUser with email auth
UserRegistrationSource # Links users to registration sources
RegistrationSource # Tracks acquisition channels
# OTP System
OTPSecret # Stores OTP codes and expiration
TwilioResponse # Tracks SMS/WhatsApp delivery statusService Layer
# Business Logic Services
OTPService # OTP generation, sending, verification
AuthEmailService # Email notifications for auth events
ActivityService # User activity trackingAPI Layer
# REST API ViewSets
OTPViewSet # OTP request/verify endpoints
ProfileViewSet # User profile management
WebhookViewSet # External service webhooksOTP Authentication System
How It Works
- User requests OTP → Email/phone provided to API
- OTP generated → 6-digit code with 10-minute expiration
- OTP delivered → Email or SMS via Twilio
- User verifies → Code submitted to verification endpoint
- JWT issued → User authenticated with refresh/access tokens
Request OTP
# API Endpoint: POST /api/auth/otp/request/
{
"identifier": "[email protected]",
"channel": "email", # or "phone"
"source_url": "https://dashboard.unrealon.com"
}
# Response
{
"success": true,
"message": "OTP sent successfully",
"channel": "email",
"expires_in": 600
}Verify OTP
# API Endpoint: POST /api/auth/otp/verify/
{
"identifier": "[email protected]",
"otp_code": "123456",
"source_url": "https://dashboard.unrealon.com"
}
# Response
{
"success": true,
"message": "Authentication successful",
"user": {
"id": 1,
"email": "[email protected]",
"full_name": "John Doe",
"is_new_user": false
},
"tokens": {
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}
}Service Usage
from django_cfg.apps.accounts.services import OTPService
# Request OTP
success, error_type = OTPService.request_otp(
email="[email protected]",
source_url="https://myapp.com"
)
if success:
print("OTP sent successfully!")
else:
print(f"Failed to send OTP: {error_type}")
# Verify OTP
user = OTPService.verify_otp(
email="[email protected]",
otp_code="123456",
source_url="https://myapp.com"
)
if user:
print(f"User authenticated: {user.email}")
else:
print("Invalid OTP code")User Management
Custom User Model
The CustomUser model extends Django’s AbstractUser with:
class CustomUser(AbstractUser):
# Email as primary identifier
email = models.EmailField(unique=True)
username = models.CharField(max_length=150, blank=True, null=True)
# Profile fields
phone_number = models.CharField(max_length=20, blank=True)
avatar = models.ImageField(upload_to=user_avatar_path, blank=True)
bio = models.TextField(max_length=500, blank=True)
# Preferences
language = models.CharField(max_length=10, default='en')
timezone = models.CharField(max_length=50, default='UTC')
# Metadata
is_verified = models.BooleanField(default=False)
last_activity = models.DateTimeField(null=True, blank=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()Manager Methods
The UserManager provides optimized business logic:
# User registration with source tracking
user, created = CustomUser.objects.register_user(
email="[email protected]",
source_url="https://dashboard.unrealon.com"
)
# Computed properties (performance optimized)
full_name = CustomUser.objects.get_full_name(user)
initials = CustomUser.objects.get_initials(user)
display_name = CustomUser.objects.get_display_username(user)
# Activity tracking
CustomUser.objects.update_last_activity(user)Registration Source Tracking
Track where users come from for analytics:
# Automatic source creation
source = RegistrationSource.objects.get_or_create(
url="https://dashboard.unrealon.com",
defaults={
'name': 'Unrealon Dashboard',
'description': 'Main application dashboard'
}
)[0]
# Link user to source
UserRegistrationSource.objects.create(
user=user,
source=source,
registration_method='otp_email'
)
# Analytics queries
from django.db.models import Count
# Users by source
sources = RegistrationSource.objects.annotate(
user_count=Count('userregistrationsource')
).order_by('-user_count')
# Registration methods
methods = UserRegistrationSource.objects.values('registration_method').annotate(
count=Count('id')
)API Integration
ViewSets and Endpoints
# OTP Authentication
POST /api/auth/otp/request/ # Request OTP code
POST /api/auth/otp/verify/ # Verify OTP and authenticate
# Profile Management
GET /api/profile/ # Get user profile
PUT /api/profile/ # Update user profile
POST /api/profile/avatar/ # Upload avatar
DELETE /api/profile/avatar/ # Remove avatar
# User Sources
GET /api/profile/sources/ # List user's registration sources
# Webhooks (for external services)
POST /api/webhooks/twilio/ # Twilio delivery status
POST /api/webhooks/sendgrid/ # SendGrid delivery statusSerializers
# OTP Operations
OTPRequestSerializer # Request OTP validation
OTPVerifySerializer # Verify OTP validation
OTPRequestResponseSerializer # Success response format
OTPVerifyResponseSerializer # Verification response format
# Profile Management
UserSerializer # User profile data
UserProfileUpdateSerializer # Profile update validation
UserAvatarSerializer # Avatar upload handling
# Registration Sources
RegistrationSourceSerializer # Source information
UserRegistrationSourceSerializer # User-source relationshipsConfiguration
Required Settings
# settings.py
AUTH_USER_MODEL = 'django_cfg_accounts.CustomUser'
# OTP Configuration
OTP_EXPIRY_MINUTES = 10
OTP_CODE_LENGTH = 6
OTP_MAX_ATTEMPTS = 3
# Email Configuration (for OTP delivery)
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# ... email settings
# Twilio Configuration (for SMS OTP)
TWILIO_ACCOUNT_SID = 'your_account_sid'
TWILIO_AUTH_TOKEN = 'your_auth_token'
TWILIO_VERIFY_SERVICE_SID = 'your_verify_service_sid'Django-CFG Integration
# api/config.py
from django_cfg.models import EmailConfig, TwilioConfig
class MyProjectConfig(DjangoConfig):
# Email for OTP delivery
email: EmailConfig = EmailConfig(
backend="sendgrid",
sendgrid_api_key=env.email.sendgrid_api_key,
from_email="[email protected]"
)
# SMS/WhatsApp for OTP delivery
twilio: TwilioConfig = TwilioConfig(
account_sid=env.twilio.account_sid,
auth_token=env.twilio.auth_token,
verify_service_sid=env.twilio.verify_service_sid
)🧪 Testing
Unit Tests
# Test OTP Service
from django_cfg.apps.accounts.services import OTPService
from django_cfg.apps.accounts.models import CustomUser, OTPSecret
class OTPServiceTest(TestCase):
def test_request_otp_success(self):
success, error = OTPService.request_otp("[email protected]")
self.assertTrue(success)
self.assertEqual(error, "")
# Check OTP was created
otp = OTPSecret.objects.get(email="[email protected]")
self.assertIsNotNone(otp.secret)
def test_verify_otp_success(self):
# Create OTP
OTPService.request_otp("[email protected]")
otp = OTPSecret.objects.get(email="[email protected]")
# Verify OTP
user = OTPService.verify_otp("[email protected]", otp.secret)
self.assertIsNotNone(user)
self.assertEqual(user.email, "[email protected]")API Tests
# Test OTP API endpoints
class OTPAPITest(APITestCase):
def test_request_otp_api(self):
response = self.client.post('/api/auth/otp/request/', {
'identifier': '[email protected]',
'channel': 'email'
})
self.assertEqual(response.status_code, 200)
self.assertTrue(response.data['success'])
def test_verify_otp_api(self):
# Request OTP first
self.client.post('/api/auth/otp/request/', {
'identifier': '[email protected]'
})
# Get OTP code
otp = OTPSecret.objects.get(email='[email protected]')
# Verify OTP
response = self.client.post('/api/auth/otp/verify/', {
'identifier': '[email protected]',
'otp_code': otp.secret
})
self.assertEqual(response.status_code, 200)
self.assertIn('tokens', response.data)Usage Examples
Basic OTP Flow
# 1. User requests OTP
from django_cfg.apps.accounts.services import OTPService
success, error = OTPService.request_otp("[email protected]")
if not success:
print(f"Failed to send OTP: {error}")
return
# 2. User receives email/SMS with code
# 3. User submits code for verification
user = OTPService.verify_otp("[email protected]", "123456")
if user:
print(f"Welcome, {user.email}!")
else:
print("Invalid OTP code")Registration with Source Tracking
# Register user from specific source
user, created = CustomUser.objects.register_user(
email="[email protected]",
source_url="https://dashboard.unrealon.com"
)
if created:
print("New user registered!")
# Get registration source info
source_link = user.userregistrationsource_set.first()
if source_link:
print(f"Registered from: {source_link.source.get_display_name()}")Profile Management
# Update user profile
user = CustomUser.objects.get(email="[email protected]")
user.bio = "Django developer passionate about clean code"
user.timezone = "America/New_York"
user.save()
# Get computed properties
full_name = CustomUser.objects.get_full_name(user)
initials = CustomUser.objects.get_initials(user)
display_name = CustomUser.objects.get_display_username(user)
print(f"User: {display_name} ({initials})")Analytics Queries
from django.db.models import Count, Q
from django_cfg.apps.accounts.models import RegistrationSource, UserRegistrationSource
# Top registration sources
top_sources = RegistrationSource.objects.annotate(
user_count=Count('userregistrationsource')
).order_by('-user_count')[:10]
# Registration methods breakdown
methods = UserRegistrationSource.objects.values('registration_method').annotate(
count=Count('id')
).order_by('-count')
# Recent registrations
recent_users = CustomUser.objects.filter(
date_joined__gte=timezone.now() - timedelta(days=7)
).select_related('userregistrationsource__source')🔒 Security Features
OTP Security
- Time-based expiration - OTP codes expire after 10 minutes
- Single-use codes - Each OTP can only be used once
- Rate limiting - Prevent OTP spam and brute force attacks
- Secure generation - Cryptographically secure random codes
Authentication Security
- JWT tokens - Secure token-based authentication
- Refresh tokens - Long-lived tokens for seamless re-authentication
- Token blacklisting - Revoke compromised tokens
- Email verification - Verify email ownership before activation
Data Protection
- Password hashing - Django’s built-in password hashing
- Sensitive data encryption - OTP codes stored securely
- GDPR compliance - User data deletion and export support
- Activity logging - Track authentication events for security
Integration Patterns
With Django-CFG Core
# Access from configuration
from api.config import config
# Email OTP delivery
if config.email.backend == "sendgrid":
# Use SendGrid for email delivery
pass
# SMS OTP delivery
if hasattr(config, 'twilio') and config.twilio.account_sid:
# Use Twilio for SMS delivery
passWith Other Apps
# Integration with AI Agents
from django_cfg.apps.agents.models import Agent
# Create user-specific agent
agent = Agent.objects.create(
name=f"Assistant for {user.email}",
owner=user,
configuration={
'language': user.language,
'timezone': user.timezone
}
)
# Integration with Tasks
from django_cfg.apps.tasks.models import Task
# Create welcome task for new users
if created: # New user registration
Task.objects.create(
title="Send welcome email",
task_type="email",
user=user,
data={'template': 'welcome', 'email': user.email}
)Monitoring & Analytics
User Metrics
# Active users
active_users = CustomUser.objects.filter(
last_activity__gte=timezone.now() - timedelta(days=30)
).count()
# Registration trends
from django.db.models import TruncDate
registrations_by_day = CustomUser.objects.extra(
select={'day': 'date(date_joined)'}
).values('day').annotate(count=Count('id'))
# OTP success rates
total_otp_requests = OTPSecret.objects.count()
successful_verifications = CustomUser.objects.filter(
date_joined__gte=timezone.now() - timedelta(days=30)
).count()Error Tracking
# Failed OTP attempts
from django_cfg.apps.accounts.signals import notify_failed_otp_attempt
# Connect to monitoring
@receiver(notify_failed_otp_attempt)
def track_failed_otp(sender, email, error_type, **kwargs):
# Log to monitoring service
logger.warning(f"Failed OTP attempt: {email} - {error_type}")Best Practices
1. Use Manager Methods
# Good: Use manager methods for computed properties
full_name = CustomUser.objects.get_full_name(user)
# Avoid: Direct property access (performance impact)
full_name = f"{user.first_name} {user.last_name}".strip()2. Track Registration Sources
# Always include source tracking for analytics
user, created = CustomUser.objects.register_user(
email=email,
source_url=request.META.get('HTTP_REFERER')
)3. Handle OTP Errors Gracefully
success, error_type = OTPService.request_otp(email)
if not success:
if error_type == "rate_limited":
return "Too many requests. Please try again later."
elif error_type == "invalid_email":
return "Please provide a valid email address."
else:
return "Unable to send OTP. Please try again."4. Secure API Endpoints
# Use proper permissions and throttling
class OTPViewSet(viewsets.GenericViewSet):
permission_classes = [permissions.AllowAny]
throttle_classes = [AnonRateThrottle]
throttle_scope = 'otp'See Also
User Management Apps
Related User Apps:
- User Management Overview** - All user management features
- Support App** - Customer support and ticketing system
- Newsletter App** - Email marketing and campaigns
- Leads App** - Lead capture and management
Authentication & Security
Security Configuration:
- Security Settings** - CORS, CSRF, SSL configuration
- Environment Variables** - Secure API key management
- Production Config** - Production auth setup
Integration:
- Twilio Integration** - SMS OTP integration
- Email Module** - Email OTP delivery
- Telegram Module** - Telegram notifications
Configuration & Setup
Getting Started:
- Installation** - Install Django-CFG with accounts
- Configuration Guide** - Enable accounts app
- First Project** - Quick start tutorial
Configuration:
- Configuration Models** - Accounts config API
- Type-Safe Configuration** - Pydantic patterns
- Built-in Apps Overview** - All production apps
Tools & Development
CLI & Management:
- CLI Tools** - Manage users via CLI
- Core Commands** - User management commands
- Troubleshooting** - Common auth issues
Advanced Features:
- AI Agents** - User-specific AI agents
- Background Tasks** - Async email sending
The Accounts app provides a complete, secure, and scalable user authentication system for your Django-CFG projects! 🚀