Tutorial Lumen API dengan OAUTH2, CORS dan DingoAPI

Jadi ceritanya, ada kerjaan kantor yang mewajibkan saya melakukan migrasi aplikasi dari PHP versi lama (versi 5) menjadi versi yang paling mutakhir. Bukan hanya dari sisi bahasa, tapi juga dari sisi arsitektur aplikasi. Well, karena saat ini yang lumayan ngetren adalah pemisahan frontend-backend aplikasi dengan implementasi container (intinya, proses deployment bisa lebih mudah dan bisa dilakukan scaling kalo misal loadnya tinggi), maka saya coba research tentang gimana sih bikin API yang sesuai dengan kebutuhan. Berhubung basic saya adalah developer Laravel, maka yang saya cari adalah implementasi pembuatan API dalam bentuk Laravel.

Fortunately, ada yang namanya Lumen, turunan dari Laravel yang emang spesialis API. Laravel emang bisa buat bikin API, tapi Lumen emang dikhususkan untuk API dengan performa yang lebih baik lagi. Berdasarkan Kiddy di tulisannya pada halaman ini, performa Lumen bisa lebih baik dari framewok lainnya. That's why, saya coba memulai research saya dari Lumen.

FYI, sebelum kalian lanjut, ada beberapa pertimbangan kalo kalian pake Lumen. Salah satunya adalah ga adanya versi Long Term Support (LTS) dari Lumen. CMIIW, di dokumentasi Lumen bahkan ga menyebutkan adanya versi LTS. Yang disebutkan hanyalan "mengikuti versi Laravel", sehingga ketika ada versi Laravel muncul maka akan ada versi Lumen yang baru pula.

Versioning di Lumen


Oke, langsung aja kita mulai prosesnya ya


1. Instalasi Lumen

Selayaknya Laravel, instalasi Lumen bisa langsung copy-paste dari project yang ada sebelumnya maupun menggunakan composer. Saya sendiri lebih suka menggunakan composer, untuk memastikan tidak ada yang tertinggal dalam project tersebut. Sintaks yang digunakan adalah

composer create-project --prefer-dist laravel/lumen namaproject

kalo misal mau install dengan versi tertentu, tambahin aja versinya di belakang. Misal saya mau install versi 6, maka sintaksnya 

composer create-project --prefer-dist laravel/lumen namaproject "6.*

* menandakan untuk install versi 6 yang terbaru, entah itu 6.1, 6.2, atau yang lain.

Disini, sebagai contoh, saya coba bikin proyek dengan nama belajar

Proses Instalasi

Ingat, komputer anda harus tersambung dengan internet. Tunggu aja sampe seluruh elemen proyeknya terunduh.  Kalo udah, selamat maka proyek Lumen anda udah jadi. Arahin cmdnya ke folder belajar. Lanjuttt


2. Install Lumen Generator

Inget kan, kalo bikin project di Laravel, kita dimudahkan dengan berbagai fungsi Composer? bikin controller tinggal php artisan make:controller. Bikin model tinggal php artisan make:model. Nah, karena Lumen emang fokus pada performa, banyak fungsi fungsi yang ilang sehingga composernya ga selengkap itu. Namun, ada beberapa fungsi yang nantinya diperlukan. Oleh karena itu, install Lumen Generator. Caranya? masuk ke folder proyek, ketikkan sintaks berikut
composer require flipbox/lumen-generator

Kalo udah diinstall, buka file di bootstrap/app.php. Yang pertama, ilangin comment di line koding berikut 
$app->register(App\Providers\AppServiceProvider::class);
$app->register(App\Providers\AuthServiceProvider::class);
$app->register(App\Providers\EventServiceProvider::class);

 Yang kedua, tambahin kode ini di bagian Register Service Providers, tepat di bagian kode yang telah di uncomment sebelumya

$app->register(Flipbox\LumenGenerator\LumenGeneratorServiceProvider::class);

Sebagai komparasi, sebelum diinstall Lumen Generator, ketikkan php artisan lalu enter. Maka akan muncul list sintaks artisan yang bisa digunakan.

Sebelum Install Lumen Generator


Setelah Install Lumen Generator

Nah, emang apa fungsinya sih install Lumen Generator? Lanjut

3. Generate Key untuk proyek Lumen

