In this article
April 3, 2026
April 3, 2026

Building authentication in Laravel applications: The complete guide for 2026

Master secure authentication in Laravel from Breeze and Sanctum to enterprise SSO, with production-ready patterns and security best practices.

Authentication in Laravel has always been one of the framework's strongest selling points. With Laravel 11's streamlined application structure, first-party packages like Breeze, Jetstream, and Fortify, and an ecosystem built around convention and developer experience, Laravel makes it straightforward to implement authentication that ranges from simple session-based login to full enterprise SSO.

This guide covers everything you need to know about authentication in Laravel: from core concepts and security patterns to implementation strategies and production best practices. Whether you're using Laravel's built-in auth scaffolding, reaching for a starter kit, or evaluating managed solutions like WorkOS, you'll gain the knowledge to make informed decisions for your Laravel application.

Understanding authentication in Laravel

Laravel approaches authentication with strong opinions about developer experience and sensible defaults. The framework ships with a complete authentication system out of the box, including guards, providers, password hashing, session management, and CSRF protection.

The Laravel request lifecycle

Understanding how Laravel processes requests is foundational to implementing authentication correctly:

  1. Request arrives: The HTTP request enters through public/index.php and is handed to the application kernel.
  2. Middleware execution: Global middleware runs first, followed by route-specific middleware. The auth middleware checks whether the user has a valid session before the request reaches your controller.
  3. Route resolution: Laravel's router matches the request to a controller action or closure.
  4. Controller execution: Your controller method processes the authenticated request. The Auth facade and $request->user() give you access to the authenticated user.
  5. Response: Laravel sends the HTTP response, writing session data and encrypting cookies automatically.

Laravel's authentication happens primarily in middleware. The auth middleware checks guards (session, token, or custom) and either allows the request through or redirects to a login page. This keeps authentication concerns separate from your business logic.

Laravel's philosophy

Laravel provides several security features by default that protect you without extra configuration:

  • Bcrypt password hashing. Laravel's Hash facade uses bcrypt by default (configurable to Argon2 in config/hashing.php). When you use Hash::make($password), Laravel automatically salts and hashes the password with a secure work factor. You never store plain text passwords.
  
use Illuminate\Support\Facades\Hash;

// Hashing
$hashed = Hash::make('secret-password');

// Verification (constant-time comparison built in)
if (Hash::check('secret-password', $hashed)) {
    // Passwords match
}
  
  • Automatic CSRF protection. Every non-GET route in Laravel is protected by CSRF verification through the VerifyCsrfToken middleware. Blade's @csrf directive generates a hidden token field in your forms, and Laravel validates it on submission. This prevents malicious sites from submitting forms on behalf of your users.
  
<form method="POST" action="/login">
    @csrf
    <input type="email" name="email">
    <input type="password" name="password">
    <button type="submit">Log in</button>
</form>
  
  • Encrypted cookies and sessions. Laravel encrypts all cookies using the APP_KEY in your .env file. Session data stored in cookies is both encrypted and signed, making it tamper-proof and unreadable to clients. This happens automatically with zero configuration.
  • Mass assignment protection. Eloquent models require you to explicitly define which attributes are fillable via the $fillable or $guarded properties. This prevents attackers from submitting unexpected fields (like is_admin=1) through form requests.
  
class User extends Authenticatable
{
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];
}
  
  • Automatic output escaping. Blade templates escape all output by default with {{ }}. User-submitted content like <script>alert('xss')</script> is rendered as harmless text, preventing cross-site scripting (XSS) attacks. Only {!! !!} renders raw HTML, and you should use it sparingly.

Critical security considerations

Laravel provides strong defaults, but understanding common vulnerabilities and how to guard against them is essential.

SQL injection protection

Eloquent and the query builder automatically use parameterized queries, making SQL injection very difficult:

  
// Safe: parameterized automatically
User::where('email', $request->email)->first();

// Safe: query builder with bindings
DB::table('users')->where('email', '=', $request->email)->first();

// Dangerous: raw expressions with user input
DB::select("SELECT * FROM users WHERE email = '{$request->email}'");
  

Always use Eloquent methods or the query builder's parameter binding. If you must write raw SQL, use bindings:

  
DB::select('SELECT * FROM users WHERE email = ?', [$request->email]);
  

Cross-site scripting (XSS) protection

Blade's {{ }} syntax escapes HTML entities automatically:

  
{{-- Safe: automatic escaping --}}
<p>Welcome, {{ $user->name }}</p>

{{-- Dangerous: raw output --}}
<p>Welcome, {!! $user->name !!}</p>

{{-- Safe: use only for trusted, sanitized HTML --}}
<div>{!! clean($user->bio) !!}</div>
  

