Md Mominul Islam | Software and Data Enginnering | SQL Server, .NET, Power BI, Azure Blog

while(!(succeed=try()));

LinkedIn Portfolio Banner

Latest

Home Top Ad

Responsive Ads Here

Post Top Ad

Responsive Ads Here

Thursday, August 28, 2025

Laravel Core Complete Course: Module 7 - Mastering Authentication and Authorization in Laravel

 

Table of Contents

  1. Introduction to Authentication and Authorization in Laravel

    • What is Authentication?

    • What is Authorization?

    • Why Authentication and Authorization Matter

    • Laravel’s Authentication Ecosystem

    • Real-World Scenarios

  2. Setting Up Laravel Breeze and Jetstream

    • Overview of Laravel Breeze

    • Overview of Laravel Jetstream

    • Installing and Configuring Laravel Breeze

    • Installing and Configuring Laravel Jetstream

    • Pros, Cons, and Alternatives

    • Best Practices for Setup

  3. Register, Login, and Logout Flows

    • Building a Registration System

    • Implementing Login Functionality

    • Handling Logout

    • Real-World Example: E-Commerce User Registration

    • Pros, Cons, and Alternatives

    • Best Practices for User Flows

  4. Password Reset and Email Verification

    • Setting Up Password Reset

    • Implementing Email Verification

    • Real-World Example: CMS User Account Recovery

    • Pros, Cons, and Alternatives

    • Best Practices for Security

  5. Role-Based Access Control (RBAC)

    • Understanding RBAC

    • Implementing RBAC in Laravel

    • Real-World Example: Task Management App with Roles

    • Pros, Cons, and Alternatives

    • Best Practices for RBAC

  6. Gates and Policies for Authorization

    • Understanding Gates

    • Understanding Policies

    • Implementing Gates and Policies

    • Real-World Example: Blog Post Authorization

    • Pros, Cons, and Alternatives

    • Best Practices for Authorization

  7. Social Login Integration (Google, Facebook)

    • Setting Up Laravel Socialite

    • Implementing Google Login

    • Implementing Facebook Login

    • Real-World Example: Social Media Integration for a Marketplace

    • Pros, Cons, and Alternatives

    • Best Practices for Social Login

  8. Best Practices and Industry Standards

    • Security Best Practices

    • Performance Optimization

    • Scalability Considerations

    • Common Pitfalls to Avoid

  9. Conclusion

    • Recap of Key Concepts

    • Next Steps in Your Laravel Journey


1. Introduction to Authentication and Authorization in Laravel

What is Authentication?

Authentication is the process of verifying who a user is. It ensures that users are who they claim to be by validating credentials like usernames, passwords, or tokens. In a web application, authentication typically involves logging in with an email and password or using social media accounts to access protected resources.

What is Authorization?

Authorization determines what a user can do after they are authenticated. It controls access to specific resources or actions based on user roles, permissions, or other criteria. For example, an admin might have permission to delete posts, while a regular user can only view them.

Why Authentication and Authorization Matter

In real-world applications, such as an e-commerce platform, task management app, or CMS, authentication and authorization are critical for:

  • Security: Protecting sensitive data like user profiles, payment details, or proprietary content.

  • User Experience: Providing seamless login, registration, and role-based access to features.

  • Compliance: Meeting legal requirements like GDPR or HIPAA for user data protection.

  • Scalability: Ensuring the system can handle thousands of users with different access levels.

Laravel’s Authentication Ecosystem

Laravel provides a robust ecosystem for authentication and authorization, including:

  • Guards and Providers: Guards define how users are authenticated (e.g., session-based or token-based), while providers retrieve user data from storage (e.g., Eloquent or database queries). Configured in config/auth.php.

  • Laravel Breeze: A lightweight package for scaffolding authentication features like login, registration, and password reset. Ideal for simple applications.

  • Laravel Jetstream: An advanced scaffolding package with additional features like two-factor authentication (2FA) and team management. Suitable for complex applications.

  • Laravel Sanctum: Simplifies API token authentication for SPAs and mobile apps.

  • Laravel Passport: Provides OAuth2 for robust API authentication.

  • Laravel Fortify: A headless authentication backend for custom UI implementations.

  • Laravel Socialite: Enables social login with providers like Google and Facebook.

  • Gates and Policies: Tools for defining authorization logic.

  • RBAC: Role-based access control for managing user permissions.

Real-World Scenarios

