Blog

Laravel Authentication: Getting Started and HTTP Auth Tutorial

laravel authentication

Laravel authentication is built into the framework. Laravel ships guards and providers that log users in, keep them signed in with sessions, and protect routes with the auth middleware. In Laravel 13, the fastest way to add a full login system is a starter kit: laravel new scaffolds login, registration, password reset, email verification and two-factor authentication, powered by Laravel Fortify. For APIs, Laravel Sanctum issues tokens, and Laravel Passport adds a full OAuth 2.0 server.

Which Laravel authentication option should you use?

You need Use Notes
A complete login UI for a new app A starter kit (React, Svelte, Vue or Livewire) Created with laravel new. Includes login, registration, password reset, email verification, TOTP two-factor auth and passkeys
Auth backend only, your own front end Laravel Fortify Headless: routes and logic, no UI
Auth for your own SPA on the same top-level domain Laravel Sanctum (SPA mode) Uses Laravel's session cookies, not API tokens
API tokens for mobile apps, scripts or third-party clients Laravel Sanctum (API tokens) php artisan install:api
Your app must be an OAuth 2.0 server for third parties Laravel Passport Use only if you need OAuth; Sanctum covers most apps
Quick protection for an internal route HTTP Basic auth (auth.basic) Uses the browser's login prompt; always over HTTPS
SSO, SCIM and multi-tenant B2B user management A CIAM platform Connect over OpenID Connect and validate its tokens in Laravel

Breeze and Jetstream, the starter kits used before Laravel 12, no longer receive updates. The older laravel/ui package is legacy too. Use the current starter kits for new projects.

How Laravel authentication works

Laravel's auth system has two building blocks:

  • Guards decide how users are authenticated on each request. The default web guard uses the session and cookies. Token guards such as Sanctum's read a token from the request.
  • Providers decide how users are loaded from storage. The default provider uses Eloquent and the App\Models\User model.

Both are configured in config/auth.php. Passwords are hashed with bcrypt by default (Argon2 is also supported) using the Hash facade, and Laravel never stores plain-text passwords.

Setting up authentication in Laravel 13

Create an app with a starter kit

Install the Laravel installer, create an app and pick a starter kit when prompted:

composer global require laravel/installer
laravel new my-app
cd my-app
npm install && npm run build
composer run dev

The React, Svelte and Vue kits use Inertia, TypeScript, Tailwind and shadcn components. The Livewire kit uses Flux UI. All four use Laravel Fortify for the auth backend. You can turn features on or off in config/fortify.php, for example registration, password resets, email verification and two-factor authentication.

Get the authenticated user

use Illuminate\Support\Facades\Auth;

$user = Auth::user(); // the current user, or null
$id = Auth::id();     // the current user's ID

In a controller, use the request:

use Illuminate\Http\Request;

public function show(Request $request)
{
    $user = $request->user();

    return view('profile', ['user' => $user]);
}

Check whether a user is logged in

if (Auth::check()) {
    // The user is logged in
}

In Blade templates:

@auth
    <p>Welcome back, {{ auth()->user()->name }}</p>
@endauth

@guest
    <a href="{{ route('login') }}">Log in</a>
@endguest

Protect routes

Add the auth middleware. Unauthenticated users are redirected to the login page (or get a 401 for JSON requests):

use Illuminate\Support\Facades\Route;

Route::get('/dashboard', function () {
    // Only logged-in users get here
})->middleware('auth');

Laravel 13 also lets you declare middleware on the controller itself:

use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class BillingController
{
    // Every action requires a logged-in user
}

To use a different guard, name it: ->middleware('auth:admin').

Log users in manually

If you build your own login form instead of using a starter kit, use Auth::attempt() and regenerate the session to prevent session fixation:

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

public function authenticate(Request $request): RedirectResponse
{
    $credentials = $request->validate([
        'email' => ['required', 'email'],
        'password' => ['required'],
    ]);

    if (Auth::attempt($credentials)) {
        $request->session()->regenerate();

        return redirect()->intended('dashboard');
    }

    return back()->withErrors([
        'email' => 'The provided credentials do not match our records.',
    ])->onlyInput('email');
}

Log out by ending the session and refreshing the CSRF token:

public function logout(Request $request): RedirectResponse
{
    Auth::logout();

    $request->session()->invalidate();
    $request->session()->regenerateToken();

    return redirect('/');
}

Two-factor authentication in Laravel

The Laravel 13 starter kits include TOTP two-factor authentication through Fortify, enabled by default. Users scan a QR code with an authenticator app, confirm a code, and get recovery codes. You control it with Features::twoFactorAuthentication() in config/fortify.php. New apps from the kits also get passkey sign-in, through Features::passkeys() and the laravel/passkeys package. SMS codes aren't included, so you'd add those with a package or an identity provider.

API authentication with Sanctum

