Saat melakukan integrasi REST API di React Native, pengembang sering kali menemui kendala di mana permintaan HTTP yang memerlukan autentikasi tiba-tiba menghasilkan respons 401 Unauthorized, meskipun token JWT atau kredensial yang dikirimkan sudah valid. Masalah ini umumnya dipicu oleh pengalihan HTTP (status kode 301, 302, 307, atau 308) yang memicu mekanisme keamanan native untuk menghapus header sensitif.

Akar Masalah: Native Credential Stripping

React Native tidak mengimplementasikan networking stack mandiri di JavaScript runtime (Hermes atau JavaScriptCore). Eksekusi fetch() atau XMLHttpRequest didelegasikan langsung ke lapisan native:

  • Android: Menggunakan pustaka OkHttp.
  • iOS: Menggunakan NSURLSession.

Berdasarkan spesifikasi RFC 7235 (diperbarui dalam RFC 9110) serta standar implementasi keamanan browser dan HTTP client modern, klien dilarang meneruskan kredensial autentikasi (seperti header Authorization atau cookie sesi) ke origin yang berbeda selama proses redirect. Tujuannya adalah mencegah kebocoran token otorisasi ke server pihak ketiga.

Masalah muncul ketika pengalihan terjadi pada infrastruktur internal yang secara teknis dianggap "berbeda origin" oleh native layer, misalnya:

  • Pengalihan protokol: dari http:// ke https://.
  • Pengalihan subdomain: dari api.example.com ke auth.example.com atau eu-api.example.com.
  • Pengalihan port: dari example.com:8080 ke example.com:443.

Ketika kondisi ini terpenuhi, OkHttp dan NSURLSession secara otomatis melakukan header stripping pada Authorization sebelum mengeksekusi request berikutnya ke URL target.

Gejala Misterius 401 Unauthorized di Sisi JavaScript

Pelacakan bug ini sering kali membingungkan karena JavaScript networking polyfill di React Native membungkus seluruh siklus redirect secara transparan jika opsi default (redirect: 'follow') aktif.

Ketika request dieksekusi:

  1. Klien mengirim GET /api/v1/profile dengan Authorization: Bearer <token>.
  2. Server merespons dengan status 301 Moved Permanently dan header Location: https://api.example.com/api/v1/profile/ (hanya perbedaan trailing slash).
  3. Native stack mengikuti header Location, namun menghapus header Authorization karena mendeteksi variasi rute atau canonical URL.
  4. Server target menerima request kedua tanpa kredensial dan mengembalikan status 401 Unauthorized.

Di konsol JavaScript React Native, developer hanya melihat endpoint awal menghasilkan respons 401, sedangkan status 301 dan hop perantara tidak tercatat dalam log aplikasi tanpa bantuan network proxy eksternal.

Solusi 1: Eliminasi Redirect pada Rute Berautentikasi (Pendekatan Terbaik)

Solusi paling efisien dan aman adalah tidak membiarkan server mengirim respons pengalihan (3xx) pada endpoint yang memerlukan autentikasi. Frontend tidak perlu menulis kode tambahan jika backend dikonfigurasi dengan benar.

Penyebab umum redirect yang tidak perlu:

  • Trailing slash mismatch: Server mengalihkan /endpoint ke /endpoint/. Selaraskan URL pada client atau matikan strict-routing di backend router (misalnya Express, FastAPI, atau Laravel).
  • HTTP ke HTTPS: Pastikan base URL pada mobile client selalu mengarah ke skema HTTPS secara eksplisit.
  • Reverse proxy / API Gateway: Jangan biarkan reverse proxy (Nginx, Cloudflare, Traefik) melakukan rewrite URL dengan status pengalihan 301/302 pada endpoint API internal.
Perhatian: Pengalihan metode POST/PUT dengan status 301 atau 302 dapat mengubah HTTP method menjadi GET secara otomatis menurut konvensi historis HTTP. Ini berisiko merusak payload data selain menghilangkan header autentikasi.

Solusi 2: Penanganan Redirect Manual dan Token Re-attaching

Jika modifikasi backend tidak memungkinkan (misalnya mengakses gateway pihak ketiga), pengalihan harus ditangani secara terkontrol di lapisan client. React Native mendukung penanganan respons redirect manual melalui inspeksi respons awal sebelum menembak URL target.

Lakukan validasi origin target terlebih dahulu sebelum menyematkan ulang header autentikasi demi mencegah token leak.

// client.js
const ALLOWED_ORIGINS = [
  'https://api.example.com',
  'https://internal-api.example.com'
];

function isSafeOrigin(targetUrl) {
  try {
    const parsed = new URL(targetUrl);
    return ALLOWED_ORIGINS.includes(parsed.origin);
  } catch {
    return false;
  }
}

export async function secureFetch(url, options = {}) {
  const headers = { ...options.headers };

  // Langkah 1: Minta manual redirect agar hop perantara tidak dieksekusi otomatis
  const response = await fetch(url, {
    ...options,
    headers,
    redirect: 'manual'
  });

  // Periksa apakah server meminta redirect (301, 302, 307, 308)
  // Catatan: Pada tipe 'opaqueredirect', status code mungkin 0 tergantung platform
  const isRedirect = response.status >= 300 && response.status < 400;
  const redirectLocation = response.headers.get('Location');

  if (isRedirect && redirectLocation) {
    const targetUrl = new URL(redirectLocation, url).toString();

    // Verifikasi keamanan target sebelum menempelkan kredensial
    if (!isSafeOrigin(targetUrl)) {
      throw new Error(`Potensi kebocoran kredensial: Pengalihan ke origin tidak tepercaya (${targetUrl})`);
    }

    // Eksekusi ulang request ke target baru dengan header lengkap
    return fetch(targetUrl, {
      ...options,
      headers
    });
  }

  return response;
}

Metode Debugging Native Network Hop

Untuk memastikan apakah request Anda mengalami drop header akibat redirect, jangan mengandalkan console.log() bawaan React Native. Gunakan alat inspeksi network langsung di level OS:

  • Proxyman / Charles Proxy: Pasang sertifikat SSL di emulator/device untuk melihat seluruh aliran request termasuk status kode 3xx dan hilangnya header Authorization pada request kedua.
  • Flipper (Network Plugin): Pantau traffic network real-time yang mencatat setiap event dari native OkHttpClient dan NSURLSession.
  • Logcat (Android): Tambahkan logging interceptor pada native code jika membangun modul native kustom untuk memverifikasi chain request yang dijalankan oleh OkHttp.

Ringkasan Tindakan

Langkah penanganan yang disarankan:

  1. Periksa base URL dan endpoint path. Hilangkan perbedaan trailing slash dan skema protokol.
  2. Konfigurasi web server/proxy agar langsung mengembalikan data tanpa hop pengalihan.
  3. Gunakan kontrol manual dan origin verification jika redirect antar-domain internal memang merupakan arsitektur yang tidak dapat dihindari.