Course outline

Laravel / Application boundaries and reliable workflows / Lesson 9

Authentication and session lifecycle

Separate identity from permission, then reason about login, session rotation, protected requests, and logout as one coherent lifecycle.

About 5 min readContent reviewed
On this page

Lesson goals

  • Identity is not authorization
  • Rotate the session after successful authentication
  • Verify success, rejection, and persistence

Identity is not authorization

Authentication establishes who the application recognizes for a request. Authorization decides what that identity may do to a resource. Laravel guards define how requests are authenticated, while providers define how users are retrieved. A session guard and a token-based API arrangement have different browser and transport assumptions. Inspect the selected guard, middleware stack, and authentication configuration rather than assuming every authenticated request uses the same cookie flow. Starter-kit authentication can provide a maintained starting point, but adopting one does not remove responsibility for deployment cookies, session storage, throttling, account recovery, and the application’s permission policy.

A cookie session depends on several boundaries

A browser session requires the intended web middleware, a working configured session store, and cookies that reach the correct application origin. HTTPS, secure-cookie behavior, same-site policy, domain, and proxy configuration affect that path. A successful credential check can still appear to “forget” login if a later request uses a different host or the session cannot be persisted. Do not repair that symptom by exposing credentials in URLs or disabling CSRF protection. Browser form requests need the framework’s CSRF arrangement, while a selected API authentication integration has its own documented requirements. Keep actual credentials out of logs and teaching fixtures.

Rotate the session after successful authentication

The first controller fragment assumes a synthetic User fixture with a framework-hashed password, web routes, CSRF protection, and an already configured named login limiter. It validates the credential shape, passes the plaintext submitted password to Auth::attempt, and regenerates the session after success. Do not hash the submitted password first; the authentication provider checks it against the stored hash. The failure response retains only the email input and uses a generic credential message. The example does not install registration, password reset, email verification, or throttling configuration. Those supporting features must be supplied by the chosen starter kit or reviewed application design before deployment.

php Contextual Laravel 12 application fragment
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
    public function store(Request $request): RedirectResponse
    {
        $credentials = $request->validate([
            "email" => ["required", "email"],
            "password" => ["required", "string"],
        ]);
        if (Auth::guard("web")->attempt($credentials)) {
            $request->session()->regenerate();
            return redirect()->intended("/dashboard");
        }
        return back()->withErrors(["email" => "The provided credentials are invalid."])
            ->withInput($request->only("email"));
    }
}
Expected output
Valid isolated credentials: authenticated web session then intended redirect. Invalid credentials: email error and no password flash.

Requires a CSRF-protected web POST route, configured login rate limiting, session storage, and a hashed synthetic user password. Successful authentication rotates the session; failure never flashes the password.

End the authenticated browser session deliberately

The second fragment logs out the web guard, invalidates the session, and regenerates its CSRF token before redirecting. It belongs behind an appropriate POST route in the web middleware stack, not a state-changing GET link. Logging out one browser session is not automatically a policy for every device, every token, or every external identity provider. Decide those scopes separately. A request arriving after logout should be treated according to the route’s authentication contract. Test subsequent protected access instead of considering a redirect alone proof that authentication state was cleared. Concurrent tabs share browser session behavior and should receive understandable results.

php Contextual Laravel 12 application fragment
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
class LogoutController extends Controller
{
    public function destroy(Request $request): RedirectResponse
    {
        Auth::guard("web")->logout();
        $request->session()->invalidate();
        $request->session()->regenerateToken();
        return redirect("/");
    }
}
Expected output
After isolated logout, a subsequent protected web request follows the guest-access contract.

Attach this action to the training web POST logout route with normal CSRF handling. It ends this session, not every possible API token or device session.

Verify success, rejection, and persistence

Use isolated feature tests for valid credentials, invalid credentials, rate limiting under its configured key, unauthenticated protected access, session rotation, and post-logout denial. actingAs is useful for downstream authenticated behavior but does not exercise the credential-check or session-login path itself. A missing user is distinct from a forbidden operation by a known user, and HTML redirects can differ from JSON status responses. Authentication state can also depend on remember-me and multi-device policies, so test those only if the application actually supports them. This lesson provides reviewable fragments; it does not connect to a configured service or claim a real login was performed.

Common errors and fixes

Login succeeds once but disappears on the next request

Why it happens
Cookie origin or session persistence does not match the request path.
How to fix it
Inspect the configured store, host, HTTPS, and cookie policy.
Prevent it next time
Test consecutive requests through the intended origin.

Correct credentials fail after manual hashing

Why it happens
The submitted password was transformed before provider verification.
How to fix it
Pass the submitted password under the authentication contract.
Prevent it next time
Use the framework hash facility only when storing passwords.

A redirect is treated as proof of logout

Why it happens
Subsequent authenticated state was never checked.
How to fix it
Assert guest behavior on a later protected request.
Prevent it next time
Test the full session lifecycle.

Check your understanding

1. Why does actingAs not prove the login form correctly verifies credentials?

Show worked solution

It establishes a test identity for subsequent application behavior and bypasses the actual credential submission path. Add separate isolated requests to the login endpoint with valid and invalid synthetic passwords, then verify authentication and session behavior under the configured guard.

2. Should a logged-in user automatically be allowed to edit another user’s post?

Show worked solution

No. The session supplies an authenticated identity, while a policy must decide the particular edit. Keep the route authenticated and authorize the bound Post before changing it. Hiding the Edit button is presentation and cannot enforce the server operation.

Key takeaways

  • Authentication supplies identity, not blanket permission.
  • Session rotation and logout need lifecycle tests.
  • Guard and cookie assumptions must be explicit.

Official references