Authentication

Laravel Intermediate/Advanced
Course 2 ยท Chapter 1 ยท Authentication

๐Ÿ” Authentication

Django's auth system is always there, active from the first request, entirely inside the framework. Laravel takes a genuinely different approach: auth is opt-in scaffolding you install via a starter kit โ€” and crucially, it hands you real, editable code rather than hiding the implementation inside the framework.

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.

โ†’ Solution

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.

โ†’ Solution

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.

โ†’ Solution

โš ๏ธ Gotcha: Editing Generated Auth Code Is Normal Here โ€” Unlike Django

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.