Django offers almost the entire user management logic of a typical website out of the box. User login, logout, change password, reset the password, all of these are ready as soon as you start a new project.
But I said "almost" because there's one thing that's needed almost in every website that's not offered in Django: email verification. Fear not though. The functionality might not be offered out-of-the-box but all the required components are there.
The token
The most crucial bit for the email verification flow is the verification token. The purpose of this token is to encode the user's current state. We want it to be valid only if the user is inactive. When the user is activated, the token should no longer be valid.
Thankfully, Django creates similar tokens when sending password reset emails. We can reuse that class to create the tokens for the email activation as well.
The only difference is what elements we use to generate the token. Since we want the tokens to be valid only when the user is not yet activated, we will include user.is_active
.
class EmailVerificationTokenGenerator(PasswordResetTokenGenerator):
def _make_hash_value(self, user, timestamp):
return (
str(user.is_active) + str(user.pk) + str(timestamp)
)
email_verification_token = EmailVerificationTokenGenerator()
email_verification_token_generator.py
Verification email
The next step is to send the verification email.
Note that we need 3 things to construct the activation URL:
- The current domain
- The user id, which we are encoding using Base64
- The token we discussed in the previous section
class RegistrationView(FormView):
# [...]
def _send_email_verification(self, user: CustomUser):
current_site = get_current_site(self.request)
subject = 'Activate Your Account'
body = render_to_string(
'emails/email_verification.html',
{
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'token': email_verification_token.make_token(user),
}
)
EmailMessage(to=[user.email], subject=subject, body=body).send()
registration.py
<div>
<a href=
"http://{{ domain }}{% url 'activate' uidb64=uid token=token %}">
Please activate your account</a>
</div>
email_verification.html
Activate the user
The last step is to activate the user. We do the reverse process we did in the previous step.
- Base64 decode to get the user id
- Fetching the user using the user id
- Using the
EmailVerificationTokenGenerator
to verify that the token is still valid
If the token is valid, which means the user is not yet activated, we set the is_active
to True
and save. From this point on, the token will not be valid anymore. So we need to handle the case where this link is clicked again to display the appropriate message in the template.
urlpatterns = [
path('activate/<uidb64>/<token>',
ActivateView.as_view(),
name='activate'),
# [...]
]
urls.py
class ActivateView(View):
def get_user_from_email_verification_token(self, token: str):
try:
uid = force_str(urlsafe_base64_decode(self))
user = get_user_model().objects.get(pk=uid)
except (TypeError, ValueError, OverflowError,
get_user_model().DoesNotExist):
return None
if user is not None \
and \
email_verification_token.check_token(user, token):
return user
return None
def get(self, request, uidb64, token):
user = self.get_user_from_email_verification(uidb64, token)
user.is_active = True
user.save()
login(request, user)
return redirect('registration_successful')
activate.py
Hopefully, this was a quick and straightforward way to implement email verification in your Django project.
Happy coding!