Skip to main content
Episode 11 of 24Storefront55 min

Add secure customer accounts with sign up and login

Add sign up, login and an account page using Auth.js, safely hashed passwords, a customer or admin role, and protected pages.

You will have: Customers can register, log in and out, see an account page with an order history area, and admin pages are blocked for everyone except admins, with a hook ready for email verification in Episode 14.

Before you start

  • Episode 10 is merged: the cart works and mergeGuestCartIntoUserCart exists
  • Local Postgres is running
  • Your own email address to use as the first admin (it goes into .env.local, not into the chat)
  • You are on the main branch with a clean working tree

Make it yours

Change the example (a fashion store) to your own. Your answers are saved in this browser and used in every episode.

Other series keywords (6)

Your prompt

Paste into: {{ai_agent}} opened in your project folder.
You are a senior full-stack security-minded developer working inside my Next.js (App Router), TypeScript, Tailwind and Drizzle project for "Thread & Needle", a fashion e-commerce store selling women's and men's clothing, shoes and accessories to customers in United States. I am not a programmer, so explain technical words briefly.

Goal: add customer accounts with Auth.js (NextAuth version 5) and email plus password login. Read the current official Auth.js documentation first and tell me which pages you used, because setup details change.

Plan first: show me your plan (tables, your recommended password hashing library, argon2 or bcrypt, whichever runs reliably on my computer and the host, how sessions work, protected pages) and wait for my OK before changing anything.

Secrets go in .env.local only, with names (no values) in the committed .env.example. Generate the auth secret with a command that writes it into .env.local without printing it. Never print, log or commit a secret. I will put my admin email in .env.local myself.

Tasks, after I approve:
1. Create a branch named feature/accounts.
2. Database (drizzle-kit migration): users (email lowercase and unique, password hash, name, role customer or admin with customer as default, email verified date, created date), plus an email verification tokens table that stores only a hash of each token, with an expiry.
3. Sign up: a form validated with Zod on both client and server (valid email, password at least 12 characters, a sensible maximum length, matching confirmation). Hash with a strong, slow algorithm and a unique salt. A new user always gets the customer role; visitors cannot choose a role.
4. Login with the Auth.js credentials provider and a signed session in an httpOnly cookie (secure in production, sameSite lax). Use one generic message for a wrong email or password so nobody can learn which emails exist. Add rate limiting on login and sign up, and tell me honestly how well it works on a host with many instances.
5. Logout, and after a successful login call mergeGuestCartIntoUserCart so the guest cart joins the account.
6. Email verification hook: create a function sendVerificationEmail and a verify page that checks the hashed, expiring token. For now it prints the link to the server console in development only; Episode 14 connects Resend. Default: unverified users may browse but must verify before ordering; ask me if I want otherwise.
7. Account page: profile name, change password, an order history area with a friendly empty state and a function getOrdersForUser that Episode 12 will fill in, and a sign out button.
8. Route protection: pages under the account area need a login, and pages under the admin area need the admin role. Check the session in server code for every protected page and action, not only in the framework's route filter. Add a script that promotes the email in my .env.local to admin.
9. Add a forgot-password page that says it arrives in Episode 14. No fake reset.

Security rules:
- Never store, log or return a plain password or a hash.
- Require the current password to change it.
- Protect forms against cross-site request forgery as Auth.js and Next.js recommend, and redirect only to pages on my own site.

Verify by running yourself and showing results: type check, lint, production build, and tests for sign up, duplicate email, weak password, right and wrong login (same message), logout, logged-out visit to the account page (redirect), customer on an admin page (denied), admin on it (allowed), role not settable through sign up, and cart merge after login.

Commit in small steps and open a pull request.

Report back in this format:
1. What you did.
2. What I should check myself.
3. Anything that needs my decision.

Review the prompt, then copy it. Never paste real passwords or API keys into a chat.

How to check it worked

  • Sign up with a test email and a long password, then log out and log in again
  • Try a wrong password and an email that does not exist: both show the same message
  • Add something to the cart as a guest, then log in: the item is still in your cart
  • While logged out, open the account page address: you are sent to the login page. While logged in as a normal customer, open an admin address: you are denied
  • After running the promote-admin script for your email, log in and open the admin address: the page opens
  • In the database tool the password column holds a long scrambled text, never the password you typed

Sample result

Fashion store example

Example account area for the fashion store and the agent's test results. The verification link shown is a development placeholder.

If something goes wrong

Login fails with a configuration or secret error.
Ask the agent to check that the auth secret exists in .env.local and that the dev server was restarted after it was added. It must not print the value.
The password library will not install on your computer.
Some hashing libraries need build tools. Tell the agent: "Use the other recommended option (bcrypt or argon2) that installs without extra tools, and explain the difference in two sentences." Both are acceptable when configured properly.
You are logged in but the admin page still denies you.
Run the promote-admin script, then log out and log in again so your session picks up the new role. Check that the email in .env.local matches your account exactly.
No verification email arrives.
That is expected in this episode. The link is printed in the terminal where npm run dev is running. Real email is added in Episode 14.

Free. Auth.js is open source and stores sessions in a signed cookie, so no paid login service is used.

More in Storefront

This is part of the series Build a secure, no-cost e-commerce website with AI agents. See all 24 episodes, the tools used and answers to common questions on the series page.

We use optional cookies for analytics and referral tracking. Learn more about our privacy policy