Never use {!! !!} with user-provided content unless you have sanitized it first (for example, with a package like mews/purifier).

Session security

Laravel encrypts session data by default, but proper configuration is still important:

  
// config/session.php
return [
    'driver' => env('SESSION_DRIVER', 'database'),
    'lifetime' => 120,           // minutes
    'expire_on_close' => false,
    'encrypt' => true,           // default in Laravel 11
    'cookie' => env('SESSION_COOKIE', 'laravel_session'),
    'secure' => env('SESSION_SECURE_COOKIE', true),   // HTTPS only
    'http_only' => true,          // no JavaScript access
    'same_site' => 'lax',        // CSRF protection
];
  

In production, always set SESSION_SECURE_COOKIE=true in your .env to ensure cookies are only transmitted over HTTPS.

Password security

Laravel defaults to bcrypt with a cost factor of 12. You can configure this or switch to Argon2 in config/hashing.php:

  
// config/hashing.php
return [
    'driver' => 'bcrypt',

    'bcrypt' => [
        'rounds' => env('BCRYPT_ROUNDS', 12),
    ],

    'argon' => [
        'memory' => 65536,
        'threads' => 1,
        'time' => 4,
    ],
];
  

The default cost of 12 takes approximately 250 to 300ms per hash, which is deliberate: it makes brute-force attacks impractical while remaining fast enough for normal login flows. Avoid lowering this in production. If you need faster hashing in tests, set BCRYPT_ROUNDS=4 in your testing .env.

!!For more on password security, see The developer's guide to strong passwords.!!

Password best practices:

  • Require a minimum of 8 characters (12 or more is strongly recommended).
  • Use Laravel's built-in Password validation rule for strength enforcement.
  • Rate limit login attempts (Laravel Breeze and Fortify do this by default).
  • Implement account lockout after repeated failures.
  • Check passwords against known breach databases using Password::uncompromised().
  • Avoid strict complexity rules like requiring special characters. Length is more effective.
  
use Illuminate\Validation\Rules\Password;

$request->validate([
    'password' => [
        'required',
        'confirmed',
        Password::min(12)
            ->letters()
            ->mixedCase()
            ->numbers()
            ->uncompromised(),
    ],
]);
  

Rate limiting

Laravel includes rate limiting through the RateLimiter facade and the throttle middleware. Protect your login endpoint from brute-force attacks:

  
// bootstrap/app.php or a service provider
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

RateLimiter::for('login', function ($request) {
    return [
        Limit::perMinute(5)->by($request->input('email')),
        Limit::perMinute(10)->by($request->ip()),
    ];
});
  
  
// routes/web.php
Route::post('/login', [LoginController::class, 'store'])
    ->middleware('throttle:login');
  

This limits login attempts to 5 per minute per email address and 10 per minute per IP, making credential-stuffing attacks impractical while still accommodating legitimate users on shared networks.

Authentication implementation approaches

Laravel offers several well-supported paths for implementing authentication, from minimal starter kits to comprehensive packages and managed providers.

Approach 1: Laravel Breeze

Breeze is Laravel's lightweight authentication starter kit. It generates all the authentication scaffolding you need: registration, login, password reset, email verification, and profile management. The generated code lives in your application, giving you full visibility and control.

  
composer require laravel/breeze --dev
php artisan breeze:install blade
php artisan migrate
npm install && npm run dev
  

Breeze supports Blade, Livewire, React (Inertia), and Vue (Inertia) stacks. It uses Laravel's built-in authentication under the hood and generates simple, readable controllers you can customize freely.

What Breeze provides:

  • Registration, login, and logout flows.
  • Password reset with email verification.
  • Profile editing.
  • Rate-limited login attempts.
  • CSRF protection on all forms.

What Breeze does not include:

  • Two-factor authentication.
  • OAuth or social login.
  • Enterprise SSO.
  • Team or organization management.
  • API token management.

When to use Breeze:

  • You want a clean starting point with minimal abstraction.
  • You prefer to own all the authentication code and customize it freely.
  • Your requirements are straightforward email and password login.

Approach 2: Laravel Jetstream

Jetstream builds on top of Fortify and adds a richer feature set, including two-factor authentication, session management, API tokens via Sanctum, and optional team support. It's available with Livewire or Inertia stacks.

  
composer require laravel/jetstream
php artisan jetstream:install livewire --teams
php artisan migrate
npm install && npm run dev
  

What Jetstream provides:

  • Everything in Breeze, plus:
  • Two-factor authentication (TOTP via authenticator apps).
  • Browser session management (view and revoke active sessions).
  • API token management (via Laravel Sanctum).
  • Optional team/organization features with role-based permissions.
  • Profile photo uploads.