Jadi, di php artisan yang baru kita install Lumen Generator, ada fungsi baru yaitu key:generate. Nah fungsinya apa sih? Kalo mau tau, coba buka .env file Laravel deh. Disitu ada variabel APP_KEY. Nah coba buka di Lumen. Harusnya sih nggak ada ya, sampe detik ini saya develop. Nah, APP_KEY ini penting apalagi buat API karena sebagai Key untuk cookies, kata halaman ini. Saya pernah ada pengalaman nggak enak karena pake ini, mau lempar parameter dari satu aplikasi ke aplikasi lain via controller langsung. Failed. Thats why perlu setting ENV yang ini, biar keamanannya bisa lebih dijaga.

Kebentuk deh APP_KEY, thanks to Lumen Generator

4. Install OAUTH2

Saya sulit menjelaskan teknisnya seperti apa. Mungkin bisa dibaca pada tulisan Tioreza di halaman ini. Gampangnya, dengan satu akun Google, kita bisa masuk ke berbagai aplikasi seperti Gmail, Spotify, Facebook, Twitter, dan sebagainya. Namun, tanpa persetujuan kita, aplikasi-aplikasi tersebut tidak akan bisa kita akses. Yaa intinya pengelolaan akses user ke berbagai aplikasi. 

Sesungguhnya, fungsi ini dimiliki oleh Laravel. Namun, karena Lumen makin populer dengan penggunaan API, maka dikembangkan juga OAUTH2 untuk Lumen. Luckily, penggunaannya juga sama dengan Laravel. Tapi, ada beberapa bug yang saya temui saat install, yang akan saya jelaskan nanti di tahapan ini.

Oke, tahapan pertama adalah install lumen passport. Caranya dalah dengan mengetikkan sintaks di bawah ini

composer require dusterio/lumen-passport

Kalo udah diinstall, lakukan konfigurasi di bootstrap/app.php seperti sebelumnya. Pertama, uncomment sintaks berikut
$app->withFacades();
$app->withEloquent();
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
]);

Lalu, tambahkan sintaks berikut di bagian yang sama dengan kita konfigurasi Lumen Generator, tetap di bootstrap/app.php.
$app->register(Laravel\Passport\PassportServiceProvider::class);
$app->register(Dusterio\LumenPassport\PassportServiceProvider::class);

Kalo udah semua, buka file .env dan lakukan setting databasenya. Tahapan ini penting banget, kalo ini ga dilakukan maka OAUTH2 ga bisa jalan. Karena disinilah bagian penyimpanan hak aksesnya. Berikut contohnya yang saya lakukan di proyek belajar.

Konfigurasi database di env

Nah, kalo udah, lakuin migrasi database dengan sintaks berikut
php artisan migrate
Bagi yang belum tau, fungsi ini adalah salah satu fungsi keren Laravel (dan Lumen) yang bisa generate table beserta kolom-kolomnya. Nah, kalo yang kita lakuin tadi bener, maka akan ada beberapa tabel oauth yang secara otomatis bakal kebentuk.

Ta-daa! Ga ada angin, ga ada asep, kebentuklah tabel-tabel


Ini buktinya

Oke, tabel udah kebentuk. Next step, jalanin passportnya. Wish us luck, kadang ini jalannya mulus selayaknya pantat bayi, kadang nggak. Jalanin sintaks berikut
php artisan passport:install

Add caption

Nah, pas banget kebetulan punya saya gagal hehe. Ga gagal sih, cuma perlu setting tambahan aja. Caranya adalah dengan install passport punya Laravel, atau eksekusi sintaks berikut

Yang harus dimasukkan

Efeknya, nanti bakal kebentuk data baru di table oauth_client. Bakal ada dua data yang kebentuk, data yang hasil php artisan passport:install yang pertama tadi dan data yang baru aja kita bentuk. Nah, di data yang baru kita bentuk, update password_client jadi 1. Seperti gambar di bawah

Isi Tabel

Nah, sebenernya ini apa sih? ribet banget? Semoga hasil riset saya benar ya, mungkin kalo ada suhu suhu yang lebih paham bisa koreksi saya