To make this module engaging, we’ll use the following real-world scenarios throughout:

  1. E-Commerce Platform: Users register, log in, and access features based on roles (customer, vendor, admin).

  2. Task Management App: Teams collaborate on tasks, with role-based access (e.g., team lead vs. member).

  3. CMS: Content creators manage articles, with authorization to edit or publish based on roles.


2. Setting Up Laravel Breeze and Jetstream

Overview of Laravel Breeze

Laravel Breeze is a minimal, simple-to-use authentication scaffolding package. It provides:

  • Login, registration, password reset, and email verification.

  • Blade-based views with Tailwind CSS.

  • Lightweight setup for quick prototyping.

Use Case: Ideal for small to medium projects like a personal blog or a startup’s MVP.

Overview of Laravel Jetstream

Laravel Jetstream is a more feature-rich scaffolding package, offering:

  • Everything in Breeze, plus 2FA, team management, and API support via Sanctum.

  • Choice of Livewire or Inertia.js for frontend stacks.

  • Advanced UI components with Tailwind CSS.

Use Case: Suitable for complex applications like SaaS platforms or team-based apps.

Installing and Configuring Laravel Breeze

Let’s set up Laravel Breeze for an e-commerce platform where users can register, log in, and access a dashboard.

Step 1: Install Laravel Breeze

composer create-project laravel/laravel ecommerce
cd ecommerce
composer require laravel/breeze --dev
php artisan breeze:install
npm install && npm run dev
php artisan migrate

This command:

  • Creates a new Laravel project.

  • Installs Breeze and sets up authentication scaffolding.

  • Compiles frontend assets using Vite.

  • Runs database migrations for the users table.

Step 2: Configure Database

Edit .env to set up your database (e.g., MySQL):

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=ecommerce
DB_USERNAME=root
DB_PASSWORD=

Run migrations to create the users table:

php artisan migrate

Step 3: Explore Breeze Scaffolding

Breeze creates:

  • Routes: routes/auth.php for authentication routes.

  • Controllers: App\Http\Controllers\Auth for login, registration, etc.

  • Views: resources/views/auth for Blade templates.

  • Middleware: Ensures only authenticated users access protected routes.

Example: Customizing the Login View

Modify resources/views/auth/login.blade.php to add a custom welcome message for the e-commerce platform:

<x-guest-layout>
    <x-auth-card>
        <x-slot name="logo">
            <a href="/">
                <x-application-logo class="w-20 h-20 fill-current text-gray-500" />
            </a>
        </x-slot>

        <div class="mb-4 text-sm text-gray-600">
            Welcome to Our E-Commerce Store! Log in to start shopping.
        </div>

        <!-- Session Status -->
        <x-auth-session-status class="mb-4" :status="session('status')" />

        <!-- Validation Errors -->
        <x-auth-validation-errors class="mb-4" :errors="$errors" />

        <form method="POST" action="{{ route('login') }}">
            @csrf

            <!-- Email Address -->
            <div>
                <x-label for="email" :value="__('Email')" />
                <x-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus />
            </div>

            <!-- Password -->
            <div class="mt-4">
                <x-label for="password" :value="__('Password')" />
                <x-input id="password" class="block mt-1 w-full" type="password" name="password" required autocomplete="current-password" />
            </div>

            <!-- Remember Me -->
            <div class="block mt-4">
                <label for="remember_me" class="inline-flex items-center">
                    <input id="remember_me" type="checkbox" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" name="remember">
                    <span class="ml-2 text-sm text-gray-600">{{ __('Remember me') }}</span>
                </label>
            </div>

            <div class="flex items-center justify-end mt-4">
                @if (Route::has('password.request'))
                    <a class="underline text-sm text-gray-600 hover:text-gray-900" href="{{ route('password.request') }}">
                        {{ __('Forgot your password?') }}
                    </a>
                @endif

                <x-button class="ml-3">
                    {{ __('Log in') }}
                </x-button>
            </div>
        </form>
    </x-auth-card>
</x-guest-layout>

This customizes the login page with a branded message.

Installing and Configuring Laravel Jetstream

For a task management app with team features, Jetstream is a better choice.

Step 1: Install Jetstream

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

Choose livewire or inertia based on your frontend preference. Livewire uses server-side rendering, while Inertia integrates with Vue.js or React.

Step 2: Configure Jetstream

Update config/jetstream.php to enable features like 2FA or teams:

