Laravel Sanctum: Autentikasi API untuk Single Page Application

Selamat datang! Apakah kamu sedang membangun Single Page Application (SPA) menggunakan Laravel dan bingung bagaimana cara mengamankan API-mu? Jangan khawatir, artikel ini akan membahas tuntas tentang Laravel Sanctum: Autentikasi API untuk Single Page Application. Kita akan kupas tuntas apa itu Sanctum, mengapa kamu membutuhkannya, bagaimana cara kerjanya, dan tentu saja, panduan implementasinya langkah demi langkah. Mari kita mulai!

1. Pengantar Laravel Sanctum: Apa Itu dan Mengapa Penting?

Laravel Sanctum adalah solusi autentikasi API yang sangat ringan, cocok untuk SPAs (Single Page Applications), mobile apps, dan API yang sederhana. Dulu, untuk mengamankan API Laravel, kita mungkin terpikir menggunakan OAuth2 atau JWT. Namun, konfigurasi dan kompleksitas yang ditawarkan oleh kedua metode tersebut bisa jadi berlebihan untuk kebutuhan proyek-proyek yang lebih kecil.

Di sinilah Laravel Sanctum hadir sebagai penyelamat! Ia menyediakan sistem autentikasi berbasis token yang sederhana, yang memungkinkan aplikasi front-end (seperti SPA) untuk mengautentikasi pengguna dan mengakses API backend Laravel tanpa perlu menangani kompleksitas implementasi OAuth2 atau JWT.

Mengapa Laravel Sanctum penting?

  • Sederhana: Implementasinya jauh lebih sederhana dibandingkan OAuth2 atau JWT.
  • Aman: Menyediakan mekanisme proteksi terhadap CSRF (Cross-Site Request Forgery) untuk SPA yang berjalan di domain yang sama dengan aplikasi Laravel.
  • Ringan: Tidak memerlukan banyak konfigurasi tambahan dan tidak membebani performa aplikasi.
  • Fleksibel: Bisa digunakan untuk mengamankan API yang diakses oleh SPA, mobile apps, atau API internal untuk layanan mikro.

2. Cara Kerja Laravel Sanctum: Memahami Alur Autentikasi

Bagaimana sebenarnya Laravel Sanctum: Autentikasi API untuk Single Page Application ini bekerja di belakang layar? Mari kita telusuri alurnya:

  1. Login: Pengguna mencoba login melalui aplikasi front-end (SPA). Aplikasi ini mengirimkan permintaan login ke endpoint Laravel dengan kredensial pengguna (username/email dan password).

  2. Autentikasi di Backend: Laravel menerima permintaan login dan memvalidasi kredensial pengguna. Jika valid, Laravel akan mengautentikasi pengguna.

  3. Penerbitan Token: Setelah autentikasi berhasil, Laravel Sanctum akan menerbitkan “Personal Access Token” (PAT) untuk pengguna tersebut. Token ini disimpan dalam database Laravel.

  4. Pengembalian Token: Token yang diterbitkan dikirim kembali ke aplikasi front-end (SPA).

  5. Penyimpanan Token di Front-end: Aplikasi front-end (SPA) menyimpan token ini secara aman, biasanya di localStorage atau sessionStorage. Perhatikan keamanan dalam menyimpan token, jangan simpan di tempat yang mudah diakses oleh script lain.

  6. Permintaan API: Saat aplikasi front-end (SPA) ingin mengakses API, ia melampirkan token yang disimpan dalam header Authorization dengan skema Bearer. Contoh: Authorization: Bearer <token>

  7. Validasi Token: Laravel menerima permintaan API dan memvalidasi token yang ada di header Authorization.

  8. Akses API: Jika token valid, Laravel akan mengizinkan akses ke endpoint API yang diminta. Jika token tidak valid atau kadaluarsa, Laravel akan menolak akses dan mengembalikan error 401 Unauthorized.