Intinya, yamg kita buat barusan adalah secret key untuk aplikasi yang mau kita beri hak akses. Jadi, ketika ada API yang mau akses aplikasi A, kita berikan id 2 dan secret untuk generate tokennya sehingga bisa diakses. Kalo mau beda aplikasi lain, Generate lagi client dan berikan id serta secretnya. 

Maksudnya gimana sih? Nanti, akan saya jelaskan. Sampe sini, ikutin aja dulu tahapannya, oke?

5. Implementasi OAUTH2 untuk API

Oke lanjut. Tahapan ini untuk memastikan bahwa OAUTH2 yang baru aja kita install dapat diimplemetasikan di API yang kita buat. Tahapnnya, buat folder baru bernama config dan buat file baru bernama auth.php. di dalam configauth.php, tambahkan kode berikut
<?php
return [
  'defaults' => [
  'guard' => 'api',
  'passwords' => 'users',
],
'guards' => [
  'api' => [
   'driver' => 'passport',
   'provider' => 'users',
  ],
],
'providers' => [
  'users' => [
   'driver' => 'eloquent',
   'model' => \App\User::class
  ]
]
];

kode diatas menjelaskan bahwa nantinya, yang semua api bakal menggunakan passport yang usernya bakal diambil dari model User. Ya, model default Laravel yang menyediakan Auth dan sejenisnya yang memudahkan pengelolaan user, kita bakal pake itu. 

Kalo udah, tambahin kedua kode ini di bootstrap/app.php
//tambahin di Register Config Files
$app->configure('auth');

//tambahin di Register Service Provider, kayak sebelumnya
\Dusterio\LumenPassport\LumenPassport::routes($app);

Untuk memastikan bahwa passport telah terimplementasi, ketikkan sintaks berikut
php artisan route:list

Nah, kalo yang muncul sesuai dengan gambar dibawah, congrats. Udah keinstall dengan baik passportnya

Route baru yang terbentuk untuk OAUTH

Oke, lanjut. Buat menghubungkan Passport dengan User, tambahkan sintaks ini di model User, yang ada di App/User.php.
// tambahin di yang paling atas
use Laravel\Passport\HasApiTokens;

