Token di chapter sebelumnya berlaku penuh: satu string membuka seluruh akun Budi. Untuk aplikasi kecil itu bisa diterima, tetapi bayangkan aplikasi mobile yang hilang — kita terpaksa mencabut token satu-satu dan tidak ada bedanya antara token yang boleh membaca dan menulis. Sanctum menjawab kebutuhan ini dengan token abilities: daftar hak akses yang melekat pada tiap token, diperiksa lewat method tokenCan() dan middleware. Chapter ini membangun pembatasan itu langkah demi langkah dengan pengujian nyata 200 versus 403.
Membuat Token dengan Abilities #
Argumen kedua createToken() menerima array abilities. Tanpa argumen tersebut, token mendapat wildcard ['*'] yang berarti boleh melakukan apa pun — kita verifikasi keduanya di sandbox:
<?php
$user = User::where('email', '[email protected]')->first();
$terbatas = $user->createToken('mobile-posting', ['posts:read', 'posts:create']);
$default = $user->createToken('default-budi');
echo json_encode($terbatas->accessToken->abilities);
echo json_encode($default->accessToken->abilities);Output yang Dihasilkan:
["posts:read","posts:create"]
["*"]Penamaan abilities bebas, tetapi konvensi umum memakai format resource:aksi seperti posts:create atau posts:read — mudah dibaca manusia dan mudah dipetakan ke route. Nilai abilities tersimpan sebagai JSON di kolom abilities, jadi tidak ada tabel tambahan untuk diurus.
Memeriksa dengan tokenCan #
Setelah token dipakai mengautentikasi request, method tokenCan() pada user memeriksa apakah token tersebut memiliki ability tertentu:
<?php
$token = $user->createToken('mobile-posting', ['posts:read', 'posts:create']);
$accessToken = $token->accessToken;
$userDenganToken = $user->withAccessToken($accessToken);
var_dump($userDenganToken->tokenCan('posts:create'));
var_dump($userDenganToken->tokenCan('posts:delete'));Output yang Dihasilkan:
bool(true)
bool(false)Dalam controller, $request->user() sudah membawa token yang sama lewat mekanisme withAccessToken tersebut, jadi pemeriksaannya cukup $request->user()->tokenCan('posts:create'). Pola ini cocok di dalam branch if biasa atau di awal method controller untuk keputusan yang tergantung logika bisnis.
Middleware ability dan abilities #
Untuk pembatasan per-route, Sanctum menyediakan dua middleware siap pakai: CheckForAnyAbility (lolos bila token punya salah satu ability yang disebut) dan CheckAbilities (harus punya semuanya). Kedua kelas ini tidak terpasang sebagai alias di aplikasi baru, jadi kita daftarkan dulu di bootstrap/app.php:
<?php
// bootstrap/app.php (cuplikan)
use Laravel\Sanctum\Http\Middleware\CheckAbilities;
use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility;
->withMiddleware(function (Middleware $middleware): void {
$middleware->alias([
'abilities' => CheckAbilities::class,
'ability' => CheckForAnyAbility::class,
]);
})Snippet di atas adalah potongan fokus dari pemanggilan withMiddleware di file bootstrap/app.php; baris use untuk Middleware dan Application tidak ditampilkan agar fokus tetap pada alias. Selanjutnya route cukup memanggil alias-nya, sama seperti middleware can yang sudah kita kenal di section Authorization
. Contoh di bawah memakai tabel posts latihan dari chapter Instalasi dan Konfigurasi
beserta relasi posts() pada model User:
<?php
// routes/api.php
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
// ability: lolos bila token punya SALAH SATU dari ability yang disebut
Route::get('/posts', fn () => Post::all(['id', 'title']))
->middleware(['auth:sanctum', 'ability:posts:read,posts:create']);
// token harus benar-benar punya posts:create untuk membuat post
Route::post('/posts', function (Request $request) {
$post = $request->user()->posts()->create([
'title' => $request->input('title', 'Belajar Sanctum'),
'body' => $request->input('body', 'Isi tulisan.'),
]);
return response()->json(['id' => $post->id, 'title' => $post->title], 201);
})->middleware(['auth:sanctum', 'ability:posts:create']);Bedah singkat kedua route: route GET /posts memakai ability:posts:read,posts:create karena client pembaca cukup punya salah satunya, sedangkan POST /posts menuntut posts:create secara eksplisit. Mari kita uji keduanya dengan token yang berbeda haknya:
Output yang Dihasilkan:
### POST /api/posts dengan token ['posts:read']
HTTP 403
{
"message": "Invalid ability provided."
}
### POST /api/posts dengan token ['posts:create']
HTTP 201
{
"id": 1,
"title": "Belajar Sanctum"
}Pesan "Invalid ability provided." adalah bentuk baku penolakan Sanctum — HTTP 403, bukan 401, karena client sudah terautentikasi dengan benar tetapi tokennya kurang hak. Ini penting dibedakan di sisi client: 403 berarti “token sah tapi tidak boleh”, bukan “token salah”.
Integrasi dengan Gate #
Bila aplikasi sudah memakai Gate dan policy, dokumentasi Sanctum menyarankan callback Gate::before agar nama ability token langsung dihormati Gate dengan nama yang sama:
<?php
use Illuminate\Support\Facades\Gate;
Gate::before(function ($user, $ability) {
if ($user->tokenCan($ability)) {
return true;
}
});
// Gate ketat: tanpa ability yang cocok, hanya Siti yang boleh
Gate::define('posts:create', fn ($user) => $user->name === 'Siti');Kita uji pola tersebut di sandbox dengan route bermiddleware can:posts:create: token yang memiliki posts:create lolos lewat Gate::before dan menerima HTTP 200, sedangkan token yang hanya punya posts:read jatuh ke definisi Gate dan ditolak HTTP 403 karena pemiliknya Budi, bukan Siti.
Matriks Uji dan Antarmuka Pemilihan Hak #
Kombinasi ability × route cepat bertambah, jadi pola yang sehat adalah matriks uji dengan data provider — satu method pengujian mengeksekusi seluruh kombinasi:
<?php
public static function penyediaMatrixAbility(): array
{
return [
'token penuh' => [['*'], 201],
'token tulis' => [['posts:create'], 201],
'token baca' => [['posts:read'], 403],
'token kosong' => [[], 403],
];
}
#[DataProvider('penyediaMatrixAbility')]
public function test_matrix_abilities(array $abilities, int $expectedStatus): void
{
$budi = User::factory()->create();
Sanctum::actingAs($budi, $abilities);
$this->postJson('/api/posts', ['title' => 'Belajar Sanctum'])
->assertStatus($expectedStatus);
}Output yang Dihasilkan:
PASS Tests\Feature\Sanctum\PengujianTest
✓ matrix abilities with data set "token penuh"
✓ matrix abilities with data set "token tulis"
✓ matrix abilities with data set "token baca"
✓ matrix abilities with data set "token kosong"
Tests: 9 passed (14 assertions)Di sisi antarmuka, form pembuatan token sebaiknya memakai daftar centang abilities alih-alih satu input teks — pengguna tinggal mencentang posts:read, posts:create, dan seterusnya tanpa perlu mengetik format resource:aksi dengan benar. Form tersebut menjadi inti chapter API Token Management
. Rujukan resminya ada di dokumentasi Sanctum — token abilities
.