Singkatnya, Laravel Sanctum menggunakan token untuk memverifikasi identitas pengguna dan mengizinkan akses ke API. Alur ini relatif sederhana dan mudah diimplementasikan.

3. Instalasi dan Konfigurasi Laravel Sanctum

Sekarang, mari kita masuk ke bagian praktisnya: instalasi dan konfigurasi Laravel Sanctum: Autentikasi API untuk Single Page Application.

Persiapan:

  • Pastikan kamu sudah memiliki proyek Laravel yang berjalan. Jika belum, kamu bisa membuatnya dengan perintah composer create-project --prefer-dist laravel/laravel nama-proyek
  • Pastikan kamu sudah mengkonfigurasi database Laravel dengan benar.

Langkah-langkah Instalasi:

  1. Install Sanctum: Buka terminal dan masuk ke direktori proyek Laravel kamu. Kemudian, jalankan perintah berikut untuk menginstall Sanctum menggunakan Composer:

    composer require laravel/sanctum
  2. Publish Konfigurasi: Setelah instalasi selesai, publish file konfigurasi Sanctum menggunakan perintah:

    php artisan vendor:publish --provider="LaravelSanctumSanctumServiceProvider"

    Perintah ini akan membuat file konfigurasi config/sanctum.php. Kamu bisa memodifikasi file ini sesuai dengan kebutuhan proyek kamu.

  3. Migrasi Database: Sanctum membutuhkan tabel database untuk menyimpan token. Jalankan migrasi database dengan perintah:

    php artisan migrate

    Perintah ini akan membuat tabel personal_access_tokens di database kamu.

  4. Setup Model User: Pastikan model User kamu menggunakan trait HasApiTokens dari Sanctum. Buka file app/Models/User.php dan tambahkan:

    <?php
    
    namespace AppModels;
    
    use IlluminateContractsAuthMustVerifyEmail;
    use IlluminateDatabaseEloquentFactoriesHasFactory;
    use IlluminateFoundationAuthUser as Authenticatable;
    use IlluminateNotificationsNotifiable;
    use LaravelSanctumHasApiTokens; // Import trait HasApiTokens
    
    class User extends Authenticatable
    {
        use HasApiTokens, HasFactory, Notifiable;
    
        // ... kode lainnya ...
    }
  5. Konfigurasi CORS (Cross-Origin Resource Sharing): Ini sangat penting jika aplikasi front-end (SPA) kamu berjalan di domain yang berbeda dengan aplikasi Laravel. Kamu perlu mengkonfigurasi CORS agar aplikasi front-end bisa mengakses API Laravel. Kamu bisa menggunakan package fruitcake/laravel-cors.

    • Install package CORS: composer require fruitcake/laravel-cors
    • Publish konfigurasi CORS: php artisan vendor:publish --tag=cors
    • Konfigurasi config/cors.php sesuai dengan kebutuhan kamu. Pastikan untuk memasukkan domain aplikasi front-end kamu di opsi allowed_origins.

    Contoh konfigurasi config/cors.php:

    <?php
    
    return [
    
        /*
        |--------------------------------------------------------------------------
        | Cross-Origin Resource Sharing (CORS) Configuration
        |--------------------------------------------------------------------------
        |
        | Here you may configure your settings for cross-origin resource sharing
        | or "CORS". This determines what cross-origin operations may execute
        | in web browsers. To learn more:
        | https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
        |
        */
    
        'paths' => ['api/*', 'sanctum/csrf-cookie'], // Penting untuk sanctum/csrf-cookie
        'allowed_methods' => ['*'],
        'allowed_origins' => ['http://localhost:3000', 'http://nama-domain-spa-kamu.com'], // Ganti dengan domain SPA kamu
        'allowed_origins_patterns' => [],
        'allowed_headers' => ['*'],
        'exposed_headers' => [],
        'max_age' => 0,
        'supports_credentials' => true, // Penting untuk cookie
    ];

    Catatan Penting: Pastikan kamu memahami implikasi keamanan dari konfigurasi CORS. Konfigurasi yang terlalu permisif dapat membuka celah keamanan.

4. Membuat Endpoint Login dan Register dengan Sanctum

Selanjutnya, kita akan membuat endpoint untuk login dan register pengguna, yang akan menghasilkan token Laravel Sanctum saat berhasil.

Endpoint Register:

  1. Route: Buka file routes/api.php dan tambahkan route untuk register:

    <?php
    
    use AppHttpControllersAuthController;
    use IlluminateSupportFacadesRoute;
    
    Route::post('/register', [AuthController::class, 'register']);
  2. Controller: Buat controller AuthController dengan perintah: php artisan make:controller AuthController

    Kemudian, tambahkan method register ke dalam AuthController.php:

    <?php
    
    namespace AppHttpControllers;
    
    use AppModelsUser;
    use IlluminateHttpRequest;
    use IlluminateSupportFacadesHash;
    use IlluminateSupportFacadesValidator;
    
    class AuthController extends Controller
    {
        public function register(Request $request)
        {
            $validator = Validator::make($request->all(), [
                'name' => 'required|string|max:255',
                'email' => 'required|string|email|max:255|unique:users',
                'password' => 'required|string|min:8|confirmed',
            ]);
    
            if ($validator->fails()) {
                return response()->json($validator->errors(), 400);
            }
    
            $user = User::create([
                'name' => $request->name,
                'email' => $request->email,
                'password' => Hash::make($request->password),
            ]);
    
            $token = $user->createToken('auth_token')->plainTextToken;
    
            return response()->json([
                'data' => $user,
                'access_token' => $token,
                'token_type' => 'Bearer',
            ], 201);
        }
    }

Endpoint Login:

  1. Route: Tambahkan route untuk login di routes/api.php:

    <?php
    
    use AppHttpControllersAuthController;
    use IlluminateSupportFacadesRoute;
    
    Route::post('/login', [AuthController::class, 'login']);
  2. Controller: Tambahkan method login ke dalam AuthController.php:

    <?php
    
    namespace AppHttpControllers;
    
    use IlluminateHttpRequest;
    use IlluminateSupportFacadesAuth;
    use IlluminateSupportFacadesValidator;
    
    class AuthController extends Controller
    {
        public function login(Request $request)
        {
            $validator = Validator::make($request->all(), [
                'email' => 'required|string|email|max:255',
                'password' => 'required|string',
            ]);
    
            if ($validator->fails()) {
                return response()->json($validator->errors(), 400);
            }
    
            if (!Auth::attempt($request->only('email', 'password'))) {
                return response()->json([
                    'message' => 'Invalid login credentials'
                ], 401);
            }
    
            $user = User::where('email', $request['email'])->firstOrFail();
    
            $token = $user->createToken('auth_token')->plainTextToken;
    
            return response()->json([
                'data' => $user,
                'access_token' => $token,
                'token_type' => 'Bearer',
            ], 200);
        }
    }

Penjelasan Kode:

  • Kedua method di atas menggunakan Validator untuk memvalidasi input dari request.
  • Method register membuat user baru dan menghasilkan token menggunakan createToken('auth_token')->plainTextToken. auth_token adalah nama token yang bisa kamu ganti sesuai kebutuhan.
  • Method login menggunakan Auth::attempt untuk memvalidasi kredensial. Jika valid, ia menghasilkan token sama seperti method register.
  • Response yang dikembalikan berisi data user, access token, dan tipe token (Bearer).

5. Mengamankan Endpoint API dengan Middleware Sanctum

Setelah berhasil mendapatkan token, saatnya mengamankan endpoint API kita. Laravel Sanctum: Autentikasi API untuk Single Page Application menyediakan middleware yang memudahkan proses ini.

Langkah-langkah:

  1. Gunakan Middleware auth:sanctum: Buka file routes/api.php dan gunakan middleware auth:sanctum pada route yang ingin kamu amankan.

    Contoh:

    <?php
    
    use AppHttpControllersApiController;
    use IlluminateSupportFacadesRoute;
    
    Route::middleware('auth:sanctum')->group(function () {
        Route::get('/profile', [ApiController::class, 'profile']);
        Route::get('/posts', [ApiController::class, 'posts']);
    });

    Kode di atas akan mengamankan endpoint /profile dan /posts. Hanya pengguna yang memiliki token Laravel Sanctum yang valid yang bisa mengakses endpoint ini.

  2. Buat Controller API: Buat controller ApiController dengan perintah: php artisan make:controller ApiController

    Contoh method profile di dalam ApiController.php:

    <?php
    
    namespace AppHttpControllers;
    
    use IlluminateHttpRequest;
    use IlluminateSupportFacadesAuth;
    
    class ApiController extends Controller
    {
        public function profile(Request $request)
        {
            return response()->json(Auth::user());
        }
    
        public function posts(Request $request) {
            // Misalnya, ambil data posts dari database
            $posts = [
                ['id' => 1, 'title' => 'Post 1', 'content' => 'Ini adalah konten post 1'],
                ['id' => 2, 'title' => 'Post 2', 'content' => 'Ini adalah konten post 2'],
            ];
            return response()->json($posts);
        }
    }

    Method profile di atas akan mengembalikan data user yang sedang login. Method posts memberikan contoh mengembalikan data yang diamankan.

Bagaimana Middleware Bekerja?

Middleware auth:sanctum secara otomatis memeriksa header Authorization pada setiap request yang masuk. Jika header tersebut ada dan berisi token Laravel Sanctum yang valid, middleware akan mengautentikasi user dan mengizinkan request untuk diproses lebih lanjut. Jika header tidak ada atau token tidak valid, middleware akan menolak request dan mengembalikan error 401 Unauthorized.

6. Implementasi di Front-End (SPA)

Sekarang, bagaimana cara menggunakan token Laravel Sanctum di aplikasi front-end (SPA)? Berikut adalah contoh implementasi menggunakan JavaScript (misalnya, dengan fetch atau axios):

Login dan Penyimpanan Token:

// Contoh menggunakan fetch
fetch('/api/login', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        email: '[email protected]',
        password: 'password'
    })
})
.then(response => response.json())
.then(data => {
    // Simpan token ke localStorage
    localStorage.setItem('authToken', data.access_token);
})
.catch(error => {
    console.error('Error:', error);
});

Melakukan Permintaan API dengan Token:

// Contoh menggunakan fetch
const token = localStorage.getItem('authToken');

fetch('/api/profile', {
    headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
    }
})
.then(response => response.json())
.then(data => {
    console.log('Profile:', data);
})
.catch(error => {
    console.error('Error:', error);
});

Penjelasan:

  • Setelah berhasil login, token Laravel Sanctum disimpan di localStorage.
  • Saat melakukan permintaan API, token diambil dari localStorage dan ditambahkan ke header Authorization dengan skema Bearer.

Keamanan Penyimpanan Token:

  • Penting: localStorage rentan terhadap serangan XSS (Cross-Site Scripting). Pertimbangkan untuk menggunakan httpOnly cookie jika memungkinkan (membutuhkan konfigurasi tambahan pada backend). Cookie httpOnly tidak bisa diakses oleh JavaScript, sehingga lebih aman.
  • Jika menggunakan localStorage, pastikan untuk melakukan sanitasi input dan output untuk mencegah serangan XSS.
  • Pertimbangkan untuk menggunakan library yang menyediakan fitur enkripsi untuk menyimpan token di front-end.

7. Logout dan Penghapusan Token

Penting untuk menyediakan fitur logout yang memungkinkan pengguna untuk menghapus token Laravel Sanctum mereka.

Endpoint Logout di Backend:

  1. Route: Tambahkan route untuk logout di routes/api.php:

    <?php
    
    use AppHttpControllersAuthController;
    use IlluminateSupportFacadesRoute;
    
    Route::middleware('auth:sanctum')->post('/logout', [AuthController::class, 'logout']);
  2. Controller: Tambahkan method logout ke dalam AuthController.php:

    <?php
    
    namespace AppHttpControllers;
    
    use IlluminateHttpRequest;
    use IlluminateSupportFacadesAuth;
    
    class AuthController extends Controller
    {
        public function logout(Request $request)
        {
            $request->user()->currentAccessToken()->delete();
    
            return response()->json([
                'message' => 'Logged out'
            ]);
        }
    }

Implementasi Logout di Front-End:

// Contoh menggunakan fetch
const token = localStorage.getItem('authToken');

fetch('/api/logout', {
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
    }
})
.then(response => response.json())
.then(data => {
    // Hapus token dari localStorage
    localStorage.removeItem('authToken');
    // Redirect ke halaman login
    window.location.href = '/login';
})
.catch(error => {
    console.error('Error:', error);
});

Penjelasan:

  • Endpoint /logout di backend menghapus token Laravel Sanctum yang sedang digunakan.
  • Di front-end, token dihapus dari localStorage dan pengguna di-redirect ke halaman login.

8. CSRF Protection untuk SPA

Laravel Sanctum menyediakan perlindungan CSRF (Cross-Site Request Forgery) untuk SPA yang berjalan di domain yang sama dengan aplikasi Laravel.

Bagaimana Cara Kerjanya?

  1. Cookie XSRF-TOKEN: Laravel secara otomatis mengirimkan cookie XSRF-TOKEN ke aplikasi front-end. Cookie ini berisi nilai token CSRF.

  2. Header X-XSRF-TOKEN: Aplikasi front-end harus menyertakan nilai token CSRF dari cookie XSRF-TOKEN ke dalam header X-XSRF-TOKEN pada setiap permintaan AJAX yang menggunakan method PUT, POST, PATCH, atau DELETE.

Konfigurasi:

  • Pastikan route /sanctum/csrf-cookie didefinisikan di routes/web.php. Sanctum secara otomatis mendaftarkan route ini saat instalasi. Jika tidak ada, tambahkan:

    <?php
    
    use IlluminateSupportFacadesRoute;
    use LaravelSanctumHttpControllersCsrfCookieController;
    
    Route::get('/sanctum/csrf-cookie', [CsrfCookieController::class, 'show']);
  • Pastikan konfigurasi CORS kamu mengizinkan kredensial ( supports_credentials di config/cors.php harus true).

Implementasi di Front-End:

// Contoh menggunakan fetch
fetch('/sanctum/csrf-cookie')
    .then(response => {
        // Dapatkan token CSRF dari cookie (otomatis dilakukan oleh browser)

        // Lakukan permintaan API
        const token = localStorage.getItem('authToken');

        fetch('/api/profile', {
            method: 'GET', // Atau PUT, POST, PATCH, DELETE
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
                // Browser secara otomatis menambahkan header X-XSRF-TOKEN jika cookie XSRF-TOKEN ada
            }
        })
        .then(response => response.json())
        .then(data => {
            console.log('Profile:', data);
        })
        .catch(error => {
            console.error('Error:', error);
        });

    });

Catatan Penting:

  • Pastikan aplikasi front-end kamu mengambil token CSRF dari cookie XSRF-TOKEN dan menyertakannya dalam header X-XSRF-TOKEN pada setiap permintaan AJAX yang menggunakan method PUT, POST, PATCH, atau DELETE.
  • Jika menggunakan library seperti Axios, library tersebut biasanya secara otomatis menangani CSRF protection jika cookie XSRF-TOKEN ada.

9. Kustomisasi Token Sanctum: Nama Token dan Batas Kemampuan

Laravel Sanctum: Autentikasi API untuk Single Page Application juga memungkinkan kamu untuk mengkustomisasi token yang dihasilkan.

