Admin Authentication
Website Rebuild with Laravel
Chapter 9 · Admin Authentication
Next.js Rebuild 9 wrote a bespoke bcryptjs.compare() call inside a NextAuth Credentials provider to match the existing site's own legacy bcrypt hash. Django Rebuild 9 had to reorder PASSWORD_HASHERS to add BCryptSHA256PasswordHasher, since Django's own default is PBKDF2, not bcrypt. Laravel needs neither workaround — its default hasher already is bcrypt.
Laravel's Default Hasher: Already Bcrypt
password_hash() has defaulted to for years. Laravel's Hash facade already defaults to that exact algorithm, with nothing to configure. Django needed a config change; Next.js needed a hand-written comparison function; Laravel needs neither — the framework's own out-of-the-box default already matches the legacy hash.
The Login Flow: Auth::attempt()
Auth::attempt($credentials) internally calls Hash::check($credentials['password'], $user->password) using whatever driver config/hashing.php names — bcrypt, by default. Since the legacy hash is itself bcrypt, that one built-in call is the entire authentication mechanism: no custom comparison function, no hasher class swap. session()->regenerate() issues a fresh session ID on login, guarding against session-fixation — the same reason Django Rebuild 9's own login flow rotated its session key.
Hash::needsRehash($user->password) — checked and re-saved on login if the hash's own cost factor is out of date — but unlike Django, it isn't automatic by default; a route has to call it explicitly.
Protecting Chapter 8's Mutation — Closing the Gap
Chapter 8's authorize() — deliberately left as a visible return true; placeholder — now returns Auth::check(). The auth middleware on the route provides one layer of protection; the FormRequest's own authorize() is a second, independent layer that fails the request with a 403 even if the route protection were ever misconfigured — the same defense-in-depth reasoning Django Rebuild 9's @staff_member_required decorator embodied for its own view.
Session-Based, Like Django — Not Like NextAuth's JWT
Laravel's default web guard is session-based — the same model Django's own session middleware already used. Next.js Rebuild 9's NextAuth setup, by contrast, issued a signed JWT stored in a cookie, with no server-side session record to invalidate directly. Laravel and Django both close a session in one server-side call (session()->invalidate() / Django's own logout()); revoking a NextAuth JWT before its own expiry needs a deliberate token-blacklist strategy that a plain session model doesn't.
Three Frameworks, Three Different Amounts of Work
| Next.js | Django | Laravel | |
|---|---|---|---|
| Default hasher | None — hand-rolled Credentials provider | PBKDF2 | Bcrypt |
| Work to match the legacy hash | Write a manual bcryptjs.compare() call | Add BCryptSHA256PasswordHasher to PASSWORD_HASHERS | None — the default already matches |
| Session model | Signed JWT cookie | Server-side session | Server-side session |
users table, can log in.
Hands-On Exercises
Explain why Laravel needs no hasher-configuration change to authenticate against the legacy site's own bcrypt hash, while Django Rebuild 9 needed to add BCryptSHA256PasswordHasher to PASSWORD_HASHERS.
Explain what changed in UpdatePageTitleRequest::authorize() in this chapter, and why both the route's auth middleware and the FormRequest's own check together provide defense in depth rather than being redundant.
Explain why revoking a Next.js/NextAuth JWT session before its natural expiry is harder than ending a Laravel or Django session, and what a JWT-based approach would need to add to close that gap.
📄 View solutionChapter 9 Quick Reference
- Standout payoff — Laravel's default
Hashdriver is already bcrypt, matching the legacy hash with zero configuration Auth::attempt($credentials)— validates the password against the stored hash using the configured driver, in one callsession()->regenerate()— issues a fresh session ID on login, guarding against session fixationHash::needsRehash()— Laravel's manual equivalent of Django's automatic hash-upgrade-on-login- Defense in depth —
authmiddleware on the route, plusauthorize()now returningAuth::check()on theFormRequestitself - Session-based — like Django, unlike Next.js's own JWT-cookie NextAuth setup
- Next chapter: Admin CRUD Interface