Xác Thực (Authentication) trong Laravel

1. Giới Thiệu

Laravel cung cấp hệ thống xác thực người dùng mạnh mẽ, hỗ trợ cả xác thực thủ công và tự động thông qua Laravel Breeze, Jetstream hoặc Fortify.

2. Tạo Authentication Tự Động

Bạn có thể dùng các starter kit để tạo nhanh hệ thống auth:

2.1 Laravel Breeze
composer require laravel/breeze --dev
php artisan breeze:install
npm install && npm run dev
php artisan migrate
2.2 Laravel Jetstream
composer require laravel/jetstream
php artisan jetstream:install livewire
npm install && npm run dev
php artisan migrate

3. Tạo Authentication Thủ Công

3.1 Tạo Route

use App\Http\Controllers\AuthController;

Route::get('/login', [AuthController::class, 'showLoginForm'])->name('login');
Route::post('/login', [AuthController::class, 'login']);
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
Route::get('/register', [AuthController::class, 'showRegisterForm'])->name('register');
Route::post('/register', [AuthController::class, 'register']);
3.2 Tạo Controller
php artisan make:controller AuthController

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;

class AuthController extends Controller
{
    public function showLoginForm() {
        return view('auth.login');
    }

    public function login(Request $request) {
        $credentials = $request->only('email', 'password');

        // Lệnh kiểm tra thông tin đăng nhập:
        // Auth::attempt($credentials) sẽ kiểm tra xem email và password có hợp lệ không.
        // Nếu đúng, Laravel sẽ đăng nhập người dùng và trả về true.
        // Nếu sai, trả về false.
        // Hàm này sẽ tự động hash password để so sánh với password trong DB.

        // ✅ Nếu muốn xác thực theo 'username' thay vì 'email':
        // $credentials = $request->only('username', 'password');
        // => Laravel sẽ dùng cột 'username' trong bảng users để xác thực.

        // ✅ Ghi nhớ đăng nhập:
        // Auth::attempt($credentials, $remember)
        // Nếu $remember = true, Laravel sẽ lưu thông tin đăng nhập vào cookie để không bị đăng xuất khi tắt trình duyệt.

        if (Auth::attempt($credentials)) {
            $request->session()->regenerate();
            return redirect()->intended('/dashboard');
        }

        return back()->withErrors([
            'email' => 'Thông tin đăng nhập không đúng.',
        ]);
    }

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

    public function showRegisterForm() {
        return view('auth.register');
    }

    public function register(Request $request) {
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:6|confirmed',
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);

        Auth::login($user);

        return redirect('/dashboard');
    }
}
3.2.1 Giải thích một số câu hỏi thường gặp
  • Auth::attempt() hoạt động như thế nào?
    Phương thức Auth::attempt($credentials) sẽ kiểm tra xem email và mật khẩu truyền vào có hợp lệ không (dựa vào bảng users). Laravel sẽ tự động hash mật khẩu đã nhập và so sánh với mật khẩu được lưu trong cơ sở dữ liệu. Nếu khớp, nó sẽ đăng nhập người dùng và trả về true; ngược lại trả về false.
  • auth()->user() và Auth::user() khác nhau không?
    Cả hai đều trả về thông tin người dùng hiện tại đã đăng nhập. Về bản chất:
    • auth() là helper function, tiện dùng khi cần chain method nhanh như auth()->user()->email
    • Auth::user() dùng khi bạn muốn gọi thông qua facade Auth
    Chúng tương đương nhau, bạn có thể dùng tùy theo phong cách code bạn thích.
  • Sử dụng điều kiện nâng cao với Auth::attempt()
    Từ Laravel 9.42+, bạn có thể truyền thêm một callback (hoặc closure) vào trong mảng Auth::attempt() để giới hạn thêm điều kiện truy vấn.
    Ví dụ:
    
    if (Auth::attempt([
        'email' => $email,
        'password' => $password,
        fn (Builder $query) => $query->has('activeSubscription'),
    ])) {
        // Người dùng có email, mật khẩu đúng và có subscription đang active
    }
            
    Trong ví dụ này, Laravel sẽ chỉ đăng nhập người dùng nếu họ có quan hệ activeSubscription (một quan hệ Eloquent đã định nghĩa trong model User như hasOne hoặc hasMany với điều kiện đang hoạt động).
3.3 Tạo Seeder Người Dùng
php artisan make:seeder UserSeeder

use App\Models\User;
use Illuminate\Support\Facades\Hash;

User::create([
    'name' => 'Admin',
    'email' => 'admin@example.com',
    'password' => Hash::make('password')
]);
3.4 Middleware auth

Route::middleware('auth')->get('/dashboard', function () {
    return view('dashboard');
});
3.5 Lấy Thông Tin Người Dùng Đang Đăng Nhập

Để lấy thông tin người dùng hiện tại, bạn có thể sử dụng:


$user = Auth::user();
$name = Auth::user()->name;
$email = auth()->user()->email;

4. Authentication API cho App

4.1 Laravel Sanctum – Dành cho SPA hoặc Mobile App đơn giản

Laravel Sanctum là package nhẹ, dễ cấu hình, hỗ trợ xác thực API token, lý tưởng cho SPA (Single Page Application) hoặc ứng dụng mobile không quá phức tạp.

Ưu điểm:
  • Cài đặt đơn giản
  • Hỗ trợ xác thực dựa trên cookie hoặc token
  • Không yêu cầu flow OAuth2 phức tạp
Các bước cài đặt:
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate

Giải thích:

  • composer require laravel/sanctum: Cài gói Sanctum để Laravel hỗ trợ xác thực API thông qua token.
  • php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider": Copy file cấu hình config/sanctum.php từ gói Sanctum về project, dùng để tuỳ chỉnh.
  • php artisan migrate: Tạo bảng personal_access_tokens trong database để lưu các token.

📌 Tạo Route cho API sử dụng Sanctum

Thêm các route sau vào routes/api.php:


use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ApiController;

// Route đăng nhập trả về token
Route::post('/login', [ApiController::class, 'login']);

// Route được bảo vệ bằng Sanctum
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

📌 Tạo Controller xử lý đăng nhập

Tạo ApiController nếu bạn chưa có:

php artisan make:controller ApiController

Thêm phương thức login vào app/Http/Controllers/ApiController.php:


namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use App\Models\User;

class ApiController extends Controller
{
    public function login(Request $request)
    {
        $user = User::where('email', $request->email)->first();

        if (! $user || ! Hash::check($request->password, $user->password)) {
            return response()->json(['message' => 'Sai thông tin'], 401);
        }

        return response()->json([
            'token' => $user->createToken('app-token')->plainTextToken
        ]);
    }
}
Thêm trait vào model User:

Để sử dụng được phương thức createToken(), bạn cần thêm trait HasApiTokens vào model User:


// File: app/Models/User.php

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    // ...
}

Sau đó bạn có thể dùng Postman để test:

  • POST /api/login: Gửi email và password để nhận token.
    Body (JSON):
    {
        "email": "user@example.com",
        "password": "your_password"
    }
    Headers:
    • Accept: application/json
    • Content-Type: application/json
  • GET /api/user: Gửi token trong header Authorization để truy cập.
    Headers:
    • Accept: application/json
    • Authorization: Bearer <token> (thay <token> bằng token bạn nhận được khi login)
4.2 Laravel Passport – Dành cho ứng dụng cần OAuth2 phức tạp

Laravel Passport là giải pháp xác thực đầy đủ dựa trên chuẩn OAuth2. Nó rất phù hợp nếu bạn cần:

  • Cấp token cho ứng dụng bên thứ ba
  • Phân biệt giữa nhiều client (ví dụ: mobile app, SPA, bên đối tác...)
  • Quản lý token có thời hạn, refresh token, và các quyền (scope)
Ưu điểm:
  • Hỗ trợ đầy đủ các flow OAuth2: Authorization Code, Client Credentials, Password Grant, Refresh Token...
  • Tích hợp sẵn giao diện cấp token (dùng trong quá trình dev hoặc quản lý)
  • Có thể cấu hình scope để giới hạn quyền của từng token
Các bước cài đặt:
composer require laravel/passport
php artisan migrate
php artisan passport:install
Cập nhật model User:
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
}
Trong App\Providers\AuthServiceProvider:
use Laravel\Passport\Passport;

public function boot()
{
    Passport::routes();
}
Trong file config/auth.php, sửa phần guard api:
'guards' => [
    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],
Route và xử lý đăng nhập để nhận token:
// Route trong routes/api.php
Route::post('/login', [AuthController::class, 'login']);
// AuthController.php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

public function login(Request $request)
{
    if (!Auth::attempt($request->only('email', 'password'))) {
        return response()->json(['message' => 'Sai thông tin đăng nhập'], 401);
    }

    $user = Auth::user();
    $token = $user->createToken('API Token')->accessToken;

    return response()->json([
        'token' => $token,
        'user' => $user,
    ]);
}
Bảo vệ route bằng middleware auth:api:
Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});
Sử dụng Postman để test:
  • POST /api/login: gửi emailpassword trong body (JSON) để lấy access token.
  • GET /api/user: gửi Authorization: Bearer <token> trong header để truy cập.
4.3 So sánh Sanctum và Passport
Tiêu chí Sanctum Passport
Chuẩn giao tiếp Tự định nghĩa token Chuẩn OAuth2
Độ phức tạp Thấp Cao
Trường hợp sử dụng SPA, mobile app đơn giản Ứng dụng cần liên kết bên thứ 3
Cơ chế xác thực Token hoặc cookie Bearer token theo chuẩn OAuth2
Hiệu năng Nhẹ Nặng hơn