Authentication
๐ Authentication
Laravel Breeze: Scaffolding, Not a Closed Box
composer require laravel/breeze --dev php artisan breeze:install # Which stack would you like to install? # > blade / react / vue / api npm install && npm run build php artisan migrate
breeze:install generates actual files into your project โ login/register controllers, Blade views, routes โ every one of them a normal, editable file sitting in app/ and resources/, not framework internals you're expected to leave alone.
Django's Built-in Auth vs Laravel's Installed Scaffolding
Django
django.contrib.auth is always active. Login/logout views, the User model, and password hashing all live inside the framework โ you use them, but generally don't edit their internals.
Laravel
breeze:install generates real controller and view files into your codebase โ editing app/Http/Controllers/Auth/RegisteredUserController.php directly is completely normal and expected.
๐ค The User Model
Unlike Django (where auth is a separate contrib app), every new Laravel project already ships with App\Models\User from create-project โ auth-readiness is baked in from day one, even before Breeze is installed:
// app/Models/User.php (already present in a fresh Laravel install) namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { protected $fillable = ['name', 'email', 'password']; protected function casts(): array { return ['password' => 'hashed']; // automatic hashing on assignment } }
Authenticatable
The base class that gives User everything the auth system needs โ password checking, "remember me" tokens โ the same role Django's AbstractUser plays.
'password' => 'hashed' Cast
Setting $user->password = 'plaintext' hashes it automatically on save โ no separate create_user()-style method required the way Django's User.objects.create_user() is.
Password Hashing
Laravel's default algorithm is bcrypt, not Django's PBKDF2 โ same underlying goal (the Auth & Session Security course's guidance), different default choice:
use Illuminate\Support\Facades\Hash; Hash::make('plaintext'); // bcrypt hash, by default Hash::check('plaintext', $user->password); // verify โ same idea as Django's check_password()
Switching to Argon2id (the algorithm the security course recommended) is a config change, exactly like Django's PASSWORD_HASHERS:
// config/hashing.php 'driver' => 'argon2id',
๐ช Session-Based Login
Breeze's generated login controller uses the Auth facade โ conceptually identical to Django's authenticate()/login()/logout():
use Illuminate\Support\Facades\Auth; public function store(Request $request) { $credentials = $request->validate([ 'email' => 'required|email', 'password' => 'required', ]); if (Auth::attempt($credentials)) { $request->session()->regenerate(); // prevents session fixation โ same as Django's login() return redirect()->intended('dashboard'); } return back()->withErrors(['email' => 'Invalid credentials.']); }
$request->session()->regenerate() is the explicit call Laravel requires for the same session-fixation protection Django's login() applies automatically โ a real difference worth noticing: Django rotates the session key for you, Breeze's generated code does it as one visible line you own and could (incorrectly) remove.
Protecting Routes: The auth Middleware
Chapter 8 of Course 1 covered route-scoped middleware โ auth is the built-in alias for exactly this:
Route::middleware('auth')->group(function () { Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard'); });
This is the same job as Django's @login_required, expressed as middleware attached to routes rather than a decorator on view functions โ an anonymous request redirects to the login route automatically, same behavior, different mechanism.
๐ซ API Authentication: Sanctum
Session-based auth (via Breeze) is for browser-facing web routes. For APIs, Laravel Sanctum issues tokens instead โ a brief preview before Course 2's API Resources chapter goes further:
// Issuing a token (e.g. from a mobile app login endpoint) $token = $user->createToken('mobile-app')->plainTextToken; // Protecting an API route with token auth instead of session auth Route::middleware('auth:sanctum')->get('/api/user', function (Request $request) { return $request->user(); });
Session Guard vs Sanctum Guard
Laravel's auth middleware defaults to the session guard for web routes; auth:sanctum switches to token-based auth โ one framework, two auth strategies selected per-route.
Comparable to FastAPI/Express JWT
Sanctum tokens play a similar role to the JWTs seen in the FastAPI/Express comparisons throughout this project โ a bearer credential the client resends, rather than a server-side session.
๐ป Coding Challenges
Challenge 1: Trace a Breeze Login Flow
After installing Breeze in a fresh project, locate the generated AuthenticatedSessionController's store() method and identify: where credentials are validated, where Auth::attempt() is called, and where the session is regenerated.
Goal: Practice reading generated scaffolding as real, ownable code rather than a black box.
Challenge 2: Protect a Route Group
Wrap an /orders route group (from Course 1's middleware challenge) with the auth middleware so only logged-in users can reach it, and explain what response an anonymous request receives.
Goal: Practice applying auth middleware to a route group, reusing Course 1's grouping pattern.
Challenge 3: Issue and Use a Sanctum Token
Write the code to issue a Sanctum token for a user after a successful API login, and a route protected with auth:sanctum that returns the authenticated user's data.
Goal: Practice the token-based auth pattern for an API, separate from session-based web auth.
A developer coming from Django often hesitates to touch anything Breeze generated, out of a (reasonable, in Django's world) instinct that "framework auth code" shouldn't be edited. In Laravel, the opposite is true: Breeze's generated controllers and views are yours the moment they're generated โ customizing RegisteredUserController to add an extra field, or restyling the login Blade view, is the expected workflow, not a maintenance risk. The trade-off is real too: because you own this code, forgetting something Django would have handled automatically (like the explicit session()->regenerate() call above) is now your responsibility to get right, not the framework's.
๐ฏ What's Next
With login working, the next chapter turns to building actual APIs on top of these models: API Resources โ Laravel's answer to DRF's serializers, for shaping Eloquent models into JSON responses.