Abhishek Roka
Abhishek Roka

Designing a Secure Password Reset Flow with Django, Next.js, and dj-rest-auth


While building the authentication module for my Placement Portal, I realized that logging users in is only one part of account security.

Eventually, every application needs a way for users to regain access to their accounts.

That is where the password reset workflow becomes important.

At first glance, it seems like a simple feature. Enter your email, receive a link, create a new password, and you're done.

Behind the scenes, however, several security and architectural decisions make this process safe and reliable.

Step 1: Requesting a Password Reset

The process begins when a user enters their email address.

The Django backend receives this request and triggers the password reset endpoint provided by dj-rest-auth.

One security behavior immediately caught my attention.

Whether the email address exists or not, the API always returns HTTP 200 OK.

This prevents Email Enumeration Attacks, where attackers attempt to discover registered accounts by comparing server responses.

Every request receives the same response, making it impossible to determine whether an email belongs to a valid user.

Step 2: Sending the Reset Email

During development, Django's console email backend simply prints the email contents in the terminal.

For production, however, a real email provider is required.

I configured an SMTP email backend using Gmail App Passwords for development and testing.

Instead of sending plain text, the email uses an HTML template so important information can be highlighted and branded consistently.

This email contains a secure password reset link.

Step 3: Understanding the Reset Link

Unlike OTP-based password reset systems, dj-rest-auth generates a unique reset URL.

That URL contains two important values:

Together, these uniquely identify the password reset request.

Users never need to understand these values—they simply click the link provided in the email.

Step 4: Frontend Route Handling

Since my frontend is built with Next.js, I created a dynamic route that captures both the UID and Token directly from the URL.

When a user opens the reset link:

The user simply enters:

Everything else happens behind the scenes.

Step 5: Completing the Password Reset

When the form is submitted, the frontend sends:

to the Django backend.

The backend validates:

If everything is valid, the user's password is updated successfully.

Three Components Working Together

Implementing password reset taught me that this feature depends on three independent systems working together.

Frontend

Receives the password reset link, extracts the UID and Token, and collects the user's new password.

Backend

Generates secure reset tokens, validates requests, and updates user credentials.

Email Service

Delivers password reset emails using reusable HTML templates and SMTP integration.

Without any one of these components, the password reset workflow cannot function correctly.

Why Email Links Instead of OTP?

Many applications still rely on OTPs.

I chose an email link because it reduces friction for users.

Instead of copying and typing a six-digit code, users simply click the link in their inbox and continue directly to the password reset page.

It requires less effort while maintaining strong security through time-limited tokens.

Final Thoughts

Building a password reset feature taught me that security is rarely about a single API.

It's about designing a workflow where the frontend, backend, email infrastructure, and security best practices work together seamlessly.

Although users only see a single "Forgot Password" button, there's a carefully designed system operating behind it.

As I continue building this Placement Portal in public, I'm learning that the smallest features often involve the most thoughtful engineering decisions.