1. Apa Itu Otentikasi API?
Bayangkan kamu memiliki apartemen. Di depan pintu ada satpam. Setiap tamu yang datang harus membuktikan identitasnya sebelum diizinkan masuk. Satpam bisa meminta:
- Kartu tanda pengenal (username & password)
- Stiker khusus yang diberikan manajemen gedung (token)
- Gelang pintar yang bisa diverifikasi otomatis (JWT)
Otentikasi API bekerja persis seperti itu — ini adalah mekanisme yang memastikan bahwa hanya pihak yang berwenang yang bisa mengakses sumber daya di sebuah API.
Secara teknis, otentikasi API adalah proses di mana klien (aplikasi yang memanggil API) membuktikan identitasnya kepada server (API yang dipanggil) sebelum server memproses permintaan tersebut.
Otentikasi vs Otorisasi — Jangan Tertukar!
Dua istilah ini sering membingungkan. Berikut perbedaan sederhananya:
| Konsep | Pertanyaan yang Dijawab | Contoh |
|---|---|---|
| Otentikasi | Siapa kamu? | "Kamu adalah pengguna dengan ID 42" |
| Otorisasi | Apa yang boleh kamu lakukan? | "Pengguna 42 boleh membaca data, tapi tidak boleh menghapus" |
Otentikasi selalu datang lebih dulu. Kamu tidak bisa mengotorisasi seseorang yang belum dikenali identitasnya.
2. Mengapa Otentikasi Itu Penting?
Tanpa otentikasi, API kamu ibarat toko yang semua pintu dan lacinya terbuka — siapa pun bisa masuk, mengambil data, memodifikasi catatan, atau bahkan menghapus semua data.
Konsekuensi nyata jika API tidak memiliki otentikasi yang baik:
- Kebocoran data pengguna — data pribadi, nomor kartu kredit, alamat email tersebar bebas
- Penyalahgunaan sumber daya — bot bisa membebani server tanpa batas (DDoS)
- Manipulasi data — pihak tidak bertanggung jawab bisa mengubah atau menghapus data penting
- Kerugian finansial — API layanan berbayar bisa disalahgunakan tanpa batas
Itulah mengapa memahami dan mengimplementasikan otentikasi yang tepat adalah fondasi utama keamanan aplikasi modern.
3. Basic Authentication (Otentikasi Dasar)
Apa Itu?
Basic Authentication adalah metode otentikasi paling sederhana dan paling tua di dunia HTTP. Caranya: klien mengirimkan username dan password setiap kali membuat permintaan ke API.
Analoginya seperti setiap kali masuk kantor, kamu harus menyebutkan nama lengkap dan nomor KTP kamu kepada satpam — berulang-ulang di setiap kunjungan.
Cara Kerjanya
Berikut alur Basic Authentication:
1. Klien ingin mengakses resource API
2. Server merespons dengan 401 Unauthorized
3. Klien mengirim header: Authorization: Basic <kredensial>
4. Server memverifikasi kredensial
5. Jika valid → akses diberikan (200 OK)
Jika tidak valid → akses ditolak (401 Unauthorized)
Bagaimana kredensial dibentuk?
Username dan password digabung dengan titik dua, lalu di-encode menggunakan Base64:
username:password → dXNlcm5hbWU6cGFzc3dvcmQ=
Kemudian dikirim dalam HTTP header:
GET /api/data HTTP/1.1
Host: api.contoh.com
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
Contoh Kode
Menggunakan cURL:
curl -u username:password https://api.contoh.com/data
# Atau secara manual dengan header:
curl -H "Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=" https://api.contoh.com/data
Menggunakan JavaScript (Fetch API):
const username = 'admin';
const password = 'rahasia123';
const credentials = btoa(`${username}:${password}`);
const response = await fetch('https://api.contoh.com/data', {
headers: {
'Authorization': `Basic ${credentials}`
}
});
const data = await response.json();
Menggunakan Python (requests):
import requests
response = requests.get(
'https://api.contoh.com/data',
auth=('admin', 'rahasia123') # Library otomatis encode ke Base64
)
print(response.json())
⚠️ Penting: Base64 BUKAN Enkripsi!
Banyak orang salah paham bahwa Base64 memberikan keamanan. Tidak! Base64 hanyalah format encoding — siapa pun bisa mendekode-nya dalam hitungan detik:
dXNlcm5hbWU6cGFzc3dvcmQ= → username:password
Ini artinya Basic Auth HARUS selalu digunakan bersama HTTPS agar kredensial tidak bisa dicegat di tengah jalan.
Kelebihan & Kekurangan
| ✅ Kelebihan | ❌ Kekurangan |
|---|---|
| Sangat sederhana diimplementasikan | Password dikirim di setiap request |
| Didukung hampir semua klien HTTP | Tidak ada mekanisme "logout" yang nyata |
| Cocok untuk testing dan internal tools | Harus selalu pakai HTTPS (tidak opsional) |
| Tidak perlu state di server | Rentan jika terjadi kebocoran (semua request terekspos) |
4. Bearer Token
Apa Itu?
Bearer Token adalah string unik yang diberikan server kepada klien setelah login berhasil. Setelah mendapat token ini, klien menggunakannya untuk mengakses resource — tanpa perlu mengirim username/password lagi.
Analoginya seperti tiket konser. Kamu membeli tiket di loket (login), lalu menunjukkan tiket itu di pintu masuk (setiap request). Siapa pun yang memegang tiket itu bisa masuk — itulah mengapa disebut "Bearer" (pemegang).
Cara Kerjanya
1. Klien mengirim username & password ke endpoint login
2. Server memverifikasi → menghasilkan token unik → menyimpannya di database
3. Server mengirim token kepada klien
4. Klien menyimpan token (di memori, localStorage, atau cookie)
5. Setiap request berikutnya, klien menyertakan token di header
6. Server mencari token di database → jika ada dan valid → izinkan akses
Token dikirim dalam HTTP header seperti ini:
GET /api/profil HTTP/1.1
Host: api.contoh.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Contoh Kode
Langkah 1 — Login dan dapatkan token:
// Login untuk mendapatkan token
const loginResponse = await fetch('https://api.contoh.com/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'admin',
password: 'rahasia123'
})
});
const { token } = await loginResponse.json();
// token = "abc123xyz789..."
Langkah 2 — Gunakan token untuk request berikutnya:
const response = await fetch('https://api.contoh.com/profil', {
headers: {
'Authorization': `Bearer ${token}`
}
});
const profil = await response.json();
Implementasi server sederhana (Node.js/Express):
// Middleware validasi Bearer Token
function autentikasiToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Token tidak ditemukan' });
}
const pengguna = database.cariTokenValid(token);
if (!pengguna) {
return res.status(403).json({ error: 'Token tidak valid' });
}
req.pengguna = pengguna;
next();
}
app.get('/api/profil', autentikasiToken, (req, res) => {
res.json({ data: req.pengguna });
});
Masalah dengan Bearer Token Sederhana
Token sederhana yang disimpan di database punya satu kelemahan besar: server harus melakukan query ke database setiap kali ada request. Bayangkan API dengan jutaan pengguna aktif — ini menjadi bottleneck yang sangat berat.
Di sinilah JWT hadir sebagai solusi.
Kelebihan & Kekurangan
| ✅ Kelebihan | ❌ Kekurangan |
|---|---|
| Lebih aman dari Basic Auth | Server harus query DB setiap request (jika stateful) |
| Password tidak dikirim berulang kali | Butuh penyimpanan token yang aman di klien |
| Mendukung "logout" dengan menghapus token | Token bisa dicuri (man-in-the-middle) |
| Mudah dibatasi (revoke token kapan saja) | Perlu manajemen siklus hidup token |
5. JWT (JSON Web Token)
Apa Itu?
JWT (JSON Web Token) adalah standar terbuka (RFC 7519) untuk membuat token yang bisa membawa informasi sekaligus memverifikasi dirinya sendiri — tanpa perlu database sama sekali.
Analoginya seperti paspor. Paspor berisi identitasmu, diterbitkan oleh otoritas berwenang (pemerintah), dan bisa diverifikasi kapan saja hanya dengan memeriksa stempel dan tanda tangan di dalamnya — tanpa perlu menelepon kantor imigrasi setiap saat.
Struktur JWT
JWT terdiri dari tiga bagian yang dipisahkan tanda titik (.):
xxxxx.yyyyy.zzzzz
│ │ │
Header Payload Signature
Contoh JWT nyata:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Bagian 1: Header
Header berisi dua informasi: jenis token dan algoritma enkripsi yang digunakan.
{
"alg": "HS256", // Algoritma: HMAC SHA-256
"typ": "JWT" // Tipe token
}
Kemudian di-encode Base64URL:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Bagian 2: Payload (Claims)
Payload berisi klaim — informasi tentang pengguna dan metadata token. Ada tiga jenis klaim:
Registered Claims (Standar):
{
"iss": "https://auth.contoh.com", // Issuer — siapa yang menerbitkan token
"sub": "user_42", // Subject — siapa pemilik token
"aud": "https://api.contoh.com", // Audience — untuk siapa token ini
"exp": 1700000000, // Expiration — kapan token kedaluwarsa
"iat": 1699996400, // Issued At — kapan token dibuat
"nbf": 1699996400 // Not Before — token belum valid sebelum waktu ini
}
Custom Claims (Sesuka kita):
{
"sub": "user_42",
"exp": 1700000000,
"nama": "Budi Santoso",
"role": "admin",
"email": "[email protected]",
"izin": ["baca", "tulis", "hapus"]
}
⚠️ Ingat: Payload tidak dienkripsi, hanya di-encode. Jangan menyimpan informasi sensitif (password, data kartu kredit) di sini!
Bagian 3: Signature (Tanda Tangan)
Inilah bagian terpenting JWT. Tanda tangan dibuat dengan cara:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret_key
)
Contoh dalam pseudocode:
data = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzQyIn0"
kunci_rahasia = "kunci-super-rahasia-server-kita"
tanda_tangan = HMAC_SHA256(data, kunci_rahasia)
Tanda tangan ini membuktikan bahwa token belum dimodifikasi. Jika seseorang mengubah payload (misalnya mengubah role dari "user" menjadi "admin"), tanda tangan tidak akan cocok lagi, dan server akan menolaknya.
Cara Kerja JWT End-to-End
┌─────────┐ ┌─────────┐
│ Klien │ │ Server │
└────┬────┘ └────┬────┘
│ │
│ POST /login {username, password} │
│ ────────────────────────────────────────►│
│ │
│ Verifikasi kredensial │
│ Buat JWT dengan payload │
│ Tandatangani dengan kunci rahasia│
│ │
│ 200 OK { token: "eyJ..." } │
│ ◄────────────────────────────────────────│
│ │
│ GET /api/data │
│ Authorization: Bearer eyJ... │
│ ────────────────────────────────────────►│
│ │
│ Decode header & payload │
│ Hitung ulang signature │
│ Cocokkan dengan signature token │
│ Cek exp, iss, aud │
│ Tidak perlu query database! ✓ │
│ │
│ 200 OK { data: ... } │
│ ◄────────────────────────────────────────│
Contoh Kode Lengkap
Membuat dan memverifikasi JWT (Node.js dengan jsonwebtoken):
const jwt = require('jsonwebtoken');
const KUNCI_RAHASIA = process.env.JWT_SECRET;
// ===== MEMBUAT TOKEN (saat login) =====
function buatToken(dataPengguna) {
const payload = {
sub: dataPengguna.id,
nama: dataPengguna.nama,
email: dataPengguna.email,
role: dataPengguna.role
};
const token = jwt.sign(payload, KUNCI_RAHASIA, {
expiresIn: '1h',
issuer: 'api.contoh.com',
audience: 'app.contoh.com'
});
return token;
}
// ===== MEMVERIFIKASI TOKEN (middleware) =====
function verifikasiJWT(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Token tidak ditemukan' });
}
try {
const decoded = jwt.verify(token, KUNCI_RAHASIA, {
issuer: 'api.contoh.com',
audience: 'app.contoh.com'
});
req.pengguna = decoded;
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Token kedaluwarsa, silakan login ulang' });
}
return res.status(403).json({ error: 'Token tidak valid' });
}
}
// ===== ENDPOINT LOGIN =====
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const pengguna = await db.cariPengguna(username);
if (!pengguna || !bcrypt.compareSync(password, pengguna.passwordHash)) {
return res.status(401).json({ error: 'Kredensial salah' });
}
const token = buatToken(pengguna);
res.json({ token });
});
// ===== ENDPOINT YANG DILINDUNGI =====
app.get('/api/profil', verifikasiJWT, (req, res) => {
res.json({ pengguna: req.pengguna });
});
Menggunakan JWT di klien (JavaScript):
// Simpan token dengan aman
localStorage.setItem('jwt_token', token);
// Fungsi helper untuk request berautentikasi
async function apiFetch(url, options = {}) {
const token = localStorage.getItem('jwt_token');
return fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
}
// Penggunaan
const response = await apiFetch('/api/profil');
const profil = await response.json();
Konsep Penting: Access Token & Refresh Token
JWT biasanya memiliki masa kedaluwarsa yang singkat (15 menit – 1 jam) demi keamanan. Tapi tentu tidak nyaman jika pengguna harus login setiap jam. Solusinya adalah pasangan Access Token dan Refresh Token:
Access Token:
- Masa berlaku singkat (15–60 menit)
- Dikirim di setiap request API
- Jika dicuri, kerugian terbatas (hanya berlaku sebentar)
Refresh Token:
- Masa berlaku panjang (7–30 hari)
- Disimpan lebih aman (httpOnly cookie)
- HANYA digunakan untuk mendapatkan Access Token baru
- Jika dicuri, bisa direvoke di server
Alur Refresh Token:
1. Access Token kedaluwarsa → API mengembalikan 401
2. Klien mengirim Refresh Token ke /auth/refresh
3. Server memverifikasi Refresh Token
4. Server mengeluarkan Access Token baru
5. Klien melanjutkan request dengan token baru
Kelebihan & Kekurangan JWT
| ✅ Kelebihan | ❌ Kekurangan |
|---|---|
| Stateless — tidak perlu query database | Sulit di-revoke sebelum kedaluwarsa |
| Skalabel untuk sistem terdistribusi | Ukuran token lebih besar dari token sederhana |
| Membawa data (tidak perlu lookup tambahan) | Payload bisa didekode (jangan simpan data sensitif) |
| Bisa digunakan lintas domain (Cross-Origin) | Implementasi yang salah bisa berbahaya |
| Standar terbuka, banyak library tersedia | Butuh pemahaman lebih untuk menggunakan dengan benar |
6. Perbandingan Ketiga Metode
Tabel Perbandingan Lengkap
| Aspek | Basic Auth | Bearer Token | JWT |
|---|---|---|---|
| Cara kirim kredensial | Setiap request | Hanya saat login | Hanya saat login |
| Apa yang dikirim per request | Username:Password (Base64) | Token opaque (string acak) | Token berisi data (self-contained) |
| Butuh query DB per request? | Ya (verifikasi password) | Ya (cari token) | Tidak |
| Bisa di-revoke? | Ya (ganti password) | Ya (hapus dari DB) | Sulit (harus tunggu exp) |
| Membawa data pengguna? | Tidak | Tidak | Ya |
| Kompleksitas implementasi | Sangat mudah | Sedang | Lebih kompleks |
| Cocok untuk microservices? | Tidak | Kurang | Ya |
| Keamanan (dengan HTTPS) | Cukup | Baik | Baik |
Ilustrasi Visual Alur Masing-masing
BASIC AUTH — Setiap request membawa password
─────────────────────────────────────────────
Klien ──[username:pass]──► Server ──[cek DB]──► Response
Klien ──[username:pass]──► Server ──[cek DB]──► Response
Klien ──[username:pass]──► Server ──[cek DB]──► Response
BEARER TOKEN — Login sekali, gunakan token
─────────────────────────────────────────────
Klien ──[login]──────────► Server ──[buat token]──► Simpan ke DB
Klien ◄──────────────────── [token: "abc123"]
Klien ──[token: "abc123"]──► Server ──[cek DB]──► Response
Klien ──[token: "abc123"]──► Server ──[cek DB]──► Response
JWT — Login sekali, verifikasi mandiri
─────────────────────────────────────────────
Klien ──[login]──────────► Server ──[buat JWT]──► (tidak disimpan)
Klien ◄──────────────────── [JWT: "eyJ..."]
Klien ──[JWT: "eyJ..."]──► Server ──[verifikasi signature]──► Response
Klien ──[JWT: "eyJ..."]──► Server ──[verifikasi signature]──► Response
(tanpa query DB!)
7. Kapan Menggunakan Masing-masing?
Gunakan Basic Authentication jika:
- Membangun internal tool atau script otomasi yang tidak berhubungan langsung dengan pengguna akhir
- Proyek sangat kecil atau prototype yang akan dibuang nanti
- Membutuhkan implementasi tercepat untuk testing API
- Tidak ada kebutuhan untuk "logout" atau sesi yang bisa dikelola
Contoh nyata: Mengakses GitHub API dari script CI/CD internal, atau mengakses API sederhana di jaringan lokal yang sudah terlindungi.
Gunakan Bearer Token (Opaque) jika:
- Kebutuhan untuk merevoke akses secara instan sangat penting (misalnya sistem keuangan, layanan sensitif)
- Aplikasi tidak terdistribusi — semua layanan mengakses satu database yang sama
- Ingin mengetahui kapan dan dari mana setiap token digunakan (audit trail per token)
- Sistem yang tidak membutuhkan skalabilitas tinggi
Contoh nyata: Aplikasi perbankan mobile yang harus bisa mencabut akses token secara instan jika terdeteksi aktivitas mencurigakan.
Gunakan JWT jika:
- Membangun microservices atau sistem terdistribusi di mana banyak layanan perlu memverifikasi identitas
- Butuh skalabilitas tinggi — tidak mau query database untuk setiap request
- Perlu menyematkan informasi konteks (role, izin, data profil) langsung di token agar layanan downstream tidak perlu lookup tambahan
- Menggunakan arsitektur Single Sign-On (SSO) atau OAuth 2.0
Contoh nyata: Platform e-commerce dengan layanan terpisah (katalog, keranjang, pembayaran, notifikasi) yang masing-masing perlu tahu siapa pengguna dan apa izinnya.
8. Praktik Terbaik Keamanan
Untuk Semua Metode
1. Selalu Gunakan HTTPS
Tanpa HTTPS, semua metode otentikasi di atas bisa dicuri dengan mudah melalui man-in-the-middle attack. HTTPS bukan pilihan — ini wajib.
2. Hindari Menyimpan Kredensial Sensitif di Log
// ❌ JANGAN lakukan ini
console.log('Request headers:', req.headers); // Akan mencetak token ke log!
// ✅ Lakukan ini
console.log('Request dari pengguna:', req.pengguna.id);
3. Tangani Error dengan Pesan yang Tidak Informatif untuk Penyerang
// ❌ Terlalu informatif untuk penyerang
res.status(401).json({ error: 'Password salah untuk user [email protected]' });
// ✅ Lebih aman
res.status(401).json({ error: 'Kredensial tidak valid' });
Khusus Basic Auth
- Wajib HTTPS — tidak ada pengecualian
- Gunakan hanya untuk komunikasi server-ke-server yang terkontrol
- Implementasikan rate limiting untuk mencegah brute-force
Khusus Bearer Token
- Simpan token di httpOnly cookie (lebih aman dari localStorage yang rentan XSS)
- Implementasikan token rotation — perbarui token secara berkala
- Simpan hash token di database, bukan plaintext
- Implementasikan batas waktu sesi yang tidak aktif
Khusus JWT
Gunakan algoritma yang benar:
// ❌ JANGAN gunakan "none" sebagai algoritma
jwt.sign(payload, '', { algorithm: 'none' }); // Celah keamanan besar!
// ❌ Hindari HS256 untuk sistem besar (kunci simetris)
jwt.sign(payload, secretKey, { algorithm: 'HS256' });
// ✅ Gunakan RS256 untuk sistem yang butuh kunci publik/privat
jwt.sign(payload, privateKey, { algorithm: 'RS256' });
Jangan simpan data sensitif di payload:
// ❌ JANGAN
const payload = {
sub: userId,
password: userPassword, // JANGAN!
kartuKredit: '4111...', // JANGAN!
};
// ✅ Lakukan
const payload = {
sub: userId,
email: userEmail, // OK
role: 'admin', // OK
exp: Math.floor(Date.now() / 1000) + 3600
};
Validasi semua klaim dengan ketat:
jwt.verify(token, kunci, {
algorithms: ['RS256'], // Tentukan algoritma secara eksplisit
issuer: 'auth.contoh.com', // Verifikasi issuer
audience: 'api.contoh.com', // Verifikasi audience
clockTolerance: 10 // Toleransi 10 detik untuk perbedaan jam server
});
9. Kesimpulan
Setelah membaca panduan ini, kamu sudah memahami tiga metode otentikasi API yang paling umum digunakan:
Basic Authentication adalah yang paling sederhana — cocok untuk penggunaan internal dan testing, tapi kredensial dikirim setiap request sehingga risikonya lebih tinggi.
Bearer Token menawarkan keamanan yang lebih baik karena password hanya dikirim saat login. Token bisa direvoke kapan saja, namun membutuhkan query database di setiap request.
JWT adalah solusi paling modern dan skalabel — token "berbicara sendiri" sehingga tidak butuh database per request. Ideal untuk microservices dan sistem besar, namun butuh kehati-hatian dalam implementasi.
Rekap Singkat
Butuh yang paling simple? → Basic Auth (dengan HTTPS!)
Butuh bisa revoke kapan saja? → Bearer Token
Butuh skalabilitas & microservice? → JWT
Apa yang Harus Dipelajari Selanjutnya?
Setelah memahami dasar-dasar ini, topik berikutnya yang relevan untuk dikuasai adalah:
- OAuth 2.0 — framework otorisasi standar yang biasanya menggunakan JWT di dalamnya
- OpenID Connect (OIDC) — lapisan identitas di atas OAuth 2.0
- API Key — metode sederhana untuk mengidentifikasi aplikasi (bukan pengguna)
- mTLS (Mutual TLS) — otentikasi dua arah untuk komunikasi service-to-service
- PKCE — ekstensi OAuth 2.0 untuk aplikasi mobile dan SPA
Artikel ini ditulis untuk membantu developer memahami konsep otentikasi API dari dasar hingga siap implementasi. Selalu ikuti praktik terbaik keamanan dan sesuaikan metode yang dipilih dengan kebutuhan aplikasimu.