Authentication Lab
Step into the Dojo
Section titled “Step into the Dojo”Alright, we’ve installed the deadbolts, hired the bouncers, and established a strict VIP list for the Projects Portfolio. Now it’s time to test the locks. Authentication is one of those systems where “almost correct” usually means “completely vulnerable,” so the best way to understand the machinery is to push it until it breaks.
Here are three tiers of challenges to solidify your grasp on Passport, sessions, and authorization middleware.
Tier 1: Maintenance (Easy)
Section titled “Tier 1: Maintenance (Easy)”The Objective: Successfully block an unauthorized user from viewing a specific, isolated route.
Right now, our requireAuth middleware is likely guarding the entire /admin area. Let’s practice applying it on a granular level.
- Create a brand new “secret” route in your app, perhaps
/projects/easter-egg. - Add a simple EJS view that just renders “You found the secret stash!”
- Apply the
requireAuthmiddleware only to this specific Easter egg route, while leaving the rest of the public/projectsarea wide open. - Try to access it while logged out. You should be bounced to
/login. - Log in, and verify you can view the secret stash.
Student-to-AI Prompt: Hey AI, I’m trying to protect a single Express
route (/projects/easter-egg) using a custom middleware function called
requireAuth. Can you show me the exact syntax for injecting middleware
directly into a single app.get() definition?
Tier 2: The Custom Job (Medium)
Section titled “Tier 2: The Custom Job (Medium)”The Objective: Customize the Login experience by adding dynamic flash messages using session data.
Currently, if a user types the wrong password, Passport intercepts the failure and redirects them. But the user experience is terrible—they are just dumped back to the /login page with no explanation. Let’s fix that.
- Review how your Passport Local Strategy Verify Callback handles errors (e.g.
return done(null, false, { message: 'Invalid credentials.' })). - Integrate the
connect-flashpackage (or simply utilizereq.sessionmanually) to store that failure message temporarily. - On the
GET /logincontroller, check if a flash message exists. If it does, pass it into the rendered EJS view. - Update your
login.ejstemplate to render the error message in bright, obnoxious red.
Student-to-AI Prompt: I am using Passport.js Local Strategy. When
passport.authenticate('local') fails, I want it to display the ‘message’
from my verify callback on the login page. Explain how connect-flash works
with Passport to achieve this, or how I can do it manually with
req.session.
Tier 3: The Solo Special (Hard)
Section titled “Tier 3: The Solo Special (Hard)”The Objective: Implement a primitive “Lockout” mechanism to deter brute-force credential stuffing.
We aren’t building a bank, but we are building secure habits. If a bot tries to log in to admin@example.com with 50 different passwords in 10 seconds, we should probably stop them.
- Modify your
UserMongoDB Schema to include two new fields:failedLoginAttempts(Number, default0) andlockoutUntil(Date). - Inside your Passport Verify Callback:
- Before checking the password, check if
lockoutUntilis in the future. If it is, immediately return a failure message: “Account temporarily locked.” - If the password comparison fails, increment
failedLoginAttempts. - If
failedLoginAttemptsreaches5, setlockoutUntilto 15 minutes from now. - If the password comparison succeeds, reset
failedLoginAttemptsto0and clearlockoutUntil.
- Before checking the password, check if
- Test the mechanism by intentionally failing to log in 5 times in a row.
Student-to-AI Prompt: I am trying to implement a failed login lockout in
my Passport Verify Callback. Given my User schema has failedLoginAttempts
and lockoutUntil, how can I update these fields in MongoDB and correctly
reject the login attempt using the return done() callback?
⏭ Next: Authentication Codex:
Section titled “⏭ Next: Authentication Codex:”Done experimenting? Grab your authentication reference guide.