Kenapa Butuh Token Auth untuk API?
Kalau aplikasi client (misal aplikasi desktop, mobile, atau SPA) perlu mengambil dan mengubah data dari Laravel lewat internet, cara paling aman adalah lewat REST API dengan autentikasi token — bukan koneksi database langsung. Alasannya sederhana: koneksi database langsung mengharuskan client menyimpan kredensial database mentah, membuka port database ke publik, dan melewati semua validasi/business logic yang ada di Laravel.
Laravel Sanctum adalah paket resmi Laravel untuk menerbitkan token API sederhana. Client login sekali, dapat token, lalu token itu dipakai di header Authorization untuk semua request berikutnya.
Contoh Kasus: CRUD Sederhana "Notes"
Supaya gampang diikuti, dokumentasi ini pakai contoh resource sederhana: Notes (catatan) dengan field title dan body. Pola yang sama bisa dipakai untuk resource lain apa pun.
1. Install Sanctum
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
Perintah migrate ini membuat tabel personal_access_tokens, tempat semua token yang diterbitkan akan disimpan.
2. Aktifkan Token di Model User
Tambahkan trait HasApiTokens di app/Models/User.php:
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
// ...
}
3. Buat Model & Migration untuk Notes
php artisan make:model Note -m
Isi file migration-nya:
public function up(): void
{
Schema::create('notes', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('title');
$table->text('body')->nullable();
$table->timestamps();
});
}
Isi model app/Models/Note.php:
class Note extends Model
{
use HasFactory;
protected $fillable = ['title', 'body', 'user_id'];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
Jalankan migration:
php artisan migrate
4. Buat Controller Auth
php artisan make:controller Api/AuthController
Isi app/Http/Controllers/Api/AuthController.php:
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
class AuthController extends Controller
{
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)) {
throw ValidationException::withMessages([
'email' => ['Email atau password salah.'],
]);
}
$token = $user->createToken($request->device_name ?? 'api-client')->plainTextToken;
return response()->json([
'token' => $token,
'user' => $user->only('id', 'name', 'email'),
]);
}
public function logout(Request $request)
{
$request->user()->currentAccessToken()->delete();
return response()->json(['message' => 'Logged out']);
}
}
5. Buat Controller CRUD Notes
php artisan make:controller Api/NoteController
Isi app/Http/Controllers/Api/NoteController.php:
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Note;
use Illuminate\Http\Request;
class NoteController extends Controller
{
// GET /api/notes
public function index(Request $request)
{
return $request->user()->notes()->latest()->get();
}
// POST /api/notes
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'body' => 'nullable|string',
]);
$note = $request->user()->notes()->create($validated);
return response()->json($note, 201);
}
// GET /api/notes/{note}
public function show(Note $note)
{
return $note;
}
// PUT/PATCH /api/notes/{note}
public function update(Request $request, Note $note)
{
$validated = $request->validate([
'title' => 'sometimes|required|string|max:255',
'body' => 'nullable|string',
]);
$note->update($validated);
return response()->json($note);
}
// DELETE /api/notes/{note}
public function destroy(Note $note)
{
$note->delete();
return response()->json(['message' => 'Note deleted']);
}
}
Tambahkan relasi notes() di model User.php:
public function notes(): HasMany
{
return $this->hasMany(Note::class);
}
6. Daftarkan Route
Di routes/api.php:
use App\Http\Controllers\Api\AuthController;
use App\Http\Controllers\Api\NoteController;
use Illuminate\Support\Facades\Route;
Route::post('/login', [AuthController::class, 'login']);
Route::middleware('auth:sanctum')->group(function () {
Route::post('/logout', [AuthController::class, 'logout']);
Route::apiResource('notes', NoteController::class);
});
/login harus di luar grup middleware('auth:sanctum'). Logikanya: user belum punya token saat mencoba login, jadi belum bisa lolos middleware yang mewajibkan token.Untuk Laravel 11 ke atas, pastikan juga routes/api.php sudah terdaftar di bootstrap/app.php:
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
7. Cek Route Sudah Terdaftar
php artisan route:list --path=api
Harus muncul baris untuk POST api/login, GET api/notes, POST api/notes, GET api/notes/{note}, PUT api/notes/{note}, DELETE api/notes/{note}, dan POST api/logout.
8. Testing API
Cara paling cepat memastikan API bekerja sebelum sibuk bikin aplikasi client: test murni pakai curl atau PowerShell. Kalau semua langkah ini lolos, artinya masalah apa pun yang muncul nanti di sisi client sudah pasti bukan dari API.
Login (dapat token)
curl -i -X POST http://localhost:8000/api/login \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"email":"user@example.com","password":"rahasia","device_name":"test-curl"}'
Response yang diharapkan (HTTP 200):
{"token":"1|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","user":{"id":1,"name":"...","email":"user@example.com"}}
Create Note
Ganti <token> dengan token hasil login diatas.
curl -i -X POST http://localhost:8000/api/notes \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"title":"Catatan pertama","body":"Isi catatan"}'
List Notes
curl -i http://localhost:8000/api/notes \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"
Update Note
curl -i -X PUT http://localhost:8000/api/notes/1 \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"title":"Judul diubah"}'
Delete Note
curl -i -X DELETE http://localhost:8000/api/notes/1 \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"
Logout & Pastikan Token Mati
curl -i -X POST http://localhost:8000/api/logout \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"
Coba request ulang ke /api/notes pakai token yang sama setelah logout — harus mendapat HTTP 401 Unauthenticated, bukti token sudah benar-benar dicabut.
Testing di PowerShell (Windows)
Perlu diperhatikan: curl di PowerShell adalah alias untuk Invoke-WebRequest, yang syntax-nya berbeda dari curl asli (tidak mendukung -H/-d). Ada dua solusi:
Opsi 1 — panggil curl asli secara eksplisit dengan curl.exe:
curl.exe -i -X POST http://localhost:8000/api/login -H "Content-Type: application/json" -H "Accept: application/json" -d '{\"email\":\"user@example.com\",\"password\":\"rahasia\"}'
Opsi 2 — pakai Invoke-RestMethod native PowerShell (lebih direkomendasikan, tanpa escaping ribet):
$body = @{
email = "user@example.com"
password = "rahasia"
device_name = "test-ps"
} | ConvertTo-Json
$response = Invoke-RestMethod -Uri "http://localhost:8000/api/login" `
-Method POST -ContentType "application/json" -Body $body
$token = $response.token
Invoke-RestMethod -Uri "http://localhost:8000/api/notes" `
-Method GET -Headers @{ Authorization = "Bearer $token" }
Troubleshooting Umum
| Gejala | Kemungkinan Penyebab |
|---|---|
| Route login 404 Not Found | Baris Route::post('/login', ...) belum ditambahkan, atau tidak sengaja ditaruh di dalam grup middleware('auth:sanctum') |
HTTP 500 saat login, error terkait HasApiTokens |
Trait HasApiTokens belum ditambahkan ke model User |
| Response HTML, bukan JSON | Header Accept: application/json belum dikirim, atau route ternyata terdaftar di web.php, bukan api.php |
| HTTP 401 Unauthenticated di endpoint yang di-protect | Token salah/kadaluarsa, atau guard di config/sanctum.php belum sesuai |
Semua route di api.php tidak terdaftar sama sekali |
Di Laravel 11+, baris api: __DIR__.'/../routes/api.php' belum ada di bootstrap/app.php |
Ringkasan Alur
- Install & setup Sanctum, tambahkan
HasApiTokenske modelUser. - Buat model, migration, dan controller untuk resource (contoh: Notes).
- Daftarkan route: login di luar grup, endpoint CRUD di dalam grup
middleware('auth:sanctum'). - Test satu per satu pakai curl/PowerShell sebelum sentuh kode client — supaya kalau ada error nanti, sudah pasti tahu itu bukan dari API.

Posting Komentar