Django DRF OTP Auth
A reusable Django app for email-based One-Time Password (OTP) authentication, designed for Django REST Framework (DRF) and integrated with dj-rest-auth.
Why OTP?
Passwords are a pain. Users forget them, reuse them, and they are a prime target for attackers. Storing them securely is a liability.
Django DRF OTP Auth solves this by eliminating passwords entirely:
- 🧠 No Memory Required: Users don't need to remember complex passwords.
- 💾 Zero Password Storage: You don't have to worry about hashing, salting, or leaking passwords.
- 🔄 Simplified Flows: No more "Forgot Password" or "Reset Password" complexity.
- 🛡️ Enhanced Security: OTPs are short-lived and one-time use.
Features
- 🔐 Secure OTP Generation: Cryptographically secure 6-character alphanumeric codes.
- ⚡ Cache-Backed: Fast and reliable storage for OTPs with automatic expiration (default 5 minutes).
- 📧 Email Delivery: Integrated email sending using Django's email backend.
- 🔌 DRF Integration: Ready-to-use API views for requesting and verifying OTPs.
- 🎫 JWT Support: Seamlessly integrates with
dj-rest-authto issue JWTs upon verification. - ⚙️ Configurable: Customizable project name, email templates, and throttling.
- 🛡️ Throttling: Built-in support for
ScopedRateThrottleto prevent abuse.
Installation
pip install django-drf-otp-auth
Or using uv:
uv add django-drf-otp-auth
Configuration
1. Add to INSTALLED_APPS
Add django_otp_auth to your INSTALLED_APPS in settings.py:
INSTALLED_APPS = [
...
'rest_framework',
'rest_framework.authtoken',
'dj_rest_auth',
'django_otp_auth',
...
]
2. Configure REST Framework & Auth
Ensure you have dj-rest-auth and simplejwt configured:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'dj_rest_auth.jwt_auth.JWTCookieAuthentication',
),
'DEFAULT_THROTTLE_RATES': {
'otp_request': '5/hour',
'otp_verify': '10/hour',
}
}
REST_AUTH = {
'USE_JWT': True,
'JWT_AUTH_COOKIE': 'access-token',
'JWT_AUTH_REFRESH_COOKIE': 'refresh-token',
}
3. Email Configuration
Configure your email backend in settings.py:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.example.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'user@example.com'
EMAIL_HOST_PASSWORD = 'password'
DEFAULT_FROM_EMAIL = 'Your App <noreply@example.com>'
4. Optional Settings
| Setting | Description | Default |
|---|---|---|
OTP_AUTH_PROJECT_NAME |
Project name used in emails | 'Lifetivation' |
OTP_AUTH_DISABLE_LOCAL_AUTH |
Enable "Magic OTP" flow (skips email, accepts 000000) |
False |
Local Development (Magic OTP)
To simplify local development and testing, you can enable the "Magic OTP" flow. This allows you to log in as any user using the fixed OTP code 000000, without sending actual emails with the OTP.
- Configure the setting in your
settings.pyto read from an environment variable:# Using django-environ OTP_AUTH_DISABLE_LOCAL_AUTH = env.bool("OTP_AUTH_DISABLE_LOCAL_AUTH", default=False)
- Set
OTP_AUTH_DISABLE_LOCAL_AUTH=Truein your local.envfile. - Request an OTP for any email (e.g.,
test@example.com). - Verify using the code
000000.
Usage
URL Configuration
Include the URLs in your project's urls.py:
from django.urls import path, include
from django_otp_auth.views import RequestOTPView, VerifyOTPView
urlpatterns = [
# ...
path('auth/otp/request/', RequestOTPView.as_view(), name='otp_request'),
path('auth/otp/verify/', VerifyOTPView.as_view(), name='otp_verify'),
# ...
]
API Endpoints
1. Request OTP
POST /auth/otp/request/
Request a new OTP to be sent to the user's email.
Payload:
{
"email": "user@example.com"
}
Response:
{
"message": "OTP sent successfully"
}
2. Verify OTP
POST /auth/otp/verify/
Verify the received OTP. If successful, returns JWT access and refresh tokens (and sets them in cookies if configured).
Payload:
{
"email": "user@example.com",
"otp": "ABC123"
}
Response:
{
"access": "eyJhbGciOiJIUzI1NiIsIn...",
"user": {
"pk": 1,
"email": "user@example.com",
...
}
}
Customization
Email Templates
You can override the email templates by creating the following files in your project's templates directory:
templates/emails/otp_email.htmltemplates/emails/otp_email.txt
Context available:
otp: The generated OTP code.project_name: The value ofOTP_AUTH_PROJECT_NAME.
Development
To run tests locally using uv:
uv run pytest
Metadata
Release files for django-drf-otp-auth 0.1.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_drf_otp_auth-0.1.4.tar.gz | 17.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_drf_otp_auth-0.1.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 25.7 kB
Release files / django_drf_otp_auth-0.1.4.tar.gz
| Download URL | django_drf_otp_auth-0.1.4.tar.gz |
|---|---|
| Size | 17.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
91a061a9a49d28be8c9a733c31ecc55df16a17e657df152e3173eb736c59b226
|
|
BLAKE2b-256 checksum How to use checksums |
3853a0ed8c11a1ef856ef164039625b99c9f7fe4122b8acabe2aec21d568493a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / django_drf_otp_auth-0.1.4-py3-none-any.whl
| Download URL | django_drf_otp_auth-0.1.4-py3-none-any.whl |
|---|---|
| Size | 7.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a222b6a4070f5618f10ad33b76c9525ea3bd27aa7b65b2ecc50e2b487476f2e7
|
|
BLAKE2b-256 checksum How to use checksums |
c4758299c720ae88ce5da089233264afb3d5ee4eaba9a5e140dbca1646c37853
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|