'features' => [
    Features::termsAndPrivacyPolicy(),
    Features::profilePhotos(),
    Features::api(),
    Features::teams(),
    Features::accountDeletion(),
],

Step 3: Explore Jetstream Scaffolding

Jetstream provides:

  • Advanced Routes: Including team management and API token routes.

  • Livewire/Inertia Components: For dynamic UIs.

  • 2FA and Teams: Built-in support for enhanced security and collaboration.

Example: Customizing the Dashboard

Modify resources/views/dashboard.blade.php to display a task management dashboard:

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Task Management Dashboard') }}
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                <div class="p-6 bg-white border-b border-gray-200">
                    Welcome, {{ Auth::user()->name }}! Manage your tasks below.
                    <!-- Add task list component here -->
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

Pros, Cons, and Alternatives

Laravel Breeze

  • Pros:

    • Lightweight and easy to customize.

    • Minimal setup for quick prototyping.

    • Uses Blade and Tailwind CSS for simplicity.

  • Cons:

    • Lacks advanced features like 2FA or team management.

    • Not ideal for SPAs or complex applications.

  • Alternatives:

    • Laravel Fortify: Headless authentication backend for custom UIs.

    • Laravel UI: Basic scaffolding for older Laravel versions.

Laravel Jetstream

  • Pros:

    • Feature-rich with 2FA, teams, and API support.

    • Supports Livewire and Inertia for modern UIs.

    • Scalable for large applications.

  • Cons:

    • Steeper learning curve for beginners.

    • Heavier footprint compared to Breeze.

  • Alternatives:

    • Custom Implementation: Build authentication from scratch using Laravel’s Auth facade.

    • Third-Party Services: Use Auth0 or Firebase for authentication.

Best Practices for Setup

  • Choose the Right Tool: Use Breeze for simple apps and Jetstream for complex ones.

  • Secure Database: Ensure the password column in the users table is at least 60 characters for hashed passwords.

  • Use HTTPS: Always deploy with SSL to secure user data.

  • Customize Views: Tailor authentication views to match your brand.

  • Monitor Dependencies: Keep Breeze/Jetstream updated with composer update.


3. Register, Login, and Logout Flows

Building a Registration System

For our e-commerce platform, users should register with an email, password, and name, with optional fields like phone number.

Step 1: Extend the User Model

Add a phone field to the users table by creating a migration:

php artisan make:migration add_phone_to_users_table --table=users

Edit the migration file:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddPhoneToUsersTable extends Migration
{
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('phone')->nullable()->after('email');
        });
    }

    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('phone');
        });
    }
}

Run the migration:

php artisan migrate

Update the User model (app/Models/User.php):

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use Notifiable;

    protected $fillable = [
        'name', 'email', 'password', 'phone',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

Step 2: Customize Registration Controller

Modify App\Http\Controllers\Auth\RegisterController.php to handle the phone field:

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;

class RegisterController extends Controller
{
    use RegistersUsers;

    protected $redirectTo = '/home';

    public function __construct()
    {
        $this->middleware('guest');
    }

    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
            'phone' => ['nullable', 'string', 'max:20'],
        ]);
    }

    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
            'phone' => $data['phone'],
        ]);
    }
}

Step 3: Update Registration View

Edit resources/views/auth/register.blade.php to include the phone field:

<x-guest-layout>
    <x-auth-card>
        <x-slot name="logo">
            <a href="/">
                <x-application-logo class="w-20 h-20 fill-current text-gray-500" />
            </a>
        </x-slot>

        <div class="mb-4 text-sm text-gray-600">
            Join Our E-Commerce Store! Create an account to start shopping.
        </div>

        <!-- Validation Errors -->
        <x-auth-validation-errors class="mb-4" :errors="$errors" />

        <form method="POST" action="{{ route('register') }}">
            @csrf

            <!-- Name -->
            <div>
                <x-label for="name" :value="__('Name')" />
                <x-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus />
            </div>

            <!-- Email Address -->
            <div class="mt-4">
                <x-label for="email" :value="__('Email')" />
                <x-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required />
            </div>

            <!-- Phone -->
            <div class="mt-4">
                <x-label for="phone" :value="__('Phone Number (Optional)')" />
                <x-input id="phone" class="block mt-1 w-full" type="text" name="phone" :value="old('phone')" />
            </div>

            <!-- Password -->
            <div class="mt-4">
                <x-label for="password" :value="__('Password')" />
                <x-input id="password" class="block mt-1 w-full" type="password" name="password" required autocomplete="new-password" />
            </div>

            <!-- Confirm Password -->
            <div class="mt-4">
                <x-label for="password_confirmation" :value="__('Confirm Password')" />
                <x-input id="password_confirmation" class="block mt-1 w-full" type="password" name="password_confirmation" required />
            </div>

            <div class="flex items-center justify-end mt-4">
                <a class="underline text-sm text-gray-600 hover:text-gray-900" href="{{ route('login') }}">
                    {{ __('Already registered?') }}
                </a>

                <x-button class="ml-4">
                    {{ __('Register') }}
                </x-button>
            </div>
        </form>
    </x-auth-card>