Nama Token:

Saat membuat token dengan createToken(), kamu bisa memberikan nama token yang deskriptif:

$token = $user->createToken('nama_token_spesifik')->plainTextToken;

Batas Kemampuan (Abilities):

Kamu bisa membatasi kemampuan token untuk mengakses endpoint tertentu dengan memberikan “abilities” saat membuat token.

Contoh:

$token = $user->createToken('token_dengan_batas_kemampuan', ['read-profile', 'create-post'])->plainTextToken;

Kemudian, di dalam middleware, kamu bisa memeriksa apakah token memiliki ability yang dibutuhkan:

Route::middleware(['auth:sanctum', 'ability:create-post'])->post('/posts', [PostController::class, 'store']);

Kode di atas akan mengizinkan akses ke endpoint /posts hanya jika token memiliki ability create-post.

10. Personal Access Tokens (PATs) untuk Aplikasi Lain

Sanctum juga mendukung Personal Access Tokens (PATs) untuk aplikasi yang bukan SPA. PATs biasanya digunakan untuk mengizinkan akses ke API oleh aplikasi pihak ketiga atau script otomatis.

Membuat PATs:

Kamu bisa membuat PATs melalui interface pengguna (misalnya, dengan membuat form di aplikasi Laravel) atau melalui Artisan console.

Melalui Interface Pengguna:

  • Buat form yang memungkinkan pengguna untuk membuat PAT baru.
  • Saat form disubmit, gunakan method createToken() pada model User untuk membuat PAT.
  • Simpan token (plain text) di database atau berikan kepada pengguna.

Melalui Artisan Console:

Kamu bisa membuat command Artisan untuk membuat PATs secara otomatis.

Penggunaan PATs:

PATs digunakan sama seperti token Laravel Sanctum yang dihasilkan saat login. Lampirkan token dalam header Authorization dengan skema Bearer.

11. Keamanan Tambahan untuk Laravel Sanctum

Selain fitur keamanan yang sudah ada, berikut adalah beberapa tips untuk meningkatkan keamanan Laravel Sanctum: Autentikasi API untuk Single Page Application kamu:

  • Rate Limiting: Implementasikan rate limiting untuk mencegah serangan brute-force pada endpoint login dan register. Kamu bisa menggunakan middleware throttle bawaan Laravel.

    Route::post('/login', [AuthController::class, 'login'])->middleware('throttle:5,1'); // Maksimal 5 request per menit
  • Input Validation: Selalu validasi input dari pengguna dengan ketat. Gunakan Validator Laravel untuk memvalidasi semua input sebelum diproses.

  • Sanitasi Input dan Output: Sanitasi input dan output untuk mencegah serangan XSS.

  • HTTPS: Pastikan aplikasi kamu menggunakan HTTPS untuk mengenkripsi semua komunikasi antara client dan server.

  • Regular Security Audits: Lakukan audit keamanan secara berkala untuk mengidentifikasi dan memperbaiki potensi celah keamanan.

12. Kesimpulan: Laravel Sanctum Pilihan Tepat untuk SPA?

Laravel Sanctum: Autentikasi API untuk Single Page Application adalah solusi yang sangat baik untuk mengamankan API Laravel kamu, terutama jika kamu sedang membangun SPA. Kesederhanaan, kemudahan penggunaan, dan fitur keamanan yang memadai menjadikannya pilihan yang tepat untuk banyak proyek. Namun, perlu diingat bahwa Sanctum mungkin tidak cocok untuk semua kasus penggunaan. Jika kamu membutuhkan fitur yang lebih canggih, seperti otorisasi berbasis peran yang kompleks atau dukungan untuk multiple grant types, OAuth2 mungkin menjadi pilihan yang lebih baik.

Semoga artikel ini membantumu memahami Laravel Sanctum dan mengimplementasikannya dengan sukses di proyek kamu! Selamat mencoba!