Created
February 26, 2025 10:07
-
-
Save garzuze/007f73a8ea630f45ae89f341a51f4d26 to your computer and use it in GitHub Desktop.
How to create an email verification system without having to add any extra field in your user model - Django
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Create a User registration serializer | |
# When the user sends the request with his data — in this case, email and password —, | |
# we create a new user with is_verified=False (builtin field in default user model) | |
# Then we send an email containing a verification link | |
# You can customize it so you link points to a route in your fronted, if you use Django only for the backend | |
# After that you can create a view that uses this serializer | |
from django.urls import reverse | |
from rest_framework import serializers | |
from django.core.mail import send_mail | |
from django.conf import settings | |
from .utils import generate_email_verification_token | |
from django.contrib.auth import get_user_model | |
User = get_user_model() | |
class RegistrationSerializer(serializers.ModelSerializer): | |
class Meta: | |
model = User | |
fields = ('email', 'password', 'is_active') | |
extra_kwargs = { | |
'password': {'write_only': True} | |
} | |
def validate(self, data): | |
return data | |
def create(self, validated_data): | |
user = User.objects.create_user(**validated_data) | |
token = generate_email_verification_token(user) | |
verify_path = reverse('verify-email') | |
verification_url = f"http://127.0.0.1:8000/{verify_path}?uid={user.pk}&token={token}" | |
# Email content | |
subject = "Verify your email" | |
message = ( | |
"Welcome!\n\n" | |
"Please, verify your addres clicking in the link below:\n" | |
f"{verification_url}\n\n" | |
) | |
send_mail( | |
subject, | |
message, | |
settings.DEFAULT_FROM_EMAIL, | |
[user.email], | |
fail_silently=False, | |
) | |
return user |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Email verification settings | |
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' | |
EMAIL_HOST = 'smtp.gmail.com' | |
EMAIL_PORT = 587 | |
EMAIL_USE_TLS = True | |
EMAIL_HOST_USER = os.environ.get('GMAIL_USER') | |
EMAIL_HOST_PASSWORD = os.environ.get('GMAIL_PASS') | |
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Configure your urls.py | |
urlpatterns = [ | |
path('api/register/', RegistrationView.as_view(), name='register'), | |
path('api/verify-email/', VerifyEmailAPIView.as_view(), name='verify-email'), | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# We use django bultin function, that receives the user as a parameter, to generate a token | |
# We use this to create the one-time use link that will be sent to the user's email | |
# You can go even crazier and make a more secure token based on user data | |
from django.contrib.auth.tokens import default_token_generator | |
def generate_email_verification_token(user): | |
return default_token_generator.make_token(user) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from rest_framework import generics | |
from .serializers import RegistrationSerializer | |
from rest_framework.views import APIView | |
from rest_framework import status | |
# Create a view that receives the user id and the token and verify it | |
class VerifyEmailAPIView(APIView): | |
def get(self, request): | |
uid = request.query_params.get('uid') | |
token = request.query_params.get('token') | |
if not uid or not token: | |
return Response({"error": "Invalid verification link."}, status=status.HTTP_400_BAD_REQUEST) | |
user = get_object_or_404(User, pk=uid) | |
# If the token matches, the user can be verified! | |
if default_token_generator.check_token(user, token): | |
user.is_active = True | |
user.save() | |
return Response({"message": "Email verified successfully."}, status=status.HTTP_200_OK) | |
else: | |
return Response({"error": "Invalid or expired token."}, status=status.HTTP_400_BAD_REQUEST) | |
# Create the RegistrationView using the serializer previouly created | |
class RegistrationView(generics.CreateAPIView): | |
serializer_class = RegistrationSerializer |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment