Pendahuluan
Salah satu fitur yang menarik perhatian dari Laravel 7 adalah Airlock. Fitur ini melengkapi sederet fitur lainnya yang serupa dalam menciptakan fungsi authentication, sebut saja yang lebih dahulu dikenali adalah Laravel Passport. Airlock adalah sistem otentikasi untuk SPA (Single Page Application), mobile application dan segala mekanisme otentikasi berbasis token. Jadi, jika berbicara tentang token, maka pembicaraan ini menyangkut tentang sistem API (Application Programmer Interface) dalam melindungi sebuah data dari pengguna yang tidak diinginkan.
Laravel Airlock hadir dengan 'gaya'-nya sendiri dalam menawarkan solusi otentikasi sebuah API. Package ini tergolong sederhana dalam penggunaannya untuk men-generate API Token untuk users dan tidak sekompleks Oauth. Token yang dihasilkan oleh Airlock memiliki expiration time yang sangat lama, tapi pengguna memiliki kemampuan untuk me-revoke (menghapus) token yang dimilikinya kapanpun diinginkan.
Baca Juga: Authorization Using Gates Laravel 7
Install & Konfigurasi Laravel Airlock
Sebelum memulai untuk menggunakan Laravel Airlock, install terlebih dahulu Laravel 7 dengan command
composer create-project --prefer-dist laravel/laravel dw-airlock
Agar perintah diatas secara otomatis meng-install Laravel terbaru, maka pastikan sistem requirements yang diminta sudah terpenuhi. Jika proses instalasinya sudah selesai, masuk ke dalam folder dw-airlock
dan install Laravel Airlock dengan command
composer require laravel/airlock
Kemudian publish konfigurasinya dengan perintah
php artisan vendor:publish --provider="Laravel\Airlock\AirlockServiceProvider"
Buka file .env
dan sesuaikan informasi database kamu
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=dw-airlock
DB_USERNAME=root
DB_PASSWORD=toor
Ketika kita melakukan publish config diatas, juga sekaligus membuat file migration. Eksekusi migration yang ada untuk men-generate struktur database.
php artisan migrate
Laravel Airlock hanya membutuhkan sebuah table bernama personal_access_tokens
untuk menyimpan semua token users. Bermodalkan table ini, kita akan mengenali & menggunakan semua kemampuan yang dimiliki oleh Airlock, tapi sebelum itu, kita harus me-register middleware dari Airlock pada route api, buka file app/Http/Kernel.php
dan modifikasi $middlewareGroups
, khususnya pada bagian api
menjadi.
'api' => [
EnsureFrontendRequestsAreStateful::class, //TAMBAHKAN BARIS INI
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
Untuk menghubungkannya dengan users, tambahkan trait berikut di dalam model User. Buka file app/User.php
dan modifikasi menjadi.
<?php
namespace App;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Airlock\HasApiTokens; //TAMBAHKAN CODE INI
class User extends Authenticatable
{
use Notifiable, HasApiTokens; //GUNAKAN TRAITNYA
//[.. CODE LAINNYA .]
Authentication & Generate Token
Simulasinya adalah kita akan membuat fitur authentication dimana sebuah users bisa men-generate beberapa token terkandung kondisi yang diinginkan. Kita asumsikan saja, 1 data user ini akan digunakan login dari dua device, yakni web based dan mobile. Kedua metode login ini akan menggunakan token masing-masing sehingga tidak saling timpa antara satu dan yang lainnya.
Langkah pertama, generate seeder untuk membuat dummy users dengan command php artisan make:seeder UserTableSeeder
dan buka file database/seeds/UserTableSeeder.php
<?php
use Illuminate\Database\Seeder;
use App\User;
class UserTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
User::create([
'name' => 'Anugrah Sandi',
'email' => '[email protected]',
'password' => bcrypt('secret')
]);
}
}
Implementasikan data tersebut dengan menjalankan perintah php artisan db:seed --class=UserTableSeeder
. Adapun schema yang diinginkan adalah kita akan membuat dua buah route, yang pertama adalah login dan yang kedua route untuk menampilkan semua data users dimana route ini akan di-protect menggunakan authentication, sehingga hanya bisa dilihat oleh user yang memiliki akses token.
Tujuan kita hanya akan memahami bagaimana Laravel Airlock bekerja, maka hanya ada 1 controller yang akan digunakan untuk membuat semua fitur yang diinginkan. Dari command line, generate sebuah controller baru
php artisan make:controller UserController
Buka file UserController.php
dan tambahkan method berikut untuk meng-handle proses otentikasi
public function login(Request $request)
{
//VALIDASI DATA YANG DIKIRIMKAN
$this->validate($request, [
'email' => 'required|email|exists:users,email',
'password' => 'required',
'type' => 'required'
]);
//CARI BERDASARKAN EMAIL
$user = User::where('email', $request->email)->first();
//LAKUKAN PENGECEKAN, JIKA PASSWORDNYA TIDAK SESUAI
if (!Hash::check($request->password, $user->password)) {
//MAKA BERIKAN RESPON FAILED
return response()->json(['status' => 'failed', 'data' => 'Password Anda Salah']);
}
//SELAIN ITU, BERIKAN RESPON SUKSES DAN GENERATE TOKEN LOGIN
return response()->json(['status' => 'success', 'data' => $user->createToken($request->type)->plainTextToken]);
}
Penjelasan: Method createToken()
meminta sebuah parameter yang dijadikan name untuk mengelompokkan jenis token. Setiap token akan di-hash menggunakan SHA-256 sebelum disimpan ke dalam database. Jadi, token yang ada di-database bukanlah token yang bisa digunakan secara langsung, sehingga untuk mengambil token yang sebenarnya, kita bisa menggunakan plainTextToken
.
Saatnya kita membuat sebuah API yang dilindungi oleh otentikasi, sehingga user harus mengirimkan token yang dimilikinya agar bisa melihat data pada API ini. Masih menggunakan file yang sama, tambahkan method
public function index()
{
$users = User::orderBy('created_at', 'DESC')->paginate(10);
return response()->json(['status' => 'success', 'data' =>$users]);
}
Jangan lupa untuk menambahkan use statement
use Illuminate\Support\Facades\Hash;
use App\User;
Definisikan routing dari kedua fungsi di atas, buka file routes/api.php
dan modifikasi menjadi
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
//ROUTING YANG INGIN DIPROTECT DENGAN AIRLOCK, MAKA HARUS MENGGUNAKAN MIDDLEWARE DARI AUTH:AIRLOCK
Route::group(['middleware' => 'auth:airlock'], function() {
//SEHINGGA SEMUA ROUTING YANG ADA DI DALAMNYA HARUS MENGIRIMKAN TOKEN
Route::get('/users', 'UserController@index');
});
Route::post('/login', 'UserController@login');
Untuk melakukan uji coba, buka Postman dan lakukan konfigurasi seperti gambar berikut dan pastikan url yang kamu tuju adalah url dari aplikasi kamu saat ini.
Value dari type adalah pengelompokan login berdasarkan device, misalnya saja kamu akan melakukan proses otentikasi pada mobile apps, maka dengan url yang sama, kita hanya perlu memasukkan type yang berbeda, proses otentikasi bisa terjadi tanpa harus me-replace token sebelumnya.
Adapun untuk mengakses data users, maka pada headers kita harus mengirimkan Authorization
dengan value Bearer (spasi) token
seperti gambar berikut
Baca Juga: Ckeditor Image Upload di Laravel 6
Set Token Abilities
Salah satu hal menarik dari Airlock ini adalah masing-masing token bisa diberikan abilities atau kemampuan sehingga user hanya bisa mengakses fitur yang sudah ditetapkan berdasarkan token yang dimilikinya.
Misalnya saja untuk contoh sederhana, masing-masing user akan kita berikan role dan setiap kali proses otentikasi berhasil, secara otomatis akan mengecek role-nya terlebih dahulu kemudian men-generate token yang dilengkapi abilities berdasarkan role. Dari command line, buat migration baru
php artisan make:migration add_field_role_to_users_table
Buka file migration yang baru dan modifikasi menjadi
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddFieldRoleToUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->enum('role', ['admin', 'users'])->after('password');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('role', ['admin', 'users']);
});
}
}
Kemudian buka file UserTableSeeder.php
karena kita akan membuat dua buah user yang memiliki role berbeda. Modifikasi file tersebut menjadi
<?php
use Illuminate\Database\Seeder;
use App\User;
class UserTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
User::truncate();
User::create([
'name' => 'Anugrah Sandi',
'email' => '[email protected]',
'password' => bcrypt('secret'),
'role' => 'admin'
]);
User::create([
'name' => 'Riski Amelia',
'email' => '[email protected]',
'password' => bcrypt('secret'),
'role' => 'users'
]);
}
}
Jalankan kembali seeder-nya dengan command php artisan db:seed --class=UserTableSeeder
. Selanjutnya modifikasi method login
yang ada di dalam file UserController.php
menjadi
public function login(Request $request)
{
$this->validate($request, [
'email' => 'required|email|exists:users,email',
'password' => 'required',
'type' => 'required'
]);
$user = User::where('email', $request->email)->first();
if (!Hash::check($request->password, $user->password)) {
return response()->json(['status' => 'failed', 'data' => 'Password Anda Salah']);
}
//JADI, JIKA DIA ADMIN, MAKA MEMILIKI DUA ABILITY, MELIHAT DAN NEMBAHKAN. JIKA DIA USER MAKA HANYA BISA MELIHAT DATA USER.
$abilities = $user->role == 'admin' ? ['user:index', 'user:create']:['user:index'];
return response()->json([
'status' => 'success',
//LALU PADA METHOD createToken(), TAMBAHKAN PARAMETER ABILITIESNYA
'data' => $user->createToken($request->type, $abilities)->plainTextToken
]);
}
Masih dengan file yang sama, kita akan memodifikasi method index
untuk mengecek apakah user yang sedang meminta data memiliki akses atau tidak, sekaligus juga menambahkan sebuah method baru untuk membuat user baru
public function index()
{
$user = request()->user(); //GET USER LOGIN
//JIKA USER MEMILIKI ABILITIES USER:INDEX
if ($user->tokenCan('user:index')) {
//MAKA DATANYA AKAN DITAMPILKAN
$users = User::orderBy('created_at', 'DESC')->paginate(10);
return response()->json(['status' => 'success', 'data' =>$users]);
}
//JIKA TIDAK, MAKA BERIKAN RESPON UNAUTHORIZED
return response()->json(['status' => 'failed', 'data' => 'Unauthorized']);
}
public function store(Request $request)
{
//VALIDASI DATA YANG AKAN DITAMBAHKAN
$this->validate($request, [
'name' => 'required|string',
'email' => 'required|email|unique:users',
'password' => 'required',
'role' => 'required',
]);
$user = $request->user(); //GET USER LOGIN
//CEK ABILITIES DARI USER TERSEBUT
if ($user->tokenCan('user:create')) {
//JIKA IYA, MAKA BUAT DATA BARU
User::create($request->all());
return response()->json(['status' => 'success']);
}
//JIKA GAGAL, BERIKAN RESPON GAGAL
return response()->json(['status' => 'faield', 'data' => 'Unauthorized']);
}
Buka file routes/api.php
dan tambahkan route berikut di dalam middleware route group.
Route::post('/users', 'UserController@store');
Adapun untuk proses uji coba, kita akan login menggunakan user yang memiliki role users
untuk membuat atau menambahkan data baru. Tujuannya adalah untuk memastikan apakah abilities-nya bekerja dengan baik atau tidak. Buka Postman dan lakukan proses otentikasi sesuai gambar berikut
Salin token yang dihasilkan, kemudian buka tab baru di Postman dan tambahkan user baru dengan konfigurasi seperti berikut
Pastikan pada bagian Headers, tambahkan token yang sudah disalin sebelumnya. Apabila menekan tombol Send, maka response yang dihasilkan adalah failed karena user dengan role users
tidak memiliki akses untuk menambahkan data baru. Jika kamu ingin benar benar memastikan, silahkan login dengan akun yang memilik role admin, dan ulangi proses menambahkan data baru dengan menggunakan token dari login admin.
Revoking Token
Sebagaimana yang kita ketahui bersama bahwasanya setiap user bisa memiliki lebih dari 1 token dan semua token yang dimilikinya bisa digunakan untuk login dalam waktu yang bersamaan. Tentu saja, setiap user terkait harus memiliki akses untuk mengelola token yang dimilikinya, misalnya saja menghapus semua token atau hanya menghapus token yang diinginkannya saja.
Langkah pertama, fitur yang akan dibuat adalah sebuah fungsi untuk menampilkan semua list token dari user yang sedang login. Buka file UserController.php
dan tambahkan method
public function getAllUserToken()
{
$users = request()->user();
return response()->json([
'status' => 'success',
'data' => $users->tokens
]);
}
Kemudian definisikan method diatas menjadi sebuah route, buka file routes/api.php
dan tambahkan route berikut
Route::get('/users/tokens', 'UserController@getAllUserToken');
Jika kita mencoba mengakses url di atas, maka hasil yang akan diperoleh akan terlihat seperti gambar berikut
Schema untuk menghapus token menggunakan dua cara, jika parameter token_id
dikirimkan, maka token yang akan dihapus adalah berdasarkan id yang diterima. Jika parameter tersebut kosong, maka semua token akan dihapus. Buka file UserController.php
dan tambahkan method
public function revokeToken()
{
$user = request()->user(); //GET USER LOGIN
//JIKA TOKEN ID NYA ADA
if (request()->token_id) {
//MAKA HAPUS BERDASARKAN ID TOKEN TERSEBUT
$user->tokens()->where('id', request()->token_id)->delete();
return response()->json(['status' => 'success']);
}
//SELAIN ITU, HAPUS SEMUA DATA TOKEN
$user->tokens()->delete();
return response()->json(['status' => 'success']);
}
Pastikan untuk mendefinisikan routing-nya, buka file routes/api.php
dan tambahkan code
Route::get('/users/delete', 'UserController@revokeToken');
Adapun url untuk uji coba nya adalah namadomain/api/users/delete?id_token=
.
Comments