Introduction
Laravel 12 provides a robust authentication system. With Sanctum, API-based authentication becomes efficient. This guide helps you implement registration, login, logout, and protect routes for categories and products. Unauthenticated users will be redirected to the login page.
Prerequisites
- Laravel 11 installed
- Composer and PHP 8.3+
- MySQL or any Laravel-supported database
- Postman or a frontend (Vue.js, React, or Blade) to test API calls
Step 1: Install Laravel 12
composer create-project laravel/laravel laravel12-email-verify
cd laravel12-email-verify
Step 2: Install Sanctum
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
Step 3: Set Up User Authentication
Create a controller:
php artisan make:controller AuthController
Update app/Models/User.php:
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}
Add this code inside AuthController.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
}
Step 4: Protect Routes
Update routes/web.php like this:
use App\Http\Controllers\AuthController;
use App\Http\Controllers\CategoryController;
use App\Http\Controllers\ProductController;
Route::get('/login', [AuthController::class, 'showLoginForm'])->name('login');
Route::get('/register', [AuthController::class, 'showRegisterForm'])->name('register');
Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
Route::middleware('auth:sanctum')->group(function () {
Route::get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
Route::resource('/categories', CategoryController::class);
Route::resource('/products', ProductController::class);
});
Step 5: Create Blade Files
Layout File
Create: resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html>
<head>
<title>@yield('title')</title>
</head>
<body>
@yield('content')
</body>
</html>
Login Page
Create: resources/views/auth/login.blade.php
@extends('layouts.app')
@section('title', 'Login')
@section('content')
<form method="POST" action="{{ route('login') }}">
@csrf
<input type="email" name="email" placeholder="Email" required>
<input type="password" name="password" placeholder="Password" required>
<button type="submit">Login</button>
</form>
@endsection
Register Page
Create: resources/views/auth/register.blade.php
@extends('layouts.app')
@section('title', 'Register')
@section('content')
<form method="POST" action="{{ route('register') }}">
@csrf
<input type="text" name="name" placeholder="Name" required>
<input type="email" name="email" placeholder="Email" required>
<input type="password" name="password" placeholder="Password" required>
<button type="submit">Register</button>
</form>
@endsection
Dashboard Page
Create: resources/views/dashboard.blade.php
@extends('layouts.app')
@section('title', 'Dashboard')
@section('content')
<h1>Welcome to Dashboard</h1>
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit">Logout</button>
</form>
@endsection
Categories Page
Create: resources/views/categories/index.blade.php
@extends('layouts.app')
@section('title', 'Categories')
@section('content')
<h1>Categories</h1>
<a href="{{ route('categories.create') }}">Add Category</a>
<ul>
@foreach($categories as $category)
<li>{{ $category->name }}</li>
@endforeach
</ul>
@endsection
Products Page
Create: resources/views/products/index.blade.php
@extends('layouts.app')
@section('title', 'Products')
@section('content')
<h1>Products</h1>
<a href="{{ route('products.create') }}">Add Product</a>
<ul>
@foreach($products as $product)
<li>{{ $product->name }}</li>
@endforeach
</ul>
@endsection
Laravel Package Discovery Summary
| Package | Purpose |
|---|---|
laravel/pail |
Internal Laravel package for structured output (often related to logs or diagnostics). |
laravel/sail |
Laravel Sail — the Docker-based local development environment for Laravel. |
laravel/sanctum |
Provides token-based API authentication. |
laravel/tinker |
Allows you to interact with your Laravel app via REPL (CLI). |
nesbot/carbon |
Date and time manipulation (used by Laravel models like created_at, updated_at). |
nunomaduro/collision |
Beautiful error reporting in the console (during artisan commands). |
nunomaduro/termwind |
Renders beautiful colored and styled console output (used in Artisan). |
Step 6: Enable Email Verification
To ensure only verified users can access protected routes, Laravel’s built-in email verification system can be enabled.
Update User.php to implement MustVerifyEmail
Generate Controllers
php artisan make:controller Auth/ForgotPasswordController
php artisan make:controller Auth/ResetPasswordController
web.php
Create View: resources/views/auth/verify.blade.php
@extends('layouts.app')
@section('content')
<p>Please verify your email address using the link sent to your inbox.</p>
@if (session('message'))
<div class=\"alert alert-success\">
{{ session('message') }}
</div>
@endif
<form method=\"POST\" action=\"{{ route('verification.send') }}\">
@csrf
<button type=\"submit\">Resend Verification Email</button>
</form>
@endsection
Create View: resources/views/auth/forgot-password.blade.php
@extends('layouts.app')
@section('content')
<h2>Forgot Password</h2>
@if (session('status'))
<div class=\"alert alert-success\">{{ session('status') }}</div>
@endif
<form method=\"POST\" action=\"{{ route('password.email') }}\">
@csrf
<input type=\"email\" name=\"email\" required placeholder=\"Enter your email\" />
<button type=\"submit\">Send Reset Link</button>
</form>
@endsection
Create View: resources/views/auth/reset-password.blade.php
@extends('layouts.app')
@section('content')
<h2>Reset Password</h2>
<form method=\"POST\" action=\"{{ route('password.update') }}\">
@csrf
<input type=\"hidden\" name=\"token\" value=\"{{ $token }}\">
<input type=\"email\" name=\"email\" value=\"{{ old('email') }}\" required />
<input type=\"password\" name=\"password\" required placeholder=\"New Password\" />
<input type=\"password\" name=\"password_confirmation\" required placeholder=\"Confirm Password\" />
<button type=\"submit\">Reset Password</button>
</form>
@endsection
Summary
We implemented Laravel 11 authentication with Sanctum and Blade templates, including registration, login, and protected routes for dashboard, categories, and products. Unauthorized users are redirected to the login page, ensuring a secure and user-friendly system.