When to use Jetstream:

  • You need two-factor authentication, API tokens, or team management out of the box.
  • You want a more complete starting point and are comfortable with Jetstream's conventions.

Jetstream considerations:

  • Jetstream is more opinionated than Breeze.
  • Customizing its views and workflows requires understanding its Fortify integration and component structure.
  • For applications that will eventually need enterprise features like SSO or directory sync, Jetstream does not include those and you would need to build or integrate them yourself.

Approach 3: Laravel Fortify (headless)

Fortify is the backend authentication engine that powers Jetstream, but it can be used independently. It provides all the authentication logic without any views, making it ideal for single-page applications or custom frontends.

  
composer require laravel/fortify
php artisan fortify:install
php artisan migrate
  

Fortify handles registration, login, password reset, email verification, and two-factor authentication through JSON-friendly routes. You build the frontend yourself and point forms at Fortify's endpoints.

When to use Fortify:

  • You're building a custom frontend (Vue SPA, React SPA, mobile app) and only need backend authentication logic.
  • You want control over the UI but don't want to re-implement password hashing, rate limiting, and session management.

Approach 4: Laravel Sanctum (API authentication)

Sanctum provides lightweight API authentication for SPAs and mobile applications. It supports two modes: cookie-based session authentication for same-domain SPAs, and token-based authentication for mobile apps or third-party consumers.

  
// Token-based authentication
$token = $user->createToken('mobile-app', ['read:orders'])->plainTextToken;

// Protecting API routes
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', function (Request $request) {
        return $request->user();
    });
});
  

For SPAs hosted on the same domain, Sanctum uses Laravel's session-based authentication with a CSRF cookie. For mobile apps or third-party consumers, it issues API tokens stored in a personal_access_tokens table.

When to use Sanctum:

  • You need API authentication for a first-party SPA or mobile app.
  • You want simple, scoped API tokens without the complexity of a full OAuth2 server.

Approach 5: Laravel Passport (OAuth2)

Passport provides a full OAuth2 server implementation for Laravel, built on the League OAuth2 Server. It supports authorization code grants, client credentials, personal access tokens, and refresh tokens.

  
composer require laravel/passport
php artisan passport:install
  

When to use Passport:

  • You're building a platform that needs to issue OAuth2 tokens to third-party developers.
  • You need full OAuth2 compliance with authorization codes, refresh tokens, and scoped access.

Approach 6: Managed authentication provider

The five approaches above all run entirely within your Laravel application. A managed authentication provider takes a different path: it handles authentication flows on external infrastructure and returns authenticated users to your app via a standard OAuth-style callback. You don't build login pages, manage password hashing, or implement MFA yourself. The provider handles all of that and gives you back a verified user.

This approach makes sense when your team wants to focus on product development rather than maintaining authentication infrastructure, especially if you anticipate enterprise requirements like SSO, directory sync, or compliance certifications. Building those features in-house can take months and cost significantly more than delegating them to a provider built for that purpose.

Several providers serve the Laravel ecosystem. When evaluating options, look for a dedicated PHP SDK, support for Laravel's middleware and session conventions, a generous free tier, and a clear path from basic auth to enterprise features without requiring a rewrite.

WorkOS is a strong fit here. Its AuthKit product covers the full range of authentication needs (email/password, magic links, social login, MFA, enterprise SSO, and directory sync) in a single platform, with a free tier that supports up to 1 million monthly active users. It also provides an AI installer that detects your Laravel setup and generates the integration automatically:

  
npx workos@latest install
  

The CLI installs the PHP SDK, generates callback routes, configures environment variables, and validates the integration. You can also set up the integration manually. The pattern follows the same redirect-and-callback flow that Laravel developers are already familiar with from Socialite:

  
// routes/web.php
use WorkOS\WorkOS;

Route::get('/login', function () {
    $workos = new WorkOS(env('WORKOS_API_KEY'));

    $url = $workos->userManagement->getAuthorizationUrl(
        provider: 'authkit',
        clientId: env('WORKOS_CLIENT_ID'),
        redirectUri: route('callback'),
    );

    return redirect($url);
});

Route::get('/callback', function (Request $request) {
    $workos = new WorkOS(env('WORKOS_API_KEY'));

    $authResponse = $workos->userManagement->authenticateWithCode(
        clientId: env('WORKOS_CLIENT_ID'),
        code: $request->query('code'),
        session: [
            'sealSession' => true,
            'cookiePassword' => env('WORKOS_COOKIE_PASSWORD'),
        ],
    );

    // Store sealed session in an encrypted cookie
    cookie()->queue(
        'wos_session',
        $authResponse->sealedSession,
        0,       // session cookie
        '/',
        null,
        true,    // secure
        true,    // httpOnly
        false,
        'Lax'    // sameSite
    );

    return redirect('/dashboard');
})->name('callback');
  