Install Sanctum for API authentication. A first-party SPA should use Sanctum's cookie-based SPA authentication; use API tokens for mobile apps, scripts and third-party clients:

php artisan install:api

Add the HasApiTokens trait to your User model, issue a token, and protect API routes:

// Issue a token, for example after login
$token = $user->createToken('mobile-app')->plainTextToken;

// routes/api.php
Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');

Clients send the token as Authorization: Bearer <token>. Sanctum tokens are stored hashed and can be revoked by deleting them. Use Laravel Passport only if your app must act as a full OAuth 2.0 authorization server for third-party clients.

Tutorial: HTTP Basic authentication in Laravel

HTTP Basic auth uses the browser's built-in login prompt. It's handy for internal tools or a quick prototype. Always use it over HTTPS, since credentials are sent with every request.

Add the Basic auth middleware

Route::get('/profile', function () {
    // Only authenticated users get here
})->middleware('auth.basic');

The browser prompts for credentials, and Laravel checks them against the email column of your users table by default.

Stateless HTTP Basic auth

For APIs, authenticate each request without creating a session. Create a middleware:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;

class AuthenticateOnceWithBasicAuth
{
    public function handle(Request $request, Closure $next): Response
    {
        return Auth::onceBasic() ?: $next($request);
    }
}

Then attach it to routes:

use App\Http\Middleware\AuthenticateOnceWithBasicAuth;

Route::get('/api/user', function () {
    // ...
})->middleware(AuthenticateOnceWithBasicAuth::class);

Best practices for secure authentication in Laravel

  • Stay on a supported version. Laravel 13 gets security fixes until March 17, 2028, Laravel 12 until February 24, 2027. Laravel 11's security support ended March 12, 2026. Run composer update regularly and read the upgrade guide before major versions.
  • Force HTTPS and secure cookies. In production, set SESSION_SECURE_COOKIE=true in .env so session cookies are only sent over HTTPS, and redirect HTTP to HTTPS at your load balancer or in AppServiceProvider:
use Illuminate\Support\Facades\URL;

public function boot(): void
{
    if ($this->app->environment('production')) {
        URL::forceScheme('https');
    }
}
  • Harden sessions. Use the database or Redis session driver for multi-server apps, keep SESSION_LIFETIME reasonable (the default is 120 minutes), and set SESSION_ENCRYPT=true to encrypt session data.
  • Enforce strong passwords. Use Laravel's password rules, including a check against known breached passwords:
use Illuminate\Validation\Rules\Password;

Password::defaults(function () {
    return Password::min(12)->mixedCase()->numbers()->uncompromised();
});
  • Rate-limit login attempts. Fortify throttles login attempts out of the box. For custom forms, use the throttle middleware or the RateLimiter facade.
  • Turn on two-factor auth and encourage users to enable it.
  • Log and monitor auth events. Listen for Laravel's Login, Failed and Lockout events, and use tools like Laravel Telescope (local) or your logging/monitoring stack in production to spot brute-force attempts.

Adding SSO and B2B user management to Laravel with Frontegg

Laravel's built-in auth covers users who sign up with a password. B2B SaaS apps usually also need enterprise SSO (SAML or OIDC), SCIM provisioning, organizations with roles, and an admin portal where each customer manages their own users. Building that on top of Laravel is a large project.

Frontegg provides these as a service and works with Laravel through standard protocols:

  • Hosted login over OpenID Connect. Redirect users to Frontegg's hosted login with the authorization code flow, using any PHP OIDC client. Frontegg handles social login, enterprise SSO, MFA and passkeys.
  • JWT validation in Laravel. Frontegg issues signed JWTs (RS256 by default) carrying the user, tenant, roles and permissions. Validate them in a Laravel middleware with the public key from the Frontegg portal:
namespace App\Http\Middleware;

use Closure;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class VerifyFronteggToken
{
    public function handle(Request $request, Closure $next): Response
    {
        $token = $request->bearerToken();
        abort_unless($token, 401);

        try {
            $claims = JWT::decode($token, new Key(config('services.frontegg.public_key'), 'RS256'));
            abort_unless(($claims->iss ?? null) === config('services.frontegg.issuer'), 401);
            abort_unless(in_array(config('services.frontegg.client_id'), (array) ($claims->aud ?? []), true), 401);
        } catch (\Throwable $e) {
            abort(401);
        }

        $request->attributes->set('frontegg_user', $claims);

        return $next($request);
    }
}

(Install the library with composer require firebase/php-jwt. It checks the signature and expiry; the two extra lines check the issuer and audience. To follow key rotation automatically, load keys from your environment's JWKS endpoint instead of a copied public key.)

  • Self-service for your customers. An embeddable admin portal lets each customer configure their own SSO and SCIM, invite users and assign roles, without tickets to your team.

See Frontegg's JWT validation guide and our Laravel SSO guide.

Learn more about Frontegg for authentication