</x-guest-layout>

Implementing Login Functionality

The login flow authenticates users based on their email and password, redirecting them to a dashboard upon success.

Step 1: Use Breeze’s Login Controller

Breeze’s LoginController (App\Http\Controllers\Auth\AuthenticatedSessionController.php) handles login logic. No changes are needed for basic functionality, but you can customize the redirect:

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

class AuthenticatedSessionController extends Controller
{
    use AuthenticatesUsers;

    protected $redirectTo = '/dashboard';

    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }
}

Step 2: Customize Login Redirect

Update app/Providers/RouteServiceProvider.php to change the redirect path:

public const HOME = '/dashboard';

Create a dashboard route in routes/web.php:

use Illuminate\Support\Facades\Route;

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth'])->name('dashboard');

Handling Logout

Breeze’s logout functionality is handled by AuthenticatedSessionController. The default logout route is POST /logout. Customize the redirect after logout by modifying the controller:

protected function logout(Request $request)
{
    Auth::logout();
    $request->session()->invalidate();
    $request->session()->regenerateToken();
    return redirect('/welcome');
}

Real-World Example: E-Commerce User Registration

In our e-commerce platform, users register with their name, email, phone, and password. Upon successful registration, they receive a welcome email and are redirected to a dashboard showing personalized product recommendations.

Example: Sending a Welcome Email

Create a mailable class:

php artisan make:mail WelcomeEmail

Edit app/Mail/WelcomeEmail.php:

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class WelcomeEmail extends Mailable
{
    use Queueable, SerializesModels;

    public $user;

    public function __construct($user)
    {
        $this->user = $user;
    }

    public function build()
    {
        return $this->view('emails.welcome')
                    ->subject('Welcome to Our E-Commerce Store!');
    }
}

Create the email view resources/views/emails/welcome.blade.php:

<!DOCTYPE html>
<html>
<head>
    <title>Welcome to Our Store</title>
</head>
<body>
    <h1>Welcome, {{ $user->name }}!</h1>
    <p>Thank you for joining our e-commerce platform. Start shopping now!</p>
    <a href="{{ url('/dashboard') }}">Go to Dashboard</a>
</body>
</html>

Update the RegisterController to send the email:

protected function create(array $data)
{
    $user = User::create([
        'name' => $data['name'],
        'email' => $data['email'],
        'password' => Hash::make($data['password']),
        'phone' => $data['phone'],
    ]);

    Mail::to($user->email)->send(new WelcomeEmail($user));

    return $user;
}

Configure mail settings in .env:

MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=your_username
MAIL_PASSWORD=your_password
MAIL_ENCRYPTION=tls

Pros, Cons, and Alternatives

  • Pros:

    • Laravel’s built-in controllers simplify implementation.

    • Breeze provides clean, customizable scaffolding.

    • Validation and CSRF protection are included out of the box.

  • Cons:

    • Default scaffolding may need heavy customization for complex UIs.

    • Session-based authentication may not suit APIs.

  • Alternatives:

    • Laravel Sanctum: For API token authentication.

    • Auth0: Third-party service for advanced authentication.

    • Custom Auth: Build custom flows using Laravel’s Auth facade.

Best Practices for User Flows

  • Validate Inputs: Use Laravel’s validation to ensure data integrity.

  • Hash Passwords: Always use Hash::make() for passwords.

  • Use CSRF Protection: Laravel includes this by default, but always verify.

  • Log Activities: Track login/logout events for security auditing.

  • Rate Limiting: Apply rate limiting to prevent brute-force attacks.


4. Password Reset and Email Verification

Setting Up Password Reset

For our CMS, users should be able to reset their passwords securely via email.

Step 1: Configure Password Reset Routes

