Melindungi API dengan Laravel Sanctum: API tokens dan SPA authentication, trait HasApiTokens, authorization Bearer, token abilities/scope, serta perbandingan Sanctum vs Passport untuk OAuth.

Di episode 17 kita membangun REST API — tapi endpoint-nya belum dilindungi. Bagaimana cara memastikan hanya user yang sah yang bisa membuat atau mengubah data? Jawaban standar Laravel untuk ini adalah Sanctum: package resmi untuk API authentication yang ringan, lengkap, dan tidak serumit OAuth.
Di episode ini kita pasang Sanctum, memahami dua mode pemakaiannya (API token & SPA), lalu membandingkannya dengan Passport agar kalian tahu kapan memakai yang mana.
Sebelum Sanctum, API auth Laravel identik dengan Passport (OAuth2) — powerful tapi berat untuk kebutuhan umum. Sanctum hadir dengan dua mode:
Authorization: Bearer.Keduanya didukung oleh satu package yang ringan. Mari pasang.
composer require laravel/sanctum
php artisan install:api
php artisan migratephp artisan install:api melakukan setup lengkap: menambahkan HasApiTokens ke User, migration tabel personal_access_tokens, mengaktifkan route sanctum/csrf-cookie, dan membuat routes/api.php jika belum ada.
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}User membuat token — biasanya saat login di endpoint khusus:
use App\Models\User;
use Illuminate\Support\Facades\Hash;
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
return response()->json(['message' => 'Kredensial salah.'], 401);
}
$token = $user->createToken('app-client')->plainTextToken;
return response()->json(['token' => $token]);
}Client menyimpan token dan mengirimnya di setiap request:
curl -H "Authorization: Bearer TOKEN" http://localhost:8000/api/postsToken yang dikirim adalah plain text hanya sekali saat pembuatan — yang disimpan di database hanyalah hash-nya (via SHA-256). Kehilangan token = logout paksa: $user->tokens()->delete().
Route API dipasangi middleware auth:sanctum:
use App\Http\Controllers\Api\PostController;
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', fn ($request) => $request->user());
Route::apiResource('posts', PostController::class);
});Di controller, user yang terautentikasi diakses via $request->user(). Jika token tidak valid atau kadaluarsa, Laravel mengembalikan 401 secara otomatis.
Tidak semua token harus memiliki kekuatan penuh. Abilities membatasi apa yang bisa dilakukan sebuah token:
$token = $user->createToken('mobile-app', ['posts:read', 'posts:create']);Cek ability di route atau controller:
Route::get('/posts', ...)->middleware('auth:sanctum')
->can('posts:read');public function destroy(Request $request, Post $post)
{
if (!$request->user()->tokenCan('posts:delete')) {
abort(403);
}
// ...
}Pola ini penting untuk integrasi pihak ketiga: beri token dengan ability seminimal mungkin yang dibutuhkan — prinsip least privilege yang sama seperti izin di aplikasi.
Tip
Gunakan abilities untuk token milik pihak ketiga/device (misalnya token mobile yang hanya boleh baca). Untuk user biasa yang login di aplikasi kalian sendiri, cukup token polos tanpa ability — abilities menambah kompleksitas yang tidak perlu jika semua token berperilaku sama.
Jika frontend kalian SPA (React/Vue/Inertia) di domain yang sama dengan backend, mode SPA lebih nyaman: tidak ada token yang dikelola manual, login memakai session cookie biasa.
config/sanctum.php (stateful).GET /sanctum/csrf-cookie sebelum login.POST /login biasa — session cookie ter-set otomatis.auth:sanctum seperti biasa.Keuntungannya: tidak perlu menyimpan token di localStorage (yang rawan XSS), cookie httpOnly lebih aman. Kekurangannya: hanya bekerja untuk SPA satu origin — mobile app tetap butuh token.
| Aspek | Sanctum | Passport |
|---|---|---|
| Tipe | API tokens + SPA cookie | OAuth2 penuh |
| Flow | Sederhana (token/hash) | Authorization Code, PKCE, Client Credentials |
| Cocok untuk | API kalian sendiri, mobile app | Login via pihak ketiga (Google, GitHub) |
| Kompleksitas | Ringan | Berat |
| Kapan dipakai | Default hampir semua kasus | Hanya jika butuh OAuth2 eksplisit |
Note
Aturan praktis: mulai dengan Sanctum. Passport (OAuth2) baru diperlukan saat ada kebutuhan nyata seperti "aplikasi pihak ketiga login atas nama user" atau "access token dengan refresh token untuk integrasi eksternal". Membawa OAuth2 sejak awal hanya menambah kompleksitas tanpa manfaat.
Inti yang harus dibawa pulang:
install:api.createToken(), dikirim via header Authorization: Bearer.auth:sanctum; batasi token dengan abilities.Di episode 19 selanjutnya kita akan membahas Reverb WebSocket real-time — konfigurasi Laravel Reverb, broadcasting dengan ShouldBroadcast, Reverb DB driver (v13), dan client Echo untuk notifikasi/chat real-time. Sampai jumpa di episode 19!