In one sentence

Authentication proves who you are once, then a session cookie lets Laravel remember you on every request that follows.

🎟️It's like a music festival. At the gate you show your ID and ticket (email + password). They check it once and put a wristband on you (the session cookie). For the rest of the weekend, staff just glance at the wristband instead of asking for your ID again.

Why it matters

Almost every real app has a “Log in” button

🪪

Personal data

Dashboards, orders and drafts belong to one person. You need to know who is asking before you show them.

🔒

Security basics

Storing passwords safely and handling sessions correctly protects your users even if something leaks.

🧠

Demystify the magic

Starter kits write the login code for you. Knowing what happens underneath makes that code easy to read and change.

The big picture

Two different questions

People mix these two words up all the time, so let's pin them down first.

Authentication
Who are you? Checking someone's identity, usually with an email and password. This is Module 07.
Authorization
What are you allowed to do? Checking whether a known user may edit this post. This is Module 08.
Session
A small bundle of data Laravel stores on the server (in a file, the database or Redis) for one visitor.
Session cookie
A random ID stored in the browser. It points to that visitor's session, so Laravel can find it again.
Hash
A one-way scramble of a password. You can check a guess against it, but you can't turn it back into the password.
📝1. Submit the login formPOST /login with email + password
🔍2. Find the user and check the hashHash::check(typed password, stored hash)
🎟️3. Store the user ID in the sessionbrowser receives a laravel_session cookie
🔁4. Every later request sends the cookieLaravel loads the session → user ID → User model
👋5. Log outsession is wiped, and the cookie no longer points to a user
The password is checked exactly once. After that, the session cookie is what identifies you.
ℹ️ HTTP forgets everything

As you saw in HTTP Basics, every request is independent. Without the session cookie, the server would have no idea that request #2 came from the same person who logged in on request #1.

Step by step

Build a login by hand (once)

In real projects a starter kit writes this for you. Building it once by hand shows you exactly what that generated code does.

  1. Never store plain passwords: hash them

    Hashing turns secret123 into a long, random-looking string. Laravel uses bcrypt by default, which is deliberately slow so attackers can't guess millions of passwords per second.

    
              use Illuminate\Support\Facades\Hash;
    
              $hash = Hash::make('secret123');
              // "$2y$12$Qm3k...a long string that changes every time"
    
              Hash::check('secret123', $hash);   // true
              Hash::check('wrong-guess', $hash); // false
            

    Run Hash::make twice on the same password and you get two different hashes. That's because of a random salt, and Hash::check still handles it correctly.

  2. Let the model hash for you

    The default User model already has a hashed cast. Any plain password you assign is hashed automatically before it's saved.

    
              protected function casts(): array
              {
                  return [
                      'email_verified_at' => 'datetime',
                      'password' => 'hashed',
                  ];
              }
            
    
              $user = User::create([
                  'name' => 'Ada',
                  'email' => 'ada@example.com',
                  'password' => 'secret123',   // stored as a bcrypt hash
              ]);
            
  3. Log in with Auth::attempt

    Auth::attempt() finds the user by email, runs Hash::check on the password and, if it matches, stores the user's ID in the session. It returns true or false.

    
              <?php
    
              namespace App\Http\Controllers;
    
              use Illuminate\Http\Request;
              use Illuminate\Support\Facades\Auth;
    
              class LoginController extends Controller
              {
                  public function store(Request $request)
                  {
                      $credentials = $request->validate([
                          'email' => ['required', 'email'],
                          'password' => ['required'],
                      ]);
    
                      if (Auth::attempt($credentials, $request->boolean('remember'))) {
                          $request->session()->regenerate();
                          return redirect()->intended('/dashboard');
                      }
    
                      return back()->withErrors([
                          'email' => 'These credentials do not match our records.',
                      ])->onlyInput('email');
                  }
              }
            

    redirect()->intended() sends the user back to the page they originally tried to visit, or to /dashboard if there wasn't one.

  4. Why session()->regenerate() matters

    Regenerating gives the visitor a brand-new session ID the moment they log in. This blocks session fixation, an attack where someone plants a known session ID in your browser and waits for you to log in with it.

    ⚠️ Always regenerate after login

    Skipping this line doesn't break anything you can see, which is exactly why it's dangerous. Starter kits include it for you.

  5. Wire up the routes and the form

    The form is ordinary HTML with @csrf. The remember checkbox sets a long-lived “remember me” cookie, so the user stays logged in after the browser closes.

    
              use App\Http\Controllers\LoginController;
    
              Route::view('/login', 'auth.login')->name('login')->middleware('guest');
              Route::post('/login', [LoginController::class, 'store'])->middleware('guest');
            
    
              <form method="POST" action="/login">
                  @csrf
                  <input type="email" name="email" value="{{ old('email') }}" required>
                  <input type="password" name="password" required>
                  <label><input type="checkbox" name="remember"> Remember me</label>
                  @error('email') <p>{{ $message }}</p> @enderror
                  <button>Log in</button>
              </form>
            

    You'll meet the guest middleware and logging out properly in Guards & Protecting Routes.

Common mistakes

Avoid this, do that

❌ Avoid

          $user = User::where('email', $email)
              ->where('password', $password)
              ->first();
        

This only works if passwords are stored in plain text, which is a disaster waiting to happen.

✅ Do

          if (Auth::attempt(['email' => $email, 'password' => $password])) {
              $request->session()->regenerate();
          }
        

Let Laravel look the user up and compare hashes safely.

❌ Avoid

          'password' => md5($request->password),
        

MD5 and SHA1 are fast, so attackers can test billions of guesses. They are not password hashes.

✅ Do

          'password' => Hash::make($request->password),
        

Use Hash::make (or the hashed cast), which is slow on purpose and salted automatically.

💡 Pro tip: friendly but vague errors

Say “These credentials do not match our records”, not “No account with that email”. Vague messages stop attackers from discovering which emails are registered. You'll learn more in Hashing & Encryption.

🏋️ Try it yourself
  1. Open php artisan tinker and run Hash::make('hello') twice. Compare the two results.
  2. Check one of those hashes with Hash::check('hello', $hash) and with a wrong password.
  3. Create a user in tinker with a plain password and look at the password column in the database.
  4. Build the LoginController above and log in. Then open your browser dev tools and find the laravel_session cookie.
  5. Log in again and watch the cookie value change, thanks to regenerate().
Recap

Key takeaways

Check yourself

Mini quiz