Breeze/Jetstream include password reset routes (/forgot-password, /reset-password). Ensure your mail configuration is set up in .env.

Step 2: Customize Password Reset Email

Create a custom notification:

php artisan make:notification CustomResetPassword

Edit app/Notifications/CustomResetPassword.php:

namespace App\Notifications;

use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
use Illuminate\Notifications\Messages\MailMessage;

class CustomResetPassword extends ResetPasswordNotification
{
    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->subject('Reset Your CMS Password')
            ->line('You are receiving this email because we received a password reset request for your account.')
            ->action('Reset Password', url(config('app.url').route('password.reset', $this->token, false)))
            ->line('This password reset link will expire in 60 minutes.')
            ->line('If you did not request a password reset, no further action is required.');
    }
}

Update the User model to use the custom notification:

public function sendPasswordResetNotification($token)
{
    $this->notify(new CustomResetPassword($token));
}

Step 3: Test Password Reset

Visit /forgot-password, enter an email, and check the email for the reset link.

Implementing Email Verification

Email verification ensures users provide valid email addresses, critical for our CMS.

Step 1: Enable Email Verification

In config/auth.php, ensure the users guard has email verification enabled:

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
],

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],
],

'passwords' => [
    'users' => [
        'provider' => 'users',
        'table' => 'password_resets',
        'expire' => 60,
    ],
],

Add the MustVerifyEmail trait to the User model:

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable implements MustVerifyEmail
{
    use Notifiable;

    // ...
}

Step 2: Update Routes

Breeze/Jetstream include email verification routes. Ensure the verified middleware is applied to protected routes:

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

Step 3: Customize Verification Email

Create a custom notification:

php artisan make:notification CustomVerifyEmail

Edit app/Notifications/CustomVerifyEmail.php:

namespace App\Notifications;

use Illuminate\Auth\Notifications\VerifyEmail as VerifyEmailNotification;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\URL;

class CustomVerifyEmail extends VerifyEmailNotification
{
    public function toMail($notifiable)
    {
        $verificationUrl = URL::temporarySignedRoute(
            'verification.verify',
            Carbon::now()->addMinutes(60),
            ['id' => $notifiable->getKey(), 'hash' => sha1($notifiable->getEmailForVerification())]
        );

        return (new MailMessage)
            ->subject('Verify Your CMS Email Address')
            ->line('Please click the button below to verify your email address.')
            ->action('Verify Email Address', $verificationUrl)
            ->line('If you did not create an account, no further action is required.');
    }
}

Update the User model:

public function sendEmailVerificationNotification()
{
    $this->notify(new CustomVerifyEmail);
}

Real-World Example: CMS User Account Recovery

In the CMS, users who forget their passwords receive a branded email with a reset link. After registering, they must verify their email before accessing the dashboard, ensuring only verified users can create content.

Pros, Cons, and Alternatives

  • Pros:

    • Built-in Laravel notifications simplify email handling.

    • Secure token-based reset links prevent unauthorized access.

  • Cons:

    • Email delivery depends on external services (e.g., Mailtrap, SendGrid).

    • Users may find email verification cumbersome.

  • Alternatives:

    • SMS Verification: Use services like Twilio for SMS-based verification.

    • Third-Party Auth: Use Auth0 or Firebase for password reset and verification.

Best Practices for Security

  • Secure Links: Use signed URLs for verification and reset links.

  • Rate Limiting: Apply rate limiting to password reset requests.

  • Monitor Emails: Log email delivery failures for debugging.

  • User Education: Inform users about strong passwords and phishing risks.


5. Role-Based Access Control (RBAC)

Understanding RBAC

RBAC assigns roles (e.g., admin, editor, user) to users, with each role having specific permissions. For our task management app, roles might include:

  • Admin: Can manage all tasks and users.

  • Team Lead: Can assign tasks within their team.

  • Member: Can view and complete assigned tasks.

Implementing RBAC in Laravel

Step 1: Create Roles and Permissions

Create roles and permissions tables:

php artisan make:migration create_roles_and_permissions_tables

