Mengenal & Mengimplementasikan Laravel Airlock

Mengenal & Mengimplementasikan Laravel Airlock

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.

laravel airlock - authentication

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

laravel airlock - get access token

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

laravel airlock - abilities

Salin token yang dihasilkan, kemudian buka tab baru di Postman dan tambahkan user baru dengan konfigurasi seperti berikut

laravel airlock - check abilities

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

laravel airlock - get all token

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=.

 

Category:
Share:

Comments