Kunci API dan OAuth menyelesaikan persoalan yang berbeda, dan pilihan di antara keduanya berangkat dari satu pertanyaan: adakah pihak ketiga dalam skema ini?
Kalau server Anda memanggil API milik orang lain atas nama sendiri, yang Anda butuhkan adalah kunci. Kalau aplikasi orang lain memanggil API Anda atas nama pengguna Anda, yang Anda butuhkan adalah OAuth. Ketika pihak ketiga tidak ada, OAuth menambah upacara tanpa menambah keamanan.
Berikut ini: di mana keduanya sebenarnya berbeda, apa yang berubah pada OAuth beberapa tahun terakhir, dan satu sifat kunci API yang nyaris tak pernah ditulis orang, padahal justru sifat itulah yang menentukan cara Anda hidup dengannya di lingkungan produksi.
Kunci API: satu untai yang berkata "program ini diizinkan"
Kunci adalah untai panjang dan acak yang Anda taruh di header permintaan:
X-Api-Key: lix_live_9f2c...
Server menemukan kunci itu, mengetahui milik akun mana, lalu meneruskan permintaannya. Seluruh mekanismenya sampai di situ.
Sifat-sifat yang mengikuti dari situ:
- kunci tidak kedaluwarsa sendiri. Ia hidup sampai seseorang mencabutnya;
- kunci menandai program, bukan orang. Siapa di tim yang melakukan panggilan biasanya tidak terlihat;
- kunci memberi segala yang bisa dilakukan akun, kecuali Anda membatasi haknya secara terpisah;
- kunci adalah satu-satunya rahasia. Kalau bocor, akses ikut bocor — tidak ada tahap perantara.
OAuth: protokol tentang persetujuan
OAuth 2.0 (RFC 6749) menjawab pertanyaan yang lain. Ia melibatkan empat pihak: pemilik data, aplikasi yang menginginkannya, server otorisasi, dan server yang menyimpan data.
Alurnya kurang lebih begini. Aplikasi mengirim orangnya ke halaman milik layanan itu sendiri. Di sana ia melihat siapa meminta apa, lalu menyetujuinya. Aplikasi menerima sebuah kode, menukarnya dengan token akses, dan memanggil API dengan token tersebut. Token berumur pendek — hitungan menit atau jam; ketika habis, aplikasi menukar token penyegar dengan yang baru.
Yang diperoleh dari sini, dan yang secara prinsip tak bisa diberikan kunci:
- kata sandi pengguna tidak pernah sampai ke aplikasi. Seluruh maknanya ada di sini;
- hak akses terpecah menjadi scope. "Membaca tautan" dan "menghapus tautan" adalah izin yang berbeda;
- persetujuan terlihat dan bisa ditarik. Pengguna melihat daftar aplikasi yang terhubung dan bisa memutus salah satunya tanpa menyentuh yang lain;
- token kedaluwarsa dengan sendirinya. Token akses yang dicuri sudah tak berguna satu jam kemudian.
Harganya adalah kerumitan: server otorisasi, pendaftaran aplikasi, layar persetujuan, penyimpanan dan perputaran token penyegar, serta penanganan kedaluwarsa pada setiap panggilan.
Perbandingan pada sumbu yang benar-benar menentukan
| Kunci API | OAuth 2.0 | |
|---|---|---|
| Siapa yang disahkan | programnya | pengguna yang diwakili program itu |
| Masa hidup | tanpa batas | token akses: menit sampai jam |
| Cara memutus akses | cabut kuncinya | tarik persetujuan atau tokennya |
| Luas hak | umumnya seluruh akun | scope per tindakan |
| Rahasia di tangan pihak ketiga | kuncinya sendiri | hanya token, tidak pernah kata sandi |
| Beban integrasi | satu header | server otorisasi dan seluruh siklusnya |
| Cocok untuk | server ke server, skrip, integrasi internal | aplikasi publik, lokapasar, "Masuk dengan…" |
Aturan memilih
Pertanyaannya bukan mana yang lebih aman secara abstrak, melainkan siapa mendelegasikan akses kepada siapa.
Server Anda → API orang lain, akun milik Anda
→ kunci API
Aplikasi orang lain → API Anda, akun milik pengguna Anda
→ OAuth
Tugas terjadwal, CI, integrasi backend
→ kunci API
Pengguna harus bisa melihat dan mencabut akses per aplikasi
→ OAuth
Menerima postback adalah kasus buku teks untuk kunci. Ketika server Anda memberi tahu server kami bahwa sebuah pesanan telah dibayar, dalam pertukaran itu tidak ada pengguna sama sekali, tidak ada yang bisa menyetujui, dan tidak ada tempat untuk menaruh layar persetujuan. Hal yang sama berlaku untuk webhook ke arah sebaliknya.
Apa yang berubah pada OAuth selagi Anda tak memperhatikan
Separuh tulisan tentang topik ini menggambarkan OAuth sebagaimana keadaannya pada 2015. Sejak itu ia menyempit cukup jauh.
Pada Januari 2025 IETF menerbitkan RFC 9700, Best Current Practice for OAuth 2.0 Security. Dokumen itu merangkum pengalaman bertahun-tahun menghadapi serangan nyata dan secara resmi menyatakan usang dua alur yang dulu dianggap dapat diterima:
- implicit grant — yang mengembalikan token langsung di bilah alamat;
- resource owner password credentials — di mana aplikasi meminta nama pengguna dan kata sandi secara langsung. Persis hal yang mestinya dihapus oleh OAuth.
Pada saat yang sama PKCE menjadi wajib untuk semua jenis klien, termasuk yang di sisi server, bukan hanya untuk seluler seperti sebelumnya.
Perlu diketahui terpisah: OAuth 2.1 sampai sekarang masih berupa rancangan, bukan RFC yang sudah terbit. Ia mengumpulkan perubahan yang sama ke dalam satu dokumen — PKCE wajib, pencocokan redirect URI secara persis, penghapusan implicit dan password grant, larangan token di query string. Mengutipnya sebagai standar yang berlaku masih terlalu dini.
Kesimpulan praktisnya: kalau panduan OAuth yang Anda baca menganjurkan alur implicit, panduan itu sudah usang.
Titik lemah kunci yang jarang disebut
Kunci tidak kedaluwarsa. Karena itu pencabutan menjadi satu-satunya tuas Anda. Dan di sinilah bagian yang tidak enak muncul: pencabutan biasanya tidak seketika.
Memeriksa kunci pada setiap permintaan berarti menyentuh basis data setiap kali. Pada API mana pun yang punya lalu lintas nyata, hasil itu disimpan dalam singgahan. Di tempat kami, pemetaan kunci ke akun bertahan lima menit dalam singgahan. Jadi antara menekan "cabut" dan akses benar-benar berhenti ada jeda sampai lima menit, dan selama itu kunci yang sudah bocor tetap bekerja.
Ini bukan kelalaian melainkan pertukaran: tanpa singgahan, setiap permintaan mengetuk basis data. Hampir semua API berbasis kunci membuat kesepakatan serupa, hanya saja jarang dikatakan terus terang. Ini penting karena dua hal. Pertama, pada kebocoran sungguhan, mencabut kunci adalah tindakan pertama, bukan terakhir: sesudahnya perlu diperiksa apa yang terjadi pada menit-menit itu. Kedua, inilah persoalan yang diserang dari ujung lain oleh token berumur pendek milik OAuth — mereka kedaluwarsa sendiri, tanpa melibatkan basis data.
Hidup dengan kunci tanpa terluka
Kalau kunci sudah memadai — dan pada sebagian besar integrasi memang memadai — batas minimalnya begini:
- Simpan hash-nya saja. Server membandingkan hash kunci yang dikirim dengan yang tersimpan; untai aslinya ditunjukkan kepada pengguna sekali saja, saat dibuat. Milik kami dicocokkan dengan SHA-256.
- Beri kunci sebuah awalan. Untai berbentuk
lix_live_...dikenali oleh pemindai rahasia di repositori maupun di jalur log. - Satu kunci untuk satu integrasi. Dengan begitu mencabut satu tidak menjatuhkan sisanya, dan dari catatan terlihat persis apa yang bocor.
- Jangan pernah menaruh kunci di query string. Alamat mengendap di log server web, di header
Referer, dan di riwayat peramban. Header saja. - Putar menurut jadwal, bukan menurut insiden. Kunci yang pernah diputar sekali akan cepat diputar lagi. Kunci yang belum pernah diganti akhirnya ketahuan tertanam di empat tempat.
- Perhatikan waktu pemakaian terakhir. Kunci yang tak tersentuh setengah tahun bukan cadangan, melainkan pintu yang dibiarkan terbuka.
Yang kami pakai
API Lix.li memakai kunci: header X-Api-Key, kunci dibuat dan dicabut di panel, pencocokan lewat hash, batas permintaan tersendiri untuk tiap kunci. Tidak ada OAuth, dan itu disengaja: skenario API-nya ada di sisi server — membuat tautan, menarik statistik, menerima konversi — dan tak satu pun melibatkan pihak ketiga yang harus disetujui oleh pengguna.
Seandainya muncul kebutuhan memasukkan aplikasi orang lain ke akun pelanggan kami, kunci tidak lagi memadai: pengguna harus bisa melihat siapa telah diberi izin apa, dan bisa memutus satu aplikasi tanpa merusak yang lain. Di titik itulah OAuth berhenti menjadi upacara dan menjadi satu-satunya pilihan yang jujur.
Panggilannya sendiri tertulis di dokumentasi API, termasuk kode respons yang kembali untuk kunci tidak sah dan untuk kunci yang sudah dicabut. Pada topik bertetangga, penjelasan mengapa pengalihan 301 mengubah POST menjadi GET layak dibaca siapa pun yang sedang menyiapkan penerimaan postback: permintaan yang sudah terautentikasi bisa tiba dengan metode yang berbeda dari saat ia berangkat.