Edit the migration:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateRolesAndPermissionsTables extends Migration
{
    public function up()
    {
        Schema::create('roles', function (Blueprint $table) {
            $table->id();
            $table->string('name')->unique();
            $table->timestamps();
        });

        Schema::create('permissions', function (Blueprint $table) {
            $table->id();
            $table->string('name')->unique();
            $table->timestamps();
        });

        Schema::create('role_user', function (Blueprint $table) {
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->foreignId('role_id')->constrained()->onDelete('cascade');
            $table->primary(['user_id', 'role_id']);
        });

        Schema::create('permission_role', function (Blueprint $table) {
            $table->foreignId('permission_id')->constrained()->onDelete('cascade');
            $table->foreignId('role_id')->constrained()->onDelete('cascade');
            $table->primary(['permission_id', 'role_id']);
        });
    }

    public function down()
    {
        Schema::dropIfExists('permission_role');
        Schema::dropIfExists('role_user');
        Schema::dropIfExists('permissions');
        Schema::dropIfExists('roles');
    }
}

Run the migration:

php artisan migrate

Step 2: Create Models

Create Role and Permission models:

php artisan make:model Role
php artisan make:model Permission

Edit app/Models/Role.php:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Role extends Model
{
    protected $fillable = ['name'];

    public function users()
    {
        return $this->belongsToMany(User::class);
    }

    public function permissions()
    {
        return $this->belongsToMany(Permission::class);
    }
}

Edit app/Models/Permission.php:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Permission extends Model
{
    protected $fillable = ['name'];

    public function roles()
    {
        return $this->belongsToMany(Role::class);
    }
}

Update the User model:

public function roles()
{
    return $this->belongsToMany(Role::class);
}

public function hasRole($role)
{
    return $this->roles()->where('name', $role)->exists();
}

public function hasPermission($permission)
{
    return $this->roles()->whereHas('permissions', function ($query) use ($permission) {
        $query->where('name', $permission);
    })->exists();
}

Step 3: Seed Roles and Permissions

Create a seeder:

php artisan make:seeder RolePermissionSeeder

Edit database/seeders/RolePermissionSeeder.php:

namespace Database\Seeders;

use App\Models\Role;
use App\Models\Permission;
use Illuminate\Database\Seeder;

class RolePermissionSeeder extends Seeder
{
    public function run()
    {
        $roles = ['admin', 'team_lead', 'member'];
        $permissions = [
            'create-tasks',
            'edit-tasks',
            'delete-tasks',
            'view-tasks',
            'manage-users',
        ];

        foreach ($roles as $roleName) {
            Role::create(['name' => $roleName]);
        }

        foreach ($permissions as $permissionName) {
            Permission::create(['name' => $permissionName]);
        }

        $admin = Role::where('name', 'admin')->first();
        $teamLead = Role::where('name', 'team_lead')->first();
        $member = Role::where('name', 'member')->first();

        $admin->permissions()->attach(Permission::all());
        $teamLead->permissions()->attach(Permission::whereIn('name', [
            'create-tasks', 'edit-tasks', 'view-tasks'
        ])->get());
        $member->permissions()->attach(Permission::where('name', 'view-tasks')->first());
    }
}

Run the seeder:

php artisan db:seed --class=RolePermissionSeeder

Step 4: Protect Routes with Middleware

Create a middleware:

php artisan make:middleware CheckRole

Edit app/Http/Middleware/CheckRole.php:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class CheckRole
{
    public function handle(Request $request, Closure $next, $role)
    {
        if (!auth()->user()->hasRole($role)) {
            abort(403, 'Unauthorized action.');
        }

        return $next($request);
    }
}

Register the middleware in app/Http/Kernel.php:

protected $routeMiddleware = [
    // ...
    'role' => \App\Http\Middleware\CheckRole::class,
];

Apply the middleware to a route:

Route::get('/admin/dashboard', function () {
    return view('admin.dashboard');
})->middleware(['auth', 'role:admin']);

Real-World Example: Task Management App with Roles

In the task management app, admins can manage all tasks, team leads can assign tasks, and members can only view their tasks. Use the RBAC system to restrict access to task creation routes:

Route::get('/tasks/create', [TaskController::class, 'create'])
    ->middleware(['auth', 'role:team_lead,admin'])
    ->name('tasks.create');

Pros, Cons, and Alternatives

  • Pros:

    • RBAC simplifies permission management for large teams.

    • Laravel’s Eloquent relationships make RBAC implementation straightforward.

  • Cons:

    • Can become complex with many roles and permissions.

    • Not suitable for fine-grained, resource-specific authorization.

  • Alternatives:

    • Spatie Permission: A popular package for advanced RBAC.

    • ACL (Access Control Lists): For more granular permissions.

