Abhishek Roka
Abhishek Roka

Building an Email Link-Based Password Reset Flow in My Placement Portal


A password reset flow appears effortless from a user's perspective. Click a link, enter a new password, and log back in.

However, implementing it securely involves significantly more work behind the scenes.

While building my Placement Portal, I decided to use an email link-based password reset mechanism instead of the traditional OTP-based approach. Although it improves the user experience considerably, it also introduces several moving parts that must work together correctly.

The Password Reset Flow

The complete flow looks like this:

  1. The user clicks the "Forgot Password?" button.
  2. A dialog box appears asking for the registered email address.
  3. The frontend sends the email address to the backend.
  4. The backend processes the password reset request.
  5. The system always returns a success response irrespective of whether the email exists or not.
  6. If the email exists, the backend generates a password reset link.
  7. The password reset email is sent to the user.
  8. The user clicks the link from their inbox.
  9. The frontend captures the password reset credentials from the URL.
  10. The user submits their new password.
  11. The password is updated successfully.
  12. The user is redirected back to the login screen.

Preventing Email Enumeration Attacks

One interesting aspect of password reset flows is that the system should never disclose whether an email address exists within the application.

Imagine the following response:

An attacker could easily use this behaviour to determine which email addresses are registered on the platform.

To prevent this, the backend always responds with a success message such as:

"If an account exists with this email address, a password reset link has been sent."

Whether the email exists or not, the response remains identical.

If the email is invalid or not registered, the user simply never receives the email.

Generating Secure Password Reset Links

When a valid user requests a password reset, the backend performs two important operations:

These values are embedded within the password reset URL.

The email sent to the user follows a structure similar to:

{FRONTEND_URL}/password/reset/[uid]/[token]/

The token ensures that only the intended user can reset their password, while the encoded UID uniquely identifies the account.

Sending Emails Securely

To send password reset emails during development, I configured Gmail as the email provider.

Instead of exposing my actual Gmail password, I used Google's App Password feature.

This approach provides:

Dynamic Routes on the Frontend

Once the user clicks the password reset link, they are redirected to a dynamic frontend route.

The route captures:

The page then presents a simple form containing:

This information is subsequently submitted to the password reset confirmation endpoint.

Updating the Password

After form submission, the frontend sends a POST request to the password reset confirmation endpoint containing:

The backend validates the request and updates the user's password if everything is correct.

Finally, the user is redirected to the login page and can authenticate using their new credentials.

Final Thoughts

Password reset is one of those features that users expect to "just work."

However, behind a seemingly simple user experience lies:

Good software engineering often hides complexity from users, and password reset flows are an excellent example of that.

I'm documenting every engineering and architectural decision while building this Placement Portal in public.