Beyond basic authentication, WorkOS provides enterprise SSO (SAML and OIDC) without additional code, SCIM-based directory sync for automatic user provisioning, organization and team management with built-in multi-tenancy, audit logs, bot protection, and compliance features. These capabilities are available from day one and build on each other as your requirements grow.

When to use a managed provider:

  • You are building B2B software and expect to sell to enterprises that require SSO, directory sync, or compliance features.
  • You want authentication fully managed so your team can focus on product development.
  • You want to ship quickly without sacrificing enterprise readiness.

Build vs. buy: a realistic comparison

Laravel's starter kits make it easy to get authentication running quickly. The real cost is everything that comes after: email verification, password resets, MFA, OAuth, session management across devices, audit logging, and the ongoing security maintenance to keep it all patched and compliant.

Realistic time estimates for building in Laravel:

  • MVP (email/password with Breeze): 1 to 2 days.
  • Production-ready (with MFA, OAuth, account management): 4 to 8 weeks.
  • Enterprise-grade (SSO, SCIM, compliance): 3 to 6 months or more.
  • Ongoing maintenance: roughly 20 to 25% of the initial effort each year.

A managed provider compresses most of that into a few hours of integration work and shifts the security maintenance burden off your team. The trade-off is a dependency on an external service, so evaluate based on SDK quality, Laravel compatibility, pricing at your expected scale, and whether the provider covers the enterprise features (SSO, directory sync, compliance) your customers will eventually require.

For most B2B SaaS teams, the question is not whether you can build authentication yourself. The question is whether it is the best use of your engineering time.

Production best practices

Security checklist

  • Keep Laravel, PHP, and Composer dependencies updated. Subscribe to Laravel's security advisories.
  • Use Hash::make() for password hashing. Never store plain text passwords.
  • Enable CSRF protection on all non-GET routes (enabled by default).
  • Set SESSION_SECURE_COOKIE=true and SESSION_HTTP_ONLY=true in production.
  • Use $fillable or $guarded on all Eloquent models to prevent mass assignment.
  • Escape all output with {{ }} in Blade templates. Never use {!! !!} with user input.
  • Use Eloquent or parameterized queries. Never interpolate user input into raw SQL.
  • Store secrets in .env and never commit them to version control.
  • Implement rate limiting on login, registration, and password reset endpoints.
  • Use Laravel's Password::uncompromised() rule to check passwords against known breaches.
  • Force HTTPS in production via APP_URL=https://... and middleware or server configuration.
  • Run static analysis tools and audit Composer dependencies regularly.
  • Log authentication events (logins, failures, password changes) and monitor for anomalies.

Performance checklist

  • Add database indexes on users.email and any columns used for authentication lookups.
  • Use Redis or Memcached for session storage in load-balanced environments.
  • Cache expensive permission and role checks with a short TTL (for example, use Cache::remember() with a 5-minute expiry and invalidate explicitly when permissions change).
  • Eager load user relationships with User::with(['roles', 'permissions']) to avoid N+1 queries in authenticated views.
  • Enable OPcache in production to reduce the overhead of bootstrapping Laravel on each request.
  • Profile authentication middleware with Laravel Debugbar or Telescope in development.

Deployment checklist

  • Generate a strong APP_KEY with php artisan key:generate. Never reuse keys across environments.
  • Configure a production session driver (database or Redis).
  • Set up queue workers for sending emails (verification, password reset, notifications).
  • Enable HTTPS and redirect all HTTP traffic.
  • Configure your web server (Nginx or Apache) as a reverse proxy. Do not expose php artisan serve to the internet.
  • Set up automated database backups and test restoration regularly.
  • Configure monitoring and alerting for authentication endpoint errors and latency.
  • Set appropriate session lifetimes and idle timeouts for your security requirements.

Conclusion

Laravel's authentication ecosystem is one of the most complete in any web framework. Between the built-in Auth system, starter kits like Breeze and Jetstream, and specialized packages like Sanctum and Passport, you have well-tested options for nearly every authentication scenario.

If you are building authentication yourself, start with Breeze or Jetstream for rapid scaffolding, leverage Laravel's built-in rate limiting, CSRF protection, and password validation, and plan for ongoing security maintenance. If you are considering a managed provider, evaluate based on Laravel SDK quality, pricing at your expected scale, and whether it covers the enterprise features your customers will eventually need.

Authentication is critical infrastructure. Choose the approach that matches where your application is headed, not just where it is today.

This site uses cookies to improve your experience. Please accept the use of cookies on this site. You can review our cookie policy here and our privacy policy here. If you choose to refuse, functionality of this site will be limited.