Best Practices for RBAC

  • Keep Roles Simple: Limit the number of roles to avoid complexity.

  • Use Descriptive Permissions: Name permissions clearly (e.g., edit-posts).

  • Cache Permissions: Use Laravel’s caching to improve performance.

  • Audit Roles: Regularly review roles and permissions for security.


6. Gates and Policies for Authorization

Understanding Gates

Gates are closure-based authorization checks defined in app/Providers/AuthServiceProvider.php. They are ideal for simple, non-model-specific permissions.

Understanding Policies

Policies are classes that group authorization logic around a specific model, such as Post or Task. They are more organized and suited for model-specific actions.

Implementing Gates and Policies

Step 1: Define a Gate

For our blog CMS, define a gate to check if a user can update a post:

Edit app/Providers/AuthServiceProvider.php:

use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->registerPolicies();

        Gate::define('update-post', function (User $user, Post $post) {
            return $user->id === $post->user_id;
        });
    }
}

Step 2: Create a Policy

Generate a policy for the Post model:

php artisan make:policy PostPolicy --model=Post

Edit app/Policies/PostPolicy.php:

namespace App\Policies;

use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;

class PostPolicy
{
    use HandlesAuthorization;

    public function viewAny(User $user)
    {
        return true; // Anyone can view posts
    }

    public function view(User $user, Post $post)
    {
        return true; // Anyone can view a single post
    }

    public function create(User $user)
    {
        return $user->hasRole('editor') || $user->hasRole('admin');
    }

    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }

    public function delete(User $user, Post $post)
    {
        return $user->hasRole('admin');
    }
}

Register the policy in AuthServiceProvider:

protected $policies = [
    Post::class => PostPolicy::class,
];

Step 3: Use Gates and Policies in Controllers

Create a PostController:

php artisan make:controller PostController

Edit app/Http/Controllers/PostController.php:

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;

class PostController extends Controller
{
    public function update(Request $request, Post $post)
    {
        // Using Gate
        if (Gate::allows('update-post', $post)) {
            $post->update($request->validated());
            return redirect()->route('posts.show', $post)->with('success', 'Post updated.');
        }

        // Using Policy
        $this->authorize('update', $post);
        $post->update($request->validated());
        return redirect()->route('posts.show', $post)->with('success', 'Post updated.');
    }
}

Real-World Example: Blog Post Authorization

In the CMS, editors can create posts, but only the post’s author or an admin can update or delete it. Use policies to enforce these rules:

Route::put('/posts/{post}', [PostController::class, 'update'])
    ->middleware(['auth'])
    ->name('posts.update');

Pros, Cons, and Alternatives

  • Pros:

    • Gates are simple for non-model permissions.

    • Policies organize model-specific logic cleanly.

  • Cons:

    • Gates can become unwieldy for complex logic.

    • Policies require additional setup for each model.

  • Alternatives:

    • Spatie Permission: Simplifies complex permission logic.

    • Middleware: Use custom middleware for route-based authorization.

Best Practices for Authorization

  • Use Policies for Models: Centralize model-specific logic in policies.

  • Keep Gates Simple: Use gates for non-model permissions only.

  • Return Detailed Responses: Use Response::deny() for custom error messages.

  • Test Authorization: Write tests to ensure authorization logic is correct.


7. Social Login Integration (Google, Facebook)

Setting Up Laravel Socialite

For our marketplace platform, users can log in using Google or Facebook for a seamless experience.

Step 1: Install Laravel Socialite

composer require laravel/socialite

Step 2: Configure Socialite

Add credentials to .env:

GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
GOOGLE_REDIRECT_URI=http://your-app.test/auth/google/callback

FACEBOOK_CLIENT_ID=your_facebook_client_id
FACEBOOK_CLIENT_SECRET=your_facebook_client_secret
FACEBOOK_REDIRECT_URI=http://your-app.test/auth/facebook/callback

Update config/services.php:

return [
    'google' => [
        'client_id' => env('GOOGLE_CLIENT_ID'),
        'client_secret' => env('GOOGLE_CLIENT_SECRET'),
        'redirect' => env('GOOGLE_REDIRECT_URI'),
    ],
    'facebook' => [
        'client_id' => env('FACEBOOK_CLIENT_ID'),
        'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
        'redirect' => env('FACEBOOK_REDIRECT_URI'),
    ],
];

Step 3: Implement Google Login

Create a SocialiteController:

php artisan make:controller SocialiteController

