NextAuth v5 and MongoDB: Production-Ready Auth for Next.js

A reusable authentication foundation for Next.js 16 that covers the whole account lifecycle: registration, login, protected routes and email password reset, built on NextAuth v5, MongoDB, Zod and Nodemailer.

By Wasif Rehman · · Security

In short

  • A NextAuth v5 credentials provider gives you full control over login while relying on a proven session layer.
  • Hash passwords with bcryptjs before they reach the database, and never store anything reversible.
  • Protect routes in middleware so the rule lives in one place.
  • Make reset tokens time-limited and single-use, remove them after use, never log them, and never reveal whether an email is registered.
  • Share Zod schemas between client and server so validation rules cannot drift.

What production-ready authentication in Next.js covers

Authentication is the feature everyone needs and nobody enjoys rebuilding. At AIDEVGEN I built Auth System, a production-ready foundation for the whole account lifecycle on Next.js 16 and NextAuth v5.

The account lifecycle, stage by stage
StageHow it is handled
RegistrationThe form is validated with Zod, and the password is hashed with bcryptjs before it reaches MongoDB
LoginA NextAuth v5 credentials provider verifies the hash on sign-in
Protected accessMiddleware redirects any request without a valid session
RecoveryA time-limited, single-use reset token is sent by email with Nodemailer

This is what goes into doing each stage properly.

NextAuth v5 credentials provider with bcrypt password hashing

The app uses NextAuth v5 (beta) with a credentials provider. That gives full control over the login flow while still leaning on a proven session layer.

Passwords are hashed with bcryptjs before they ever touch the database, and the provider verifies the hash on sign-in.

Protecting Next.js routes with middleware

Protected pages are guarded in middleware. A request without a valid session is redirected before the page renders.

I used the same middleware-first approach, with JWT sessions, in VoltOutreach, where it enforces the split between the public portal and the admin area.

Building a secure password reset flow with Nodemailer

Password reset is the trickiest part of any auth system. Auth System runs it over email, with Nodemailer and Gmail SMTP:

  1. The user requests a reset They enter their email address. The response never reveals whether that address is registered.
  2. A token is emailed Nodemailer sends a time-limited, single-use token.
  3. The user sets a new password The token is checked, the new password is set, and the used token is removed.

Zod validation shared between client and server

Every form and API route is validated with Zod, and the same schemas run on both the client and the server.

One schema, two jobs
Where it runsIts role
ServerThe source of truth: where the rules are actually enforced
ClientA mirror of the server, so the user is told early when something is invalid

Sharing schemas means the rules cannot drift between where the user is told something is invalid and where it is enforced.

Storing users and password reset tokens in MongoDB

MongoDB stores users and reset tokens. It is a simple data model, but the details matter. Reset tokens:

  • Carry an expiry
  • Are removed once used
  • Are never logged

Why build a reusable auth foundation

The point of Auth System is reuse. Auth is the same across most products. Only the branding changes.

Getting a secure, modern implementation right once and dropping it into the next project saves real time. It also avoids reintroducing the same subtle bugs every time.

Frequently asked questions

Can NextAuth v5 use a credentials provider with MongoDB?

Yes. In this project NextAuth v5 (beta) uses a credentials provider that verifies a bcryptjs password hash, and MongoDB stores the users and the password reset tokens.

How do you build a secure password reset in Next.js?

Email the user a time-limited, single-use token (here with Nodemailer over Gmail SMTP), remove the token once it is used, never log it, and never reveal whether an email address is registered.

Should route protection live in middleware or in each page?

In middleware. A request without a valid session is redirected before the page renders, which keeps protection consistent and puts the rule in one place.

Why validate with Zod on both the client and the server?

Running the same schemas on both sides means the rules cannot drift. The server is the source of truth, and the client mirrors it so users see problems early.

All posts