This is a simple opinionated module that implements JWT authentication via REST API. For more complex applications please consider using an external authentication service such as https://goauthentik.io
The module is a glue and uses:
- drf - for the API
- simplejwt - for JWT
- pysocial - for social login
- django-otp* for otp and 2fa
- twilio - for text messages
The module also provides very limited extra functionality to the packages above:
- otp devices management OTPDeviceViewSet
- Create, Delete
- user registration and invitation methods and template
- standard User fields = first_name, last_name, email, phone
- extra User fields / serializer override in settings
- phone number white/black listing rules (to be removed?) => registration identity blacklist?
Blacklisting:
- phone / email registration blacklisting (e.g. premium numbers, disposable emails ) regex
- otp sending blacklisting
- ip address blacklisting (honey trap)
- usernames pattern - avoid religiously offensive words
The OTP supports following flows:
- otp (email/phone/static/totp) verification - can also be used for confirming email/phone
- 2FA
- magic signin link
Add to settings.py:
from df_auth.defaults import DF_AUTH_INSTALLED_APPS
INSTALLED_APPS = [
# ... your apps
*DF_AUTH_INSTALLED_APPS,
]
AUTHENTICATION_BACKENDS = [
'df_auth.backends.EmailOTPBackend',
'df_auth.backends.TwilioSMSOTPBackend',
'django.contrib.auth.backends.ModelBackend',
# Optional: social auth backends
'social_core.backends.google.GoogleOAuth2',
]Add to urls.py:
urlpatterns = [
path('api/v1/auth/', include('df_auth.drf.urls')),
]By default, accounts are created immediately when users request an OTP:
DF_AUTH = {
'OTP_AUTO_CREATE_ACCOUNT': True, # Default
'SIGNUP_ALLOWED': True, # Default
}Flow:
- User requests OTP at
/auth/otp/with email/phone - User account created immediately (unverified)
- OTP sent to email/phone
- User authenticates with OTP to get JWT token
- User can confirm device via
/auth/otp-devices/{id}/confirm/
To prevent account creation until OTP is confirmed, disable auto-creation:
DF_AUTH = {
'OTP_AUTO_CREATE_ACCOUNT': False,
'SIGNUP_ALLOWED': True,
}Flow:
- User must first register via
/auth/users/with email/phone/password - An unconfirmed OTP device is created automatically
- User requests OTP via
/auth/otp/ - User confirms device via
/auth/otp-devices/{id}/confirm/ - User's email/phone is updated from confirmed device (if
OTP_IDENTITY_UPDATE_FIELD=True)
This ensures users must complete the full registration flow before their identity is verified.
To allow users to register with a username but defer email/phone assignment until OTP is confirmed:
DF_AUTH = {
'USER_IDENTITY_FIELDS': {
'username': 'rest_framework.serializers.CharField',
},
'USER_OPTIONAL_FIELDS': {
'email': 'rest_framework.serializers.CharField',
'phone_number': 'phonenumber_field.serializerfields.PhoneNumberField',
'password': 'rest_framework.serializers.CharField',
},
'DEFER_IDENTITY_UPDATE': True, # Defer email/phone until device confirmed
'OTP_IDENTITY_UPDATE_FIELD': True, # Update user from confirmed device
}Flow:
- User registers:
POST /auth/users/with{ username: "john", email: "john@example.com", password: "..." } - User created with
username="john",email=""(blank!) ✅ - EmailDevice created with
email="john@example.com",confirmed=False✅ - User requests OTP:
POST /auth/otp/with{ email: "john@example.com" } - OTP sent to john@example.com ✅
- User confirms:
POST /auth/otp-devices/{id}/confirm/with{ otp: "123456" } - Device marked
confirmed=True✅ - User's
emailfield NOW updated to "john@example.com" ✅
Benefits:
- Email/phone not saved to user model until verified
- Multiple users can register with the same unconfirmed email (no uniqueness constraint)
- Only confirmed email/phone is assigned to the user
- Prevents spam registrations with disposable emails
- Username remains the unique identity field
Automatically send verification OTPs to email/phone when users register:
DF_AUTH = {
'AUTO_SEND_IDENTITY_VERIFICATION_OTP': True, # Auto-send OTPs on signup
}Flow:
- User registers:
POST /auth/users/with{ email: "john@example.com", password: "..." } - User created with email/phone ✅
- EmailDevice/TwilioSMSDevice created with
confirmed=False✅ - OTP automatically sent to email/phone ✅
- User confirms via
/auth/otp-devices/{id}/confirm/with received OTP ✅ - Device marked
confirmed=True✅
Combine with Deferred Identity:
DF_AUTH = {
'USER_IDENTITY_FIELDS': {'username': '...'},
'USER_OPTIONAL_FIELDS': {'email': '...', 'phone_number': '...'},
'DEFER_IDENTITY_UPDATE': True, # Don't save until confirmed
'AUTO_SEND_IDENTITY_VERIFICATION_OTP': True, # Auto-send OTP
'OTP_IDENTITY_UPDATE_FIELD': True, # Update on confirmation
}Combined Flow:
- User registers with username + email
- User created:
username="john",email=""(deferred) - Device created with email, OTP automatically sent 📧
- User confirms with received OTP
- User's email updated to confirmed email ✅
Benefits:
- Seamless onboarding - users receive OTP immediately
- No need to manually request OTP via
/auth/otp/endpoint - Works with both standard and deferred identity flows
- Ensures email/phone ownership from registration
Disable public signups and require authenticated users to invite:
DF_AUTH = {
'SIGNUP_ALLOWED': False, # Public signup disabled
'INVITE_ALLOWED': True, # Default - authenticated users can invite
}Flow:
- Unauthenticated users cannot POST to
/auth/users/ - Authenticated users can create invitations via
/auth/users/ - Created user has
UserRegistration.invited_bytracking inviter
DF_AUTH = {
# Update user's email/phone when OTP device is confirmed
'OTP_IDENTITY_UPDATE_FIELD': True, # Default
# Allow OTP requests from non-authenticated users
'OTP_SEND_UNAUTHORIZED_USER': True, # Default
# Available OTP device types
'OTP_DEVICE_MODELS': {
'email': 'django_otp.plugins.otp_email.models.EmailDevice',
'totp': 'django_otp.plugins.otp_totp.models.TOTPDevice',
'sms': 'otp_twilio.models.TwilioSMSDevice',
},
}Configure which fields uniquely identify users:
DF_AUTH = {
# Default: email, phone, and username are all identity fields
'USER_IDENTITY_FIELDS': {
'username': 'rest_framework.serializers.CharField',
'email': 'rest_framework.serializers.CharField',
'phone_number': 'phonenumber_field.serializerfields.PhoneNumberField',
},
}Username-only identity (allow duplicate emails/phones):
DF_AUTH = {
'USER_IDENTITY_FIELDS': {
'username': 'rest_framework.serializers.CharField',
},
'USER_OPTIONAL_FIELDS': {
'email': 'rest_framework.serializers.CharField',
'phone_number': 'phonenumber_field.serializerfields.PhoneNumberField',
'password': 'rest_framework.serializers.CharField',
},
}2FA is controlled per-user via the User2FA model:
# Enable 2FA for a user
from df_auth.models import User2FA
User2FA.objects.create(user=user, is_required=True)
# Via API
PATCH /api/v1/auth/users/0/two-fa/
{
"is_required": true
}When 2FA is enabled:
- User must provide valid OTP from a confirmed device
- Works with password auth, social login, and magic links
- API returns available devices if 2FA required but not provided
Configure Twilio for SMS OTP:
OTP_TWILIO_ACCOUNT = 'your-account-sid'
OTP_TWILIO_AUTH = 'your-auth-token'
OTP_TWILIO_FROM = 'your-twilio-phone-number'
OTP_TWILIO_TOKEN_VALIDITY = 300 # 5 minutesExample Google OAuth2 setup:
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = 'your-client-id'
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = 'your-client-secret'
# Fields to populate from social auth
DF_AUTH = {
'USER_SOCIAL_AUTH_FIELDS': {
'first_name': 'rest_framework.serializers.CharField',
'last_name': 'rest_framework.serializers.CharField',
},
}POST /auth/token/- Obtain JWT token (username/email/phone + password/otp)POST /auth/token/refresh/- Refresh JWT tokenPOST /auth/token/verify/- Verify JWT tokenPOST /auth/token/blacklist/- Blacklist JWT token
POST /auth/users/- Register new user or invite user (if authenticated)GET /auth/users/0/- Get current user profilePATCH /auth/users/0/- Update user profilePOST /auth/users/0/set-password/- Change passwordGET /auth/users/0/two-fa/- Get 2FA statusPATCH /auth/users/0/two-fa/- Update 2FA requirement
POST /auth/otp/- Request OTP (email or SMS)
GET /auth/otp-devices/- List user's OTP devicesPOST /auth/otp-devices/- Create OTP device (email, sms, totp)GET /auth/otp-devices/{id}/?type={type}- Get device detailsPOST /auth/otp-devices/{id}/confirm/?type={type}- Confirm device with OTPDELETE /auth/otp-devices/{id}/?type={type}- Delete device
POST /auth/social/- Login with social provider (google-oauth2, facebook, apple)POST /auth/social/connect/- Connect social account (requires authentication)