Edit app/Http/Controllers/SocialiteController.php:

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;

class SocialiteController extends Controller
{
    public function redirectToGoogle()
    {
        return Socialite::driver('google')->redirect();
    }

    public function handleGoogleCallback()
    {
        $googleUser = Socialite::driver('google')->user();
        $user = User::updateOrCreate(
            ['email' => $googleUser->email],
            [
                'name' => $googleUser->name,
                'password' => bcrypt(str_random(16)),
                'email_verified_at' => now(),
            ]
        );

        Auth::login($user, true);
        return redirect('/dashboard');
    }
}

Add routes in routes/web.php:

Route::get('/auth/google', [SocialiteController::class, 'redirectToGoogle'])->name('auth.google');
Route::get('/auth/google/callback', [SocialiteController::class, 'handleGoogleCallback']);

Step 4: Implement Facebook Login

Add to SocialiteController:

public function redirectToFacebook()
{
    return Socialite::driver('facebook')->redirect();
}

public function handleFacebookCallback()
{
    $facebookUser = Socialite::driver('facebook')->user();
    $user = User::updateOrCreate(
        ['email' => $facebookUser->email],
        [
            'name' => $facebookUser->name,
            'password' => bcrypt(str_random(16)),
            'email_verified_at' => now(),
        ]
    );

    Auth::login($user, true);
    return redirect('/dashboard');
}

Add routes:

Route::get('/auth/facebook', [SocialiteController::class, 'redirectToFacebook'])->name('auth.facebook');
Route::get('/auth/facebook/callback', [SocialiteController::class, 'handleFacebookCallback']);

Step 5: Update Login View

Add social login buttons to resources/views/auth/login.blade.php:

<div class="mt-4">
    <a href="{{ route('auth.google') }}" class="btn btn-primary">Login with Google</a>
    <a href="{{ route('auth.facebook') }}" class="btn btn-primary">Login with Facebook</a>
</div>

Real-World Example: Social Media Integration for a Marketplace

In the marketplace, users can log in with Google or Facebook to quickly create accounts, reducing friction and increasing sign-ups.

Pros, Cons, and Alternatives

  • Pros:

    • Socialite simplifies social login integration.

    • Improves user experience by reducing signup steps.

  • Cons:

    • Relies on third-party services, which may have downtime.

    • Requires secure handling of API keys.

  • Alternatives:

    • Auth0: Comprehensive third-party authentication solution.

    • Firebase Auth: Google’s authentication service for web and mobile apps.

Best Practices for Social Login

  • Secure API Keys: Store credentials in .env and never commit them.

  • Handle Errors: Gracefully handle failed logins or missing email addresses.

  • Verify Emails: Mark social login emails as verified to skip manual verification.

  • Log Activities: Track social login attempts for security auditing.


8. Best Practices and Industry Standards

Security Best Practices

  • Use HTTPS: Encrypt all data in transit.

  • Hash Passwords: Use Laravel’s Hash facade.

  • Validate Inputs: Prevent injection attacks with Laravel’s validation.

  • Enable 2FA: Use Jetstream or third-party packages for 2FA.

  • Rate Limiting: Protect against brute-force attacks.

Performance Optimization

  • Cache Permissions: Cache RBAC permissions to reduce database queries.

  • Use Queues: Queue email sending for password resets and verifications.

  • Optimize Queries: Use Eloquent efficiently to avoid N+1 issues.

Scalability Considerations

  • Use Sanctum for APIs: For SPAs or mobile apps.

  • Database Indexing: Index frequently queried fields like email.

  • Load Balancing: Deploy with Laravel Forge or Cloudways for scalability.

Common Pitfalls to Avoid

  • Weak Passwords: Enforce strong password policies.

  • Exposed API Keys: Never expose Socialite credentials.

  • Unverified Emails: Always enforce email verification for sensitive actions.


9. Conclusion

In this Module 7 of the Laravel Core Complete Course, we’ve covered the essentials of authentication and authorization in Laravel 12. From setting up Laravel Breeze and Jetstream to implementing register, login, logout flows, password reset, email verification, RBAC, gates and policies, and social login integration, you now have the tools to build secure, user-friendly web applications. The real-world examples—e-commerce platform, task management app, and CMS—demonstrate how these concepts apply to practical scenarios.

No comments:

Post a Comment

Thanks for your valuable comment...........
Md. Mominul Islam

Post Bottom Ad

Responsive Ads Here