class User extends Model implements AuthenticatableContract, AuthorizableContract
{


//tambahin HasApiTokens di kode tepat di bawahnya class
use HasApiTokens, Authenticatable, Authorizable;

Nah, karena kita butuh data User tapi belum ada tabel user, kita buat dulu tabel usernya. Buat dulu migrasinya, baru isi datanya. Well, ini sebenernya materi Laravel sih. Yang jelas, di tabelnya harus mengikuti default dari Auth bawaan Laravel, terlepas dari dia menggunakan email atau username. Untuk tahapan ini silahkan dikonfigurasi sendiri, karena kebutuhan user berbeda beda.

Nah, kalo udah jadi tabel User berserta isinya, jalanin API yang /oauth/token. Parameternya adalah sebagai berikut :

"grant_type":"password",
"client_id" : "2", //samain kayak id di oauth_user, yang saya bilang sebelumnya
"client_secret":"Y5KlL3ltfUYhCl5Tajif2ingBT1Q9rG16SiSGNUJ", ->samain kayak secret
"username":"yogha3722", ->samain kyk email di tabel user
"password":"xxxxxxx", ->samain kyk passwordnya
"scope":"*"

kalo berhasil, maka akan muncul response sebagai berikut

Respon dari APInya

Nah, disitu kan ada access token. Copy token tersebut, paste di API yang akan mau digunakan nantinya pada Authorization->Bearer Token. Oh iya, buat jalanin APInya saya pake Postman ya, kalo di Postman kurang lebih ada di sini nanti.

Disini nanti paste access tokennya

Oke, itu nanti aja dicobanya. Kurang lebih, begitulah implementasi OAUTH2 di Lumen. Melelahkan? iya. Tapi masih belum selesai. API ini masih belum siap diimplementasikan, karena ada beberapa hal yang perlu diinstall lagi, seperti yang ada di judulnya. Ikan hiu ikan cucut, lanjut!

6. Install CORS

Ini, yang paling saya benci dari semua dunia API : CORS. Dulu proyek saya selalu terkendala masalah ini, sebelum tau caranya hehehe.

Sedikit pembukaan, kalo kita menggunakan arsitektur Frontend-Backend dan Frontend yang menggunakan javascript seperti Angular, React maupun Vue, 100% pasti bakal kena CORS kalo kedua domain Frontend dan Backend berbeda (kalo misal sama, buat apa dipisah kalo gitu). Long story short, kita harus implementasi CORS kalo misal frontend kita berbasis javascript dan beda domain. 

Nah, gimana caranya sih? Sebenernya banyak banget caranya. Tapi saya coba sampaikan tiga cara ya, biar kalian bisa memilih

a. Menggunakan package CORS

Ini cara paling cepet dan sederhana sih. Ga banyak coding. pertama, install package laravel-cors
composer require fruitcake/laravel-cors

terus, seperti biasa, tambahin di bootstrap/app.php
//daftarin di bootstrap/app.php
$app->register(Fruitcake\Cors\CorsServiceProvider::class);
$app->configure('cors');

//tambahin di middleware, biar dipanggil terus. Nanti jadinya gini
$app->middleware([
    Fruitcake\Cors\HandleCors::class,
]);


kalo udah, bikin file baru di folder config dengan nama cors.php. copy-paste kode berikut
<?php
return[
'supports_credentials'=>true,
'allowed_origins'=>['*'],
'allowed_headers'=>['Content-Type','Authorization','X-Requested-With'],
'allowed_methods'=>['*'],
'exposed_headers'=>[],
'max_age'=>0
]; 

udah deh, selesai. gampang kan?

b. Bikin sendiri

Ada cara gampang kok malah bikin sendiri? yaa alternatif aja kalo misalnya ga bisa. Inget, dunia pemrograman itu ada aja masalahya.

Caranya? bikin file baru bernama CorsMiddleware.php di App\Http\Middleware. Terus, copy-paste code berikut ini
<?php
namespace App\Http\Middleware;
use Closure;
class CorsMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$headers = [
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'POST, GET, OPTIONS, PUT, DELETE',
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Max-Age' => '86400',
'Access-Control-Allow-Headers' => 'Content-Type, Authorization, X-Requested-With'
];
if ($request->isMethod('OPTIONS'))
{
return response()->json('{"method":"OPTIONS"}', 200, $headers);
}
$response = $next($request);
foreach($headers as $key => $value)
{
$response->header($key, $value);
}
return $response;
}
}

Kalo udah, sama kayak cara sebelumnya, tambahin di bootstrap/app yang ada di Middleware biar kepanggil di tiap API
$app->middleware([
App\Http\Middleware\CorsMiddleware::class
]);
Done~

7. Install Dingo API

Nah, kalo yang ini, sebenernya tambahan aja sih. Dari banyak fungsi dingoAPI, yang saya suka adalah error responsenya sehingga kalo APInya ada error bisa lebih cantik. Caranya sih mayan gampang, install aja dingoAPI di composer dengan sintaks berikut
composer require dingo/api

kalo udah, seperti biasa, tambahin di bootstrap
$app->register(Dingo\Api\Provider\LumenServiceProvider::class);

Nah, karena ini rada custom, nulis sintaksnya agak beda di routingnya. di folder routes/web.php, gini cara nulis kodenya. Tapi inget, yang ditambahin ini yang kira-kira route intinya aja, yang perlu token. 

$api = app('Dingo\Api\Routing\Router');

$api->version('v1',function($api){
  $api->group(['prefix'=>'oauth'],function($api){
    $api->post('token','\Laravel\Passport\Http\Controllers\AccessTokenController@issueToken');
  });
  $api->group(['namespace'=>'App\Http\Controllers','middleware'=>['auth:api','cors']],function($api){

  //controller route, semua route nanti diisi di sini. ini contohnya
   $api->get('birthday','Dashboard\DashboardController@show');

   });
});

terakhir, tambahin API_PREFIX=api di file env. 



It's a wraaap! congrats, pondasi API bertoken dan lain lain udah siap digunakan. Wew panjang juga bikin tulisan gini. Nah, kalo dari saya sih kurang lebih begitu. Mungkin kalo ada rekan-rekan yang lebih paham dan bisa mengkoreksi, bisa komen di bawah atau bisa DM saya di IG ke @yoghadj.

Mungkin saya juga bakal bikin versi videonya, biar bisa lebih gampang ditiruin. 



once again, thanks!


Regards,